From b014812705fc80bff0a5c120dfcef88f349816dc Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 24 Aug 2018 12:15:48 +0200 Subject: BASELINE: Update Chromium to 68.0.3440.125 Change-Id: I23f19369e01f688e496f5bf179abb521ad73874f Reviewed-by: Allan Sandfeld Jensen --- chromium/content/BUILD.gn | 4 +- chromium/content/DEPS | 6 +- chromium/content/OWNERS | 6 + chromium/content/app/BUILD.gn | 11 +- chromium/content/app/content_main_runner.cc | 985 -------- chromium/content/app/content_main_runner_impl.cc | 1010 +++++++++ chromium/content/app/content_main_runner_impl.h | 89 + .../app/content_service_manager_main_delegate.cc | 15 +- .../app/content_service_manager_main_delegate.h | 10 +- chromium/content/app/strings/content_strings.grd | 128 +- .../strings/translations/content_strings_am.xtb | 44 +- .../strings/translations/content_strings_ar.xtb | 44 +- .../strings/translations/content_strings_bg.xtb | 44 +- .../strings/translations/content_strings_bn.xtb | 46 +- .../strings/translations/content_strings_ca.xtb | 44 +- .../strings/translations/content_strings_cs.xtb | 44 +- .../strings/translations/content_strings_da.xtb | 44 +- .../strings/translations/content_strings_de.xtb | 44 +- .../strings/translations/content_strings_el.xtb | 44 +- .../strings/translations/content_strings_en-GB.xtb | 44 +- .../translations/content_strings_es-419.xtb | 44 +- .../strings/translations/content_strings_es.xtb | 44 +- .../strings/translations/content_strings_et.xtb | 44 +- .../strings/translations/content_strings_fa.xtb | 52 +- .../strings/translations/content_strings_fi.xtb | 44 +- .../strings/translations/content_strings_fil.xtb | 44 +- .../strings/translations/content_strings_fr.xtb | 44 +- .../strings/translations/content_strings_gu.xtb | 44 +- .../strings/translations/content_strings_hi.xtb | 54 +- .../strings/translations/content_strings_hr.xtb | 44 +- .../strings/translations/content_strings_hu.xtb | 44 +- .../strings/translations/content_strings_id.xtb | 44 +- .../strings/translations/content_strings_it.xtb | 44 +- .../strings/translations/content_strings_iw.xtb | 44 +- .../strings/translations/content_strings_ja.xtb | 44 +- .../strings/translations/content_strings_kn.xtb | 44 +- .../strings/translations/content_strings_ko.xtb | 44 +- .../strings/translations/content_strings_lt.xtb | 44 +- .../strings/translations/content_strings_lv.xtb | 44 +- .../strings/translations/content_strings_ml.xtb | 44 +- .../strings/translations/content_strings_mr.xtb | 44 +- .../strings/translations/content_strings_ms.xtb | 44 +- .../strings/translations/content_strings_nl.xtb | 44 +- .../strings/translations/content_strings_no.xtb | 44 +- .../strings/translations/content_strings_pl.xtb | 44 +- .../strings/translations/content_strings_pt-BR.xtb | 44 +- .../strings/translations/content_strings_pt-PT.xtb | 46 +- .../strings/translations/content_strings_ro.xtb | 44 +- .../strings/translations/content_strings_ru.xtb | 44 +- .../strings/translations/content_strings_sk.xtb | 44 +- .../strings/translations/content_strings_sl.xtb | 44 +- .../strings/translations/content_strings_sr.xtb | 44 +- .../strings/translations/content_strings_sv.xtb | 44 +- .../strings/translations/content_strings_sw.xtb | 44 +- .../strings/translations/content_strings_ta.xtb | 44 +- .../strings/translations/content_strings_te.xtb | 44 +- .../strings/translations/content_strings_th.xtb | 46 +- .../strings/translations/content_strings_tr.xtb | 46 +- .../strings/translations/content_strings_uk.xtb | 44 +- .../strings/translations/content_strings_vi.xtb | 44 +- .../strings/translations/content_strings_zh-CN.xtb | 44 +- .../strings/translations/content_strings_zh-TW.xtb | 44 +- chromium/content/browser/BUILD.gn | 252 ++- chromium/content/browser/DEPS | 17 +- chromium/content/browser/OWNERS | 2 +- .../accessibility_action_browsertest.cc | 8 +- .../accessibility_tree_formatter_blink.cc | 4 + .../accessibility_tree_formatter_utils_win.cc | 4 +- .../accessibility/accessibility_win_browsertest.cc | 534 +++-- .../browser/accessibility/browser_accessibility.cc | 80 + .../browser/accessibility/browser_accessibility.h | 7 + .../accessibility/browser_accessibility_android.cc | 156 +- .../accessibility/browser_accessibility_cocoa.mm | 92 +- .../accessibility/browser_accessibility_com_win.cc | 234 +- .../accessibility/browser_accessibility_com_win.h | 2 + .../accessibility/browser_accessibility_manager.cc | 22 + .../accessibility/browser_accessibility_manager.h | 4 +- .../browser_accessibility_win_unittest.cc | 75 +- .../browser/accessibility/captioning_controller.cc | 2 +- .../cross_platform_accessibility_browsertest.cc | 12 - .../dump_accessibility_events_browsertest.cc | 3 +- .../dump_accessibility_tree_browsertest.cc | 18 + .../accessibility/fullscreen_browsertest.cc | 4 +- .../one_shot_accessibility_tree_search.cc | 28 +- .../web_contents_accessibility_android.cc | 24 +- .../web_contents_accessibility_android.h | 22 +- chromium/content/browser/android/OWNERS | 4 +- .../browser/android/app_web_message_port.cc | 8 +- .../android/content_protocol_handler_impl.cc | 5 +- .../browser/android/content_ui_event_handler.cc | 220 ++ .../browser/android/content_ui_event_handler.h | 82 + .../content/browser/android/content_video_view.cc | 24 +- .../content/browser/android/content_view_core.cc | 139 +- .../content/browser/android/content_view_core.h | 47 +- .../browser/android/content_view_render_view.cc | 7 +- .../browser/android/content_view_render_view.h | 2 + .../browser/android/content_view_statics.cc | 10 +- .../browser/android/gesture_listener_manager.cc | 24 + .../browser/android/gesture_listener_manager.h | 11 + .../content/browser/android/ime_adapter_android.cc | 9 +- .../android/interstitial_page_delegate_android.cc | 95 - .../android/interstitial_page_delegate_android.h | 54 - .../browser/android/java/java_bridge_thread.cc | 1 - .../overscroll_controller_android_unittest.cc | 7 +- .../browser/android/string_message_codec.cc | 145 -- .../content/browser/android/string_message_codec.h | 33 - .../android/string_message_codec_unittest.cc | 139 -- .../synchronous_compositor_browser_filter.cc | 124 -- .../synchronous_compositor_browser_filter.h | 53 - .../browser/android/synchronous_compositor_host.cc | 145 +- .../browser/android/synchronous_compositor_host.h | 10 - .../synchronous_compositor_sync_call_bridge.cc | 66 - .../synchronous_compositor_sync_call_bridge.h | 18 - .../browser/android/url_request_content_job.cc | 1 - .../android/url_request_content_job_unittest.cc | 2 +- chromium/content/browser/appcache/OWNERS | 3 - .../appcache/appcache_disk_cache_unittest.cc | 1 - .../browser/appcache/appcache_interceptor.cc | 39 - .../browser/appcache/appcache_interceptor.h | 12 - .../browser/appcache/appcache_request_handler.cc | 35 - .../browser/appcache/appcache_request_handler.h | 19 +- .../appcache/appcache_request_handler_unittest.cc | 27 - .../browser/appcache/appcache_service_impl.h | 1 + .../appcache/appcache_storage_impl_unittest.cc | 1 + .../appcache/appcache_subresource_url_factory.cc | 10 +- .../browser/appcache/appcache_url_loader_job.cc | 3 +- .../browser/appcache/appcache_url_loader_job.h | 3 +- .../background_fetch/background_fetch.proto | 40 +- .../background_fetch/background_fetch_context.cc | 124 +- .../background_fetch/background_fetch_context.h | 17 +- .../background_fetch_data_manager.cc | 190 +- .../background_fetch_data_manager.h | 21 +- .../background_fetch_data_manager_unittest.cc | 489 +++- .../background_fetch_delegate_proxy.cc | 53 +- .../background_fetch_delegate_proxy.h | 43 +- .../background_fetch_delegate_proxy_unittest.cc | 43 +- ...background_fetch_embedded_worker_test_helper.cc | 6 + .../background_fetch_embedded_worker_test_helper.h | 3 + .../background_fetch_event_dispatcher.cc | 14 +- .../background_fetch_event_dispatcher.h | 4 + .../background_fetch_event_dispatcher_unittest.cc | 23 +- .../background_fetch_job_controller.cc | 43 +- .../background_fetch_job_controller.h | 11 +- .../background_fetch_job_controller_unittest.cc | 4 +- .../background_fetch/background_fetch_metrics.cc | 27 + .../background_fetch/background_fetch_metrics.h | 31 + .../background_fetch/background_fetch_scheduler.cc | 11 +- .../background_fetch/background_fetch_scheduler.h | 5 +- .../background_fetch_scheduler_unittest.cc | 2 +- .../background_fetch_service_impl.cc | 12 +- .../background_fetch_service_unittest.cc | 18 + .../mock_background_fetch_delegate.cc | 9 +- .../mock_background_fetch_delegate.h | 8 +- .../browser/background_fetch/storage/README.md | 28 +- .../storage/create_metadata_task.cc | 22 +- .../background_fetch/storage/database_helpers.cc | 54 +- .../background_fetch/storage/database_helpers.h | 29 +- .../storage/delete_registration_task.cc | 2 +- .../storage/get_num_requests_task.cc | 88 + .../storage/get_num_requests_task.h | 53 + .../storage/get_settled_fetches_task.cc | 111 + .../storage/get_settled_fetches_task.h | 61 + .../storage/mark_registration_for_deletion_task.cc | 7 +- .../storage/mark_request_complete_task.cc | 106 + .../storage/mark_request_complete_task.h | 60 + .../storage/start_next_pending_request_task.cc | 161 ++ .../storage/start_next_pending_request_task.h | 72 + .../storage/update_registration_ui_task.cc | 48 +- .../storage/update_registration_ui_task.h | 7 +- .../background_sync/background_sync_context.cc | 19 +- .../background_sync/background_sync_context.h | 9 +- .../background_sync/background_sync_manager.cc | 41 +- .../background_sync/background_sync_manager.h | 12 +- .../background_sync_manager_unittest.cc | 112 +- .../background_sync_service_impl_unittest.cc | 3 +- chromium/content/browser/bad_message.h | 17 +- .../browser/blob_storage/blob_url_unittest.cc | 1 - .../blob_storage/chrome_blob_storage_context.cc | 25 +- .../blob_storage/chrome_blob_storage_context.h | 15 +- .../browser_associated_interface_unittest.cc | 2 +- .../browser/browser_child_process_host_impl.cc | 85 +- .../browser/browser_child_process_host_impl.h | 3 +- chromium/content/browser/browser_context.cc | 38 +- chromium/content/browser/browser_main.cc | 13 +- chromium/content/browser/browser_main.h | 7 +- chromium/content/browser/browser_main_loop.cc | 301 ++- chromium/content/browser/browser_main_loop.h | 48 +- .../content/browser/browser_main_loop_unittest.cc | 2 +- chromium/content/browser/browser_main_runner.cc | 269 --- .../content/browser/browser_main_runner_impl.cc | 256 +++ .../content/browser/browser_main_runner_impl.h | 65 + .../browser/browser_plugin/browser_plugin_guest.cc | 78 +- .../browser/browser_plugin/browser_plugin_guest.h | 24 +- .../browser_plugin_message_filter.cc | 10 + .../browser_plugin/browser_plugin_message_filter.h | 5 + .../content/browser/browser_process_sub_thread.cc | 57 + .../content/browser/browser_process_sub_thread.h | 4 + .../browser/browser_side_navigation_browsertest.cc | 258 +-- .../content/browser/browser_thread_unittest.cc | 82 +- chromium/content/browser/browsing_data/OWNERS | 2 +- .../browsing_data_filter_builder_impl.cc | 79 +- .../browsing_data_filter_builder_impl.h | 7 +- .../browsing_data_filter_builder_impl_unittest.cc | 94 +- .../browsing_data/browsing_data_remover_impl.cc | 117 +- .../browsing_data_remover_impl_browsertest.cc | 90 +- .../browsing_data_remover_impl_unittest.cc | 386 ++-- .../clear_site_data_throttle_browsertest.cc | 2 +- .../conditional_cache_deletion_helper.h | 2 +- chromium/content/browser/byte_stream.cc | 8 +- chromium/content/browser/byte_stream.h | 6 +- chromium/content/browser/byte_stream_unittest.cc | 14 +- chromium/content/browser/cache_storage/OWNERS | 1 - .../content/browser/cache_storage/cache_storage.cc | 104 +- .../content/browser/cache_storage/cache_storage.h | 27 +- .../browser/cache_storage/cache_storage_cache.cc | 164 +- .../browser/cache_storage/cache_storage_cache.h | 38 +- .../cache_storage/cache_storage_cache_unittest.cc | 364 +-- .../cache_storage/cache_storage_context_impl.cc | 5 +- .../cache_storage/cache_storage_dispatcher_host.cc | 117 +- .../cache_storage/cache_storage_dispatcher_host.h | 2 +- .../browser/cache_storage/cache_storage_manager.cc | 152 +- .../browser/cache_storage/cache_storage_manager.h | 54 +- .../cache_storage_manager_unittest.cc | 335 ++- .../cache_storage/cache_storage_quota_client.cc | 30 +- .../cache_storage/cache_storage_quota_client.h | 9 +- chromium/content/browser/child_process_launcher.cc | 55 +- chromium/content/browser/child_process_launcher.h | 20 +- .../browser/child_process_launcher_helper.h | 29 +- .../child_process_launcher_helper_android.cc | 46 +- .../child_process_launcher_helper_fuchsia.cc | 17 +- .../browser/child_process_launcher_helper_linux.cc | 43 +- .../browser/child_process_launcher_helper_mac.cc | 30 +- .../browser/child_process_launcher_helper_posix.cc | 7 +- .../browser/child_process_launcher_helper_posix.h | 4 +- .../browser/child_process_launcher_helper_win.cc | 17 +- .../browser/child_process_security_policy_impl.cc | 10 +- .../browser/cocoa/system_hotkey_helper_mac.mm | 1 - .../browser/cocoa/system_hotkey_map_unittest.mm | 2 +- .../browser_compositor_output_surface.cc | 2 + .../compositor/browser_compositor_output_surface.h | 7 +- .../gpu_browser_compositor_output_surface.cc | 84 +- .../gpu_browser_compositor_output_surface.h | 26 +- .../browser/compositor/gpu_output_surface_mac.cc | 50 +- .../browser/compositor/gpu_output_surface_mac.h | 23 - .../compositor/gpu_process_transport_factory.cc | 135 +- .../compositor/gpu_process_transport_factory.h | 19 +- ...urfaceless_browser_compositor_output_surface.cc | 23 +- ...surfaceless_browser_compositor_output_surface.h | 4 + .../browser/compositor/image_transport_factory.h | 12 - .../compositor/in_process_display_client.cc | 32 +- .../browser/compositor/in_process_display_client.h | 20 +- .../offscreen_browser_compositor_output_surface.cc | 21 +- .../offscreen_browser_compositor_output_surface.h | 11 +- .../browser/compositor/reflector_impl_unittest.cc | 16 +- .../software_browser_compositor_output_surface.cc | 54 +- .../software_browser_compositor_output_surface.h | 22 +- ...e_browser_compositor_output_surface_unittest.cc | 3 +- .../compositor/viz_process_transport_factory.cc | 135 +- .../compositor/viz_process_transport_factory.h | 29 +- .../vulkan_browser_compositor_output_surface.cc | 19 +- .../vulkan_browser_compositor_output_surface.h | 4 +- chromium/content/browser/cookie_store/BUILD.gn | 11 + chromium/content/browser/cookie_store/OWNERS | 8 + .../cookie_store/cookie_change_subscription.cc | 182 ++ .../cookie_store/cookie_change_subscription.h | 114 + .../cookie_store/cookie_change_subscriptions.proto | 27 + .../browser/cookie_store/cookie_store_context.cc | 117 + .../browser/cookie_store/cookie_store_context.h | 98 + .../browser/cookie_store/cookie_store_host.cc | 38 + .../browser/cookie_store/cookie_store_host.h | 59 + .../browser/cookie_store/cookie_store_manager.cc | 530 +++++ .../browser/cookie_store/cookie_store_manager.h | 228 ++ .../cookie_store/cookie_store_manager_unittest.cc | 863 +++++++ .../dedicated_worker/dedicated_worker_host.cc | 62 +- .../dedicated_worker/dedicated_worker_host.h | 8 +- .../device_sensors/device_sensor_browsertest.cc | 1 - .../browser/devtools/devtools_agent_host_impl.cc | 11 - .../browser/devtools/devtools_agent_host_impl.h | 1 - .../browser/devtools/devtools_http_handler.cc | 2 +- .../browser/devtools/devtools_io_context.cc | 523 +---- .../content/browser/devtools/devtools_io_context.h | 53 +- .../content/browser/devtools/devtools_manager.cc | 1 - .../browser/devtools/devtools_manager_unittest.cc | 5 +- .../devtools/devtools_network_interceptor.cc | 2 +- .../devtools/devtools_network_interceptor.h | 6 + .../content/browser/devtools/devtools_session.cc | 6 +- .../content/browser/devtools/devtools_session.h | 4 +- .../browser/devtools/devtools_stream_blob.cc | 251 +++ .../browser/devtools/devtools_stream_blob.h | 94 + .../browser/devtools/devtools_stream_file.cc | 142 ++ .../browser/devtools/devtools_stream_file.h | 44 + .../browser/devtools/devtools_stream_pipe.cc | 133 ++ .../browser/devtools/devtools_stream_pipe.h | 55 + .../browser/devtools/devtools_target_registry.cc | 4 +- .../devtools_url_interceptor_request_job.cc | 45 +- .../devtools/devtools_url_loader_interceptor.cc | 165 +- .../devtools/devtools_url_loader_interceptor.h | 4 + .../browser/devtools/devtools_video_consumer.cc | 5 +- .../devtools/devtools_video_consumer_unittest.cc | 3 +- .../browser/devtools/forwarding_agent_host.cc | 6 - .../browser/devtools/forwarding_agent_host.h | 1 - .../protocol/devtools_protocol_browsertest.cc | 210 +- .../browser/devtools/protocol/emulation_handler.cc | 18 +- .../browser/devtools/protocol/input_handler.cc | 16 +- .../browser/devtools/protocol/io_handler.cc | 17 +- .../browser/devtools/protocol/memory_handler.cc | 48 +- .../browser/devtools/protocol/memory_handler.h | 14 + .../browser/devtools/protocol/network_handler.cc | 313 ++- .../browser/devtools/protocol/network_handler.h | 36 +- .../browser/devtools/protocol/page_handler.cc | 294 ++- .../browser/devtools/protocol/page_handler.h | 40 +- .../devtools/protocol/system_info_handler.cc | 3 +- .../devtools/protocol/target_auto_attacher.cc | 6 +- .../browser/devtools/protocol/target_handler.cc | 11 +- .../browser/devtools/protocol/target_handler.h | 5 +- .../browser/devtools/protocol/tracing_handler.cc | 19 +- .../content/browser/devtools/protocol_config.json | 11 +- .../devtools/render_frame_devtools_agent_host.cc | 67 +- .../devtools/render_frame_devtools_agent_host.h | 42 +- .../devtools/service_worker_devtools_agent_host.cc | 3 +- .../devtools/service_worker_devtools_agent_host.h | 2 + .../devtools/service_worker_devtools_manager.cc | 3 +- .../devtools/shared_worker_devtools_agent_host.cc | 3 +- chromium/content/browser/dom_storage/OWNERS | 3 - .../dom_storage/dom_storage_context_wrapper.cc | 19 +- .../browser/dom_storage/dom_storage_database.h | 2 + .../dom_storage/dom_storage_database_unittest.cc | 16 +- .../dom_storage/local_storage_context_mojo.cc | 2 +- .../dom_storage/local_storage_context_mojo.h | 1 - .../local_storage_context_mojo_unittest.cc | 382 +--- .../dom_storage/session_storage_context_mojo.cc | 835 ++++++- .../dom_storage/session_storage_context_mojo.h | 176 +- .../session_storage_context_mojo_unittest.cc | 762 +++++++ .../dom_storage/session_storage_data_map.cc | 105 + .../browser/dom_storage/session_storage_data_map.h | 107 + .../session_storage_data_map_unittest.cc | 186 ++ .../dom_storage/session_storage_database.cc | 7 + .../browser/dom_storage/session_storage_database.h | 6 + .../dom_storage/session_storage_leveldb_wrapper.cc | 165 ++ .../dom_storage/session_storage_leveldb_wrapper.h | 106 + .../session_storage_leveldb_wrapper_unittest.cc | 376 ++++ .../dom_storage/session_storage_metadata.cc | 427 ++++ .../browser/dom_storage/session_storage_metadata.h | 182 ++ .../session_storage_metadata_unittest.cc | 480 ++++ .../session_storage_namespace_impl_mojo.cc | 178 ++ .../session_storage_namespace_impl_mojo.h | 158 ++ ...session_storage_namespace_impl_mojo_unittest.cc | 458 ++++ .../blob_download_url_loader_factory_getter.cc | 4 +- .../browser/download/byte_stream_input_stream.cc | 5 +- .../browser/download/byte_stream_input_stream.h | 2 +- .../browser/download/download_browsertest.cc | 150 +- .../browser/download/download_manager_impl.cc | 644 ++---- .../browser/download/download_manager_impl.h | 92 +- .../download/download_manager_impl_unittest.cc | 45 +- .../browser/download/download_request_core.cc | 10 +- .../download_url_loader_factory_getter_impl.cc | 25 + .../download_url_loader_factory_getter_impl.h | 39 + .../content/browser/download/download_utils.cc | 1 + .../download/drag_download_file_browsertest.cc | 8 +- .../browser/download/mhtml_generation_manager.cc | 6 +- .../network_download_url_loader_factory_getter.cc | 22 +- .../network_download_url_loader_factory_getter.h | 10 +- chromium/content/browser/download/save_package.cc | 28 +- chromium/content/browser/download/save_package.h | 5 + .../browser/download/save_package_unittest.cc | 4 +- .../browser/download/url_downloader_factory.cc | 4 +- .../browser/download/url_downloader_factory.h | 7 +- .../content/browser/file_url_loader_factory.cc | 23 +- chromium/content/browser/fileapi/OWNERS | 2 +- .../browser/fileapi/browser_file_system_helper.cc | 16 +- .../fileapi/browser_file_system_helper_unittest.cc | 2 +- .../fileapi/file_system_url_loader_factory.cc | 644 ++++++ .../fileapi/file_system_url_loader_factory.h | 31 + .../file_system_url_loader_factory_browsertest.cc | 817 +++++++ .../content/browser/fileapi/fileapi_browsertest.cc | 2 +- .../browser/fileapi/fileapi_message_filter.h | 1 - .../fileapi/fileapi_message_filter_unittest.cc | 8 +- chromium/content/browser/find_request_manager.cc | 71 +- chromium/content/browser/find_request_manager.h | 32 +- chromium/content/browser/frame_host/OWNERS | 3 + .../browser/frame_host/ancestor_throttle.cc | 45 +- .../content/browser/frame_host/ancestor_throttle.h | 5 + .../blocked_scheme_navigation_browsertest.cc | 1418 ++++++++++++ .../blocked_scheme_navigation_throttle.cc | 68 + .../blocked_scheme_navigation_throttle.h | 35 + .../frame_host/cross_process_frame_connector.cc | 124 +- .../frame_host/cross_process_frame_connector.h | 57 +- .../frame_host/data_url_navigation_browsertest.cc | 1032 --------- .../frame_host/data_url_navigation_throttle.cc | 72 - .../frame_host/data_url_navigation_throttle.h | 33 - .../browser/frame_host/form_submission_throttle.cc | 7 +- .../form_submission_throttle_browsertest.cc | 2 - .../browser/frame_host/frame_navigation_entry.cc | 12 +- .../browser/frame_host/frame_navigation_entry.h | 60 +- .../content/browser/frame_host/frame_tree_node.cc | 50 +- .../frame_host/frame_tree_node_blame_context.cc | 12 +- .../frame_tree_node_blame_context_unittest.cc | 2 +- .../input/legacy_ipc_frame_input_handler.cc | 158 -- .../input/legacy_ipc_frame_input_handler.h | 70 - .../browser/frame_host/interstitial_page_impl.cc | 74 +- .../browser/frame_host/interstitial_page_impl.h | 9 + .../frame_host/keep_alive_handle_factory.cc | 37 +- .../browser/frame_host/keep_alive_handle_factory.h | 6 +- .../frame_host/navigation_controller_android.cc | 8 +- .../frame_host/navigation_controller_android.h | 6 +- .../frame_host/navigation_controller_impl.cc | 216 +- .../frame_host/navigation_controller_impl.h | 25 +- .../navigation_controller_impl_browsertest.cc | 132 +- .../navigation_controller_impl_unittest.cc | 63 +- .../browser/frame_host/navigation_entry_impl.cc | 63 +- .../browser/frame_host/navigation_entry_impl.h | 30 +- .../frame_host/navigation_entry_impl_unittest.cc | 3 +- .../browser/frame_host/navigation_handle_impl.cc | 86 +- .../browser/frame_host/navigation_handle_impl.h | 21 +- .../navigation_handle_impl_browsertest.cc | 172 +- .../frame_host/navigation_handle_impl_unittest.cc | 41 +- .../browser/frame_host/navigation_request.cc | 444 ++-- .../browser/frame_host/navigation_request.h | 57 +- .../browser/frame_host/navigation_request_info.cc | 10 +- .../browser/frame_host/navigation_request_info.h | 29 +- chromium/content/browser/frame_host/navigator.cc | 18 +- chromium/content/browser/frame_host/navigator.h | 39 +- .../content/browser/frame_host/navigator_impl.cc | 198 +- .../content/browser/frame_host/navigator_impl.h | 61 +- .../browser/frame_host/navigator_impl_unittest.cc | 117 +- .../browser/frame_host/popup_menu_helper_mac.h | 16 +- .../browser/frame_host/popup_menu_helper_mac.mm | 47 +- .../frame_host/render_frame_host_delegate.cc | 4 + .../frame_host/render_frame_host_delegate.h | 18 +- .../render_frame_host_feature_policy_unittest.cc | 2 +- .../browser/frame_host/render_frame_host_impl.cc | 723 ++++-- .../browser/frame_host/render_frame_host_impl.h | 198 +- .../render_frame_host_impl_browsertest.cc | 596 ++++- .../frame_host/render_frame_host_manager.cc | 83 +- .../browser/frame_host/render_frame_host_manager.h | 9 +- .../render_frame_host_manager_browsertest.cc | 510 ++++- .../render_frame_host_manager_unittest.cc | 307 +-- .../frame_host/render_frame_message_filter.cc | 68 +- .../frame_host/render_frame_message_filter.h | 17 +- .../browser/frame_host/render_frame_proxy_host.cc | 53 +- .../browser/frame_host/render_frame_proxy_host.h | 6 + .../frame_host/render_widget_host_view_guest.cc | 45 +- .../frame_host/render_widget_host_view_guest.h | 19 +- .../render_widget_host_view_guest_unittest.cc | 16 +- .../frame_host/webui_navigation_browsertest.cc | 238 ++ .../frame_host/webui_navigation_throttle.cc | 59 + .../browser/frame_host/webui_navigation_throttle.h | 46 + chromium/content/browser/gpu/compositor_util.cc | 406 ++-- chromium/content/browser/gpu/compositor_util.h | 13 +- chromium/content/browser/gpu/gpu_client.cc | 184 -- chromium/content/browser/gpu/gpu_client.h | 64 - chromium/content/browser/gpu/gpu_client_impl.cc | 203 ++ chromium/content/browser/gpu/gpu_client_impl.h | 75 + .../content/browser/gpu/gpu_data_manager_impl.cc | 51 +- .../content/browser/gpu/gpu_data_manager_impl.h | 27 +- .../browser/gpu/gpu_data_manager_impl_private.cc | 127 +- .../browser/gpu/gpu_data_manager_impl_private.h | 26 +- .../gpu/gpu_data_manager_impl_private_unittest.cc | 2 +- ...ta_manager_testing_arrays_and_structs_autogen.h | 3 +- .../gpu/gpu_data_manager_testing_autogen.cc | 130 +- .../gpu_data_manager_testing_exceptions_autogen.h | 2 + chromium/content/browser/gpu/gpu_internals_ui.cc | 107 +- chromium/content/browser/gpu/gpu_process_host.cc | 124 +- chromium/content/browser/gpu/gpu_process_host.h | 10 +- .../browser/histogram_internals_request_job.cc | 92 - .../browser/histogram_internals_request_job.h | 52 - .../browser/histogram_internals_url_loader.cc | 36 - .../browser/histogram_internals_url_loader.h | 18 - .../content/browser/histograms_internals_ui.cc | 113 + chromium/content/browser/histograms_internals_ui.h | 25 + .../content/browser/hyphenation/hyphenation_impl.h | 1 + chromium/content/browser/indexed_db/OWNERS | 3 - .../browser/indexed_db/indexed_db_backing_store.cc | 62 +- .../browser/indexed_db/indexed_db_backing_store.h | 2 + .../indexed_db_backing_store_unittest.cc | 134 +- .../browser/indexed_db/indexed_db_context_impl.cc | 32 +- .../indexed_db/indexed_db_dispatcher_host.cc | 3 +- .../indexed_db/indexed_db_dispatcher_host.h | 3 +- .../browser/indexed_db/indexed_db_factory.h | 4 + .../browser/indexed_db/indexed_db_factory_impl.cc | 60 +- .../browser/indexed_db/indexed_db_factory_impl.h | 4 + .../indexed_db/indexed_db_fake_backing_store.cc | 3 +- .../browser/indexed_db/indexed_db_internals_ui.cc | 2 +- .../indexed_db_pre_close_task_queue_unittest.cc | 2 +- .../indexed_db_tombstone_sweeper_unittest.cc | 4 + .../browser/indexed_db/leveldb/leveldb_database.cc | 50 +- .../browser/indexed_db/leveldb/leveldb_database.h | 7 + .../browser/indexed_db/leveldb/leveldb_unittest.cc | 39 + .../indexed_db/leveldb/leveldb_write_batch.h | 3 +- .../browser/indexed_db/leveldb/mock_level_db.h | 4 +- .../indexed_db/leveldb/mock_leveldb_factory.h | 2 +- .../browser/indexed_db/mock_indexed_db_factory.h | 25 +- .../indexed_db/mock_mojo_indexed_db_callbacks.h | 2 +- .../mock_mojo_indexed_db_database_callbacks.h | 2 +- chromium/content/browser/initiator_csp_context.cc | 55 + chromium/content/browser/initiator_csp_context.h | 42 + .../content/browser/isolated_origin_browsertest.cc | 112 +- .../browser/keyboard_lock/keyboard_lock_metrics.h | 26 + .../keyboard_lock/keyboard_lock_service_impl.cc | 59 +- .../keyboard_lock/keyboard_lock_service_impl.h | 1 + .../content/browser/keyboard_lock_browsertest.cc | 598 ++++- .../content/browser/keyboard_lock_browsertest.h | 16 + .../browser/keyboard_lock_browsertest_mac.mm | 39 + chromium/content/browser/leveldb_wrapper_impl.cc | 8 +- chromium/content/browser/leveldb_wrapper_impl.h | 1 + .../browser/leveldb_wrapper_impl_unittest.cc | 179 +- chromium/content/browser/linux_ipc_browsertest.cc | 3 +- .../browser/loader/cors_file_origin_browsertest.cc | 2 +- .../cross_site_document_blocking_browsertest.cc | 22 +- .../loader/cross_site_document_resource_handler.cc | 434 +--- .../loader/cross_site_document_resource_handler.h | 62 +- ...ross_site_document_resource_handler_unittest.cc | 54 +- .../loader/data_pipe_to_source_stream_unittest.cc | 4 +- .../content/browser/loader/loader_browsertest.cc | 1224 ++++++++++ .../loader/mime_sniffing_resource_handler.cc | 24 +- .../browser/loader/mojo_async_resource_handler.cc | 62 +- .../browser/loader/mojo_async_resource_handler.h | 5 +- .../loader/mojo_async_resource_handler_unittest.cc | 14 +- .../browser/loader/navigation_loader_util.cc | 58 + .../browser/loader/navigation_loader_util.h | 34 + .../content/browser/loader/navigation_metrics.cc | 25 - .../content/browser/loader/navigation_metrics.h | 32 - .../browser/loader/navigation_resource_handler.cc | 182 -- .../browser/loader/navigation_resource_handler.h | 83 - .../browser/loader/navigation_url_loader.cc | 16 +- .../loader/navigation_url_loader_delegate.h | 31 +- .../browser/loader/navigation_url_loader_impl.cc | 1412 +++++++++++- .../browser/loader/navigation_url_loader_impl.h | 110 +- .../loader/navigation_url_loader_impl_core.cc | 169 -- .../loader/navigation_url_loader_impl_core.h | 105 - .../loader/navigation_url_loader_impl_unittest.cc | 348 +++ .../navigation_url_loader_network_service.cc | 1344 ----------- .../loader/navigation_url_loader_network_service.h | 93 - ...vigation_url_loader_network_service_unittest.cc | 328 --- .../loader/navigation_url_loader_unittest.cc | 51 +- .../content/browser/loader/prefetch_url_loader.cc | 25 +- .../content/browser/loader/prefetch_url_loader.h | 12 +- .../browser/loader/prefetch_url_loader_service.cc | 23 +- .../browser/loader/prefetch_url_loader_service.h | 7 +- .../loader/resource_dispatcher_host_browsertest.cc | 1241 ----------- .../loader/resource_dispatcher_host_impl.cc | 755 +++---- .../browser/loader/resource_dispatcher_host_impl.h | 60 +- .../loader/resource_dispatcher_host_unittest.cc | 8 +- .../content/browser/loader/resource_hints_impl.cc | 4 +- chromium/content/browser/loader/resource_loader.cc | 59 +- chromium/content/browser/loader/resource_loader.h | 13 - .../browser/loader/resource_loader_unittest.cc | 4 +- .../browser/loader/resource_message_filter.cc | 25 +- .../browser/loader/resource_request_info_impl.cc | 42 +- .../browser/loader/resource_request_info_impl.h | 55 +- .../browser/loader/resource_requester_info.cc | 3 +- .../loader/source_stream_to_data_pipe_unittest.cc | 4 +- .../browser/loader/temporary_file_stream.cc | 1 + .../browser/loader/upload_data_stream_builder.cc | 3 +- .../loader/url_loader_factory_impl_unittest.cc | 34 +- chromium/content/browser/mach_broker_mac.h | 8 +- chromium/content/browser/mach_broker_mac.mm | 8 +- .../browser/manifest/manifest_browsertest.cc | 13 +- .../browser/manifest/manifest_icon_selector.cc | 4 +- .../manifest/manifest_icon_selector_unittest.cc | 52 +- .../browser/manifest/manifest_manager_host.cc | 11 +- .../browser/manifest/manifest_manager_host.h | 11 +- chromium/content/browser/media/OWNERS | 4 +- .../media/android/media_resource_getter_impl.cc | 3 +- .../browser/media/audio_input_stream_broker.cc | 219 ++ .../browser/media/audio_input_stream_broker.h | 86 + .../media/audio_input_stream_broker_unittest.cc | 278 +++ .../browser/media/audio_loopback_stream_broker.cc | 164 ++ .../browser/media/audio_loopback_stream_broker.h | 74 + .../media/audio_loopback_stream_broker_unittest.cc | 374 ++++ .../content/browser/media/audio_muting_session.cc | 22 + .../content/browser/media/audio_muting_session.h | 32 + .../browser/media/audio_output_stream_broker.cc | 157 ++ .../browser/media/audio_output_stream_broker.h | 79 + .../media/audio_output_stream_broker_unittest.cc | 281 +++ .../content/browser/media/audio_stream_broker.cc | 85 + .../content/browser/media/audio_stream_broker.h | 139 ++ chromium/content/browser/media/capture/OWNERS | 3 +- .../capture/audio_mirroring_manager_unittest.cc | 1 - .../capture/aura_window_video_capture_device.cc | 171 ++ .../capture/aura_window_video_capture_device.h | 57 + ...aura_window_video_capture_device_browsertest.cc | 358 +++ .../content_capture_device_browsertest_base.cc | 267 +++ .../content_capture_device_browsertest_base.h | 129 ++ .../browser/media/capture/cursor_renderer_aura.cc | 5 +- .../media/capture/cursor_renderer_aura_unittest.cc | 8 +- .../media/capture/desktop_capture_device.cc | 16 +- .../desktop_capture_device_aura_unittest.cc | 11 +- .../capture/desktop_capture_device_uma_types.h | 2 + .../capture/desktop_capture_device_unittest.cc | 19 +- .../media/capture/fake_video_capture_stack.cc | 159 ++ .../media/capture/fake_video_capture_stack.h | 80 + .../capture/frame_sink_video_capture_device.cc | 173 +- .../capture/frame_sink_video_capture_device.h | 33 +- .../frame_sink_video_capture_device_unittest.cc | 108 +- .../browser/media/capture/frame_test_util.cc | 93 + .../browser/media/capture/frame_test_util.h | 64 + .../media/capture/lame_window_capturer_chromeos.cc | 373 ++++ .../media/capture/lame_window_capturer_chromeos.h | 139 ++ .../screen_capture_device_android_unittest.cc | 11 +- .../capture/web_contents_audio_input_stream.cc | 5 + .../capture/web_contents_audio_input_stream.h | 1 + .../web_contents_audio_input_stream_unittest.cc | 13 +- ...eb_contents_video_capture_device_browsertest.cc | 705 ++---- .../browser/media/cdm_registry_impl_unittest.cc | 61 +- .../browser/media/encrypted_media_browsertest.cc | 74 +- .../content/browser/media/flinging_renderer.cc | 100 + chromium/content/browser/media/flinging_renderer.h | 62 + .../browser/media/flinging_renderer_unittest.cc | 84 + .../media/forwarding_audio_stream_factory.cc | 256 +++ .../media/forwarding_audio_stream_factory.h | 144 ++ .../forwarding_audio_stream_factory_unittest.cc | 711 ++++++ .../browser/media/key_system_support_impl.cc | 10 +- .../media/key_system_support_impl_unittest.cc | 42 +- .../browser/media/keyboard_mic_registration.cc | 31 + .../browser/media/keyboard_mic_registration.h | 31 + .../browser/media/media_canplaytype_browsertest.cc | 2 +- .../content/browser/media/media_devices_util.cc | 62 +- .../content/browser/media/media_devices_util.h | 33 +- .../content/browser/media/media_interface_proxy.cc | 36 +- .../content/browser/media/media_interface_proxy.h | 2 + chromium/content/browser/media/media_internals.cc | 3 +- .../browser/media/media_internals_unittest.cc | 43 +- .../browser/media/media_source_browsertest.cc | 36 +- .../browser/media/media_web_contents_observer.cc | 133 +- .../browser/media/media_web_contents_observer.h | 35 +- .../media/session/audio_focus_manager_unittest.cc | 9 +- .../browser/media/session/media_session_android.cc | 8 + .../browser/media/session/media_session_android.h | 3 + .../browser/media/session/media_session_impl.cc | 4 + .../browser/media/session/media_session_impl.h | 10 +- .../session/media_session_impl_browsertest.cc | 28 + .../media/session/mock_media_session_observer.h | 2 +- .../content/browser/media/url_provision_fetcher.cc | 73 +- .../content/browser/media/url_provision_fetcher.h | 20 +- .../browser/memory/memory_coordinator_impl.cc | 3 +- .../memory/memory_coordinator_impl_unittest.cc | 18 +- chromium/content/browser/message_port_provider.cc | 6 +- .../browser/net/accept_header_browsertest.cc | 2 +- .../content/browser/network_service_browsertest.cc | 41 +- chromium/content/browser/network_service_client.cc | 82 +- chromium/content/browser/network_service_client.h | 20 +- .../content/browser/network_service_instance.cc | 1 - .../browser/network_service_restart_browsertest.cc | 317 ++- .../blink_notification_service_impl.cc | 23 +- .../blink_notification_service_impl.h | 1 + .../blink_notification_service_impl_unittest.cc | 89 +- .../browser/notifications/notification_database.cc | 2 +- .../notifications/notification_database_data.proto | 16 +- .../notification_database_data_conversions.cc | 48 + .../notification_database_data_unittest.cc | 63 +- .../notification_event_dispatcher_impl.cc | 24 +- .../notification_event_dispatcher_impl.h | 11 +- .../notification_event_dispatcher_impl_unittest.cc | 14 +- .../notifications/notification_message_filter.cc | 326 --- .../notifications/notification_message_filter.h | 133 -- chromium/content/browser/oop_browsertest.cc | 4 +- .../browser/payments/payment_app_browsertest.cc | 30 +- .../payments/payment_app_content_unittest_base.cc | 32 +- .../payments/payment_app_content_unittest_base.h | 3 + .../browser/payments/payment_app_context_impl.cc | 7 +- .../browser/payments/payment_app_database.cc | 16 +- .../browser/payments/payment_app_info_fetcher.cc | 4 +- .../browser/payments/payment_app_info_fetcher.h | 4 +- .../browser/payments/payment_app_provider_impl.cc | 150 +- .../browser/payments/payment_app_provider_impl.h | 5 + .../payments/payment_app_provider_impl_unittest.cc | 53 + .../payments/payment_instrument_icon_fetcher.cc | 14 +- .../payments/payment_instrument_icon_fetcher.h | 4 +- .../permissions/permission_service_context.h | 3 + .../browser/permissions/permission_service_impl.cc | 109 +- .../browser/permissions/permission_service_impl.h | 1 + .../picture_in_picture/overlay_surface_embedder.cc | 55 +- .../picture_in_picture/overlay_surface_embedder.h | 9 + .../picture_in_picture_window_controller_impl.cc | 96 +- .../picture_in_picture_window_controller_impl.h | 20 +- .../content/browser/plugin_data_remover_impl.cc | 6 - .../content/browser/pointer_lock_browsertest.cc | 6 +- .../content/browser/power_monitor_browsertest.cc | 10 +- .../content/browser/ppapi_plugin_process_host.cc | 19 +- .../presentation/presentation_service_impl.cc | 121 +- .../presentation/presentation_service_impl.h | 48 +- .../presentation_service_impl_unittest.cc | 173 +- .../content/browser/process_internals/BUILD.gn | 11 + chromium/content/browser/process_internals/OWNERS | 2 + .../process_internals/process_internals.mojom | 17 + .../process_internals_handler_impl.cc | 44 + .../process_internals_handler_impl.h | 35 + .../process_internals/process_internals_ui.cc | 73 + .../process_internals/process_internals_ui.h | 46 + chromium/content/browser/renderer_host/DEPS | 3 + chromium/content/browser/renderer_host/OWNERS | 16 +- .../renderer_host/browser_compositor_view_mac.h | 80 +- .../renderer_host/browser_compositor_view_mac.mm | 315 ++- .../browser/renderer_host/clipboard_host_impl.cc | 2 +- .../browser/renderer_host/clipboard_host_impl.h | 3 +- .../renderer_host/clipboard_host_impl_unittest.cc | 11 - .../renderer_host/compositor_impl_android.cc | 349 ++- .../renderer_host/compositor_impl_android.h | 17 +- .../renderer_host/compositor_resize_lock.cc | 60 - .../browser/renderer_host/compositor_resize_lock.h | 68 - .../compositor_resize_lock_unittest.cc | 172 -- .../browser/renderer_host/delegated_frame_host.cc | 392 +--- .../browser/renderer_host/delegated_frame_host.h | 84 +- .../delegated_frame_host_client_aura.cc | 48 +- .../delegated_frame_host_client_aura.h | 13 +- .../direct_manipulation_browsertest.cc | 4 +- .../renderer_host/embedded_frame_sink_impl.cc | 77 + .../renderer_host/embedded_frame_sink_impl.h | 70 + .../embedded_frame_sink_provider_impl.cc | 89 + .../embedded_frame_sink_provider_impl.h | 74 + .../embedded_frame_sink_provider_impl_unittest.cc | 321 +++ .../renderer_host/file_utilities_host_impl.cc | 2 +- .../renderer_host/file_utilities_host_impl.h | 6 +- .../renderer_host/frame_connector_delegate.cc | 24 +- .../renderer_host/frame_connector_delegate.h | 44 +- .../renderer_host/frame_sink_provider_impl.cc | 20 +- .../renderer_host/frame_sink_provider_impl.h | 5 +- .../input/composited_scrolling_browsertest.cc | 4 +- .../input/compositor_event_ack_browsertest.cc | 9 +- .../renderer_host/input/fling_browsertest.cc | 75 +- .../renderer_host/input/fling_controller.cc | 121 +- .../browser/renderer_host/input/fling_controller.h | 35 +- .../input/fling_controller_unittest.cc | 50 +- .../browser/renderer_host/input/fling_scheduler.cc | 91 + .../browser/renderer_host/input/fling_scheduler.h | 49 + .../renderer_host/input/fling_scheduler_mac.h | 27 + .../renderer_host/input/fling_scheduler_mac.mm | 36 + .../input/fling_scheduler_unittest.cc | 162 ++ .../renderer_host/input/gesture_event_queue.cc | 19 +- .../renderer_host/input/gesture_event_queue.h | 10 +- .../input/gesture_event_queue_unittest.cc | 29 +- .../browser/renderer_host/input/input_router.h | 3 - .../renderer_host/input/input_router_client.h | 9 +- .../input/input_router_config_helper.cc | 23 +- .../renderer_host/input/input_router_impl.cc | 44 +- .../renderer_host/input/input_router_impl.h | 36 +- .../input/input_router_impl_unittest.cc | 67 +- .../input/legacy_input_router_impl.cc | 706 ------ .../renderer_host/input/legacy_input_router_impl.h | 290 --- .../input/legacy_input_router_impl_perftest.cc | 407 ---- .../input/legacy_input_router_impl_unittest.cc | 2350 -------------------- .../input/legacy_ipc_widget_input_handler.cc | 110 - .../input/legacy_ipc_widget_input_handler.h | 63 - .../input/main_thread_event_queue_browsertest.cc | 2 +- .../input/mock_input_router_client.cc | 15 +- .../renderer_host/input/mock_input_router_client.h | 15 +- .../renderer_host/input/motion_event_web.cc | 4 +- .../renderer_host/input/mouse_wheel_event_queue.cc | 77 +- .../renderer_host/input/mouse_wheel_event_queue.h | 8 +- .../input/mouse_wheel_event_queue_unittest.cc | 16 +- .../input/mouse_wheel_phase_handler.cc | 26 +- .../input/passthrough_touch_event_queue.cc | 8 +- .../passthrough_touch_event_queue_unittest.cc | 59 +- .../input/render_widget_host_latency_tracker.cc | 130 +- .../input/render_widget_host_latency_tracker.h | 14 +- .../render_widget_host_latency_tracker_unittest.cc | 322 +-- .../input/scroll_latency_browsertest.cc | 2 +- .../input/synthetic_gesture_controller_unittest.cc | 17 +- .../renderer_host/input/synthetic_gesture_target.h | 4 + .../input/synthetic_gesture_target_android.cc | 10 +- .../input/synthetic_gesture_target_android.h | 2 +- .../input/synthetic_gesture_target_aura.cc | 34 +- .../input/synthetic_gesture_target_aura.h | 5 + .../input/synthetic_gesture_target_base.cc | 28 +- .../input/synthetic_gesture_target_base.h | 7 + .../input/synthetic_gesture_target_mac.h | 9 +- .../input/synthetic_gesture_target_mac.mm | 90 +- .../renderer_host/input/synthetic_mouse_driver.cc | 2 +- .../input/synthetic_pointer_action_unittest.cc | 5 + .../input/synthetic_pointer_driver.cc | 6 - .../renderer_host/input/synthetic_pointer_driver.h | 3 - .../input/synthetic_smooth_move_gesture.cc | 6 +- .../renderer_host/input/synthetic_touch_driver.cc | 2 +- .../input/synthetic_touchscreen_pinch_gesture.cc | 7 +- .../input/tap_suppression_controller.cc | 114 +- .../input/tap_suppression_controller.h | 42 +- .../input/tap_suppression_controller_client.h | 40 - .../input/tap_suppression_controller_unittest.cc | 415 +--- .../input/touch_action_browsertest.cc | 327 ++- .../renderer_host/input/touch_action_filter.cc | 27 +- .../renderer_host/input/touch_action_filter.h | 16 +- .../input/touch_action_filter_unittest.cc | 490 ++-- .../browser/renderer_host/input/touch_emulator.cc | 78 +- .../browser/renderer_host/input/touch_emulator.h | 21 +- .../renderer_host/input/touch_emulator_client.h | 5 +- .../renderer_host/input/touch_emulator_unittest.cc | 41 +- .../renderer_host/input/touch_input_browsertest.cc | 8 +- ...selection_controller_client_aura_browsertest.cc | 4 +- .../input/touchpad_tap_suppression_controller.cc | 36 +- .../input/touchpad_tap_suppression_controller.h | 36 +- .../touchscreen_tap_suppression_controller.cc | 67 +- .../input/touchscreen_tap_suppression_controller.h | 33 +- .../input/web_input_event_builders_android.cc | 18 +- .../input/web_input_event_builders_android.h | 5 +- .../web_input_event_builders_android_unittest.cc | 18 +- .../input/web_input_event_builders_mac.mm | 21 +- .../input/web_input_event_util_unittest.cc | 5 +- .../input/wheel_scroll_latching_browsertest.cc | 6 +- .../renderer_host/legacy_render_widget_host_win.cc | 2 + .../media/audio_input_delegate_impl.cc | 9 +- .../media/audio_input_delegate_impl.h | 2 + .../media/audio_input_delegate_impl_unittest.cc | 12 +- .../media/audio_input_device_manager.h | 6 +- .../media/audio_input_device_manager_unittest.cc | 2 +- .../media/audio_input_renderer_host.cc | 266 --- .../media/audio_input_renderer_host.h | 203 -- .../media/audio_input_renderer_host_unittest.cc | 529 ----- .../media/audio_input_stream_handle.cc | 11 +- .../media/audio_input_stream_handle.h | 4 + .../media/audio_input_stream_handle_unittest.cc | 28 +- .../media/audio_output_authorization_handler.cc | 140 +- .../media/audio_output_authorization_handler.h | 23 +- .../audio_output_authorization_handler_unittest.cc | 9 +- .../media/audio_output_delegate_impl.cc | 2 +- .../media/audio_output_delegate_impl_unittest.cc | 11 +- .../renderer_host/media/audio_renderer_host.cc | 390 ---- .../renderer_host/media/audio_renderer_host.h | 207 -- .../media/audio_renderer_host_unittest.cc | 628 ------ .../renderer_host/media/audio_service_listener.cc | 206 ++ .../renderer_host/media/audio_service_listener.h | 129 ++ .../media/audio_service_listener_unittest.cc | 248 +++ .../in_process_launched_video_capture_device.cc | 4 +- .../in_process_video_capture_device_launcher.cc | 71 +- .../in_process_video_capture_device_launcher.h | 6 + .../media/media_devices_dispatcher_host.cc | 75 +- .../media/media_devices_dispatcher_host.h | 19 +- .../media_devices_dispatcher_host_unittest.cc | 7 +- .../renderer_host/media/media_devices_manager.cc | 307 ++- .../renderer_host/media/media_devices_manager.h | 48 +- .../media/media_devices_manager_unittest.cc | 51 +- .../media/media_stream_dispatcher_host.cc | 18 +- .../media/media_stream_dispatcher_host.h | 17 +- .../media/media_stream_dispatcher_host_unittest.cc | 53 +- .../renderer_host/media/media_stream_manager.cc | 198 +- .../renderer_host/media/media_stream_manager.h | 52 +- .../media/media_stream_manager_unittest.cc | 15 +- .../media/mock_video_capture_provider.h | 2 +- .../old_render_frame_audio_input_stream_factory.cc | 237 ++ .../old_render_frame_audio_input_stream_factory.h | 149 ++ ...er_frame_audio_input_stream_factory_unittest.cc | 176 ++ ...old_render_frame_audio_output_stream_factory.cc | 140 ++ .../old_render_frame_audio_output_stream_factory.h | 109 + ...r_frame_audio_output_stream_factory_unittest.cc | 337 +++ .../render_frame_audio_input_stream_factory.cc | 272 ++- .../render_frame_audio_input_stream_factory.h | 109 +- ...er_frame_audio_input_stream_factory_unittest.cc | 368 +-- .../render_frame_audio_output_stream_factory.cc | 199 +- .../render_frame_audio_output_stream_factory.h | 84 +- ...r_frame_audio_output_stream_factory_unittest.cc | 500 ++--- .../renderer_audio_output_stream_factory_context.h | 2 + ...rer_audio_output_stream_factory_context_impl.cc | 7 - ...erer_audio_output_stream_factory_context_impl.h | 6 +- ...rvice_video_capture_device_launcher_unittest.cc | 27 +- .../service_video_capture_provider_unittest.cc | 26 +- .../media/video_capture_browsertest.cc | 29 +- .../media/video_capture_buffer_pool_unittest.cc | 58 +- .../media/video_capture_controller.cc | 60 +- .../renderer_host/media/video_capture_controller.h | 15 +- .../media/video_capture_controller_event_handler.h | 9 +- .../media/video_capture_controller_unittest.cc | 63 +- .../media/video_capture_gpu_jpeg_decoder.cc | 2 +- .../renderer_host/media/video_capture_host.cc | 14 +- .../renderer_host/media/video_capture_host.h | 8 +- .../renderer_host/media/video_capture_manager.h | 1 - .../media/video_capture_manager_unittest.cc | 9 +- .../renderer_host/media/video_capture_unittest.cc | 19 +- .../native_web_keyboard_event_android.cc | 13 +- .../native_web_keyboard_event_aura.cc | 14 +- .../renderer_host/native_web_keyboard_event_mac.mm | 12 +- .../offscreen_canvas_provider_impl.cc | 74 - .../renderer_host/offscreen_canvas_provider_impl.h | 67 - .../offscreen_canvas_provider_impl_unittest.cc | 309 --- .../renderer_host/offscreen_canvas_surface_impl.cc | 71 - .../renderer_host/offscreen_canvas_surface_impl.h | 74 - .../renderer_host/overscroll_configuration.cc | 11 + .../browser/renderer_host/overscroll_controller.cc | 77 +- .../browser/renderer_host/overscroll_controller.h | 31 +- .../overscroll_controller_unittest.cc | 266 ++- .../browser/renderer_host/p2p/socket_host.h | 1 - .../browser/renderer_host/p2p/socket_host_tcp.cc | 92 +- .../browser/renderer_host/p2p/socket_host_tcp.h | 4 - .../renderer_host/p2p/socket_host_tcp_server.h | 1 - .../renderer_host/p2p/socket_host_tcp_unittest.cc | 114 + .../renderer_host/p2p/socket_host_test_utils.cc | 33 +- .../renderer_host/p2p/socket_host_test_utils.h | 17 +- .../browser/renderer_host/p2p/socket_host_udp.h | 1 - .../pepper/pepper_external_file_ref_backend.cc | 55 +- .../renderer_host/pepper/pepper_file_io_host.cc | 6 +- .../renderer_host/pepper/pepper_gamepad_host.cc | 4 +- .../pepper/pepper_gamepad_host_unittest.cc | 93 +- .../pepper/pepper_internal_file_ref_backend.cc | 1 - .../pepper/pepper_truetype_font_linux.cc | 27 +- .../renderer_host/pepper/ssl_context_helper.cc | 2 +- .../render_frame_metadata_provider_impl.cc | 14 +- .../render_frame_metadata_provider_impl.h | 7 +- .../browser/renderer_host/render_message_filter.cc | 28 +- .../browser/renderer_host/render_message_filter.h | 1 - .../render_process_host_browsertest.cc | 98 +- .../renderer_host/render_process_host_impl.cc | 378 ++-- .../renderer_host/render_process_host_impl.h | 101 +- .../browser/renderer_host/render_view_host_impl.cc | 29 +- .../browser/renderer_host/render_view_host_impl.h | 3 +- .../renderer_host/render_view_host_unittest.cc | 20 +- .../browser/renderer_host/render_widget_helper.cc | 13 - .../browser/renderer_host/render_widget_helper.h | 15 - .../render_widget_host_browsertest.cc | 282 ++- .../renderer_host/render_widget_host_delegate.h | 5 +- .../renderer_host/render_widget_host_impl.cc | 778 +++---- .../renderer_host/render_widget_host_impl.h | 196 +- .../render_widget_host_input_event_router.cc | 329 ++- .../render_widget_host_input_event_router.h | 40 +- ...nder_widget_host_input_event_router_unittest.cc | 3 +- .../render_widget_host_ns_view_bridge.h | 25 + .../render_widget_host_ns_view_bridge.mm | 48 +- .../render_widget_host_ns_view_client.h | 7 +- .../renderer_host/render_widget_host_unittest.cc | 1270 +++-------- .../render_widget_host_view_android.cc | 320 +-- .../render_widget_host_view_android.h | 76 +- .../renderer_host/render_widget_host_view_aura.cc | 262 ++- .../renderer_host/render_widget_host_view_aura.h | 74 +- .../render_widget_host_view_aura_unittest.cc | 1206 ++++------ .../renderer_host/render_widget_host_view_base.cc | 224 +- .../renderer_host/render_widget_host_view_base.h | 100 +- .../render_widget_host_view_base_unittest.cc | 2 +- .../render_widget_host_view_browsertest.cc | 13 +- .../render_widget_host_view_child_frame.cc | 115 +- .../render_widget_host_view_child_frame.h | 39 +- ...der_widget_host_view_child_frame_browsertest.cc | 25 +- ...render_widget_host_view_child_frame_unittest.cc | 32 +- .../renderer_host/render_widget_host_view_cocoa.h | 15 +- .../renderer_host/render_widget_host_view_cocoa.mm | 102 +- .../render_widget_host_view_event_handler.cc | 15 +- .../render_widget_host_view_event_handler.h | 3 +- .../renderer_host/render_widget_host_view_mac.h | 86 +- .../renderer_host/render_widget_host_view_mac.mm | 281 ++- .../render_widget_host_view_mac_unittest.mm | 91 +- .../renderer_host/render_widget_targeter.cc | 67 +- .../browser/renderer_host/render_widget_targeter.h | 17 +- .../browser/renderer_host/text_input_manager.cc | 3 +- .../browser/renderer_host/text_input_manager.h | 8 +- .../browser/renderer_host/ui_events_helper.cc | 3 +- .../content/browser/renderer_interface_binders.cc | 35 +- chromium/content/browser/resources/BUILD.gn | 13 + chromium/content/browser/resources/appcache/OWNERS | 3 - .../browser/resources/gpu/browser_bridge.js | 4 +- .../content/browser/resources/gpu/info_view.html | 23 + .../content/browser/resources/gpu/info_view.js | 281 ++- .../content/browser/resources/histograms/BUILD.gn | 18 + .../content/browser/resources/histograms/OWNERS | 1 + .../resources/histograms/histograms_internals.html | 18 + .../resources/histograms/histograms_internals.js | 37 + .../browser/resources/media/client_renderer.js | 14 +- chromium/content/browser/resources/media/main.js | 4 +- .../media/peer_connection_update_table.js | 2 +- .../browser/resources/media/ssrc_info_manager.js | 2 +- .../content/browser/resources/media/stats_table.js | 10 +- .../browser/resources/media/timeline_graph_view.js | 6 +- .../browser/resources/media/webrtc_internals.js | 2 +- .../resources/process/process_internals.html | 16 + .../browser/resources/process/process_internals.js | 33 + .../service_worker/serviceworker_internals.js | 4 +- chromium/content/browser/sandbox_parameters_mac.mm | 3 +- .../screen_orientation_delegate_android.h | 2 +- .../screen_orientation_provider.cc | 1 - .../screen_orientation_provider.h | 2 +- .../screen_orientation_provider_unittest.cc | 18 +- .../browser/security_exploit_browsertest.cc | 5 +- .../service_manager/common_browser_interfaces.cc | 2 +- .../service_manager/service_manager_context.cc | 116 +- chromium/content/browser/service_worker/OWNERS | 1 - .../service_worker/embedded_worker_instance.cc | 213 +- .../service_worker/embedded_worker_instance.h | 35 +- .../embedded_worker_instance_unittest.cc | 214 +- .../service_worker/embedded_worker_test_helper.cc | 131 +- .../service_worker/embedded_worker_test_helper.h | 57 +- .../service_worker/service_worker_browsertest.cc | 27 +- .../service_worker/service_worker_client_info.cc | 34 + .../service_worker/service_worker_client_info.h | 43 + .../service_worker/service_worker_client_utils.cc | 9 +- .../service_worker_content_settings_proxy_impl.cc | 5 + .../service_worker/service_worker_context_core.cc | 172 +- .../service_worker/service_worker_context_core.h | 48 +- .../service_worker_context_core_observer.h | 23 +- .../service_worker_context_request_handler.cc | 30 +- ...vice_worker_context_request_handler_unittest.cc | 43 +- .../service_worker_context_unittest.cc | 195 +- .../service_worker_context_watcher.cc | 13 +- .../service_worker_context_watcher.h | 14 +- .../service_worker_context_wrapper.cc | 21 +- .../service_worker_context_wrapper.h | 16 +- .../service_worker_controllee_request_handler.cc | 9 +- .../service_worker/service_worker_database.cc | 15 +- .../service_worker_dispatcher_host.cc | 41 +- .../service_worker_dispatcher_host.h | 23 +- .../service_worker_dispatcher_host_unittest.cc | 28 +- .../service_worker_fetch_dispatcher.cc | 53 +- .../service_worker/service_worker_handle.cc | 62 +- .../browser/service_worker/service_worker_handle.h | 49 +- .../service_worker_handle_unittest.cc | 125 +- .../browser/service_worker/service_worker_info.cc | 22 - .../browser/service_worker/service_worker_info.h | 20 +- .../service_worker_installed_script_loader.cc | 5 +- .../service_worker_installed_script_loader.h | 5 +- .../service_worker_installed_script_reader.cc | 2 +- .../service_worker_installed_script_reader.h | 4 +- .../service_worker_installed_scripts_sender.cc | 2 +- .../service_worker_installed_scripts_sender.h | 3 +- .../service_worker/service_worker_internals_ui.cc | 10 +- .../service_worker/service_worker_job_unittest.cc | 313 +-- .../service_worker/service_worker_metrics.cc | 9 +- .../service_worker/service_worker_metrics.h | 1 + .../service_worker_navigation_handle.h | 15 +- .../service_worker_navigation_handle_core.cc | 37 +- .../service_worker_navigation_handle_core.h | 13 +- .../service_worker_navigation_loader.cc | 3 +- .../service_worker_navigation_loader.h | 10 +- .../service_worker_navigation_loader_unittest.cc | 5 +- .../service_worker_new_script_loader.cc | 36 +- .../service_worker_new_script_loader.h | 20 +- .../service_worker_new_script_loader_unittest.cc | 2 +- .../service_worker_process_manager_unittest.cc | 6 +- .../service_worker/service_worker_provider_host.cc | 176 +- .../service_worker/service_worker_provider_host.h | 82 +- .../service_worker_provider_host_unittest.cc | 69 +- .../service_worker/service_worker_register_job.cc | 21 +- .../service_worker/service_worker_register_job.h | 4 + .../service_worker/service_worker_registration.cc | 23 +- .../service_worker/service_worker_registration.h | 11 +- .../service_worker_registration_object_host.cc | 44 +- .../service_worker_registration_object_host.h | 6 +- .../service_worker_registration_unittest.cc | 85 +- .../service_worker_request_handler.cc | 70 +- .../service_worker_request_handler.h | 12 - .../service_worker_script_loader_factory.cc | 18 +- .../service_worker_script_loader_factory.h | 22 +- .../service_worker/service_worker_storage.cc | 34 +- .../service_worker/service_worker_storage.h | 6 +- .../service_worker_storage_unittest.cc | 29 +- .../service_worker/service_worker_test_utils.cc | 9 +- .../service_worker/service_worker_test_utils.h | 4 - .../service_worker_url_request_job_unittest.cc | 8 +- .../service_worker/service_worker_version.cc | 136 +- .../service_worker/service_worker_version.h | 23 +- .../service_worker_version_unittest.cc | 67 +- .../service_worker_write_to_cache_job.h | 5 - .../shape_detection/shape_detection_browsertest.cc | 4 +- .../browser/shared_worker/mock_shared_worker.cc | 197 ++ .../browser/shared_worker/mock_shared_worker.h | 132 ++ .../browser/shared_worker/shared_worker_host.cc | 151 +- .../browser/shared_worker/shared_worker_host.h | 28 +- .../shared_worker/shared_worker_host_unittest.cc | 263 +++ .../shared_worker/shared_worker_script_loader.cc | 14 +- .../shared_worker/shared_worker_script_loader.h | 33 +- .../shared_worker_script_loader_factory.cc | 10 +- .../shared_worker_script_loader_factory.h | 13 +- .../shared_worker/shared_worker_service_impl.cc | 130 +- .../shared_worker/shared_worker_service_impl.h | 17 +- .../shared_worker_service_impl_unittest.cc | 537 ++--- .../browser/shared_worker/worker_browsertest.cc | 29 +- chromium/content/browser/site_instance_impl.cc | 21 +- chromium/content/browser/site_instance_impl.h | 25 +- .../content/browser/site_instance_impl_unittest.cc | 18 +- .../browser/site_per_process_browsertest.cc | 1004 ++++++++- .../site_per_process_hit_test_browsertest.cc | 591 ++++- .../speech/speech_recognition_browsertest.cc | 228 +- .../speech/speech_recognition_dispatcher_host.cc | 253 +-- .../speech/speech_recognition_dispatcher_host.h | 103 +- .../browser/speech/speech_recognition_engine.cc | 4 +- .../browser/speech/speech_recognition_engine.h | 4 +- .../speech/speech_recognition_manager_impl.cc | 31 +- .../speech/speech_recognition_manager_impl.h | 6 - .../browser/speech/speech_recognizer_impl.cc | 145 +- .../browser/speech/speech_recognizer_impl.h | 51 +- .../speech/speech_recognizer_impl_android.cc | 77 +- .../speech/speech_recognizer_impl_unittest.cc | 142 +- chromium/content/browser/startup_task_runner.cc | 1 - chromium/content/browser/storage_partition_impl.cc | 148 +- chromium/content/browser/storage_partition_impl.h | 33 +- .../browser/storage_partition_impl_browsertest.cc | 80 +- .../content/browser/storage_partition_impl_map.cc | 16 +- .../browser/storage_partition_impl_unittest.cc | 72 +- chromium/content/browser/streams/stream.cc | 8 +- .../background_tracing_manager_browsertest.cc | 29 +- .../tracing/background_tracing_manager_impl.cc | 39 +- .../tracing/background_tracing_manager_impl.h | 4 +- .../content/browser/tracing/cast_tracing_agent.cc | 19 +- .../content/browser/tracing/cast_tracing_agent.h | 4 +- .../content/browser/tracing/cros_tracing_agent.cc | 26 +- .../content/browser/tracing/cros_tracing_agent.h | 7 +- .../browser/tracing/etw_tracing_agent_win.cc | 17 +- .../browser/tracing/etw_tracing_agent_win.h | 2 +- .../tracing/memory_instrumentation_browsertest.cc | 6 +- .../content/browser/tracing/power_tracing_agent.cc | 59 +- .../content/browser/tracing/power_tracing_agent.h | 15 +- .../tracing/tracing_controller_browsertest.cc | 16 +- .../browser/tracing/tracing_controller_impl.cc | 54 +- .../browser/tracing/tracing_controller_impl.h | 11 +- .../content/browser/url_loader_factory_getter.cc | 51 +- .../content/browser/url_loader_factory_getter.h | 20 +- chromium/content/browser/utility_process_host.cc | 44 +- chromium/content/browser/utility_process_host.h | 2 + .../browser/wake_lock/wake_lock_browsertest.cc | 1 - .../aura/gesture_nav_simple_unittest.cc | 25 +- .../aura/overscroll_navigation_overlay_unittest.cc | 39 +- .../browser/web_contents/web_contents_android.cc | 119 +- .../browser/web_contents/web_contents_android.h | 27 +- .../web_contents/web_contents_delegate_unittest.cc | 51 +- .../browser/web_contents/web_contents_impl.cc | 361 +-- .../browser/web_contents/web_contents_impl.h | 73 +- .../web_contents/web_contents_impl_browsertest.cc | 620 ++++-- .../web_contents/web_contents_impl_unittest.cc | 125 +- .../web_contents/web_contents_view_android.cc | 46 +- .../web_contents/web_contents_view_android.h | 25 +- .../browser/web_contents/web_contents_view_aura.cc | 5 +- .../web_contents_view_aura_browsertest.cc | 96 +- .../browser/web_contents/web_contents_view_mac.h | 8 + .../browser/web_contents/web_contents_view_mac.mm | 29 +- .../browser/web_contents/web_drag_source_mac.mm | 6 +- .../web_package/signed_exchange_cert_fetcher.cc | 63 +- .../web_package/signed_exchange_cert_fetcher.h | 15 +- .../signed_exchange_cert_fetcher_factory.cc | 11 +- .../signed_exchange_cert_fetcher_factory.h | 5 +- .../signed_exchange_cert_fetcher_unittest.cc | 19 +- .../signed_exchange_certificate_chain.cc | 242 +- .../signed_exchange_certificate_chain.h | 24 +- .../signed_exchange_certificate_chain_fuzzer.cc | 6 +- .../signed_exchange_certificate_chain_unittest.cc | 217 +- .../browser/web_package/signed_exchange_consts.h | 9 + .../web_package/signed_exchange_devtools_proxy.cc | 185 ++ .../web_package/signed_exchange_devtools_proxy.h | 86 + .../browser/web_package/signed_exchange_handler.cc | 178 +- .../browser/web_package/signed_exchange_handler.h | 10 +- .../signed_exchange_handler_unittest.cc | 314 ++- .../browser/web_package/signed_exchange_header.cc | 129 +- .../browser/web_package/signed_exchange_header.h | 14 +- .../web_package/signed_exchange_header_parser.cc | 61 +- .../web_package/signed_exchange_header_parser.h | 12 +- .../signed_exchange_header_parser_unittest.cc | 43 +- .../web_package/signed_exchange_header_unittest.cc | 24 +- .../signed_exchange_signature_verifier.cc | 107 +- .../signed_exchange_signature_verifier.h | 11 +- .../signed_exchange_signature_verifier_unittest.cc | 282 ++- ...e_url_loader_factory_for_non_network_service.cc | 8 +- ...ge_url_loader_factory_for_non_network_service.h | 1 + .../browser/web_package/signed_exchange_utils.cc | 47 +- .../browser/web_package/signed_exchange_utils.h | 35 +- .../browser/web_package/web_package_loader.cc | 29 +- .../browser/web_package/web_package_loader.h | 17 +- .../web_package/web_package_prefetch_handler.cc | 29 +- .../web_package/web_package_prefetch_handler.h | 8 +- .../web_package/web_package_request_handler.cc | 24 +- .../web_package/web_package_request_handler.h | 7 + .../web_package_request_handler_browsertest.cc | 6 +- .../content/browser/webauth/authenticator_impl.cc | 77 +- .../browser/webauth/authenticator_impl_unittest.cc | 454 +++- .../webauth/authenticator_type_converters.cc | 37 +- .../webauth/authenticator_type_converters.h | 9 + .../browser/webauth/virtual_authenticator.cc | 3 +- .../content/browser/webauth/webauth_browsertest.cc | 7 +- .../browser/webrtc/webrtc_audio_browsertest.cc | 62 +- .../webrtc_audio_debug_recordings_browsertest.cc | 4 +- .../content/browser/webrtc/webrtc_browsertest.cc | 14 +- .../webrtc_capture_from_element_browsertest.cc | 4 +- .../webrtc/webrtc_getusermedia_browsertest.cc | 247 +- .../content/browser/webrtc/webrtc_internals.cc | 25 +- chromium/content/browser/webrtc/webrtc_internals.h | 5 +- .../browser/webrtc/webrtc_internals_unittest.cc | 1 - chromium/content/browser/websockets/README.md | 2 +- .../browser/websockets/websocket_manager.cc | 40 +- .../content/browser/websockets/websocket_manager.h | 23 +- .../websockets/websocket_manager_unittest.cc | 2 +- .../webui/content_web_ui_controller_factory.cc | 18 +- chromium/content/browser/webui/url_data_manager.cc | 1 - .../browser/webui/url_data_manager_backend.cc | 7 - .../browser/webui/web_ui_data_source_impl.cc | 19 +- .../browser/webui/web_ui_data_source_impl.h | 5 + .../browser/webui/web_ui_data_source_unittest.cc | 20 + chromium/content/browser/webui/web_ui_impl.cc | 11 +- chromium/content/browser/webui/web_ui_impl.h | 1 - .../browser/webui/web_ui_mojo_browsertest.cc | 2 +- .../browser/webui/web_ui_url_loader_factory.cc | 12 +- .../zygote_host/zygote_communication_linux.cc | 323 --- .../zygote_host/zygote_communication_linux.h | 103 - .../browser/zygote_host/zygote_handle_linux.cc | 30 - .../browser/zygote_host/zygote_host_impl_linux.cc | 297 --- .../browser/zygote_host/zygote_host_impl_linux.h | 71 - chromium/content/child/BUILD.gn | 7 +- chromium/content/child/OWNERS | 11 - chromium/content/child/assert_matching_enums.cc | 2 +- chromium/content/child/blink_platform_impl.cc | 21 +- chromium/content/child/blink_platform_impl.h | 6 - .../content/child/child_histogram_fetcher_impl.cc | 2 +- chromium/content/child/child_process.cc | 4 +- .../child_process_sandbox_support_impl_linux.cc | 11 +- chromium/content/child/child_thread_impl.cc | 54 +- chromium/content/child/child_thread_impl.h | 16 +- chromium/content/child/runtime_features.cc | 102 +- .../content/child/webfallbackthemeengine_impl.cc | 215 -- .../content/child/webfallbackthemeengine_impl.h | 40 - .../content/child/webthemeengine_impl_android.cc | 1 - .../content/child/webthemeengine_impl_default.cc | 1 - chromium/content/common/BUILD.gn | 45 +- chromium/content/common/DEPS | 6 +- chromium/content/common/OWNERS | 10 +- .../common/associated_interface_provider_impl.h | 1 + .../browser_plugin/browser_plugin_messages.h | 15 +- chromium/content/common/cache_storage/OWNERS | 1 - .../common/cache_storage/cache_storage.typemap | 19 - .../cache_storage/cache_storage_mojom_traits.cc | 75 - .../common/cache_storage/cache_storage_types.cc | 19 - .../common/cache_storage/cache_storage_types.h | 52 - chromium/content/common/child_process_host_impl.cc | 3 +- chromium/content/common/child_process_host_impl.h | 2 + .../content/common/common_param_traits_unittest.cc | 22 + .../content/common/common_sandbox_support_linux.cc | 28 +- .../content/common/content_message_generator.h | 12 - chromium/content/common/content_param_traits.cc | 141 +- chromium/content/common/content_param_traits.h | 47 + .../content/common/content_param_traits_macros.h | 8 +- chromium/content/common/content_paths.cc | 8 +- .../content_security_policy.cc | 51 +- .../content_security_policy.h | 4 +- .../content_security_policy_unittest.cc | 207 +- .../common/content_security_policy/csp_context.cc | 39 +- .../common/content_security_policy/csp_context.h | 16 +- .../csp_context_unittest.cc | 94 +- .../content_security_policy/csp_directive.cc | 4 + .../common/content_security_policy/csp_directive.h | 1 + .../content_security_policy/csp_source_list.cc | 26 +- .../content_security_policy/csp_source_list.h | 5 +- .../csp_source_list_unittest.cc | 25 +- chromium/content/common/cursors/webcursor_aura.cc | 5 + chromium/content/common/dom_storage/OWNERS | 3 - chromium/content/common/external_ipc_dumper.cc | 3 +- chromium/content/common/file_utilities.mojom | 15 - chromium/content/common/fileapi/OWNERS | 2 +- chromium/content/common/frame.mojom | 27 +- chromium/content/common/frame_messages.h | 149 +- chromium/content/common/frame_resize_params.cc | 19 - chromium/content/common/frame_resize_params.h | 47 - chromium/content/common/frame_sink_provider.mojom | 9 +- chromium/content/common/frame_visual_properties.cc | 19 + chromium/content/common/frame_visual_properties.h | 43 + .../content/common/input/event_with_latency_info.h | 8 +- .../input/event_with_latency_info_unittest.cc | 34 +- .../common/input/input_event_struct_traits.cc | 24 +- .../common/input/input_event_struct_traits.h | 4 +- chromium/content/common/input/input_handler.mojom | 12 +- .../common/input/input_param_traits_unittest.cc | 121 - .../common/input/sync_compositor_messages.h | 82 - .../common/input/synchronous_compositor.typemap | 1 - .../input/synthetic_web_input_event_builders.cc | 33 +- .../input/touch_action_optional_struct_traits.cc | 2 +- .../content/common/input/web_touch_event_traits.cc | 8 +- .../content/common/input/web_touch_event_traits.h | 5 +- chromium/content/common/input_messages.h | 230 +- .../common/inter_process_time_ticks_converter.cc | 96 +- .../common/inter_process_time_ticks_converter.h | 185 +- .../inter_process_time_ticks_converter_unittest.cc | 265 ++- .../common/manifest_share_target_util_unittest.cc | 266 --- chromium/content/common/media/audio_messages.h | 136 -- chromium/content/common/media/cdm_info.cc | 24 +- chromium/content/common/media/media_devices.cc | 13 +- chromium/content/common/media/media_devices.h | 10 +- .../common/media/media_player_delegate_messages.h | 38 +- .../common/media/media_stream_param_traits.h | 3 +- .../renderer_audio_input_stream_factory.mojom | 17 +- chromium/content/common/native_types.mojom | 2 +- chromium/content/common/native_types.typemap | 5 +- chromium/content/common/native_types_mac.typemap | 1 - chromium/content/common/navigation_params.cc | 6 +- chromium/content/common/navigation_params.h | 14 +- .../common/navigation_subresource_loader_params.cc | 1 + .../common/navigation_subresource_loader_params.h | 9 + .../notifications/notification_struct_traits.cc | 2 +- chromium/content/common/origin_trials/OWNERS | 13 - .../common/origin_trials/trial_policy_impl.cc | 44 - .../common/origin_trials/trial_policy_impl.h | 42 - chromium/content/common/origin_util.cc | 22 +- .../common/page_state_serialization_unittest.cc | 6 +- .../common/platform_notification_messages.h | 57 +- .../common/possibly_associated_interface_ptr.h | 1 + ..._associated_wrapper_shared_url_loader_factory.h | 18 + .../common/presentation/presentation.typemap | 14 +- .../presentation/presentation_struct_traits.cc | 34 - .../presentation/presentation_struct_traits.h | 156 -- .../content/common/render_frame_metadata.mojom | 27 + .../common/render_frame_metadata_struct_traits.cc | 10 +- .../common/render_frame_metadata_struct_traits.h | 37 + chromium/content/common/renderer.mojom | 31 +- chromium/content/common/resize_params.cc | 25 - chromium/content/common/resize_params.h | 89 - chromium/content/common/resource_messages.h | 2 +- chromium/content/common/resource_timing_info.h | 39 +- chromium/content/common/sandbox_init_mac.cc | 8 +- chromium/content/common/sandbox_policy_fuchsia.cc | 43 +- .../content/common/send_zygote_child_ping_linux.cc | 21 - .../service_manager_connection_impl.cc | 34 +- chromium/content/common/service_worker/BUILD.gn | 11 + .../dispatch_fetch_event_params.mojom | 20 +- .../common/service_worker/embedded_worker.mojom | 4 - .../service_worker/service_worker_container.mojom | 2 +- .../service_worker_event_dispatcher.mojom | 29 +- .../service_worker_fetch_request_mojom_traits.cc | 8 +- .../service_worker_fetch_request_mojom_traits.h | 13 +- .../service_worker_fetch_response_mojom_traits.cc | 28 +- .../service_worker_fetch_response_mojom_traits.h | 33 +- .../service_worker_loader_helpers.cc | 43 - .../service_worker/service_worker_loader_helpers.h | 10 - .../service_worker/service_worker_messages.h | 22 +- .../service_worker/service_worker_provider.mojom | 11 +- .../common/service_worker/service_worker_types.cc | 57 + .../common/service_worker/service_worker_types.h | 6 +- .../service_worker/service_worker_types.proto | 41 + .../service_worker_types_unittest.cc | 65 + .../common/service_worker/service_worker_utils.cc | 21 + .../common/service_worker/service_worker_utils.h | 5 +- .../common/single_request_url_loader_factory.cc | 5 + .../common/single_request_url_loader_factory.h | 1 + .../content/common/speech_recognition_messages.h | 121 - chromium/content/common/speech_recognizer.mojom | 95 + chromium/content/common/swapped_out_messages.cc | 6 - chromium/content/common/throttling_url_loader.cc | 2 +- .../common/throttling_url_loader_unittest.cc | 12 +- chromium/content/common/typemaps.gni | 1 - .../content/common/url_loader_factory_bundle.cc | 5 + .../content/common/url_loader_factory_bundle.h | 2 +- chromium/content/common/url_schemes.cc | 17 +- chromium/content/common/url_schemes.h | 6 +- chromium/content/common/user_agent.cc | 48 +- chromium/content/common/view_messages.h | 68 +- chromium/content/common/visual_properties.cc | 18 + chromium/content/common/visual_properties.h | 81 + .../common/wrapper_shared_url_loader_factory.cc | 26 - .../common/wrapper_shared_url_loader_factory.h | 88 - chromium/content/common/zygote_commands_linux.h | 54 - chromium/content/content_resources.grd | 5 + chromium/content/gpu/BUILD.gn | 4 + chromium/content/gpu/gpu_child_thread.cc | 3 +- chromium/content/gpu/gpu_main.cc | 8 +- chromium/content/gpu/gpu_service_factory.cc | 6 +- chromium/content/gpu/gpu_service_factory.h | 3 + .../ppapi_plugin/ppapi_blink_platform_impl.cc | 10 - .../ppapi_plugin/ppapi_blink_platform_impl.h | 2 - chromium/content/ppapi_plugin/ppapi_plugin_main.cc | 2 +- chromium/content/ppapi_plugin/ppapi_thread.cc | 4 +- chromium/content/public/android/BUILD.gn | 57 +- chromium/content/public/android/OWNERS | 1 + .../content/public/app/content_main_delegate.cc | 7 +- .../content/public/app/content_main_delegate.h | 13 +- .../public/app/mojo/content_browser_manifest.json | 289 +-- .../public/app/mojo/content_gpu_manifest.json | 24 +- .../mojo/content_packaged_services_manifest.json | 2 +- .../public/app/mojo/content_plugin_manifest.json | 14 +- .../public/app/mojo/content_renderer_manifest.json | 57 +- .../public/app/mojo/content_utility_manifest.json | 20 +- chromium/content/public/browser/BUILD.gn | 26 +- chromium/content/public/browser/DEPS | 1 + .../public/browser/android/compositor_client.h | 3 +- .../browser/android/content_view_layer_renderer.h | 13 +- .../content/public/browser/audio_service_info.cc | 26 + .../content/public/browser/audio_service_info.h | 20 + .../public/browser/background_fetch_delegate.h | 26 +- .../public/browser/background_fetch_description.cc | 31 + .../public/browser/background_fetch_description.h | 45 + .../public/browser/background_tracing_manager.h | 17 +- .../public/browser/browser_child_process_host.h | 11 +- .../browser/browser_child_process_observer.h | 18 +- chromium/content/public/browser/browser_context.h | 18 +- .../content/public/browser/browser_main_parts.cc | 4 +- .../content/public/browser/browser_main_parts.h | 2 +- .../public/browser/browsing_data_filter_builder.h | 25 +- .../content/public/browser/browsing_data_remover.h | 16 +- .../browser/child_process_termination_info.h | 39 + .../public/browser/content_browser_client.cc | 28 +- .../public/browser/content_browser_client.h | 56 +- chromium/content/public/browser/desktop_capture.cc | 7 +- .../content/public/browser/devtools_agent_host.h | 4 - chromium/content/public/browser/download_manager.h | 15 +- .../public/browser/download_manager_delegate.cc | 4 - .../public/browser/download_manager_delegate.h | 6 - chromium/content/public/browser/file_url_loader.h | 1 - chromium/content/public/browser/gpu_client.h | 31 + chromium/content/public/browser/gpu_data_manager.h | 4 - chromium/content/public/browser/gpu_utils.cc | 59 +- .../browser/keyboard_event_processing_result.h | 3 + .../public/browser/manifest_icon_selector.h | 7 +- .../public/browser/native_web_keyboard_event.h | 5 +- .../content/public/browser/navigation_controller.h | 10 +- .../content/public/browser/navigation_handle.cc | 1 - .../content/public/browser/navigation_handle.h | 16 +- .../public/browser/network_service_instance.h | 3 +- .../public/browser/notification_database_data.h | 45 + .../public/browser/notification_event_dispatcher.h | 3 +- .../content/public/browser/notification_types.h | 15 +- chromium/content/public/browser/overlay_window.h | 13 +- .../public/browser/overscroll_configuration.h | 3 + chromium/content/public/browser/page_navigator.h | 7 +- .../content/public/browser/payment_app_provider.h | 11 + .../browser/picture_in_picture_window_controller.h | 12 +- .../public/browser/presentation_service_delegate.h | 25 +- .../public/browser/provision_fetcher_factory.h | 6 +- .../public/browser/provision_fetcher_impl.cc | 13 +- .../public/browser/provision_fetcher_impl.h | 11 +- .../content/public/browser/render_frame_host.h | 9 - .../browser/render_frame_metadata_provider.h | 7 + .../content/public/browser/render_process_host.h | 58 +- .../public/browser/render_process_host_observer.h | 8 +- .../content/public/browser/render_widget_host.h | 22 +- .../public/browser/render_widget_host_observer.h | 33 + .../public/browser/render_widget_host_view.h | 38 +- .../browser/resource_dispatcher_host_delegate.cc | 8 - .../browser/resource_dispatcher_host_delegate.h | 6 - chromium/content/public/browser/resource_hints.h | 12 +- .../content/public/browser/resource_request_info.h | 8 +- .../public/browser/screen_orientation_delegate.h | 2 +- .../browser/service_worker_context_observer.h | 6 +- .../public/browser/site_isolation_policy.cc | 9 +- .../content/public/browser/site_isolation_policy.h | 3 + .../public/browser/speech_recognition_manager.h | 6 - .../browser/speech_recognition_session_config.h | 7 +- .../browser/speech_recognition_session_context.cc | 3 +- .../browser/speech_recognition_session_context.h | 10 +- .../content/public/browser/storage_partition.h | 36 +- chromium/content/public/browser/trace_uploader.h | 7 +- .../browser/url_loader_request_interceptor.h | 48 + chromium/content/public/browser/web_contents.cc | 2 +- chromium/content/public/browser/web_contents.h | 78 +- .../public/browser/web_contents_delegate.cc | 7 +- .../content/public/browser/web_contents_delegate.h | 25 +- .../content/public/browser/web_contents_observer.h | 8 +- .../public/browser/web_ui_message_handler.h | 2 + chromium/content/public/browser/webrtc_log.h | 4 - .../content/public/browser/zygote_host_linux.h | 42 - chromium/content/public/common/BUILD.gn | 53 +- .../common/browser_side_navigation_policy.cc | 15 +- .../public/common/browser_side_navigation_policy.h | 2 +- chromium/content/public/common/cdm_info.h | 30 +- .../public/common/common_param_traits_macros.h | 6 +- .../public/common/common_sandbox_support_linux.h | 16 +- chromium/content/public/common/content_client.cc | 2 +- chromium/content/public/common/content_client.h | 12 +- .../content/public/common/content_descriptors.h | 9 +- chromium/content/public/common/content_features.cc | 155 +- chromium/content/public/common/content_features.h | 35 +- chromium/content/public/common/content_switches.cc | 32 +- chromium/content/public/common/content_switches.h | 14 +- .../content/public/common/load_timing_info.mojom | 33 + .../content/public/common/load_timing_info.typemap | 14 + .../common/load_timing_info_struct_traits.cc | 40 + .../public/common/load_timing_info_struct_traits.h | 109 + chromium/content/public/common/manifest.cc | 60 - chromium/content/public/common/manifest.h | 177 -- chromium/content/public/common/manifest.typemap | 27 - .../public/common/manifest_share_target_util.cc | 127 -- .../public/common/manifest_share_target_util.h | 37 - .../public/common/manifest_struct_traits.cc | 153 -- .../content/public/common/manifest_struct_traits.h | 16 +- chromium/content/public/common/manifest_util.h | 4 +- .../content/public/common/media_stream_request.cc | 32 +- .../content/public/common/media_stream_request.h | 13 +- .../content/public/common/mojo_channel_switches.cc | 13 - .../content/public/common/mojo_channel_switches.h | 16 - .../content/public/common/origin_trial_policy.cc | 22 - .../content/public/common/origin_trial_policy.h | 25 - chromium/content/public/common/page_state.h | 14 +- .../public/common/platform_notification_data.h | 4 - .../content/public/common/presentation_info.cc | 24 - chromium/content/public/common/presentation_info.h | 69 - .../content/public/common/resource_load_info.mojom | 50 +- chromium/content/public/common/result_codes.h | 7 +- .../common/sandboxed_process_launcher_delegate.cc | 8 +- .../common/sandboxed_process_launcher_delegate.h | 6 +- .../public/common/send_zygote_child_ping_linux.h | 18 - .../public/common/speech_recognition_error.h | 2 + .../public/common/speech_recognition_error.mojom | 34 + .../public/common/speech_recognition_error.typemap | 14 + .../speech_recognition_error_struct_traits.cc | 22 + .../speech_recognition_error_struct_traits.h | 152 ++ .../public/common/speech_recognition_grammar.h | 35 - .../public/common/speech_recognition_grammar.mojom | 12 + .../public/common/speech_recognition_result.mojom | 29 + .../common/speech_recognition_result.typemap | 16 + .../speech_recognition_result_struct_traits.cc | 29 + .../speech_recognition_result_struct_traits.h | 47 + chromium/content/public/common/stop_find_action.h | 15 +- chromium/content/public/common/typemaps.gni | 4 +- chromium/content/public/common/url_constants.cc | 2 + chromium/content/public/common/url_constants.h | 2 + .../weak_wrapper_shared_url_loader_factory.cc | 47 - .../weak_wrapper_shared_url_loader_factory.h | 47 - chromium/content/public/common/web_preferences.cc | 2 + chromium/content/public/common/web_preferences.h | 8 + chromium/content/public/common/zygote_features.gni | 5 - .../public/common/zygote_fork_delegate_linux.h | 87 - chromium/content/public/common/zygote_handle.h | 45 - chromium/content/public/renderer/BUILD.gn | 36 +- .../public/renderer/content_renderer_client.cc | 19 +- .../public/renderer/content_renderer_client.h | 23 +- .../content/public/renderer/key_system_support.cc | 9 +- .../content/public/renderer/key_system_support.h | 10 +- .../public/renderer/media_stream_audio_renderer.h | 2 - .../renderer/media_stream_renderer_factory.h | 4 +- .../content/public/renderer/media_stream_utils.cc | 2 +- .../public/renderer/pepper_plugin_instance.h | 17 + chromium/content/public/renderer/render_thread.h | 10 +- chromium/content/public/renderer/render_view.h | 6 - chromium/content/public/renderer/request_peer.h | 1 - .../public/renderer/url_loader_throttle_provider.h | 3 + .../websocket_handshake_throttle_provider.h | 38 + chromium/content/public/test/android/BUILD.gn | 24 + chromium/content/renderer/BUILD.gn | 582 +++-- chromium/content/renderer/DEPS | 1 - chromium/content/renderer/OWNERS | 4 + .../accessibility/blink_ax_enum_conversion.cc | 128 +- .../accessibility/blink_ax_enum_conversion.h | 5 + .../renderer/accessibility/blink_ax_tree_source.cc | 52 +- .../accessibility/render_accessibility_impl.cc | 8 +- .../android/synchronous_compositor_filter.cc | 208 -- .../android/synchronous_compositor_filter.h | 112 - .../android/synchronous_compositor_proxy.cc | 1 - .../synchronous_compositor_proxy_chrome_ipc.cc | 171 -- .../synchronous_compositor_proxy_chrome_ipc.h | 84 - .../android/synchronous_layer_tree_frame_sink.cc | 6 +- .../android/synchronous_layer_tree_frame_sink.h | 3 + chromium/content/renderer/appcache/OWNERS | 3 - .../renderer/browser_plugin/browser_plugin.cc | 154 +- .../renderer/browser_plugin/browser_plugin.h | 36 +- .../renderer/browser_render_view_browsertest.cc | 97 +- chromium/content/renderer/cache_storage/OWNERS | 7 - .../webserviceworkercachestorage_impl.cc | 481 ---- .../webserviceworkercachestorage_impl.h | 103 - .../content/renderer/categorized_worker_pool.cc | 42 +- .../content/renderer/categorized_worker_pool.h | 15 +- .../renderer/child_frame_compositing_helper.cc | 30 +- .../child_frame_compositing_helper_unittest.cc | 11 +- chromium/content/renderer/child_frame_compositor.h | 17 +- .../renderer/content_security_policy_util.cc | 17 +- .../renderer/content_security_policy_util.h | 7 + .../device_motion_event_pump_unittest.cc | 2 +- .../render_widget_screen_metrics_emulator.cc | 59 +- .../render_widget_screen_metrics_emulator.h | 12 +- ...ender_widget_screen_metrics_emulator_delegate.h | 7 +- .../content/renderer/dom_serializer_browsertest.cc | 5 +- chromium/content/renderer/dom_storage/DEPS | 3 +- chromium/content/renderer/dom_storage/OWNERS | 5 +- .../dom_storage/local_storage_cached_area.cc | 214 +- .../dom_storage/local_storage_cached_area.h | 10 +- .../local_storage_cached_area_unittest.cc | 6 +- .../dom_storage/local_storage_cached_areas.cc | 35 +- .../local_storage_cached_areas_unittest.cc | 25 + .../renderer/dom_storage/mock_leveldb_wrapper.h | 2 + .../fetchers/associated_resource_fetcher_impl.cc | 2 +- .../fetchers/resource_fetcher_browsertest.cc | 1 - .../renderer/fetchers/resource_fetcher_impl.cc | 3 +- chromium/content/renderer/fileapi/OWNERS | 1 - .../fileapi/webfilewriter_base_unittest.cc | 1 - chromium/content/renderer/gpu/actions_parser.cc | 8 - .../renderer/gpu/gpu_benchmarking_extension.cc | 66 +- .../renderer/gpu/gpu_benchmarking_extension.h | 9 + .../renderer/gpu/render_widget_compositor.cc | 159 +- .../renderer/gpu/render_widget_compositor.h | 21 +- .../gpu/render_widget_compositor_delegate.h | 3 +- .../gpu/render_widget_compositor_unittest.cc | 2 +- chromium/content/renderer/idle_user_detector.cc | 22 +- chromium/content/renderer/idle_user_detector.h | 27 +- .../image_downloader/image_downloader_base.cc | 2 +- .../indexed_db_database_callbacks_impl.cc | 8 +- .../indexed_db_database_callbacks_impl.h | 1 + .../renderer/indexed_db/mock_webidbcallbacks.h | 2 +- .../renderer/indexed_db/webidbcursor_impl.cc | 1 + .../renderer/input/frame_input_handler_impl.cc | 3 +- .../renderer/input/frame_input_handler_impl.h | 3 +- .../content/renderer/input/input_event_filter.cc | 317 --- .../content/renderer/input/input_event_filter.h | 148 -- .../renderer/input/input_event_filter_ipc_names.cc | 25 - .../renderer/input/input_event_filter_unittest.cc | 573 ----- .../renderer/input/input_event_prediction.cc | 160 ++ .../renderer/input/input_event_prediction.h | 66 + .../input/input_event_prediction_unittest.cc | 213 ++ .../renderer/input/input_handler_manager.cc | 301 --- .../content/renderer/input/input_handler_manager.h | 175 -- .../renderer/input/input_handler_wrapper.cc | 121 - .../content/renderer/input/input_handler_wrapper.h | 80 - .../renderer/input/main_thread_event_queue.cc | 69 +- .../renderer/input/main_thread_event_queue.h | 15 +- .../input/main_thread_event_queue_unittest.cc | 58 +- .../input/main_thread_input_event_filter.cc | 40 - .../input/main_thread_input_event_filter.h | 43 - .../renderer/input/render_widget_input_handler.cc | 49 +- .../renderer/input/render_widget_input_handler.h | 7 +- .../scoped_web_input_event_with_latency_info.cc | 4 +- .../renderer/input/widget_input_handler_impl.h | 1 + .../renderer/input/widget_input_handler_manager.cc | 48 +- .../renderer/input/widget_input_handler_manager.h | 1 + .../renderer/installedapp/related_apps_fetcher.cc | 8 +- .../renderer/installedapp/related_apps_fetcher.h | 4 +- .../content/renderer/layout_test_dependencies.h | 4 + .../loader/child_url_loader_factory_bundle.cc | 68 +- .../loader/child_url_loader_factory_bundle.h | 7 + .../navigation_response_override_parameters.cc | 17 + .../navigation_response_override_parameters.h | 33 + .../content/renderer/loader/request_extra_data.cc | 2 - .../content/renderer/loader/request_extra_data.h | 33 +- .../content/renderer/loader/resource_dispatcher.cc | 236 +- .../content/renderer/loader/resource_dispatcher.h | 37 +- .../loader/resource_dispatcher_unittest.cc | 88 +- .../loader/shared_memory_data_consumer_handle.cc | 1 - .../content/renderer/loader/sync_load_context.cc | 122 +- .../content/renderer/loader/sync_load_context.h | 14 +- .../content/renderer/loader/sync_load_response.h | 5 + .../content/renderer/loader/test_request_peer.cc | 3 + .../content/renderer/loader/test_request_peer.h | 5 + .../tracked_child_url_loader_factory_bundle.h | 1 + .../renderer/loader/url_loader_client_impl.h | 8 +- .../loader/url_loader_client_impl_unittest.cc | 9 +- .../loader/url_response_body_consumer_unittest.cc | 10 +- .../loader/web_data_consumer_handle_impl.cc | 6 +- .../loader/web_data_consumer_handle_impl.h | 1 + .../web_data_consumer_handle_impl_unittest.cc | 40 +- .../content/renderer/loader/web_url_loader_impl.cc | 139 +- .../content/renderer/loader/web_url_loader_impl.h | 26 +- .../loader/web_url_loader_impl_unittest.cc | 48 +- .../renderer/loader/web_url_request_util.cc | 44 +- .../content/renderer/loader/web_url_request_util.h | 14 + .../content/renderer/manifest/manifest_manager.cc | 8 +- .../content/renderer/manifest/manifest_manager.h | 8 +- .../content/renderer/manifest/manifest_parser.cc | 60 +- .../content/renderer/manifest/manifest_parser.h | 38 +- .../renderer/manifest/manifest_parser_unittest.cc | 603 ++--- .../content/renderer/manifest/manifest_uma_util.cc | 4 +- .../content/renderer/manifest/manifest_uma_util.h | 8 +- .../content/renderer/media/audio_device_factory.cc | 70 +- .../content/renderer/media/audio_device_factory.h | 39 +- .../renderer/media/audio_input_ipc_factory.cc | 44 +- .../renderer/media/audio_input_ipc_factory.h | 10 +- .../renderer/media/audio_input_message_filter.cc | 230 -- .../renderer/media/audio_input_message_filter.h | 101 - .../content/renderer/media/audio_message_filter.cc | 232 -- .../content/renderer/media/audio_message_filter.h | 108 - .../media/audio_message_filter_unittest.cc | 194 -- .../renderer/media/audio_output_ipc_factory.cc | 21 +- .../renderer/media/audio_output_ipc_factory.h | 20 +- .../media/audio_output_ipc_factory_unittest.cc | 22 +- .../renderer/media/audio_renderer_mixer_manager.cc | 34 +- .../renderer/media/audio_renderer_mixer_manager.h | 40 +- .../media/audio_renderer_mixer_manager_unittest.cc | 224 +- .../renderer/media/audio_renderer_sink_cache.h | 23 +- .../media/audio_renderer_sink_cache_impl.cc | 145 +- .../media/audio_renderer_sink_cache_impl.h | 23 +- .../media/audio_renderer_sink_cache_unittest.cc | 107 +- .../gpu/gpu_video_accelerator_factories_impl.cc | 75 +- .../gpu/gpu_video_accelerator_factories_impl.h | 43 +- chromium/content/renderer/media/media_factory.cc | 105 +- chromium/content/renderer/media/media_factory.h | 2 + .../renderer/media/media_interface_factory.cc | 14 + .../renderer/media/media_interface_factory.h | 2 + .../renderer/media/mock_audio_device_factory.cc | 3 + .../renderer/media/mock_audio_device_factory.h | 41 +- .../content/renderer/media/mojo_audio_input_ipc.cc | 26 +- .../content/renderer/media/mojo_audio_input_ipc.h | 16 +- .../media/mojo_audio_input_ipc_unittest.cc | 74 +- .../renderer/media/mojo_audio_output_ipc.cc | 86 +- .../content/renderer/media/mojo_audio_output_ipc.h | 34 +- .../media/mojo_audio_output_ipc_unittest.cc | 115 +- chromium/content/renderer/media/render_media_log.h | 1 + .../renderer/media/renderer_webaudiodevice_impl.cc | 36 +- .../renderer/media/renderer_webaudiodevice_impl.h | 20 +- .../media/renderer_webaudiodevice_impl_unittest.cc | 28 +- .../media/renderer_webmediaplayer_delegate.cc | 92 +- .../media/renderer_webmediaplayer_delegate.h | 49 +- ...renderer_webmediaplayer_delegate_browsertest.cc | 1 + .../media/stream/apply_constraints_processor.cc | 2 + .../media/stream/apply_constraints_processor.h | 1 + .../stream/external_media_stream_audio_source.cc | 3 +- .../stream/local_media_stream_audio_source.cc | 7 +- .../media/stream/media_stream_audio_processor.cc | 3 +- .../stream/media_stream_audio_processor_options.cc | 1 + .../stream/media_stream_audio_processor_options.h | 4 +- .../media_stream_audio_processor_unittest.cc | 20 +- .../media/stream/media_stream_audio_source.cc | 4 + .../media/stream/media_stream_audio_source.h | 3 + .../media/stream/media_stream_audio_unittest.cc | 6 +- .../media/stream/media_stream_constraints_util.cc | 11 +- .../media/stream/media_stream_constraints_util.h | 10 +- .../stream/media_stream_constraints_util_audio.cc | 208 +- .../stream/media_stream_constraints_util_audio.h | 9 +- ...media_stream_constraints_util_audio_unittest.cc | 442 +++- .../media_stream_constraints_util_video_device.cc | 29 +- ...tream_constraints_util_video_device_unittest.cc | 106 +- .../stream/media_stream_renderer_factory_impl.cc | 8 +- .../stream/media_stream_renderer_factory_impl.h | 7 +- .../media_stream_video_capturer_source_unittest.cc | 2 +- .../media/stream/media_stream_video_source.h | 5 +- .../media/stream/mock_media_stream_registry.cc | 2 +- .../media/stream/mock_media_stream_video_source.cc | 1 + .../media/stream/mock_media_stream_video_source.h | 2 +- .../mock_mojo_media_stream_dispatcher_host.h | 2 +- .../media/stream/processed_local_audio_source.cc | 68 +- .../media/stream/processed_local_audio_source.h | 12 +- .../processed_local_audio_source_unittest.cc | 2 +- .../media/stream/secure_display_link_tracker.h | 1 + .../renderer/media/stream/track_audio_renderer.cc | 19 +- .../renderer/media/stream/track_audio_renderer.h | 9 +- .../renderer/media/stream/user_media_client_impl.h | 1 + .../stream/user_media_client_impl_unittest.cc | 6 +- .../renderer/media/stream/user_media_processor.cc | 58 +- .../media/stream/webaudio_media_stream_source.cc | 2 +- .../renderer/media/stream/webmediaplayer_ms.cc | 113 +- .../renderer/media/stream/webmediaplayer_ms.h | 27 +- .../media/stream/webmediaplayer_ms_compositor.cc | 63 +- .../media/stream/webmediaplayer_ms_compositor.h | 30 +- .../media/stream/webmediaplayer_ms_unittest.cc | 134 +- .../content/renderer/media/video_capture_impl.cc | 155 +- .../content/renderer/media/video_capture_impl.h | 19 +- .../media/video_capture_impl_manager_unittest.cc | 4 +- .../renderer/media/video_capture_impl_unittest.cc | 11 +- .../renderer/media/webrtc/audio_codec_factory.cc | 10 +- .../webrtc/media_stream_remote_video_source.cc | 25 +- .../media_stream_remote_video_source_unittest.cc | 1 + .../webrtc/media_stream_track_metrics_unittest.cc | 8 +- .../media/webrtc/media_stream_video_webrtc_sink.cc | 5 +- .../media/webrtc/mock_peer_connection_impl.cc | 2 +- .../media/webrtc/mock_peer_connection_impl.h | 6 +- .../mock_web_rtc_peer_connection_handler_client.h | 7 +- .../webrtc/peer_connection_dependency_factory.cc | 2 +- .../webrtc/peer_connection_dependency_factory.h | 6 +- .../webrtc/peer_connection_remote_audio_source.cc | 2 +- .../media/webrtc/peer_connection_tracker.cc | 19 +- .../media/webrtc/rtc_certificate_generator.cc | 4 - .../media/webrtc/rtc_certificate_generator.h | 1 + .../media/webrtc/rtc_data_channel_handler.cc | 1 - .../content/renderer/media/webrtc/rtc_error.cc | 61 - chromium/content/renderer/media/webrtc/rtc_error.h | 21 - .../media/webrtc/rtc_peer_connection_handler.cc | 28 +- .../media/webrtc/rtc_peer_connection_handler.h | 32 +- .../webrtc/rtc_peer_connection_handler_unittest.cc | 8 +- .../renderer/media/webrtc/rtc_rtp_parameters.cc | 158 -- .../renderer/media/webrtc/rtc_rtp_parameters.h | 33 - .../media/webrtc/rtc_rtp_parameters_unittest.cc | 165 -- .../renderer/media/webrtc/rtc_rtp_receiver.h | 1 + .../renderer/media/webrtc/rtc_rtp_sender.cc | 88 +- .../content/renderer/media/webrtc/rtc_rtp_sender.h | 6 +- .../renderer/media/webrtc/rtc_video_decoder.cc | 48 +- .../renderer/media/webrtc/rtc_video_decoder.h | 6 +- .../media/webrtc/rtc_video_decoder_unittest.cc | 18 +- .../renderer/media/webrtc/rtc_video_encoder.cc | 58 +- .../renderer/media/webrtc/rtc_video_encoder.h | 5 +- .../media/webrtc/webrtc_audio_device_impl.cc | 11 + .../media/webrtc/webrtc_audio_device_impl.h | 7 + .../renderer/media/webrtc/webrtc_audio_renderer.cc | 26 +- .../renderer/media/webrtc/webrtc_audio_renderer.h | 5 +- .../media/webrtc/webrtc_audio_renderer_unittest.cc | 64 +- .../media/webrtc/webrtc_media_stream_adapter.h | 1 + .../webrtc/webrtc_media_stream_adapter_map.cc | 2 +- .../webrtc/webrtc_media_stream_adapter_unittest.cc | 2 +- .../webrtc/webrtc_media_stream_track_adapter.cc | 1 - ...brtc_media_stream_track_adapter_map_unittest.cc | 95 + .../media/webrtc_local_audio_source_provider.cc | 8 +- .../webrtc_local_audio_source_provider_unittest.cc | 4 +- .../content/renderer/media/webrtc_logging_noop.cc | 11 - .../canvas_capture_handler.cc | 41 +- .../html_audio_element_capturer_source.cc | 2 +- .../html_audio_element_capturer_source_unittest.cc | 4 +- .../html_video_element_capturer_source_unittest.cc | 11 +- .../media_recorder/audio_track_opus_encoder.cc | 6 +- .../media_recorder/audio_track_pcm_encoder.cc | 2 +- .../audio_track_recorder_unittest.cc | 35 +- .../content/renderer/media_recorder/h264_encoder.h | 1 + .../media_recorder/media_recorder_handler.h | 1 + .../media_recorder_handler_unittest.cc | 3 +- .../content/renderer/media_recorder/vea_encoder.cc | 14 +- .../content/renderer/media_recorder/vea_encoder.h | 7 + .../content/renderer/media_recorder/vpx_encoder.h | 1 + .../renderer/mojo/blink_interface_provider_impl.h | 4 +- .../renderer/mojo/blink_interface_registry_impl.h | 1 + chromium/content/renderer/mus/BUILD.gn | 1 - chromium/content/renderer/mus/OWNERS | 2 +- .../content/renderer/mus/mus_embedded_frame.cc | 2 +- .../renderer/mus/renderer_window_tree_client.cc | 8 +- .../renderer/mus/renderer_window_tree_client.h | 5 +- .../notifications/notification_data_conversions.cc | 60 - .../notifications/notification_data_conversions.h | 4 - .../notification_data_conversions_unittest.cc | 139 -- .../notifications/notification_dispatcher.cc | 57 - .../notifications/notification_dispatcher.h | 48 - .../renderer/notifications/notification_manager.cc | 221 -- .../renderer/notifications/notification_manager.h | 84 - chromium/content/renderer/origin_trials/OWNERS | 1 - .../web_trial_token_validator_impl.cc | 33 - .../origin_trials/web_trial_token_validator_impl.h | 49 - .../pepper/content_renderer_pepper_host_factory.cc | 4 - .../content/renderer/pepper/event_conversion.cc | 31 +- .../renderer/pepper/fake_pepper_plugin_instance.cc | 18 + .../renderer/pepper/fake_pepper_plugin_instance.h | 8 + .../content/renderer/pepper/fullscreen_container.h | 7 +- .../renderer/pepper/pepper_audio_output_host.cc | 6 +- .../renderer/pepper/pepper_compositor_host.cc | 37 +- .../renderer/pepper/pepper_compositor_host.h | 10 +- .../renderer/pepper/pepper_graphics_2d_host.cc | 3 +- .../pepper/pepper_in_process_resource_creation.cc | 1 - .../renderer/pepper/pepper_media_device_manager.cc | 28 - .../pepper/pepper_media_stream_audio_track_host.cc | 35 +- .../renderer/pepper/pepper_platform_audio_input.cc | 26 +- .../renderer/pepper/pepper_platform_audio_input.h | 4 + .../pepper/pepper_platform_audio_output.cc | 9 +- .../renderer/pepper/pepper_platform_audio_output.h | 3 +- .../pepper/pepper_platform_audio_output_dev.cc | 19 +- .../pepper/pepper_platform_audio_output_dev.h | 6 +- .../renderer/pepper/pepper_plugin_instance_impl.cc | 160 +- .../renderer/pepper/pepper_plugin_instance_impl.h | 19 +- .../renderer/pepper/pepper_url_loader_host.cc | 2 +- .../renderer/pepper/pepper_url_loader_host.h | 2 +- .../renderer/pepper/pepper_url_request_unittest.cc | 6 +- .../renderer/pepper/pepper_video_encoder_host.h | 4 + .../renderer/pepper/pepper_webplugin_impl.cc | 72 +- .../renderer/pepper/pepper_webplugin_impl.h | 5 + .../plugin_instance_throttler_impl_unittest.cc | 13 +- chromium/content/renderer/pepper/plugin_module.cc | 1 - .../renderer/pepper/ppb_flash_message_loop_impl.cc | 1 - .../renderer/pepper/ppb_graphics_3d_impl.cc | 3 + .../content/renderer/pepper/ppb_graphics_3d_impl.h | 4 + .../renderer/pepper/ppb_video_decoder_impl.cc | 1 - .../content/renderer/pepper/resource_converter.cc | 3 - .../content/renderer/pepper/video_decoder_shim.cc | 10 +- chromium/content/renderer/presentation/OWNERS | 1 - .../presentation/presentation_dispatcher.cc | 46 - .../presentation/presentation_dispatcher.h | 67 - .../push_messaging/push_messaging_client.cc | 4 +- .../push_messaging/push_messaging_client.h | 6 +- chromium/content/renderer/render_frame_impl.cc | 677 ++---- chromium/content/renderer/render_frame_impl.h | 95 +- .../renderer/render_frame_impl_browsertest.cc | 69 +- chromium/content/renderer/render_frame_proxy.cc | 180 +- chromium/content/renderer/render_frame_proxy.h | 48 +- chromium/content/renderer/render_process_impl.cc | 8 +- chromium/content/renderer/render_thread_impl.cc | 312 +-- chromium/content/renderer/render_thread_impl.h | 50 +- .../renderer/render_thread_impl_browsertest.cc | 23 - .../content/renderer/render_view_browsertest.cc | 49 +- .../renderer/render_view_browsertest_mac.mm | 9 - chromium/content/renderer/render_view_impl.cc | 210 +- chromium/content/renderer/render_view_impl.h | 28 +- .../content/renderer/render_view_impl_android.cc | 1 - chromium/content/renderer/render_widget.cc | 410 +--- chromium/content/renderer/render_widget.h | 64 +- .../content/renderer/render_widget_browsertest.cc | 90 +- .../renderer/render_widget_fullscreen_pepper.cc | 18 +- .../renderer/render_widget_fullscreen_pepper.h | 11 +- .../content/renderer/render_widget_unittest.cc | 144 +- .../renderer/renderer_blink_platform_impl.cc | 199 +- .../renderer/renderer_blink_platform_impl.h | 37 +- chromium/content/renderer/renderer_main.cc | 17 +- .../renderer_main_platform_delegate_linux.cc | 2 - .../renderer_main_platform_delegate_mac.mm | 13 +- .../renderer_main_platform_delegate_win.cc | 5 +- .../content/renderer/resizing_mode_selector.cc | 12 +- chromium/content/renderer/resizing_mode_selector.h | 5 +- .../controller_service_worker_connector.cc | 48 +- .../controller_service_worker_connector.h | 32 +- .../embedded_worker_instance_client_impl.cc | 27 +- .../embedded_worker_instance_client_impl.h | 14 +- .../service_worker_context_client.cc | 302 +-- .../service_worker/service_worker_context_client.h | 39 +- .../service_worker_context_client_unittest.cc | 205 +- .../service_worker/service_worker_dispatcher.cc | 158 -- .../service_worker/service_worker_dispatcher.h | 104 - .../service_worker_dispatcher_unittest.cc | 117 - .../service_worker_fetch_context_impl.cc | 25 +- .../service_worker_fetch_context_impl.h | 9 +- .../service_worker_message_filter.cc | 3 - .../service_worker/service_worker_message_filter.h | 3 + .../service_worker_network_provider.cc | 46 +- .../service_worker_network_provider.h | 18 +- .../service_worker_provider_context.cc | 150 +- .../service_worker_provider_context.h | 56 +- .../service_worker_provider_context_unittest.cc | 234 +- .../service_worker_subresource_loader.cc | 80 +- .../service_worker_subresource_loader.h | 18 +- .../service_worker_subresource_loader_unittest.cc | 17 +- .../service_worker_type_converters.cc | 7 +- .../service_worker/web_service_worker_impl.cc | 72 +- .../service_worker/web_service_worker_impl.h | 96 +- ...service_worker_installed_scripts_manager_impl.h | 1 + .../web_service_worker_provider_impl.cc | 18 +- .../web_service_worker_provider_impl.h | 3 - .../web_service_worker_registration_impl.cc | 376 +--- .../web_service_worker_registration_impl.h | 225 +- .../service_worker/worker_fetch_context_impl.cc | 180 +- .../service_worker/worker_fetch_context_impl.h | 88 +- .../renderer/shared_memory_seqlock_reader.cc | 62 - .../renderer/shared_memory_seqlock_reader.h | 78 - .../shared_worker/embedded_shared_worker_stub.cc | 43 +- .../renderer/speech_recognition_dispatcher.cc | 242 +- .../renderer/speech_recognition_dispatcher.h | 85 +- .../content/renderer/visual_state_browsertest.cc | 4 +- chromium/content/renderer/web_ui_extension.cc | 1 - chromium/content/renderer/webfileutilities_impl.cc | 55 - chromium/content/renderer/webfileutilities_impl.h | 36 - .../renderer/webgraphicscontext3d_provider_impl.cc | 5 - .../renderer/webgraphicscontext3d_provider_impl.h | 1 - .../renderer/worker_thread_message_filter.cc | 1 + chromium/content/shell/BUILD.gn | 70 +- chromium/content/shell/android/BUILD.gn | 22 + chromium/content/shell/common/layout_test.mojom | 5 +- .../common/layout_test/layout_test_messages.h | 5 +- .../common/layout_test/layout_test_switches.cc | 11 +- .../common/layout_test/layout_test_switches.h | 3 +- .../content/shell/common/leak_detection_result.h | 19 - .../content/shell/common/shell_content_client.cc | 6 +- .../content/shell/common/shell_content_client.h | 3 +- chromium/content/shell/common/shell_messages.h | 15 - .../shell/common/shell_origin_trial_policy.cc | 13 + .../shell/common/shell_origin_trial_policy.h | 8 +- chromium/content/shell/common/shell_switches.cc | 4 +- chromium/content/shell/common/shell_switches.h | 4 +- chromium/content/shell/test_runner/BUILD.gn | 46 +- chromium/content/test/BUILD.gn | 390 ++-- chromium/content/utility/BUILD.gn | 3 +- chromium/content/utility/DEPS | 1 + .../content/utility/utility_service_factory.cc | 6 + chromium/content/zygote/OWNERS | 8 +- chromium/content/zygote/zygote_browsertest.cc | 4 +- chromium/content/zygote/zygote_linux.cc | 668 ------ chromium/content/zygote/zygote_linux.h | 152 -- chromium/content/zygote/zygote_main.h | 22 - chromium/content/zygote/zygote_main_linux.cc | 244 -- 1928 files changed, 72510 insertions(+), 57605 deletions(-) delete mode 100644 chromium/content/app/content_main_runner.cc create mode 100644 chromium/content/app/content_main_runner_impl.cc create mode 100644 chromium/content/app/content_main_runner_impl.h create mode 100644 chromium/content/browser/android/content_ui_event_handler.cc create mode 100644 chromium/content/browser/android/content_ui_event_handler.h delete mode 100644 chromium/content/browser/android/interstitial_page_delegate_android.cc delete mode 100644 chromium/content/browser/android/interstitial_page_delegate_android.h delete mode 100644 chromium/content/browser/android/string_message_codec.cc delete mode 100644 chromium/content/browser/android/string_message_codec.h delete mode 100644 chromium/content/browser/android/string_message_codec_unittest.cc delete mode 100644 chromium/content/browser/android/synchronous_compositor_browser_filter.cc delete mode 100644 chromium/content/browser/android/synchronous_compositor_browser_filter.h create mode 100644 chromium/content/browser/background_fetch/background_fetch_metrics.cc create mode 100644 chromium/content/browser/background_fetch/background_fetch_metrics.h create mode 100644 chromium/content/browser/background_fetch/storage/get_num_requests_task.cc create mode 100644 chromium/content/browser/background_fetch/storage/get_num_requests_task.h create mode 100644 chromium/content/browser/background_fetch/storage/get_settled_fetches_task.cc create mode 100644 chromium/content/browser/background_fetch/storage/get_settled_fetches_task.h create mode 100644 chromium/content/browser/background_fetch/storage/mark_request_complete_task.cc create mode 100644 chromium/content/browser/background_fetch/storage/mark_request_complete_task.h create mode 100644 chromium/content/browser/background_fetch/storage/start_next_pending_request_task.cc create mode 100644 chromium/content/browser/background_fetch/storage/start_next_pending_request_task.h delete mode 100644 chromium/content/browser/browser_main_runner.cc create mode 100644 chromium/content/browser/browser_main_runner_impl.cc create mode 100644 chromium/content/browser/browser_main_runner_impl.h create mode 100644 chromium/content/browser/cookie_store/BUILD.gn create mode 100644 chromium/content/browser/cookie_store/OWNERS create mode 100644 chromium/content/browser/cookie_store/cookie_change_subscription.cc create mode 100644 chromium/content/browser/cookie_store/cookie_change_subscription.h create mode 100644 chromium/content/browser/cookie_store/cookie_change_subscriptions.proto create mode 100644 chromium/content/browser/cookie_store/cookie_store_context.cc create mode 100644 chromium/content/browser/cookie_store/cookie_store_context.h create mode 100644 chromium/content/browser/cookie_store/cookie_store_host.cc create mode 100644 chromium/content/browser/cookie_store/cookie_store_host.h create mode 100644 chromium/content/browser/cookie_store/cookie_store_manager.cc create mode 100644 chromium/content/browser/cookie_store/cookie_store_manager.h create mode 100644 chromium/content/browser/cookie_store/cookie_store_manager_unittest.cc create mode 100644 chromium/content/browser/devtools/devtools_stream_blob.cc create mode 100644 chromium/content/browser/devtools/devtools_stream_blob.h create mode 100644 chromium/content/browser/devtools/devtools_stream_file.cc create mode 100644 chromium/content/browser/devtools/devtools_stream_file.h create mode 100644 chromium/content/browser/devtools/devtools_stream_pipe.cc create mode 100644 chromium/content/browser/devtools/devtools_stream_pipe.h create mode 100644 chromium/content/browser/dom_storage/session_storage_context_mojo_unittest.cc create mode 100644 chromium/content/browser/dom_storage/session_storage_data_map.cc create mode 100644 chromium/content/browser/dom_storage/session_storage_data_map.h create mode 100644 chromium/content/browser/dom_storage/session_storage_data_map_unittest.cc create mode 100644 chromium/content/browser/dom_storage/session_storage_leveldb_wrapper.cc create mode 100644 chromium/content/browser/dom_storage/session_storage_leveldb_wrapper.h create mode 100644 chromium/content/browser/dom_storage/session_storage_leveldb_wrapper_unittest.cc create mode 100644 chromium/content/browser/dom_storage/session_storage_metadata.cc create mode 100644 chromium/content/browser/dom_storage/session_storage_metadata.h create mode 100644 chromium/content/browser/dom_storage/session_storage_metadata_unittest.cc create mode 100644 chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo.cc create mode 100644 chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo.h create mode 100644 chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc create mode 100644 chromium/content/browser/download/download_url_loader_factory_getter_impl.cc create mode 100644 chromium/content/browser/download/download_url_loader_factory_getter_impl.h create mode 100644 chromium/content/browser/fileapi/file_system_url_loader_factory.cc create mode 100644 chromium/content/browser/fileapi/file_system_url_loader_factory.h create mode 100644 chromium/content/browser/fileapi/file_system_url_loader_factory_browsertest.cc create mode 100644 chromium/content/browser/frame_host/blocked_scheme_navigation_browsertest.cc create mode 100644 chromium/content/browser/frame_host/blocked_scheme_navigation_throttle.cc create mode 100644 chromium/content/browser/frame_host/blocked_scheme_navigation_throttle.h delete mode 100644 chromium/content/browser/frame_host/data_url_navigation_browsertest.cc delete mode 100644 chromium/content/browser/frame_host/data_url_navigation_throttle.cc delete mode 100644 chromium/content/browser/frame_host/data_url_navigation_throttle.h delete mode 100644 chromium/content/browser/frame_host/input/legacy_ipc_frame_input_handler.cc delete mode 100644 chromium/content/browser/frame_host/input/legacy_ipc_frame_input_handler.h create mode 100644 chromium/content/browser/frame_host/webui_navigation_browsertest.cc create mode 100644 chromium/content/browser/frame_host/webui_navigation_throttle.cc create mode 100644 chromium/content/browser/frame_host/webui_navigation_throttle.h delete mode 100644 chromium/content/browser/gpu/gpu_client.cc delete mode 100644 chromium/content/browser/gpu/gpu_client.h create mode 100644 chromium/content/browser/gpu/gpu_client_impl.cc create mode 100644 chromium/content/browser/gpu/gpu_client_impl.h delete mode 100644 chromium/content/browser/histogram_internals_request_job.cc delete mode 100644 chromium/content/browser/histogram_internals_request_job.h delete mode 100644 chromium/content/browser/histogram_internals_url_loader.cc delete mode 100644 chromium/content/browser/histogram_internals_url_loader.h create mode 100644 chromium/content/browser/histograms_internals_ui.cc create mode 100644 chromium/content/browser/histograms_internals_ui.h create mode 100644 chromium/content/browser/initiator_csp_context.cc create mode 100644 chromium/content/browser/initiator_csp_context.h create mode 100644 chromium/content/browser/keyboard_lock/keyboard_lock_metrics.h create mode 100644 chromium/content/browser/keyboard_lock_browsertest.h create mode 100644 chromium/content/browser/keyboard_lock_browsertest_mac.mm create mode 100644 chromium/content/browser/loader/loader_browsertest.cc create mode 100644 chromium/content/browser/loader/navigation_loader_util.cc create mode 100644 chromium/content/browser/loader/navigation_loader_util.h delete mode 100644 chromium/content/browser/loader/navigation_metrics.cc delete mode 100644 chromium/content/browser/loader/navigation_metrics.h delete mode 100644 chromium/content/browser/loader/navigation_resource_handler.cc delete mode 100644 chromium/content/browser/loader/navigation_resource_handler.h delete mode 100644 chromium/content/browser/loader/navigation_url_loader_impl_core.cc delete mode 100644 chromium/content/browser/loader/navigation_url_loader_impl_core.h create mode 100644 chromium/content/browser/loader/navigation_url_loader_impl_unittest.cc delete mode 100644 chromium/content/browser/loader/navigation_url_loader_network_service.cc delete mode 100644 chromium/content/browser/loader/navigation_url_loader_network_service.h delete mode 100644 chromium/content/browser/loader/navigation_url_loader_network_service_unittest.cc delete mode 100644 chromium/content/browser/loader/resource_dispatcher_host_browsertest.cc create mode 100644 chromium/content/browser/media/audio_input_stream_broker.cc create mode 100644 chromium/content/browser/media/audio_input_stream_broker.h create mode 100644 chromium/content/browser/media/audio_input_stream_broker_unittest.cc create mode 100644 chromium/content/browser/media/audio_loopback_stream_broker.cc create mode 100644 chromium/content/browser/media/audio_loopback_stream_broker.h create mode 100644 chromium/content/browser/media/audio_loopback_stream_broker_unittest.cc create mode 100644 chromium/content/browser/media/audio_muting_session.cc create mode 100644 chromium/content/browser/media/audio_muting_session.h create mode 100644 chromium/content/browser/media/audio_output_stream_broker.cc create mode 100644 chromium/content/browser/media/audio_output_stream_broker.h create mode 100644 chromium/content/browser/media/audio_output_stream_broker_unittest.cc create mode 100644 chromium/content/browser/media/audio_stream_broker.cc create mode 100644 chromium/content/browser/media/audio_stream_broker.h create mode 100644 chromium/content/browser/media/capture/aura_window_video_capture_device.cc create mode 100644 chromium/content/browser/media/capture/aura_window_video_capture_device.h create mode 100644 chromium/content/browser/media/capture/aura_window_video_capture_device_browsertest.cc create mode 100644 chromium/content/browser/media/capture/content_capture_device_browsertest_base.cc create mode 100644 chromium/content/browser/media/capture/content_capture_device_browsertest_base.h create mode 100644 chromium/content/browser/media/capture/fake_video_capture_stack.cc create mode 100644 chromium/content/browser/media/capture/fake_video_capture_stack.h create mode 100644 chromium/content/browser/media/capture/frame_test_util.cc create mode 100644 chromium/content/browser/media/capture/frame_test_util.h create mode 100644 chromium/content/browser/media/capture/lame_window_capturer_chromeos.cc create mode 100644 chromium/content/browser/media/capture/lame_window_capturer_chromeos.h create mode 100644 chromium/content/browser/media/flinging_renderer.cc create mode 100644 chromium/content/browser/media/flinging_renderer.h create mode 100644 chromium/content/browser/media/flinging_renderer_unittest.cc create mode 100644 chromium/content/browser/media/forwarding_audio_stream_factory.cc create mode 100644 chromium/content/browser/media/forwarding_audio_stream_factory.h create mode 100644 chromium/content/browser/media/forwarding_audio_stream_factory_unittest.cc create mode 100644 chromium/content/browser/media/keyboard_mic_registration.cc create mode 100644 chromium/content/browser/media/keyboard_mic_registration.h delete mode 100644 chromium/content/browser/notifications/notification_message_filter.cc delete mode 100644 chromium/content/browser/notifications/notification_message_filter.h create mode 100644 chromium/content/browser/process_internals/BUILD.gn create mode 100644 chromium/content/browser/process_internals/OWNERS create mode 100644 chromium/content/browser/process_internals/process_internals.mojom create mode 100644 chromium/content/browser/process_internals/process_internals_handler_impl.cc create mode 100644 chromium/content/browser/process_internals/process_internals_handler_impl.h create mode 100644 chromium/content/browser/process_internals/process_internals_ui.cc create mode 100644 chromium/content/browser/process_internals/process_internals_ui.h delete mode 100644 chromium/content/browser/renderer_host/compositor_resize_lock.cc delete mode 100644 chromium/content/browser/renderer_host/compositor_resize_lock.h delete mode 100644 chromium/content/browser/renderer_host/compositor_resize_lock_unittest.cc create mode 100644 chromium/content/browser/renderer_host/embedded_frame_sink_impl.cc create mode 100644 chromium/content/browser/renderer_host/embedded_frame_sink_impl.h create mode 100644 chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl.cc create mode 100644 chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl.h create mode 100644 chromium/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc create mode 100644 chromium/content/browser/renderer_host/input/fling_scheduler.cc create mode 100644 chromium/content/browser/renderer_host/input/fling_scheduler.h create mode 100644 chromium/content/browser/renderer_host/input/fling_scheduler_mac.h create mode 100644 chromium/content/browser/renderer_host/input/fling_scheduler_mac.mm create mode 100644 chromium/content/browser/renderer_host/input/fling_scheduler_unittest.cc delete mode 100644 chromium/content/browser/renderer_host/input/legacy_input_router_impl.cc delete mode 100644 chromium/content/browser/renderer_host/input/legacy_input_router_impl.h delete mode 100644 chromium/content/browser/renderer_host/input/legacy_input_router_impl_perftest.cc delete mode 100644 chromium/content/browser/renderer_host/input/legacy_input_router_impl_unittest.cc delete mode 100644 chromium/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.cc delete mode 100644 chromium/content/browser/renderer_host/input/legacy_ipc_widget_input_handler.h delete mode 100644 chromium/content/browser/renderer_host/input/tap_suppression_controller_client.h delete mode 100644 chromium/content/browser/renderer_host/media/audio_input_renderer_host.cc delete mode 100644 chromium/content/browser/renderer_host/media/audio_input_renderer_host.h delete mode 100644 chromium/content/browser/renderer_host/media/audio_input_renderer_host_unittest.cc delete mode 100644 chromium/content/browser/renderer_host/media/audio_renderer_host.cc delete mode 100644 chromium/content/browser/renderer_host/media/audio_renderer_host.h delete mode 100644 chromium/content/browser/renderer_host/media/audio_renderer_host_unittest.cc create mode 100644 chromium/content/browser/renderer_host/media/audio_service_listener.cc create mode 100644 chromium/content/browser/renderer_host/media/audio_service_listener.h create mode 100644 chromium/content/browser/renderer_host/media/audio_service_listener_unittest.cc create mode 100644 chromium/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.cc create mode 100644 chromium/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.h create mode 100644 chromium/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory_unittest.cc create mode 100644 chromium/content/browser/renderer_host/media/old_render_frame_audio_output_stream_factory.cc create mode 100644 chromium/content/browser/renderer_host/media/old_render_frame_audio_output_stream_factory.h create mode 100644 chromium/content/browser/renderer_host/media/old_render_frame_audio_output_stream_factory_unittest.cc delete mode 100644 chromium/content/browser/renderer_host/offscreen_canvas_provider_impl.cc delete mode 100644 chromium/content/browser/renderer_host/offscreen_canvas_provider_impl.h delete mode 100644 chromium/content/browser/renderer_host/offscreen_canvas_provider_impl_unittest.cc delete mode 100644 chromium/content/browser/renderer_host/offscreen_canvas_surface_impl.cc delete mode 100644 chromium/content/browser/renderer_host/offscreen_canvas_surface_impl.h create mode 100644 chromium/content/browser/resources/BUILD.gn create mode 100644 chromium/content/browser/resources/histograms/BUILD.gn create mode 100644 chromium/content/browser/resources/histograms/OWNERS create mode 100644 chromium/content/browser/resources/histograms/histograms_internals.html create mode 100644 chromium/content/browser/resources/histograms/histograms_internals.js create mode 100644 chromium/content/browser/resources/process/process_internals.html create mode 100644 chromium/content/browser/resources/process/process_internals.js create mode 100644 chromium/content/browser/service_worker/service_worker_client_info.cc create mode 100644 chromium/content/browser/service_worker/service_worker_client_info.h create mode 100644 chromium/content/browser/shared_worker/mock_shared_worker.cc create mode 100644 chromium/content/browser/shared_worker/mock_shared_worker.h create mode 100644 chromium/content/browser/shared_worker/shared_worker_host_unittest.cc create mode 100644 chromium/content/browser/web_package/signed_exchange_devtools_proxy.cc create mode 100644 chromium/content/browser/web_package/signed_exchange_devtools_proxy.h delete mode 100644 chromium/content/browser/zygote_host/zygote_communication_linux.cc delete mode 100644 chromium/content/browser/zygote_host/zygote_communication_linux.h delete mode 100644 chromium/content/browser/zygote_host/zygote_handle_linux.cc delete mode 100644 chromium/content/browser/zygote_host/zygote_host_impl_linux.cc delete mode 100644 chromium/content/browser/zygote_host/zygote_host_impl_linux.h delete mode 100644 chromium/content/child/webfallbackthemeengine_impl.cc delete mode 100644 chromium/content/child/webfallbackthemeengine_impl.h delete mode 100644 chromium/content/common/cache_storage/cache_storage.typemap delete mode 100644 chromium/content/common/cache_storage/cache_storage_mojom_traits.cc delete mode 100644 chromium/content/common/cache_storage/cache_storage_types.cc delete mode 100644 chromium/content/common/cache_storage/cache_storage_types.h delete mode 100644 chromium/content/common/file_utilities.mojom delete mode 100644 chromium/content/common/frame_resize_params.cc delete mode 100644 chromium/content/common/frame_resize_params.h create mode 100644 chromium/content/common/frame_visual_properties.cc create mode 100644 chromium/content/common/frame_visual_properties.h delete mode 100644 chromium/content/common/input/input_param_traits_unittest.cc delete mode 100644 chromium/content/common/manifest_share_target_util_unittest.cc delete mode 100644 chromium/content/common/media/audio_messages.h delete mode 100644 chromium/content/common/origin_trials/OWNERS delete mode 100644 chromium/content/common/origin_trials/trial_policy_impl.cc delete mode 100644 chromium/content/common/origin_trials/trial_policy_impl.h create mode 100644 chromium/content/common/possibly_associated_wrapper_shared_url_loader_factory.h delete mode 100644 chromium/content/common/resize_params.cc delete mode 100644 chromium/content/common/resize_params.h delete mode 100644 chromium/content/common/send_zygote_child_ping_linux.cc create mode 100644 chromium/content/common/service_worker/BUILD.gn create mode 100644 chromium/content/common/service_worker/service_worker_types.proto delete mode 100644 chromium/content/common/speech_recognition_messages.h create mode 100644 chromium/content/common/speech_recognizer.mojom create mode 100644 chromium/content/common/visual_properties.cc create mode 100644 chromium/content/common/visual_properties.h delete mode 100644 chromium/content/common/wrapper_shared_url_loader_factory.cc delete mode 100644 chromium/content/common/wrapper_shared_url_loader_factory.h delete mode 100644 chromium/content/common/zygote_commands_linux.h create mode 100644 chromium/content/public/browser/audio_service_info.cc create mode 100644 chromium/content/public/browser/audio_service_info.h create mode 100644 chromium/content/public/browser/background_fetch_description.cc create mode 100644 chromium/content/public/browser/background_fetch_description.h create mode 100644 chromium/content/public/browser/child_process_termination_info.h create mode 100644 chromium/content/public/browser/gpu_client.h create mode 100644 chromium/content/public/browser/render_widget_host_observer.h create mode 100644 chromium/content/public/browser/url_loader_request_interceptor.h delete mode 100644 chromium/content/public/browser/zygote_host_linux.h create mode 100644 chromium/content/public/common/load_timing_info.mojom create mode 100644 chromium/content/public/common/load_timing_info.typemap create mode 100644 chromium/content/public/common/load_timing_info_struct_traits.cc create mode 100644 chromium/content/public/common/load_timing_info_struct_traits.h delete mode 100644 chromium/content/public/common/manifest.cc delete mode 100644 chromium/content/public/common/manifest.h delete mode 100644 chromium/content/public/common/manifest.typemap delete mode 100644 chromium/content/public/common/manifest_share_target_util.cc delete mode 100644 chromium/content/public/common/manifest_share_target_util.h delete mode 100644 chromium/content/public/common/manifest_struct_traits.cc delete mode 100644 chromium/content/public/common/mojo_channel_switches.cc delete mode 100644 chromium/content/public/common/mojo_channel_switches.h delete mode 100644 chromium/content/public/common/origin_trial_policy.cc delete mode 100644 chromium/content/public/common/origin_trial_policy.h delete mode 100644 chromium/content/public/common/presentation_info.cc delete mode 100644 chromium/content/public/common/presentation_info.h delete mode 100644 chromium/content/public/common/send_zygote_child_ping_linux.h create mode 100644 chromium/content/public/common/speech_recognition_error.mojom create mode 100644 chromium/content/public/common/speech_recognition_error.typemap create mode 100644 chromium/content/public/common/speech_recognition_error_struct_traits.cc create mode 100644 chromium/content/public/common/speech_recognition_error_struct_traits.h delete mode 100644 chromium/content/public/common/speech_recognition_grammar.h create mode 100644 chromium/content/public/common/speech_recognition_grammar.mojom create mode 100644 chromium/content/public/common/speech_recognition_result.mojom create mode 100644 chromium/content/public/common/speech_recognition_result.typemap create mode 100644 chromium/content/public/common/speech_recognition_result_struct_traits.cc create mode 100644 chromium/content/public/common/speech_recognition_result_struct_traits.h delete mode 100644 chromium/content/public/common/weak_wrapper_shared_url_loader_factory.cc delete mode 100644 chromium/content/public/common/weak_wrapper_shared_url_loader_factory.h delete mode 100644 chromium/content/public/common/zygote_features.gni delete mode 100644 chromium/content/public/common/zygote_fork_delegate_linux.h delete mode 100644 chromium/content/public/common/zygote_handle.h create mode 100644 chromium/content/public/renderer/websocket_handshake_throttle_provider.h delete mode 100644 chromium/content/renderer/android/synchronous_compositor_filter.cc delete mode 100644 chromium/content/renderer/android/synchronous_compositor_filter.h delete mode 100644 chromium/content/renderer/android/synchronous_compositor_proxy_chrome_ipc.cc delete mode 100644 chromium/content/renderer/android/synchronous_compositor_proxy_chrome_ipc.h delete mode 100644 chromium/content/renderer/cache_storage/OWNERS delete mode 100644 chromium/content/renderer/cache_storage/webserviceworkercachestorage_impl.cc delete mode 100644 chromium/content/renderer/cache_storage/webserviceworkercachestorage_impl.h delete mode 100644 chromium/content/renderer/input/input_event_filter.cc delete mode 100644 chromium/content/renderer/input/input_event_filter.h delete mode 100644 chromium/content/renderer/input/input_event_filter_ipc_names.cc delete mode 100644 chromium/content/renderer/input/input_event_filter_unittest.cc create mode 100644 chromium/content/renderer/input/input_event_prediction.cc create mode 100644 chromium/content/renderer/input/input_event_prediction.h create mode 100644 chromium/content/renderer/input/input_event_prediction_unittest.cc delete mode 100644 chromium/content/renderer/input/input_handler_manager.cc delete mode 100644 chromium/content/renderer/input/input_handler_manager.h delete mode 100644 chromium/content/renderer/input/input_handler_wrapper.cc delete mode 100644 chromium/content/renderer/input/input_handler_wrapper.h delete mode 100644 chromium/content/renderer/input/main_thread_input_event_filter.cc delete mode 100644 chromium/content/renderer/input/main_thread_input_event_filter.h create mode 100644 chromium/content/renderer/loader/navigation_response_override_parameters.cc create mode 100644 chromium/content/renderer/loader/navigation_response_override_parameters.h delete mode 100644 chromium/content/renderer/media/audio_input_message_filter.cc delete mode 100644 chromium/content/renderer/media/audio_input_message_filter.h delete mode 100644 chromium/content/renderer/media/audio_message_filter.cc delete mode 100644 chromium/content/renderer/media/audio_message_filter.h delete mode 100644 chromium/content/renderer/media/audio_message_filter_unittest.cc delete mode 100644 chromium/content/renderer/media/webrtc/rtc_error.cc delete mode 100644 chromium/content/renderer/media/webrtc/rtc_error.h delete mode 100644 chromium/content/renderer/media/webrtc/rtc_rtp_parameters.cc delete mode 100644 chromium/content/renderer/media/webrtc/rtc_rtp_parameters.h delete mode 100644 chromium/content/renderer/media/webrtc/rtc_rtp_parameters_unittest.cc delete mode 100644 chromium/content/renderer/media/webrtc_logging_noop.cc delete mode 100644 chromium/content/renderer/notifications/notification_dispatcher.cc delete mode 100644 chromium/content/renderer/notifications/notification_dispatcher.h delete mode 100644 chromium/content/renderer/notifications/notification_manager.cc delete mode 100644 chromium/content/renderer/notifications/notification_manager.h delete mode 100644 chromium/content/renderer/origin_trials/OWNERS delete mode 100644 chromium/content/renderer/origin_trials/web_trial_token_validator_impl.cc delete mode 100644 chromium/content/renderer/origin_trials/web_trial_token_validator_impl.h delete mode 100644 chromium/content/renderer/presentation/OWNERS delete mode 100644 chromium/content/renderer/presentation/presentation_dispatcher.cc delete mode 100644 chromium/content/renderer/presentation/presentation_dispatcher.h delete mode 100644 chromium/content/renderer/service_worker/service_worker_dispatcher.cc delete mode 100644 chromium/content/renderer/service_worker/service_worker_dispatcher.h delete mode 100644 chromium/content/renderer/service_worker/service_worker_dispatcher_unittest.cc delete mode 100644 chromium/content/renderer/shared_memory_seqlock_reader.cc delete mode 100644 chromium/content/renderer/shared_memory_seqlock_reader.h delete mode 100644 chromium/content/renderer/webfileutilities_impl.cc delete mode 100644 chromium/content/renderer/webfileutilities_impl.h delete mode 100644 chromium/content/shell/common/leak_detection_result.h delete mode 100644 chromium/content/zygote/zygote_linux.cc delete mode 100644 chromium/content/zygote/zygote_linux.h delete mode 100644 chromium/content/zygote/zygote_main.h delete mode 100644 chromium/content/zygote/zygote_main_linux.cc (limited to 'chromium/content') diff --git a/chromium/content/BUILD.gn b/chromium/content/BUILD.gn index 7c567481db6..09962146a24 100644 --- a/chromium/content/BUILD.gn +++ b/chromium/content/BUILD.gn @@ -91,11 +91,8 @@ if (is_component_build) { sources = [ "//services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.cc", "common/sandbox_init_linux.cc", - "common/send_zygote_child_ping_linux.cc", "public/common/content_switches.cc", "public/common/content_switches.h", - "public/common/mojo_channel_switches.cc", - "public/common/mojo_channel_switches.h", ] set_sources_assignment_filter(sources_assignment_filter) deps = [ @@ -127,6 +124,7 @@ grit("resources") { "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir), ] deps = [ + "//content/browser/process_internals:mojo_bindings_js", "//content/public/app:browser_manifest", "//content/public/app:gpu_manifest", "//content/public/app:packaged_services_manifest", diff --git a/chromium/content/DEPS b/chromium/content/DEPS index c11b886fbd6..1ec0d628f39 100644 --- a/chromium/content/DEPS +++ b/chromium/content/DEPS @@ -10,11 +10,8 @@ include_rules = [ "+content/public/common", "+content/public/test", "+content/test", - "+blink/public/resources/grit", - "+cc", - "-cc/blink", "-components", # Content can depend on components that are: @@ -35,7 +32,6 @@ include_rules = [ "+dbus", "+gpu", "+media", - "+mojo/common", "+mojo/edk/embedder", "+mojo/edk/js", "+mojo/message_pump", @@ -46,6 +42,8 @@ include_rules = [ "+sandbox", "+services/proxy_resolver/public/mojom", "+services/service_manager/embedder", + "+services/service_manager/sandbox", + "+services/service_manager/zygote", "+skia", # In general, content/ should not rely on google_apis, since URLs diff --git a/chromium/content/OWNERS b/chromium/content/OWNERS index 53d2f572750..239b4e6552c 100644 --- a/chromium/content/OWNERS +++ b/chromium/content/OWNERS @@ -28,4 +28,10 @@ per-file *.sb=rsesek@chromium.org # For Fuchsia-specific changes: per-file *_fuchsia*=file://build/fuchsia/OWNERS +# For Mac-specific changes: +per-file *_mac*=ellyjones@chromium.org +per-file *_mac*=tapted@chromium.org +per-file *.mm=ellyjones@chromium.org +per-file *.mm=tapted@chromium.org + # COMPONENT: Internals>Core diff --git a/chromium/content/app/BUILD.gn b/chromium/content/app/BUILD.gn index 8d8f2826384..518bcc9435d 100644 --- a/chromium/content/app/BUILD.gn +++ b/chromium/content/app/BUILD.gn @@ -71,10 +71,10 @@ template("implement_content_app") { content_app_deps += [ "//content/ppapi_plugin:ppapi_plugin_sources" ] } - # Compile content_main_runner.cc in a separate target to exempt from GN - # header checking without exempting any other source file. This file includes - # headers of all process types and varies significantly per platform in - # between browser and child. Otherwise it would require many "nogncheck" + # Compile content_main_runner_impl.[h, cc] in a separate target to exempt from + # GN header checking without exempting any other source file. These files + # includes headers of all process types and varies significantly per platform + # in between browser and child. Otherwise it would require many "nogncheck" # annotations that would both be useless and add noise. # # This will generate :content_main_runner_both, :content_main_runner_browser, @@ -84,7 +84,8 @@ template("implement_content_app") { check_includes = false sources = [ - "content_main_runner.cc", + "content_main_runner_impl.cc", + "content_main_runner_impl.h", ] configs += extra_configs diff --git a/chromium/content/app/content_main_runner.cc b/chromium/content/app/content_main_runner.cc deleted file mode 100644 index d1e6e7d28d4..00000000000 --- a/chromium/content/app/content_main_runner.cc +++ /dev/null @@ -1,985 +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/public/app/content_main_runner.h" - -#include -#include -#include - -#include -#include -#include -#include - -#include "base/allocator/allocator_check.h" -#include "base/allocator/allocator_extension.h" -#include "base/allocator/buildflags.h" -#include "base/at_exit.h" -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/debug/debugger.h" -#include "base/debug/stack_trace.h" -#include "base/feature_list.h" -#include "base/files/file_path.h" -#include "base/i18n/icu_util.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/metrics/field_trial.h" -#include "base/metrics/histogram_base.h" -#include "base/path_service.h" -#include "base/process/launch.h" -#include "base/process/memory.h" -#include "base/process/process.h" -#include "base/process/process_handle.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/trace_event.h" -#include "build/build_config.h" -#include "components/tracing/common/trace_startup.h" -#include "content/app/mojo/mojo_init.h" -#include "content/common/url_schemes.h" -#include "content/public/app/content_main.h" -#include "content/public/app/content_main_delegate.h" -#include "content/public/common/content_client.h" -#include "content/public/common/content_constants.h" -#include "content/public/common/content_descriptor_keys.h" -#include "content/public/common/content_features.h" -#include "content/public/common/content_paths.h" -#include "content/public/common/content_switches.h" -#include "content/public/common/main_function_params.h" -#include "content/public/common/sandbox_init.h" -#include "content/public/common/zygote_buildflags.h" -#include "gin/v8_initializer.h" -#include "media/base/media.h" -#include "media/media_buildflags.h" -#include "ppapi/buildflags/buildflags.h" -#include "services/service_manager/embedder/switches.h" -#include "services/service_manager/sandbox/sandbox_type.h" -#include "ui/base/ui_base_paths.h" -#include "ui/base/ui_base_switches.h" -#include "ui/display/display_switches.h" -#include "ui/gfx/switches.h" - -#if defined(OS_WIN) -#include -#include - -#include "base/trace_event/trace_event_etw_export_win.h" -#include "sandbox/win/src/sandbox_types.h" -#include "ui/display/win/dpi.h" -#elif defined(OS_MACOSX) -#include "base/mac/scoped_nsautorelease_pool.h" -#include "base/power_monitor/power_monitor_device_source.h" -#include "content/browser/mach_broker_mac.h" -#endif // OS_WIN - -#if defined(OS_POSIX) -#include - -#include "base/file_descriptor_store.h" -#include "base/posix/global_descriptors.h" -#include "content/public/common/content_descriptors.h" - -#if !defined(OS_MACOSX) -#include "content/public/common/zygote_fork_delegate_linux.h" -#endif -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) -#include "content/zygote/zygote_main.h" -#include "sandbox/linux/services/libc_interceptor.h" -#endif - -#endif // OS_POSIX - -#if defined(OS_LINUX) -#include "base/native_library.h" -#include "base/rand_util.h" -#include "content/common/font_config_ipc_linux.h" -#include "content/public/common/common_sandbox_support_linux.h" -#include "third_party/blink/public/platform/web_font_render_style.h" -#include "third_party/boringssl/src/include/openssl/crypto.h" -#include "third_party/boringssl/src/include/openssl/rand.h" -#include "third_party/skia/include/ports/SkFontConfigInterface.h" -#include "third_party/skia/include/ports/SkFontMgr.h" -#include "third_party/skia/include/ports/SkFontMgr_android.h" - -#if BUILDFLAG(ENABLE_PLUGINS) -#include "content/common/pepper_plugin_list.h" -#include "content/public/common/pepper_plugin_info.h" -#endif - -#if BUILDFLAG(ENABLE_LIBRARY_CDMS) -#include "content/public/common/cdm_info.h" -#include "content/public/common/content_client.h" -#endif - -#if BUILDFLAG(ENABLE_WEBRTC) -#include "third_party/webrtc_overrides/init_webrtc.h" // nogncheck -#endif -#endif // OS_LINUX - -#if !defined(CHROME_MULTIPLE_DLL_BROWSER) -#include "content/public/gpu/content_gpu_client.h" -#include "content/public/renderer/content_renderer_client.h" -#include "content/public/utility/content_utility_client.h" -#endif - -#if !defined(CHROME_MULTIPLE_DLL_CHILD) -#include "content/browser/browser_main.h" -#include "content/public/browser/content_browser_client.h" -#endif - -#if !defined(CHROME_MULTIPLE_DLL_BROWSER) && !defined(CHROME_MULTIPLE_DLL_CHILD) -#include "content/browser/gpu/gpu_main_thread_factory.h" -#include "content/browser/renderer_host/render_process_host_impl.h" -#include "content/browser/utility_process_host.h" -#include "content/gpu/in_process_gpu_thread.h" -#include "content/renderer/in_process_renderer_thread.h" -#include "content/utility/in_process_utility_thread.h" -#endif - -#if BUILDFLAG(USE_ZYGOTE_HANDLE) -#include "content/browser/sandbox_host_linux.h" -#include "content/browser/zygote_host/zygote_communication_linux.h" -#include "content/browser/zygote_host/zygote_host_impl_linux.h" -#include "content/public/common/common_sandbox_support_linux.h" -#include "content/public/common/zygote_handle.h" -#include "media/base/media_switches.h" -#endif - -namespace content { -extern int GpuMain(const content::MainFunctionParams&); -#if BUILDFLAG(ENABLE_PLUGINS) -#if !defined(OS_LINUX) -extern int PluginMain(const content::MainFunctionParams&); -#endif -extern int PpapiPluginMain(const MainFunctionParams&); -extern int PpapiBrokerMain(const MainFunctionParams&); -#endif -extern int RendererMain(const content::MainFunctionParams&); -extern int UtilityMain(const MainFunctionParams&); -} // namespace content - -namespace content { - -namespace { - -#if defined(V8_USE_EXTERNAL_STARTUP_DATA) && defined(OS_ANDROID) -#if defined __LP64__ -#define kV8SnapshotDataDescriptor kV8Snapshot64DataDescriptor -#else -#define kV8SnapshotDataDescriptor kV8Snapshot32DataDescriptor -#endif -#endif - -// This sets up two singletons responsible for managing field trials. The -// |field_trial_list| singleton lives on the stack and must outlive the Run() -// method of the process. -void InitializeFieldTrialAndFeatureList( - std::unique_ptr* field_trial_list) { - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - - // Initialize statistical testing infrastructure. We set the entropy - // provider to nullptr to disallow non-browser processes from creating - // their own one-time randomized trials; they should be created in the - // browser process. - field_trial_list->reset(new base::FieldTrialList(nullptr)); - - // Ensure any field trials in browser are reflected into the child - // process. -#if defined(OS_POSIX) - // On POSIX systems that use the zygote, we get the trials from a shared - // memory segment backed by an fd instead of the command line. - base::FieldTrialList::CreateTrialsFromCommandLine( - command_line, switches::kFieldTrialHandle, kFieldTrialDescriptor); -#else - base::FieldTrialList::CreateTrialsFromCommandLine( - command_line, switches::kFieldTrialHandle, -1); -#endif - - std::unique_ptr feature_list(new base::FeatureList); - base::FieldTrialList::CreateFeaturesFromCommandLine( - command_line, switches::kEnableFeatures, switches::kDisableFeatures, - feature_list.get()); - base::FeatureList::SetInstance(std::move(feature_list)); -} - -#if defined(V8_USE_EXTERNAL_STARTUP_DATA) -void LoadV8SnapshotFile() { -#if defined(USE_V8_CONTEXT_SNAPSHOT) - static constexpr gin::V8Initializer::V8SnapshotFileType kSnapshotType = - gin::V8Initializer::V8SnapshotFileType::kWithAdditionalContext; - static const char* snapshot_data_descriptor = - kV8ContextSnapshotDataDescriptor; -#else - static constexpr gin::V8Initializer::V8SnapshotFileType kSnapshotType = - gin::V8Initializer::V8SnapshotFileType::kDefault; - static const char* snapshot_data_descriptor = kV8SnapshotDataDescriptor; -#endif // USE_V8_CONTEXT_SNAPSHOT - ALLOW_UNUSED_LOCAL(kSnapshotType); - ALLOW_UNUSED_LOCAL(snapshot_data_descriptor); - -#if defined(OS_POSIX) && !defined(OS_MACOSX) - base::FileDescriptorStore& file_descriptor_store = - base::FileDescriptorStore::GetInstance(); - base::MemoryMappedFile::Region region; - base::ScopedFD fd = - file_descriptor_store.MaybeTakeFD(snapshot_data_descriptor, ®ion); - if (fd.is_valid()) { - gin::V8Initializer::LoadV8SnapshotFromFD(fd.get(), region.offset, - region.size, kSnapshotType); - return; - } -#endif // OS_POSIX && !OS_MACOSX - -#if !defined(CHROME_MULTIPLE_DLL_BROWSER) - gin::V8Initializer::LoadV8Snapshot(kSnapshotType); -#endif // !CHROME_MULTIPLE_DLL_BROWSER -} - -void LoadV8NativesFile() { -#if defined(OS_POSIX) && !defined(OS_MACOSX) - base::FileDescriptorStore& file_descriptor_store = - base::FileDescriptorStore::GetInstance(); - base::MemoryMappedFile::Region region; - base::ScopedFD fd = - file_descriptor_store.MaybeTakeFD(kV8NativesDataDescriptor, ®ion); - if (fd.is_valid()) { - gin::V8Initializer::LoadV8NativesFromFD(fd.get(), region.offset, - region.size); - return; - } -#endif // OS_POSIX && !OS_MACOSX -#if !defined(CHROME_MULTIPLE_DLL_BROWSER) - gin::V8Initializer::LoadV8Natives(); -#endif // !CHROME_MULTIPLE_DLL_BROWSER -} -#endif // V8_USE_EXTERNAL_STARTUP_DATA - -void InitializeV8IfNeeded(const base::CommandLine& command_line, - const std::string& process_type) { - if (process_type == switches::kGpuProcess) - return; - -#if defined(V8_USE_EXTERNAL_STARTUP_DATA) - LoadV8SnapshotFile(); - LoadV8NativesFile(); -#endif // V8_USE_EXTERNAL_STARTUP_DATA -} - -#if BUILDFLAG(USE_ZYGOTE_HANDLE) -pid_t LaunchZygoteHelper(base::CommandLine* cmd_line, - base::ScopedFD* control_fd) { - // Append any switches from the browser process that need to be forwarded on - // to the zygote/renderers. - static const char* const kForwardSwitches[] = { - switches::kAndroidFontsPath, switches::kClearKeyCdmPathForTesting, - switches::kEnableHeapProfiling, - switches::kEnableLogging, // Support, e.g., --enable-logging=stderr. - // Need to tell the zygote that it is headless so that we don't try to use - // the wrong type of main delegate. - switches::kHeadless, - // Zygote process needs to know what resources to have loaded when it - // becomes a renderer process. - switches::kForceDeviceScaleFactor, switches::kLoggingLevel, - switches::kPpapiInProcess, switches::kRegisterPepperPlugins, switches::kV, - switches::kVModule, - }; - cmd_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), - kForwardSwitches, arraysize(kForwardSwitches)); - - GetContentClient()->browser()->AppendExtraCommandLineSwitches(cmd_line, -1); - - // Start up the sandbox host process and get the file descriptor for the - // sandboxed processes to talk to it. - base::FileHandleMappingVector additional_remapped_fds; - additional_remapped_fds.emplace_back( - SandboxHostLinux::GetInstance()->GetChildSocket(), GetSandboxFD()); - - return ZygoteHostImpl::GetInstance()->LaunchZygote( - cmd_line, control_fd, std::move(additional_remapped_fds)); -} - -// Initializes the Zygote sandbox host. No thread should be created before this -// call, as InitializeZygoteSandboxForBrowserProcess() will end-up using fork(). -void InitializeZygoteSandboxForBrowserProcess( - const base::CommandLine& parsed_command_line) { - TRACE_EVENT0("startup", "SetupSandbox"); - // SandboxHostLinux needs to be initialized even if the sandbox and - // zygote are both disabled. It initializes the sandboxed process socket. - SandboxHostLinux::GetInstance()->Init(); - - if (parsed_command_line.HasSwitch(switches::kNoZygote) && - !parsed_command_line.HasSwitch(switches::kNoSandbox)) { - LOG(ERROR) << "--no-sandbox should be used together with --no--zygote"; - exit(EXIT_FAILURE); - } - - // Tickle the zygote host so it forks now. - ZygoteHostImpl::GetInstance()->Init(parsed_command_line); - ZygoteHandle generic_zygote = - CreateGenericZygote(base::BindOnce(LaunchZygoteHelper)); - - // TODO(kerrnel): Investigate doing this without the ZygoteHostImpl as a - // proxy. It is currently done this way due to concerns about race - // conditions. - ZygoteHostImpl::GetInstance()->SetRendererSandboxStatus( - generic_zygote->GetSandboxStatus()); -} -#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) - -#if defined(OS_LINUX) - -#if BUILDFLAG(ENABLE_PLUGINS) -// Loads the (native) libraries but does not initialize them (i.e., does not -// call PPP_InitializeModule). This is needed by the zygote on Linux to get -// access to the plugins before entering the sandbox. -void PreloadPepperPlugins() { - std::vector plugins; - ComputePepperPluginList(&plugins); - for (const auto& plugin : plugins) { - if (!plugin.is_internal) { - base::NativeLibraryLoadError error; - base::NativeLibrary library = - base::LoadNativeLibrary(plugin.path, &error); - VLOG_IF(1, !library) << "Unable to load plugin " << plugin.path.value() - << " " << error.ToString(); - - ignore_result(library); // Prevent release-mode warning. - } - } -} -#endif - -#if BUILDFLAG(ENABLE_LIBRARY_CDMS) -// Loads registered library CDMs but does not initialize them. This is needed by -// the zygote on Linux to get access to the CDMs before entering the sandbox. -void PreloadLibraryCdms() { - std::vector cdms; - GetContentClient()->AddContentDecryptionModules(&cdms, nullptr); - for (const auto& cdm : cdms) { - base::NativeLibraryLoadError error; - base::NativeLibrary library = base::LoadNativeLibrary(cdm.path, &error); - VLOG_IF(1, !library) << "Unable to load CDM " << cdm.path.value() - << " (error: " << error.ToString() << ")"; - ignore_result(library); // Prevent release-mode warning. - } -} -#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) - -#if BUILDFLAG(USE_ZYGOTE_HANDLE) -void PreSandboxInit() { -#if defined(ARCH_CPU_ARM_FAMILY) - // On ARM, BoringSSL requires access to /proc/cpuinfo to determine processor - // features. Query this before entering the sandbox. - CRYPTO_library_init(); -#endif - - // Pass BoringSSL a copy of the /dev/urandom file descriptor so RAND_bytes - // will work inside the sandbox. - RAND_set_urandom_fd(base::GetUrandomFD()); - -#if BUILDFLAG(ENABLE_PLUGINS) - // Ensure access to the Pepper plugins before the sandbox is turned on. - PreloadPepperPlugins(); -#endif -#if BUILDFLAG(ENABLE_LIBRARY_CDMS) - // Ensure access to the library CDMs before the sandbox is turned on. - PreloadLibraryCdms(); -#endif -#if BUILDFLAG(ENABLE_WEBRTC) - InitializeWebRtcModule(); -#endif - - SkFontConfigInterface::SetGlobal(new FontConfigIPC(GetSandboxFD()))->unref(); - - // Set the android SkFontMgr for blink. We need to ensure this is done - // before the sandbox is initialized to allow the font manager to access - // font configuration files on disk. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kAndroidFontsPath)) { - std::string android_fonts_dir = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kAndroidFontsPath); - - if (android_fonts_dir.size() > 0 && android_fonts_dir.back() != '/') - android_fonts_dir += '/'; - - SkFontMgr_Android_CustomFonts custom; - custom.fSystemFontUse = - SkFontMgr_Android_CustomFonts::SystemFontUse::kOnlyCustom; - custom.fBasePath = android_fonts_dir.c_str(); - - std::string font_config; - std::string fallback_font_config; - if (android_fonts_dir.find("kitkat") != std::string::npos) { - font_config = android_fonts_dir + "system_fonts.xml"; - fallback_font_config = android_fonts_dir + "fallback_fonts.xml"; - custom.fFallbackFontsXml = fallback_font_config.c_str(); - } else { - font_config = android_fonts_dir + "fonts.xml"; - custom.fFallbackFontsXml = nullptr; - } - custom.fFontsXml = font_config.c_str(); - custom.fIsolated = true; - - blink::WebFontRenderStyle::SetSkiaFontManager( - SkFontMgr_New_Android(&custom)); - } -} -#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) - -#endif // OS_LINUX - -} // namespace - -#if !defined(CHROME_MULTIPLE_DLL_CHILD) -base::LazyInstance::DestructorAtExit - g_empty_content_browser_client = LAZY_INSTANCE_INITIALIZER; -#endif // !CHROME_MULTIPLE_DLL_CHILD - -#if !defined(CHROME_MULTIPLE_DLL_BROWSER) -base::LazyInstance::DestructorAtExit - g_empty_content_gpu_client = LAZY_INSTANCE_INITIALIZER; -base::LazyInstance::DestructorAtExit - g_empty_content_renderer_client = LAZY_INSTANCE_INITIALIZER; -base::LazyInstance::DestructorAtExit - g_empty_content_utility_client = LAZY_INSTANCE_INITIALIZER; -#endif // !CHROME_MULTIPLE_DLL_BROWSER - -class ContentClientInitializer { - public: - static void Set(const std::string& process_type, - ContentMainDelegate* delegate) { - ContentClient* content_client = GetContentClient(); -#if !defined(CHROME_MULTIPLE_DLL_CHILD) - if (process_type.empty()) { - if (delegate) - content_client->browser_ = delegate->CreateContentBrowserClient(); - if (!content_client->browser_) - content_client->browser_ = &g_empty_content_browser_client.Get(); - } -#endif // !CHROME_MULTIPLE_DLL_CHILD - -#if !defined(CHROME_MULTIPLE_DLL_BROWSER) - base::CommandLine* cmd = base::CommandLine::ForCurrentProcess(); - if (process_type == switches::kGpuProcess || - cmd->HasSwitch(switches::kSingleProcess) || - (process_type.empty() && cmd->HasSwitch(switches::kInProcessGPU))) { - if (delegate) - content_client->gpu_ = delegate->CreateContentGpuClient(); - if (!content_client->gpu_) - content_client->gpu_ = &g_empty_content_gpu_client.Get(); - } - - if (process_type == switches::kRendererProcess || - cmd->HasSwitch(switches::kSingleProcess)) { - if (delegate) - content_client->renderer_ = delegate->CreateContentRendererClient(); - if (!content_client->renderer_) - content_client->renderer_ = &g_empty_content_renderer_client.Get(); - } - - if (process_type == switches::kUtilityProcess || - cmd->HasSwitch(switches::kSingleProcess)) { - if (delegate) - content_client->utility_ = delegate->CreateContentUtilityClient(); - // TODO(scottmg): http://crbug.com/237249 Should be in _child. - if (!content_client->utility_) - content_client->utility_ = &g_empty_content_utility_client.Get(); - } -#endif // !CHROME_MULTIPLE_DLL_BROWSER - } -}; - -// We dispatch to a process-type-specific FooMain() based on a command-line -// flag. This struct is used to build a table of (flag, main function) pairs. -struct MainFunction { - const char* name; - int (*function)(const MainFunctionParams&); -}; - -#if BUILDFLAG(USE_ZYGOTE_HANDLE) -// On platforms that use the zygote, we have a special subset of -// subprocesses that are launched via the zygote. This function -// fills in some process-launching bits around ZygoteMain(). -// Returns the exit code of the subprocess. -int RunZygote(ContentMainDelegate* delegate) { - static const MainFunction kMainFunctions[] = { - {switches::kRendererProcess, RendererMain}, - {switches::kUtilityProcess, UtilityMain}, -#if BUILDFLAG(ENABLE_PLUGINS) - {switches::kPpapiPluginProcess, PpapiPluginMain}, -#endif - }; - - std::vector> zygote_fork_delegates; - if (delegate) { - delegate->ZygoteStarting(&zygote_fork_delegates); - media::InitializeMediaLibrary(); - } - -#if defined(OS_LINUX) - PreSandboxInit(); -#endif - - // This function call can return multiple times, once per fork(). - if (!ZygoteMain(std::move(zygote_fork_delegates))) - return 1; - - if (delegate) - delegate->ZygoteForked(); - - // Zygote::HandleForkRequest may have reallocated the command - // line so update it here with the new version. - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - std::string process_type = - command_line.GetSwitchValueASCII(switches::kProcessType); - ContentClientInitializer::Set(process_type, delegate); - -#if !defined(OS_ANDROID) - tracing::EnableStartupTracingIfNeeded(); -#endif // !OS_ANDROID - - MainFunctionParams main_params(command_line); - main_params.zygote_child = true; - - std::unique_ptr field_trial_list; - InitializeFieldTrialAndFeatureList(&field_trial_list); - - service_manager::SandboxType sandbox_type = - service_manager::SandboxTypeFromCommandLine(command_line); - if (sandbox_type == service_manager::SANDBOX_TYPE_PROFILING) - sandbox::SetUseLocaltimeOverride(false); - - for (size_t i = 0; i < arraysize(kMainFunctions); ++i) { - if (process_type == kMainFunctions[i].name) - return kMainFunctions[i].function(main_params); - } - - if (delegate) - return delegate->RunProcess(process_type, main_params); - - NOTREACHED() << "Unknown zygote process type: " << process_type; - return 1; -} -#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) - -static void RegisterMainThreadFactories() { -#if !defined(CHROME_MULTIPLE_DLL_BROWSER) && !defined(CHROME_MULTIPLE_DLL_CHILD) - UtilityProcessHost::RegisterUtilityMainThreadFactory( - CreateInProcessUtilityThread); - RenderProcessHostImpl::RegisterRendererMainThreadFactory( - CreateInProcessRendererThread); - content::RegisterGpuMainThreadFactory(CreateInProcessGpuThread); -#else - base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kSingleProcess)) { - LOG(FATAL) << - "--single-process is not supported in chrome multiple dll browser."; - } - if (command_line.HasSwitch(switches::kInProcessGPU)) { - LOG(FATAL) << - "--in-process-gpu is not supported in chrome multiple dll browser."; - } -#endif // !CHROME_MULTIPLE_DLL_BROWSER && !CHROME_MULTIPLE_DLL_CHILD -} - -// Run the FooMain() for a given process type. -// If |process_type| is empty, runs BrowserMain(). -// Returns the exit code for this process. -int RunNamedProcessTypeMain( - const std::string& process_type, - const MainFunctionParams& main_function_params, - ContentMainDelegate* delegate) { - static const MainFunction kMainFunctions[] = { -#if !defined(CHROME_MULTIPLE_DLL_CHILD) - { "", BrowserMain }, -#endif -#if !defined(CHROME_MULTIPLE_DLL_BROWSER) -#if BUILDFLAG(ENABLE_PLUGINS) - { switches::kPpapiPluginProcess, PpapiPluginMain }, - { switches::kPpapiBrokerProcess, PpapiBrokerMain }, -#endif // ENABLE_PLUGINS - { switches::kUtilityProcess, UtilityMain }, - { switches::kRendererProcess, RendererMain }, - { switches::kGpuProcess, GpuMain }, -#endif // !CHROME_MULTIPLE_DLL_BROWSER - }; - - RegisterMainThreadFactories(); - - for (size_t i = 0; i < arraysize(kMainFunctions); ++i) { - if (process_type == kMainFunctions[i].name) { - if (delegate) { - int exit_code = delegate->RunProcess(process_type, - main_function_params); -#if defined(OS_ANDROID) - // In Android's browser process, the negative exit code doesn't mean the - // default behavior should be used as the UI message loop is managed by - // the Java and the browser process's default behavior is always - // overridden. - if (process_type.empty()) - return exit_code; -#endif - if (exit_code >= 0) - return exit_code; - } - return kMainFunctions[i].function(main_function_params); - } - } - -#if BUILDFLAG(USE_ZYGOTE_HANDLE) - // Zygote startup is special -- see RunZygote comments above - // for why we don't use ZygoteMain directly. - if (process_type == switches::kZygoteProcess) - return RunZygote(delegate); -#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) - - // If it's a process we don't know about, the embedder should know. - if (delegate) - return delegate->RunProcess(process_type, main_function_params); - - NOTREACHED() << "Unknown process type: " << process_type; - return 1; -} - -class ContentMainRunnerImpl : public ContentMainRunner { - public: - ContentMainRunnerImpl() { -#if defined(OS_WIN) - memset(&sandbox_info_, 0, sizeof(sandbox_info_)); -#endif - } - - ~ContentMainRunnerImpl() override { - if (is_initialized_ && !is_shutdown_) - Shutdown(); - } - - int TerminateForFatalInitializationError() { - if (delegate_) - return delegate_->TerminateForFatalInitializationError(); - - CHECK(false); - return 0; - } - - int Initialize(const ContentMainParams& params) override { - ui_task_ = params.ui_task; - created_main_parts_closure_ = params.created_main_parts_closure; - -#if defined(OS_WIN) - sandbox_info_ = *params.sandbox_info; -#else // !OS_WIN - -#if defined(OS_MACOSX) - autorelease_pool_ = params.autorelease_pool; -#endif // defined(OS_MACOSX) - -#if defined(OS_ANDROID) - // See note at the initialization of ExitManager, below; basically, - // only Android builds have the ctor/dtor handlers set up to use - // TRACE_EVENT right away. - TRACE_EVENT0("startup,benchmark,rail", "ContentMainRunnerImpl::Initialize"); -#endif // OS_ANDROID - - base::GlobalDescriptors* g_fds = base::GlobalDescriptors::GetInstance(); - ALLOW_UNUSED_LOCAL(g_fds); - -// On Android, the ipc_fd is passed through the Java service. -#if !defined(OS_ANDROID) - g_fds->Set(kMojoIPCChannel, - kMojoIPCChannel + base::GlobalDescriptors::kBaseDescriptor); - - g_fds->Set( - kFieldTrialDescriptor, - kFieldTrialDescriptor + base::GlobalDescriptors::kBaseDescriptor); -#endif // !OS_ANDROID - -#if defined(OS_LINUX) || defined(OS_OPENBSD) - g_fds->Set(kCrashDumpSignal, - kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor); -#endif // OS_LINUX || OS_OPENBSD - -#endif // !OS_WIN - - is_initialized_ = true; - delegate_ = params.delegate; - - // The exit manager is in charge of calling the dtors of singleton objects. - // On Android, AtExitManager is set up when library is loaded. - // A consequence of this is that you can't use the ctor/dtor-based - // TRACE_EVENT methods on Linux or iOS builds till after we set this up. -#if !defined(OS_ANDROID) - if (!ui_task_) { - // When running browser tests, don't create a second AtExitManager as that - // interfers with shutdown when objects created before ContentMain is - // called are destructed when it returns. - exit_manager_.reset(new base::AtExitManager); - } -#endif // !OS_ANDROID - - int exit_code = 0; - if (delegate_ && delegate_->BasicStartupComplete(&exit_code)) - return exit_code; - - completed_basic_startup_ = true; - - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - std::string process_type = - command_line.GetSwitchValueASCII(switches::kProcessType); - -#if defined(OS_WIN) - if (command_line.HasSwitch(switches::kDeviceScaleFactor)) { - std::string scale_factor_string = command_line.GetSwitchValueASCII( - switches::kDeviceScaleFactor); - double scale_factor = 0; - if (base::StringToDouble(scale_factor_string, &scale_factor)) - display::win::SetDefaultDeviceScaleFactor(scale_factor); - } -#endif - - if (!GetContentClient()) - SetContentClient(&empty_content_client_); - ContentClientInitializer::Set(process_type, delegate_); - -#if !defined(OS_ANDROID) - // Enable startup tracing asap to avoid early TRACE_EVENT calls being - // ignored. For Android, startup tracing is enabled in an even earlier place - // content/app/android/library_loader_hooks.cc. - // - // Startup tracing flags are not (and should not) passed to Zygote - // processes. We will enable tracing when forked, if needed. - if (process_type != switches::kZygoteProcess) - tracing::EnableStartupTracingIfNeeded(); -#endif // !OS_ANDROID - -#if defined(OS_WIN) - // Enable exporting of events to ETW if requested on the command line. - if (command_line.HasSwitch(switches::kTraceExportEventsToETW)) - base::trace_event::TraceEventETWExport::EnableETWExport(); -#endif // OS_WIN - -#if !defined(OS_ANDROID) - // Android tracing started at the beginning of the method. - // Other OSes have to wait till we get here in order for all the memory - // management setup to be completed. - TRACE_EVENT0("startup,benchmark,rail", "ContentMainRunnerImpl::Initialize"); -#endif // !OS_ANDROID - -#if defined(OS_MACOSX) - // We need to allocate the IO Ports before the Sandbox is initialized or - // the first instance of PowerMonitor is created. - // It's important not to allocate the ports for processes which don't - // register with the power monitor - see crbug.com/88867. - if (process_type.empty() || - (delegate_ && - delegate_->ProcessRegistersWithSystemProcess(process_type))) { - base::PowerMonitorDeviceSource::AllocateSystemIOPorts(); - } - - if (!process_type.empty() && - (!delegate_ || delegate_->ShouldSendMachPort(process_type))) { - MachBroker::ChildSendTaskPortToParent(); - } -#endif - - // If we are on a platform where the default allocator is overridden (shim - // layer on windows, tcmalloc on Linux Desktop) smoke-tests that the - // overriding logic is working correctly. If not causes a hard crash, as its - // unexpected absence has security implications. - CHECK(base::allocator::IsAllocatorInitialized()); - -#if defined(OS_POSIX) - if (!process_type.empty()) { - // When you hit Ctrl-C in a terminal running the browser - // process, a SIGINT is delivered to the entire process group. - // When debugging the browser process via gdb, gdb catches the - // SIGINT for the browser process (and dumps you back to the gdb - // console) but doesn't for the child processes, killing them. - // The fix is to have child processes ignore SIGINT; they'll die - // on their own when the browser process goes away. - // - // Note that we *can't* rely on BeingDebugged to catch this case because - // we are the child process, which is not being debugged. - // TODO(evanm): move this to some shared subprocess-init function. - if (!base::debug::BeingDebugged()) - signal(SIGINT, SIG_IGN); - } -#endif - - RegisterPathProvider(); - RegisterContentSchemes(true); - -#if defined(OS_ANDROID) && (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) - int icudata_fd = g_fds->MaybeGet(kAndroidICUDataDescriptor); - if (icudata_fd != -1) { - auto icudata_region = g_fds->GetRegion(kAndroidICUDataDescriptor); - if (!base::i18n::InitializeICUWithFileDescriptor(icudata_fd, - icudata_region)) - return TerminateForFatalInitializationError(); - } else { - if (!base::i18n::InitializeICU()) - return TerminateForFatalInitializationError(); - } -#else - if (!base::i18n::InitializeICU()) - return TerminateForFatalInitializationError(); -#endif // OS_ANDROID && (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) - - InitializeV8IfNeeded(command_line, process_type); - -#if !defined(OFFICIAL_BUILD) -#if defined(OS_WIN) - bool should_enable_stack_dump = !process_type.empty(); -#else - bool should_enable_stack_dump = true; -#endif - // Print stack traces to stderr when crashes occur. This opens up security - // holes so it should never be enabled for official builds. This needs to - // happen before crash reporting is initialized (which for chrome happens in - // the call to PreSandboxStartup() on the delegate below), because otherwise - // this would interfere with signal handlers used by crash reporting. - if (should_enable_stack_dump && - !command_line.HasSwitch( - service_manager::switches::kDisableInProcessStackTraces)) { - base::debug::EnableInProcessStackDumping(); - } -#endif // !defined(OFFICIAL_BUILD) - - if (delegate_) - delegate_->PreSandboxStartup(); - -#if defined(OS_WIN) - if (!InitializeSandbox( - service_manager::SandboxTypeFromCommandLine(command_line), - params.sandbox_info)) - return TerminateForFatalInitializationError(); -#elif defined(OS_MACOSX) - // Do not initialize the sandbox at this point if the V2 - // sandbox is enabled for the process type. - bool v2_enabled = base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableV2Sandbox); - - if (process_type == switches::kRendererProcess || - process_type == switches::kPpapiPluginProcess || v2_enabled || - (delegate_ && delegate_->DelaySandboxInitialization(process_type))) { - // On OS X the renderer sandbox needs to be initialized later in the - // startup sequence in RendererMainPlatformDelegate::EnableSandbox(). - } else { - if (!InitializeSandbox()) - return TerminateForFatalInitializationError(); - } -#endif - - if (delegate_) - delegate_->SandboxInitialized(process_type); - -#if BUILDFLAG(USE_ZYGOTE_HANDLE) - if (process_type.empty()) { - // The sandbox host needs to be initialized before forking a thread to - // start the ServiceManager, and after setting up the sandbox and invoking - // SandboxInitialized(). - InitializeZygoteSandboxForBrowserProcess( - *base::CommandLine::ForCurrentProcess()); - } -#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) - - // Return -1 to indicate no early termination. - return -1; - } - - int Run() override { - DCHECK(is_initialized_); - DCHECK(!is_shutdown_); - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - std::string process_type = - command_line.GetSwitchValueASCII(switches::kProcessType); - - // Run this logic on all child processes. Zygotes will run this at a later - // point in time when the command line has been updated. - std::unique_ptr field_trial_list; - if (!process_type.empty() && process_type != switches::kZygoteProcess) - InitializeFieldTrialAndFeatureList(&field_trial_list); - - MainFunctionParams main_params(command_line); - main_params.ui_task = ui_task_; - main_params.created_main_parts_closure = created_main_parts_closure_; -#if defined(OS_WIN) - main_params.sandbox_info = &sandbox_info_; -#elif defined(OS_MACOSX) - main_params.autorelease_pool = autorelease_pool_; -#endif - - return RunNamedProcessTypeMain(process_type, main_params, delegate_); - } - - void Shutdown() override { - DCHECK(is_initialized_); - DCHECK(!is_shutdown_); - - if (completed_basic_startup_ && delegate_) { - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - std::string process_type = - command_line.GetSwitchValueASCII(switches::kProcessType); - - delegate_->ProcessExiting(process_type); - } - -#if defined(OS_WIN) -#ifdef _CRTDBG_MAP_ALLOC - _CrtDumpMemoryLeaks(); -#endif // _CRTDBG_MAP_ALLOC -#endif // OS_WIN - - exit_manager_.reset(nullptr); - - delegate_ = nullptr; - is_shutdown_ = true; - } - - private: - // True if the runner has been initialized. - bool is_initialized_ = false; - - // True if the runner has been shut down. - bool is_shutdown_ = false; - - // True if basic startup was completed. - bool completed_basic_startup_ = false; - - // Used if the embedder doesn't set one. - ContentClient empty_content_client_; - - // The delegate will outlive this object. - ContentMainDelegate* delegate_ = nullptr; - - std::unique_ptr exit_manager_; -#if defined(OS_WIN) - sandbox::SandboxInterfaceInfo sandbox_info_; -#elif defined(OS_MACOSX) - base::mac::ScopedNSAutoreleasePool* autorelease_pool_ = nullptr; -#endif - - base::Closure* ui_task_ = nullptr; - - CreatedMainPartsClosure* created_main_parts_closure_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(ContentMainRunnerImpl); -}; - -// static -ContentMainRunner* ContentMainRunner::Create() { - return new ContentMainRunnerImpl(); -} - -} // namespace content diff --git a/chromium/content/app/content_main_runner_impl.cc b/chromium/content/app/content_main_runner_impl.cc new file mode 100644 index 00000000000..83e8990edf1 --- /dev/null +++ b/chromium/content/app/content_main_runner_impl.cc @@ -0,0 +1,1010 @@ +// Copyright (c) 2012 The Chromium Authors. 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/app/content_main_runner_impl.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "base/allocator/allocator_check.h" +#include "base/allocator/allocator_extension.h" +#include "base/allocator/buildflags.h" +#include "base/at_exit.h" +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/debug/debugger.h" +#include "base/debug/stack_trace.h" +#include "base/feature_list.h" +#include "base/files/file_path.h" +#include "base/i18n/icu_util.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/metrics/field_trial.h" +#include "base/metrics/histogram_base.h" +#include "base/path_service.h" +#include "base/process/launch.h" +#include "base/process/memory.h" +#include "base/process/process.h" +#include "base/process/process_handle.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/trace_event/trace_event.h" +#include "components/tracing/common/trace_startup.h" +#include "content/app/mojo/mojo_init.h" +#include "content/browser/browser_process_sub_thread.h" +#include "content/common/url_schemes.h" +#include "content/public/app/content_main_delegate.h" +#include "content/public/common/content_constants.h" +#include "content/public/common/content_descriptor_keys.h" +#include "content/public/common/content_features.h" +#include "content/public/common/content_paths.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/main_function_params.h" +#include "content/public/common/sandbox_init.h" +#include "gin/v8_initializer.h" +#include "media/base/media.h" +#include "media/media_buildflags.h" +#include "ppapi/buildflags/buildflags.h" +#include "services/service_manager/embedder/switches.h" +#include "services/service_manager/sandbox/sandbox_type.h" +#include "services/service_manager/sandbox/switches.h" +#include "services/service_manager/zygote/common/zygote_buildflags.h" +#include "third_party/blink/public/common/origin_trials/trial_token_validator.h" +#include "ui/base/ui_base_paths.h" +#include "ui/base/ui_base_switches.h" +#include "ui/display/display_switches.h" +#include "ui/gfx/switches.h" + +#if defined(OS_WIN) +#include +#include + +#include "base/trace_event/trace_event_etw_export_win.h" +#include "ui/display/win/dpi.h" +#elif defined(OS_MACOSX) +#include "base/power_monitor/power_monitor_device_source.h" +#include "content/browser/mach_broker_mac.h" +#endif // OS_WIN + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +#include + +#include "base/file_descriptor_store.h" +#include "base/posix/global_descriptors.h" +#include "content/public/common/content_descriptors.h" + +#if !defined(OS_MACOSX) +#include "services/service_manager/zygote/common/zygote_fork_delegate_linux.h" +#endif +#if !defined(OS_MACOSX) && !defined(OS_ANDROID) +#include "sandbox/linux/services/libc_interceptor.h" +#include "services/service_manager/zygote/zygote_main.h" +#endif + +#endif // OS_POSIX || OS_FUCHSIA + +#if defined(OS_LINUX) +#include "base/native_library.h" +#include "base/rand_util.h" +#include "content/common/font_config_ipc_linux.h" +#include "services/service_manager/zygote/common/common_sandbox_support_linux.h" +#include "third_party/blink/public/platform/web_font_render_style.h" +#include "third_party/boringssl/src/include/openssl/crypto.h" +#include "third_party/boringssl/src/include/openssl/rand.h" +#include "third_party/skia/include/ports/SkFontConfigInterface.h" +#include "third_party/skia/include/ports/SkFontMgr.h" +#include "third_party/skia/include/ports/SkFontMgr_android.h" +#include "third_party/webrtc_overrides/init_webrtc.h" // nogncheck + +#if BUILDFLAG(ENABLE_PLUGINS) +#include "content/common/pepper_plugin_list.h" +#include "content/public/common/pepper_plugin_info.h" +#endif + +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) +#include "content/public/common/cdm_info.h" +#include "content/public/common/content_client.h" +#endif + +#endif // OS_LINUX + +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) +#include "content/public/gpu/content_gpu_client.h" +#include "content/public/renderer/content_renderer_client.h" +#include "content/public/utility/content_utility_client.h" +#endif + +#if !defined(CHROME_MULTIPLE_DLL_CHILD) +#include "content/browser/browser_main.h" +#include "content/public/browser/content_browser_client.h" +#endif + +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) && !defined(CHROME_MULTIPLE_DLL_CHILD) +#include "content/browser/gpu/gpu_main_thread_factory.h" +#include "content/browser/renderer_host/render_process_host_impl.h" +#include "content/browser/utility_process_host.h" +#include "content/gpu/in_process_gpu_thread.h" +#include "content/renderer/in_process_renderer_thread.h" +#include "content/utility/in_process_utility_thread.h" +#endif + +#if BUILDFLAG(USE_ZYGOTE_HANDLE) +#include "content/browser/sandbox_host_linux.h" +#include "media/base/media_switches.h" +#include "services/service_manager/zygote/common/common_sandbox_support_linux.h" +#include "services/service_manager/zygote/common/zygote_handle.h" +#include "services/service_manager/zygote/host/zygote_communication_linux.h" +#include "services/service_manager/zygote/host/zygote_host_impl_linux.h" +#endif + +namespace content { +extern int GpuMain(const content::MainFunctionParams&); +#if BUILDFLAG(ENABLE_PLUGINS) +#if !defined(OS_LINUX) +extern int PluginMain(const content::MainFunctionParams&); +#endif +extern int PpapiPluginMain(const MainFunctionParams&); +extern int PpapiBrokerMain(const MainFunctionParams&); +#endif +extern int RendererMain(const content::MainFunctionParams&); +extern int UtilityMain(const MainFunctionParams&); +} // namespace content + +namespace content { + +namespace { + +#if defined(V8_USE_EXTERNAL_STARTUP_DATA) && defined(OS_ANDROID) +#if defined __LP64__ +#define kV8SnapshotDataDescriptor kV8Snapshot64DataDescriptor +#else +#define kV8SnapshotDataDescriptor kV8Snapshot32DataDescriptor +#endif +#endif + +// This sets up two singletons responsible for managing field trials. The +// |field_trial_list| singleton lives on the stack and must outlive the Run() +// method of the process. +void InitializeFieldTrialAndFeatureList( + std::unique_ptr* field_trial_list) { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + // Initialize statistical testing infrastructure. We set the entropy + // provider to nullptr to disallow non-browser processes from creating + // their own one-time randomized trials; they should be created in the + // browser process. + field_trial_list->reset(new base::FieldTrialList(nullptr)); + +// Ensure any field trials in browser are reflected into the child +// process. +#if defined(OS_WIN) + base::FieldTrialList::CreateTrialsFromCommandLine( + command_line, switches::kFieldTrialHandle, -1); +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + // On POSIX systems that use the zygote, we get the trials from a shared + // memory segment backed by an fd instead of the command line. + base::FieldTrialList::CreateTrialsFromCommandLine( + command_line, switches::kFieldTrialHandle, + service_manager::kFieldTrialDescriptor); +#endif + + std::unique_ptr feature_list(new base::FeatureList); + base::FieldTrialList::CreateFeaturesFromCommandLine( + command_line, switches::kEnableFeatures, switches::kDisableFeatures, + feature_list.get()); + base::FeatureList::SetInstance(std::move(feature_list)); +} + +#if defined(V8_USE_EXTERNAL_STARTUP_DATA) +void LoadV8SnapshotFile() { +#if defined(USE_V8_CONTEXT_SNAPSHOT) + static constexpr gin::V8Initializer::V8SnapshotFileType kSnapshotType = + gin::V8Initializer::V8SnapshotFileType::kWithAdditionalContext; + static const char* snapshot_data_descriptor = + kV8ContextSnapshotDataDescriptor; +#else + static constexpr gin::V8Initializer::V8SnapshotFileType kSnapshotType = + gin::V8Initializer::V8SnapshotFileType::kDefault; + static const char* snapshot_data_descriptor = kV8SnapshotDataDescriptor; +#endif // USE_V8_CONTEXT_SNAPSHOT + ALLOW_UNUSED_LOCAL(kSnapshotType); + ALLOW_UNUSED_LOCAL(snapshot_data_descriptor); + +#if defined(OS_POSIX) && !defined(OS_MACOSX) + base::FileDescriptorStore& file_descriptor_store = + base::FileDescriptorStore::GetInstance(); + base::MemoryMappedFile::Region region; + base::ScopedFD fd = + file_descriptor_store.MaybeTakeFD(snapshot_data_descriptor, ®ion); + if (fd.is_valid()) { + gin::V8Initializer::LoadV8SnapshotFromFD(fd.get(), region.offset, + region.size, kSnapshotType); + return; + } +#endif // OS_POSIX && !OS_MACOSX + +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) + gin::V8Initializer::LoadV8Snapshot(kSnapshotType); +#endif // !CHROME_MULTIPLE_DLL_BROWSER +} + +void LoadV8NativesFile() { +#if defined(OS_POSIX) && !defined(OS_MACOSX) + base::FileDescriptorStore& file_descriptor_store = + base::FileDescriptorStore::GetInstance(); + base::MemoryMappedFile::Region region; + base::ScopedFD fd = + file_descriptor_store.MaybeTakeFD(kV8NativesDataDescriptor, ®ion); + if (fd.is_valid()) { + gin::V8Initializer::LoadV8NativesFromFD(fd.get(), region.offset, + region.size); + return; + } +#endif // OS_POSIX && !OS_MACOSX +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) + gin::V8Initializer::LoadV8Natives(); +#endif // !CHROME_MULTIPLE_DLL_BROWSER +} +#endif // V8_USE_EXTERNAL_STARTUP_DATA + +void InitializeV8IfNeeded(const base::CommandLine& command_line, + const std::string& process_type) { + if (process_type == switches::kGpuProcess) + return; + +#if defined(V8_USE_EXTERNAL_STARTUP_DATA) + LoadV8SnapshotFile(); + LoadV8NativesFile(); +#endif // V8_USE_EXTERNAL_STARTUP_DATA +} + +#if BUILDFLAG(USE_ZYGOTE_HANDLE) +pid_t LaunchZygoteHelper(base::CommandLine* cmd_line, + base::ScopedFD* control_fd) { + // Append any switches from the browser process that need to be forwarded on + // to the zygote/renderers. + static const char* const kForwardSwitches[] = { + switches::kAndroidFontsPath, switches::kClearKeyCdmPathForTesting, + switches::kEnableLogging, // Support, e.g., --enable-logging=stderr. + // Need to tell the zygote that it is headless so that we don't try to use + // the wrong type of main delegate. + switches::kHeadless, + // Zygote process needs to know what resources to have loaded when it + // becomes a renderer process. + switches::kForceDeviceScaleFactor, switches::kLoggingLevel, + switches::kPpapiInProcess, switches::kRegisterPepperPlugins, switches::kV, + switches::kVModule, + }; + cmd_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), + kForwardSwitches, base::size(kForwardSwitches)); + + GetContentClient()->browser()->AppendExtraCommandLineSwitches(cmd_line, -1); + + // Start up the sandbox host process and get the file descriptor for the + // sandboxed processes to talk to it. + base::FileHandleMappingVector additional_remapped_fds; + additional_remapped_fds.emplace_back( + SandboxHostLinux::GetInstance()->GetChildSocket(), + service_manager::GetSandboxFD()); + + return service_manager::ZygoteHostImpl::GetInstance()->LaunchZygote( + cmd_line, control_fd, std::move(additional_remapped_fds)); +} + +// Initializes the Zygote sandbox host. No thread should be created before this +// call, as InitializeZygoteSandboxForBrowserProcess() will end-up using fork(). +void InitializeZygoteSandboxForBrowserProcess( + const base::CommandLine& parsed_command_line) { + TRACE_EVENT0("startup", "SetupSandbox"); + // SandboxHostLinux needs to be initialized even if the sandbox and + // zygote are both disabled. It initializes the sandboxed process socket. + SandboxHostLinux::GetInstance()->Init(); + + if (parsed_command_line.HasSwitch(switches::kNoZygote) && + !parsed_command_line.HasSwitch(service_manager::switches::kNoSandbox)) { + LOG(ERROR) << "--no-sandbox should be used together with --no--zygote"; + exit(EXIT_FAILURE); + } + + // Tickle the zygote host so it forks now. + service_manager::ZygoteHostImpl::GetInstance()->Init(parsed_command_line); + service_manager::ZygoteHandle generic_zygote = + service_manager::CreateGenericZygote(base::BindOnce(LaunchZygoteHelper)); + + // TODO(kerrnel): Investigate doing this without the ZygoteHostImpl as a + // proxy. It is currently done this way due to concerns about race + // conditions. + service_manager::ZygoteHostImpl::GetInstance()->SetRendererSandboxStatus( + generic_zygote->GetSandboxStatus()); +} +#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) + +#if defined(OS_LINUX) + +#if BUILDFLAG(ENABLE_PLUGINS) +// Loads the (native) libraries but does not initialize them (i.e., does not +// call PPP_InitializeModule). This is needed by the zygote on Linux to get +// access to the plugins before entering the sandbox. +void PreloadPepperPlugins() { + std::vector plugins; + ComputePepperPluginList(&plugins); + for (const auto& plugin : plugins) { + if (!plugin.is_internal) { + base::NativeLibraryLoadError error; + base::NativeLibrary library = + base::LoadNativeLibrary(plugin.path, &error); + VLOG_IF(1, !library) << "Unable to load plugin " << plugin.path.value() + << " " << error.ToString(); + + ignore_result(library); // Prevent release-mode warning. + } + } +} +#endif + +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) +// Loads registered library CDMs but does not initialize them. This is needed by +// the zygote on Linux to get access to the CDMs before entering the sandbox. +void PreloadLibraryCdms() { + std::vector cdms; + GetContentClient()->AddContentDecryptionModules(&cdms, nullptr); + for (const auto& cdm : cdms) { + base::NativeLibraryLoadError error; + base::NativeLibrary library = base::LoadNativeLibrary(cdm.path, &error); + VLOG_IF(1, !library) << "Unable to load CDM " << cdm.path.value() + << " (error: " << error.ToString() << ")"; + ignore_result(library); // Prevent release-mode warning. + } +} +#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) + +#if BUILDFLAG(USE_ZYGOTE_HANDLE) +void PreSandboxInit() { +#if defined(ARCH_CPU_ARM_FAMILY) + // On ARM, BoringSSL requires access to /proc/cpuinfo to determine processor + // features. Query this before entering the sandbox. + CRYPTO_library_init(); +#endif + + // Pass BoringSSL a copy of the /dev/urandom file descriptor so RAND_bytes + // will work inside the sandbox. + RAND_set_urandom_fd(base::GetUrandomFD()); + +#if BUILDFLAG(ENABLE_PLUGINS) + // Ensure access to the Pepper plugins before the sandbox is turned on. + PreloadPepperPlugins(); +#endif +#if BUILDFLAG(ENABLE_LIBRARY_CDMS) + // Ensure access to the library CDMs before the sandbox is turned on. + PreloadLibraryCdms(); +#endif + InitializeWebRtcModule(); + + SkFontConfigInterface::SetGlobal( + sk_make_sp(service_manager::GetSandboxFD())); + + // Set the android SkFontMgr for blink. We need to ensure this is done + // before the sandbox is initialized to allow the font manager to access + // font configuration files on disk. + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAndroidFontsPath)) { + std::string android_fonts_dir = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kAndroidFontsPath); + + if (android_fonts_dir.size() > 0 && android_fonts_dir.back() != '/') + android_fonts_dir += '/'; + + SkFontMgr_Android_CustomFonts custom; + custom.fSystemFontUse = + SkFontMgr_Android_CustomFonts::SystemFontUse::kOnlyCustom; + custom.fBasePath = android_fonts_dir.c_str(); + + std::string font_config; + std::string fallback_font_config; + if (android_fonts_dir.find("kitkat") != std::string::npos) { + font_config = android_fonts_dir + "system_fonts.xml"; + fallback_font_config = android_fonts_dir + "fallback_fonts.xml"; + custom.fFallbackFontsXml = fallback_font_config.c_str(); + } else { + font_config = android_fonts_dir + "fonts.xml"; + custom.fFallbackFontsXml = nullptr; + } + custom.fFontsXml = font_config.c_str(); + custom.fIsolated = true; + + blink::WebFontRenderStyle::SetSkiaFontManager( + SkFontMgr_New_Android(&custom)); + } +} +#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) + +#endif // OS_LINUX + +bool IsRootProcess() { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + return command_line.GetSwitchValueASCII(switches::kProcessType).empty(); +} + +} // namespace + +#if !defined(CHROME_MULTIPLE_DLL_CHILD) +base::LazyInstance::DestructorAtExit + g_empty_content_browser_client = LAZY_INSTANCE_INITIALIZER; +#endif // !CHROME_MULTIPLE_DLL_CHILD + +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) +base::LazyInstance::DestructorAtExit + g_empty_content_gpu_client = LAZY_INSTANCE_INITIALIZER; +base::LazyInstance::DestructorAtExit + g_empty_content_renderer_client = LAZY_INSTANCE_INITIALIZER; +base::LazyInstance::DestructorAtExit + g_empty_content_utility_client = LAZY_INSTANCE_INITIALIZER; +#endif // !CHROME_MULTIPLE_DLL_BROWSER + +class ContentClientInitializer { + public: + static void Set(const std::string& process_type, + ContentMainDelegate* delegate) { + ContentClient* content_client = GetContentClient(); +#if !defined(CHROME_MULTIPLE_DLL_CHILD) + if (process_type.empty()) { + if (delegate) + content_client->browser_ = delegate->CreateContentBrowserClient(); + if (!content_client->browser_) + content_client->browser_ = &g_empty_content_browser_client.Get(); + } +#endif // !CHROME_MULTIPLE_DLL_CHILD + +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) + base::CommandLine* cmd = base::CommandLine::ForCurrentProcess(); + if (process_type == switches::kGpuProcess || + cmd->HasSwitch(switches::kSingleProcess) || + (process_type.empty() && cmd->HasSwitch(switches::kInProcessGPU))) { + if (delegate) + content_client->gpu_ = delegate->CreateContentGpuClient(); + if (!content_client->gpu_) + content_client->gpu_ = &g_empty_content_gpu_client.Get(); + } + + if (process_type == switches::kRendererProcess || + cmd->HasSwitch(switches::kSingleProcess)) { + if (delegate) + content_client->renderer_ = delegate->CreateContentRendererClient(); + if (!content_client->renderer_) + content_client->renderer_ = &g_empty_content_renderer_client.Get(); + } + + if (process_type == switches::kUtilityProcess || + cmd->HasSwitch(switches::kSingleProcess)) { + if (delegate) + content_client->utility_ = delegate->CreateContentUtilityClient(); + // TODO(scottmg): http://crbug.com/237249 Should be in _child. + if (!content_client->utility_) + content_client->utility_ = &g_empty_content_utility_client.Get(); + } +#endif // !CHROME_MULTIPLE_DLL_BROWSER + } +}; + +// We dispatch to a process-type-specific FooMain() based on a command-line +// flag. This struct is used to build a table of (flag, main function) pairs. +struct MainFunction { + const char* name; + int (*function)(const MainFunctionParams&); +}; + +#if BUILDFLAG(USE_ZYGOTE_HANDLE) +// On platforms that use the zygote, we have a special subset of +// subprocesses that are launched via the zygote. This function +// fills in some process-launching bits around ZygoteMain(). +// Returns the exit code of the subprocess. +int RunZygote(ContentMainDelegate* delegate) { + static const MainFunction kMainFunctions[] = { + {switches::kRendererProcess, RendererMain}, + {switches::kUtilityProcess, UtilityMain}, +#if BUILDFLAG(ENABLE_PLUGINS) + {switches::kPpapiPluginProcess, PpapiPluginMain}, +#endif + }; + + std::vector> + zygote_fork_delegates; + if (delegate) { + delegate->ZygoteStarting(&zygote_fork_delegates); + media::InitializeMediaLibrary(); + } + +#if defined(OS_LINUX) + PreSandboxInit(); +#endif + + // This function call can return multiple times, once per fork(). + if (!service_manager::ZygoteMain(std::move(zygote_fork_delegates))) { + return 1; + } + + if (delegate) + delegate->ZygoteForked(); + + // Zygote::HandleForkRequest may have reallocated the command + // line so update it here with the new version. + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + ContentClientInitializer::Set(process_type, delegate); + +#if !defined(OS_ANDROID) + tracing::EnableStartupTracingIfNeeded(); +#endif // !OS_ANDROID + + MainFunctionParams main_params(command_line); + main_params.zygote_child = true; + + std::unique_ptr field_trial_list; + InitializeFieldTrialAndFeatureList(&field_trial_list); + + service_manager::SandboxType sandbox_type = + service_manager::SandboxTypeFromCommandLine(command_line); + if (sandbox_type == service_manager::SANDBOX_TYPE_PROFILING) + sandbox::SetUseLocaltimeOverride(false); + + for (size_t i = 0; i < base::size(kMainFunctions); ++i) { + if (process_type == kMainFunctions[i].name) + return kMainFunctions[i].function(main_params); + } + + if (delegate) + return delegate->RunProcess(process_type, main_params); + + NOTREACHED() << "Unknown zygote process type: " << process_type; + return 1; +} +#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) + +static void RegisterMainThreadFactories() { +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) && !defined(CHROME_MULTIPLE_DLL_CHILD) + UtilityProcessHost::RegisterUtilityMainThreadFactory( + CreateInProcessUtilityThread); + RenderProcessHostImpl::RegisterRendererMainThreadFactory( + CreateInProcessRendererThread); + content::RegisterGpuMainThreadFactory(CreateInProcessGpuThread); +#else + base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kSingleProcess)) { + LOG(FATAL) + << "--single-process is not supported in chrome multiple dll browser."; + } + if (command_line.HasSwitch(switches::kInProcessGPU)) { + LOG(FATAL) + << "--in-process-gpu is not supported in chrome multiple dll browser."; + } +#endif // !CHROME_MULTIPLE_DLL_BROWSER && !CHROME_MULTIPLE_DLL_CHILD +} + +#if !defined(CHROME_MULTIPLE_DLL_CHILD) +// Run the main function for browser process. +// Returns the exit code for this process. +int RunBrowserProcessMain( + const MainFunctionParams& main_function_params, + ContentMainDelegate* delegate, + std::unique_ptr service_manager_thread) { + if (delegate) { + int exit_code = delegate->RunProcess("", main_function_params); +#if defined(OS_ANDROID) + // In Android's browser process, the negative exit code doesn't mean the + // default behavior should be used as the UI message loop is managed by + // the Java and the browser process's default behavior is always + // overridden. + return exit_code; +#endif + if (exit_code >= 0) + return exit_code; + } + // GetServiceManagerTaskRunnerForEmbedderProcess() needs to be invoked before + // Run() for the browser process. + DCHECK(service_manager_thread); + return BrowserMain(main_function_params, std::move(service_manager_thread)); +} +#endif // !defined(CHROME_MULTIPLE_DLL_CHILD) + +// Run the FooMain() for a given process type. +// Returns the exit code for this process. +int RunOtherNamedProcessTypeMain(const std::string& process_type, + const MainFunctionParams& main_function_params, + ContentMainDelegate* delegate) { +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) + static const MainFunction kMainFunctions[] = { +#if BUILDFLAG(ENABLE_PLUGINS) + {switches::kPpapiPluginProcess, PpapiPluginMain}, + {switches::kPpapiBrokerProcess, PpapiBrokerMain}, +#endif // ENABLE_PLUGINS + {switches::kUtilityProcess, UtilityMain}, + {switches::kRendererProcess, RendererMain}, + {switches::kGpuProcess, GpuMain}, + }; + + for (size_t i = 0; i < base::size(kMainFunctions); ++i) { + if (process_type == kMainFunctions[i].name) { + if (delegate) { + int exit_code = + delegate->RunProcess(process_type, main_function_params); + if (exit_code >= 0) + return exit_code; + } + return kMainFunctions[i].function(main_function_params); + } + } +#endif // !CHROME_MULTIPLE_DLL_BROWSER + +#if BUILDFLAG(USE_ZYGOTE_HANDLE) + // Zygote startup is special -- see RunZygote comments above + // for why we don't use ZygoteMain directly. + if (process_type == service_manager::switches::kZygoteProcess) + return RunZygote(delegate); +#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) + + // If it's a process we don't know about, the embedder should know. + if (delegate) + return delegate->RunProcess(process_type, main_function_params); + + NOTREACHED() << "Unknown process type: " << process_type; + return 1; +} + +// static +ContentMainRunnerImpl* ContentMainRunnerImpl::Create() { + return new ContentMainRunnerImpl(); +} + +ContentMainRunnerImpl::ContentMainRunnerImpl() { +#if defined(OS_WIN) + memset(&sandbox_info_, 0, sizeof(sandbox_info_)); +#endif +} + +ContentMainRunnerImpl::~ContentMainRunnerImpl() { + if (is_initialized_ && !is_shutdown_) + Shutdown(); +} + +int ContentMainRunnerImpl::TerminateForFatalInitializationError() { + if (delegate_) + return delegate_->TerminateForFatalInitializationError(); + + CHECK(false); + return 0; +} + +int ContentMainRunnerImpl::Initialize(const ContentMainParams& params) { + ui_task_ = params.ui_task; + created_main_parts_closure_ = params.created_main_parts_closure; + +#if defined(OS_WIN) + sandbox_info_ = *params.sandbox_info; +#else // !OS_WIN + +#if defined(OS_MACOSX) + autorelease_pool_ = params.autorelease_pool; +#endif // defined(OS_MACOSX) + +#if defined(OS_ANDROID) + // See note at the initialization of ExitManager, below; basically, + // only Android builds have the ctor/dtor handlers set up to use + // TRACE_EVENT right away. + TRACE_EVENT0("startup,benchmark,rail", "ContentMainRunnerImpl::Initialize"); +#endif // OS_ANDROID + + base::GlobalDescriptors* g_fds = base::GlobalDescriptors::GetInstance(); + ALLOW_UNUSED_LOCAL(g_fds); + +// On Android, the ipc_fd is passed through the Java service. +#if !defined(OS_ANDROID) + g_fds->Set(service_manager::kMojoIPCChannel, + service_manager::kMojoIPCChannel + + base::GlobalDescriptors::kBaseDescriptor); + + g_fds->Set(service_manager::kFieldTrialDescriptor, + service_manager::kFieldTrialDescriptor + + base::GlobalDescriptors::kBaseDescriptor); +#endif // !OS_ANDROID + +#if defined(OS_LINUX) || defined(OS_OPENBSD) + g_fds->Set(service_manager::kCrashDumpSignal, + service_manager::kCrashDumpSignal + + base::GlobalDescriptors::kBaseDescriptor); +#endif // OS_LINUX || OS_OPENBSD + +#endif // !OS_WIN + + is_initialized_ = true; + delegate_ = params.delegate; + +// The exit manager is in charge of calling the dtors of singleton objects. +// On Android, AtExitManager is set up when library is loaded. +// A consequence of this is that you can't use the ctor/dtor-based +// TRACE_EVENT methods on Linux or iOS builds till after we set this up. +#if !defined(OS_ANDROID) + if (!ui_task_) { + // When running browser tests, don't create a second AtExitManager as that + // interfers with shutdown when objects created before ContentMain is + // called are destructed when it returns. + exit_manager_.reset(new base::AtExitManager); + } +#endif // !OS_ANDROID + + int exit_code = 0; + if (delegate_ && delegate_->BasicStartupComplete(&exit_code)) + return exit_code; + completed_basic_startup_ = true; + + // We will need to use data from resources.pak in later cl, so load the file + // now. + if (IsRootProcess()) { + ui::DataPack* data_pack = delegate_->LoadServiceManifestDataPack(); + // TODO(ranj): Read manifest from this data pack. + ignore_result(data_pack); + } + + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + +#if defined(OS_WIN) + if (command_line.HasSwitch(switches::kDeviceScaleFactor)) { + std::string scale_factor_string = + command_line.GetSwitchValueASCII(switches::kDeviceScaleFactor); + double scale_factor = 0; + if (base::StringToDouble(scale_factor_string, &scale_factor)) + display::win::SetDefaultDeviceScaleFactor(scale_factor); + } +#endif + + if (!GetContentClient()) + SetContentClient(&empty_content_client_); + ContentClientInitializer::Set(process_type, delegate_); + +#if !defined(OS_ANDROID) + // Enable startup tracing asap to avoid early TRACE_EVENT calls being + // ignored. For Android, startup tracing is enabled in an even earlier place + // content/app/android/library_loader_hooks.cc. + // + // Startup tracing flags are not (and should not) passed to Zygote + // processes. We will enable tracing when forked, if needed. + if (process_type != service_manager::switches::kZygoteProcess) + tracing::EnableStartupTracingIfNeeded(); +#endif // !OS_ANDROID + +#if defined(OS_WIN) + // Enable exporting of events to ETW if requested on the command line. + if (command_line.HasSwitch(switches::kTraceExportEventsToETW)) + base::trace_event::TraceEventETWExport::EnableETWExport(); +#endif // OS_WIN + +#if !defined(OS_ANDROID) + // Android tracing started at the beginning of the method. + // Other OSes have to wait till we get here in order for all the memory + // management setup to be completed. + TRACE_EVENT0("startup,benchmark,rail", "ContentMainRunnerImpl::Initialize"); +#endif // !OS_ANDROID + +#if defined(OS_MACOSX) + // We need to allocate the IO Ports before the Sandbox is initialized or + // the first instance of PowerMonitor is created. + // It's important not to allocate the ports for processes which don't + // register with the power monitor - see https://crbug.com/88867. + if (process_type.empty() || + (delegate_ && + delegate_->ProcessRegistersWithSystemProcess(process_type))) { + base::PowerMonitorDeviceSource::AllocateSystemIOPorts(); + } + + if (!process_type.empty() && + (!delegate_ || delegate_->ShouldSendMachPort(process_type))) { + MachBroker::ChildSendTaskPortToParent(); + } +#endif + + // If we are on a platform where the default allocator is overridden (shim + // layer on windows, tcmalloc on Linux Desktop) smoke-tests that the + // overriding logic is working correctly. If not causes a hard crash, as its + // unexpected absence has security implications. + CHECK(base::allocator::IsAllocatorInitialized()); + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) + if (!process_type.empty()) { + // When you hit Ctrl-C in a terminal running the browser + // process, a SIGINT is delivered to the entire process group. + // When debugging the browser process via gdb, gdb catches the + // SIGINT for the browser process (and dumps you back to the gdb + // console) but doesn't for the child processes, killing them. + // The fix is to have child processes ignore SIGINT; they'll die + // on their own when the browser process goes away. + // + // Note that we *can't* rely on BeingDebugged to catch this case because + // we are the child process, which is not being debugged. + // TODO(evanm): move this to some shared subprocess-init function. + if (!base::debug::BeingDebugged()) + signal(SIGINT, SIG_IGN); + } +#endif + + RegisterPathProvider(); + RegisterContentSchemes(true); + +#if defined(OS_ANDROID) && (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) + int icudata_fd = g_fds->MaybeGet(kAndroidICUDataDescriptor); + if (icudata_fd != -1) { + auto icudata_region = g_fds->GetRegion(kAndroidICUDataDescriptor); + if (!base::i18n::InitializeICUWithFileDescriptor(icudata_fd, + icudata_region)) + return TerminateForFatalInitializationError(); + } else { + if (!base::i18n::InitializeICU()) + return TerminateForFatalInitializationError(); + } +#else + if (!base::i18n::InitializeICU()) + return TerminateForFatalInitializationError(); +#endif // OS_ANDROID && (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) + + InitializeV8IfNeeded(command_line, process_type); + + blink::TrialTokenValidator::SetOriginTrialPolicyGetter( + base::BindRepeating([]() -> blink::OriginTrialPolicy* { + if (auto* client = GetContentClient()) + return client->GetOriginTrialPolicy(); + return nullptr; + })); + +#if !defined(OFFICIAL_BUILD) +#if defined(OS_WIN) + bool should_enable_stack_dump = !process_type.empty(); +#else + bool should_enable_stack_dump = true; +#endif + // Print stack traces to stderr when crashes occur. This opens up security + // holes so it should never be enabled for official builds. This needs to + // happen before crash reporting is initialized (which for chrome happens in + // the call to PreSandboxStartup() on the delegate below), because otherwise + // this would interfere with signal handlers used by crash reporting. + if (should_enable_stack_dump && + !command_line.HasSwitch( + service_manager::switches::kDisableInProcessStackTraces)) { + base::debug::EnableInProcessStackDumping(); + } +#endif // !defined(OFFICIAL_BUILD) + + if (delegate_) + delegate_->PreSandboxStartup(); + +#if defined(OS_WIN) + if (!InitializeSandbox( + service_manager::SandboxTypeFromCommandLine(command_line), + params.sandbox_info)) + return TerminateForFatalInitializationError(); +#elif defined(OS_MACOSX) + // Do not initialize the sandbox at this point if the V2 + // sandbox is enabled for the process type. + bool v2_enabled = base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableV2Sandbox); + + if (process_type == switches::kRendererProcess || + process_type == switches::kPpapiPluginProcess || v2_enabled || + (delegate_ && delegate_->DelaySandboxInitialization(process_type))) { + // On OS X the renderer sandbox needs to be initialized later in the + // startup sequence in RendererMainPlatformDelegate::EnableSandbox(). + } else { + if (!InitializeSandbox()) + return TerminateForFatalInitializationError(); + } +#endif + + if (delegate_) + delegate_->SandboxInitialized(process_type); + +#if BUILDFLAG(USE_ZYGOTE_HANDLE) + if (process_type.empty()) { + // The sandbox host needs to be initialized before forking a thread to + // start the ServiceManager, and after setting up the sandbox and invoking + // SandboxInitialized(). + InitializeZygoteSandboxForBrowserProcess( + *base::CommandLine::ForCurrentProcess()); + } +#endif // BUILDFLAG(USE_ZYGOTE_HANDLE) + + // Return -1 to indicate no early termination. + return -1; +} + +int ContentMainRunnerImpl::Run() { + DCHECK(is_initialized_); + DCHECK(!is_shutdown_); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + + // Run this logic on all child processes. Zygotes will run this at a later + // point in time when the command line has been updated. + std::unique_ptr field_trial_list; + if (!process_type.empty() && + process_type != service_manager::switches::kZygoteProcess) + InitializeFieldTrialAndFeatureList(&field_trial_list); + + MainFunctionParams main_params(command_line); + main_params.ui_task = ui_task_; + main_params.created_main_parts_closure = created_main_parts_closure_; +#if defined(OS_WIN) + main_params.sandbox_info = &sandbox_info_; +#elif defined(OS_MACOSX) + main_params.autorelease_pool = autorelease_pool_; +#endif + + RegisterMainThreadFactories(); + +#if !defined(CHROME_MULTIPLE_DLL_CHILD) + // The thread used to start the ServiceManager is handed-off to + // BrowserMain() which may elect to promote it (e.g. to BrowserThread::IO). + if (process_type.empty()) { + return RunBrowserProcessMain(main_params, delegate_, + std::move(service_manager_thread_)); + } +#endif + + return RunOtherNamedProcessTypeMain(process_type, main_params, delegate_); +} + +void ContentMainRunnerImpl::Shutdown() { + DCHECK(is_initialized_); + DCHECK(!is_shutdown_); + + if (completed_basic_startup_ && delegate_) { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + + delegate_->ProcessExiting(process_type); + } + +#if defined(OS_WIN) +#ifdef _CRTDBG_MAP_ALLOC + _CrtDumpMemoryLeaks(); +#endif // _CRTDBG_MAP_ALLOC +#endif // OS_WIN + + exit_manager_.reset(nullptr); + + delegate_ = nullptr; + is_shutdown_ = true; +} + +#if !defined(CHROME_MULTIPLE_DLL_CHILD) +scoped_refptr +ContentMainRunnerImpl::GetServiceManagerTaskRunnerForEmbedderProcess() { + service_manager_thread_ = BrowserProcessSubThread::CreateIOThread(); + return service_manager_thread_->task_runner(); +} +#endif // !defined(CHROME_MULTIPLE_DLL_CHILD) + +// static +ContentMainRunner* ContentMainRunner::Create() { + return ContentMainRunnerImpl::Create(); +} + +} // namespace content diff --git a/chromium/content/app/content_main_runner_impl.h b/chromium/content/app/content_main_runner_impl.h new file mode 100644 index 00000000000..7fab6f739f7 --- /dev/null +++ b/chromium/content/app/content_main_runner_impl.h @@ -0,0 +1,89 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_APP_CONTENT_MAIN_RUNNER_IMPL_H_ +#define CONTENT_APP_CONTENT_MAIN_RUNNER_IMPL_H_ + +#include + +#include "base/callback_forward.h" +#include "base/memory/scoped_refptr.h" +#include "build/build_config.h" +#include "content/public/app/content_main.h" +#include "content/public/app/content_main_runner.h" +#include "content/public/common/content_client.h" + +#if defined(OS_WIN) +#include "sandbox/win/src/sandbox_types.h" +#elif defined(OS_MACOSX) +#include "base/mac/scoped_nsautorelease_pool.h" +#endif // OS_WIN + +namespace base { +class AtExitManager; +class SingleThreadTaskRunner; +} // namespace base + +namespace content { +class BrowserProcessSubThread; +class ContentMainDelegate; +struct ContentMainParams; + +class ContentMainRunnerImpl : public ContentMainRunner { + public: + static ContentMainRunnerImpl* Create(); + + ContentMainRunnerImpl(); + ~ContentMainRunnerImpl() override; + + int TerminateForFatalInitializationError(); + + // ContentMainRunner: + int Initialize(const ContentMainParams& params) override; + int Run() override; + void Shutdown() override; + +#if !defined(CHROME_MULTIPLE_DLL_CHILD) + // Creates a thread and returns the SingleThreadTaskRunner on which + // ServiceManager should run. + scoped_refptr + GetServiceManagerTaskRunnerForEmbedderProcess(); +#endif // !defined(CHROME_MULTIPLE_DLL_CHILD) + + private: + // True if the runner has been initialized. + bool is_initialized_ = false; + + // True if the runner has been shut down. + bool is_shutdown_ = false; + + // True if basic startup was completed. + bool completed_basic_startup_ = false; + + // Used if the embedder doesn't set one. + ContentClient empty_content_client_; + + // The delegate will outlive this object. + ContentMainDelegate* delegate_ = nullptr; + + std::unique_ptr exit_manager_; + +#if defined(OS_WIN) + sandbox::SandboxInterfaceInfo sandbox_info_; +#elif defined(OS_MACOSX) + base::mac::ScopedNSAutoreleasePool* autorelease_pool_ = nullptr; +#endif + + std::unique_ptr service_manager_thread_; + + base::Closure* ui_task_ = nullptr; + + CreatedMainPartsClosure* created_main_parts_closure_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(ContentMainRunnerImpl); +}; + +} // namespace content + +#endif // CONTENT_APP_CONTENT_MAIN_RUNNER_IMPL_H_ diff --git a/chromium/content/app/content_service_manager_main_delegate.cc b/chromium/content/app/content_service_manager_main_delegate.cc index 62dceb57886..f3132ac7103 100644 --- a/chromium/content/app/content_service_manager_main_delegate.cc +++ b/chromium/content/app/content_service_manager_main_delegate.cc @@ -5,10 +5,11 @@ #include "content/app/content_service_manager_main_delegate.h" #include "base/command_line.h" +#include "content/app/content_main_runner_impl.h" #include "content/public/app/content_main_delegate.h" -#include "content/public/app/content_main_runner.h" #include "content/public/common/content_switches.h" #include "content/public/common/service_names.mojom.h" +#include "services/service_manager/embedder/switches.h" #include "services/service_manager/runner/common/client_util.h" namespace content { @@ -16,7 +17,7 @@ namespace content { ContentServiceManagerMainDelegate::ContentServiceManagerMainDelegate( const ContentMainParams& params) : content_main_params_(params), - content_main_runner_(ContentMainRunner::Create()) {} + content_main_runner_(ContentMainRunnerImpl::Create()) {} ContentServiceManagerMainDelegate::~ContentServiceManagerMainDelegate() = default; @@ -44,7 +45,8 @@ bool ContentServiceManagerMainDelegate::IsEmbedderSubprocess() { type == switches::kPpapiBrokerProcess || type == switches::kPpapiPluginProcess || type == switches::kRendererProcess || - type == switches::kUtilityProcess || type == switches::kZygoteProcess; + type == switches::kUtilityProcess || + type == service_manager::switches::kZygoteProcess; } int ContentServiceManagerMainDelegate::RunEmbedderProcess() { @@ -124,4 +126,11 @@ ContentServiceManagerMainDelegate::CreateEmbeddedService( return nullptr; } +#if !defined(CHROME_MULTIPLE_DLL_CHILD) +scoped_refptr ContentServiceManagerMainDelegate:: + GetServiceManagerTaskRunnerForEmbedderProcess() { + return content_main_runner_->GetServiceManagerTaskRunnerForEmbedderProcess(); +} +#endif // !defined(CHROME_MULTIPLE_DLL_CHILD) + } // namespace content diff --git a/chromium/content/app/content_service_manager_main_delegate.h b/chromium/content/app/content_service_manager_main_delegate.h index 4393a8fac23..a6ab54a14bc 100644 --- a/chromium/content/app/content_service_manager_main_delegate.h +++ b/chromium/content/app/content_service_manager_main_delegate.h @@ -8,13 +8,15 @@ #include #include "base/macros.h" +#include "base/memory/scoped_refptr.h" +#include "base/single_thread_task_runner.h" #include "build/build_config.h" #include "content/public/app/content_main.h" #include "services/service_manager/embedder/main_delegate.h" namespace content { -class ContentMainRunner; +class ContentMainRunnerImpl; class ContentServiceManagerMainDelegate : public service_manager::MainDelegate { public: @@ -23,6 +25,10 @@ class ContentServiceManagerMainDelegate : public service_manager::MainDelegate { // service_manager::MainDelegate: int Initialize(const InitializeParams& params) override; +#if !defined(CHROME_MULTIPLE_DLL_CHILD) + scoped_refptr + GetServiceManagerTaskRunnerForEmbedderProcess() override; +#endif // !defined(CHROME_MULTIPLE_DLL_CHILD) bool IsEmbedderSubprocess() override; int RunEmbedderProcess() override; void ShutDownEmbedderProcess() override; @@ -42,7 +48,7 @@ class ContentServiceManagerMainDelegate : public service_manager::MainDelegate { private: ContentMainParams content_main_params_; - std::unique_ptr content_main_runner_; + std::unique_ptr content_main_runner_; #if defined(OS_ANDROID) bool initialized_ = false; diff --git a/chromium/content/app/strings/content_strings.grd b/chromium/content/app/strings/content_strings.grd index d847258a81d..e2ea8177ef6 100644 --- a/chromium/content/app/strings/content_strings.grd +++ b/chromium/content/app/strings/content_strings.grd @@ -269,6 +269,123 @@ below: disclosure triangle + + abstract + + + acknowledgments + + + afterword + + + appendix + + + back link + + + bibliography entry + + + bibliography + + + bibliography reference + + + chapter + + + colophon + + + conclusion + + + cover + + + credit + + + credits + + + dedication + + + endnote + + + endnotes + + + epigraph + + + epilogue + + + errata + + + example + + + footnote + + + foreword + + + glossary + + + glossary reference + + + index + + + introduction + + + note reference + + + notice + + + page break + + + page list + + + part + + + preface + + + prologue + + + pullquote + + + Q&A + + + subtitle + + + tip + + + table of contents + feed @@ -281,6 +398,15 @@ below: footer + + graphics document + + + graphics object + + + graphics symbol + Autofill @@ -810,7 +936,7 @@ below: Picture-in-Picture - Now in Picture-in-Picture mode + This video is playing in Picture-in-Picture Now casting to $1Living Room TV diff --git a/chromium/content/app/strings/translations/content_strings_am.xtb b/chromium/content/app/strings/translations/content_strings_am.xtb index 336cfe9cac9..6e66f3b1caa 100644 --- a/chromium/content/app/strings/translations/content_strings_am.xtb +++ b/chromium/content/app/strings/translations/content_strings_am.xtb @@ -10,28 +10,34 @@ ማሟያ መልሶ መልስ ተንሸራታች +የግርጌ ማስታወሻ መገናኛ +ክሬዲቶች ጽሑፍ ሰንደቅ ክልል በዚህ ወር ፋይሎችን ይምረጡ +ጥቅስ እባክዎ ባዶ ያልሆነ የኢሜይል አድራሻ ያስገቡ። ሌላ... የማንቂያ_መገናኛ የዛፍ ፍርግርግ ጥዋት/ከሰዓት በርቀት መሳሪያ ላይ አጫውት +ምሳሌ ቀቀ ሁኔታ ዋጋ ወይም ከዚያ በፊት መሆን አለበት። ወደ ማንጸባረቅ ተቀይሯል «» በ«» ውስጥ በተሳሳተ ቦታ ላይ ነው የገባው። ዛሬ +የገጽ ዝርዝር እባክዎ ይህ ጽሑፍ ወደ ወይም ከዚያ በታች ቁምፊዎች ያሳጥሩት (በአሁኑ ጊዜ ቁምፊዎችን እየተጠቀሙ ነዎት)። ሳምንት ትራክ አመልካች ሳጥን +ገጽ ከፋይ የቀን እና የሰዓት መራጭ ቀን እባክዎ ይህን መስክ ይሙሉት። @@ -40,6 +46,7 @@ አጫውት እባክዎ የሚሰራ ዋጋ ያስገቡ። የሚቀርበው ዋጋ ነው። አስገባ +ግራፊካዊ ነገር ግርጌ ራስ-ሙላ ድምጽ @@ -70,30 +77,41 @@ እባክዎ ይህን ጽሑፍ ወደ ቁምፊዎች ወይም ከዚያ በላይ ያራዝሙት (አሁን እየተጠቀሙ ያሉት ቁምፊዎችን ነው)። ከ«» በፊት የሚመጣ ክፍል የ«» ምልክት መያዝ የለበትም። ከሙሉ ማያገጽ ውጣ +ድምዳሜ መተግበሪያ +ቅድመ-ታሪክ የማሸብለያ አሞሌ ሚሊሰኮንዶች ግራፊክ +መቅድም ቀዳሚውን ወር አሳይ color picker የምናሌ አሞሌ የይዘት መረጃ እባክዎ የሚሰራ ዋጋ ያስገቡ። ሁለቱ የሚቀርቡ ዋጋዎች እና ናቸው። +ጥያቄ እና መልስ የምናሌ አዝራር +የሙዳየ ቃላት ዋቢ ዝርዝሮች ቅጽ ዛፍ ከ«» በኋላ የሚመጣ ክፍል የ«» ምልክት መያዝ የለበትም። +ቅጥያዎች +ሽፋን የትር ፓነል ቪዲዮ ፈልግ እባክዎ ቁጥር ያስገቡ። +የዋቢ መጽሐፍት ግቤት ተመርጠዋል ድምጸ-ከል አንሳ እባክዎ ከ«» በኋላ አንድ ክፍል ያስገቡ። «» ያልተሟላ ነው። የሂደት አመልካች +የተቀረጸ ጽሑፍ ቀጣዩን ወር አሳይ የረድፍ ራስጌ +ድሕረ ታሪክ +ይህ ቪዲዮ ስዕል-በስዕል ውስጥ እያጫወተ ነው ዋጋ ከ የሚበልጥ ወይም ከእሱ እኩል መሆን አለበት። ፔታ እባክዎ አንድ ክፍል ያስገቡና «»ን ያስከትሉ። «» ያልተሟላ ነው። @@ -106,9 +124,11 @@ ድምፅ-ከልን አንሳ ሰዓት 10 ሴ ለመዝለል ወደ ግራ ወይም ቀኝ ሁለቴ መታ ያድርጉ +ረቂቅ መልሶ ማጫወትን ለአፍታ አቁም አዝራር ተጨማሪ አማራጮች +መግቢያ ድምጽ ይዝጉ አጥፋ ሙሉ ገጽ ዕይታ @@ -119,12 +139,15 @@ ዓመት የዝርዝር ሳጥን የድምጹ ትራክ ድምጸ-ከል አንሳ +ዋቢ መጽሐፍት ሜትር +ድሕረ ቃል +ሙዳየ ቃላት የወር መምረጫ ፓነል አሳይ የኦዲዮ ሰዓት አንፏቃቂ +ምስጋና ሰዓት መራጭ አማራጮች -አሁን በስዕል-ውስጥ-ስዕል ሁነታ ውስጥ ወደ ሙሉ ገጽ ዕይታ ግባ ምግብ የምዝግብ ማስታወሻ @@ -135,22 +158,31 @@ ጠቃሚ የመሣሪያ ምክር አጫውት ተሰኪን መጫን አልተቻለም። +የዋቢ መጽሐፍት ማጣቀሻ ድምጸ-ከል ያድርጉ የተዘጉ የስዕል መግለጫዎችን ይደብቁ ማውጫ +ኮሎፎን የጽሑፍ ጥቅስ ማህደረ መረጃን አውርድ እባክዎ ይህን ጽሑፍ ወደ ወይም ከዚያ በላይ ቁምፊዎች ያራዝሙት (አሁን እየተጠቀሙ ያሉት 1 ቁምፊ ነው)። እባክዎ የተጠየቀውን ቅርጸት ያዛምዱ። +መቅድም ያለፈው ጊዜ እባክዎ በዝርዝሩ ውስጥ አንድ ንጥል ይምረጡ። የአምድ ራስጌ +ክሬዲት ፋይል ምረጥ ሌላ... +ግርጌ ማስታወሻዎች ቴባ TAB አውርድ +የጀርባ አገናኝ +ጠቃሚ ምክር +ግራፊካዊ ሰነድ ሰዓቶች +መረጃ ጠቋሚ ቀሪ ጊዜ የተዘጉ የስዕል መግለጫዎችን ማሳየት አቁም የኤች ቲ ኤም ኤል ይዘት @@ -160,24 +192,33 @@ መክፈያ ምንም ፋይል አልተመረጠም የጽሑፍ መስክ ፈልግ +ማስታወቂያ +ማስታወሻ ማጣቀሻ የተዘጉ የስዕል መግለጫዎችን ማሳየት ጀምር በዚህ ሳምንት ፊልሙን በሙሉ ማያ ገጽ ሁነታ አጫውት ዓዓዓዓ መግለጫ ጽሑፎች ልክ ያልሆነ እሴት። +ክፍለ አካል የመሣሪያ አሞሌ ፋይሎች እባክዎ ፋይል ይምረጡ። የሬዲዮ አዝራር +መታሰቢያነቱ +እርማት ደቂቃዎች የማህደረ መረጃ ቁጥጥር +ሥዕላዊ ምልክት የብየና ዝርዝር ርዕስ +ማውጫ በርቀት መልሶ ማጫወትን ተቆጣጠር +የግርጌ ጽሑፍ በስዕል-ውስጥ-ስዕል እባክዎ ከእነዚህ አማራጮች ውስጥ አንዱን ይምረጡ። መልሶ ማጫወት ይጀምሩ +ምዕራፍ አሁን ወደ የእርስዎ ቴሌቪዥን cast በማድረግ ላይ main የቪዲዮ መልሶ ማጫወት ስህተት @@ -191,6 +232,7 @@ ሌላ... ማንቂያ ፣ የሚጀምረው በ +የግርጌ ማስታወሻ ሕዋስ ምናሌ \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ar.xtb b/chromium/content/app/strings/translations/content_strings_ar.xtb index c5fb18d95ae..1b74c3db7b1 100644 --- a/chromium/content/app/strings/translations/content_strings_ar.xtb +++ b/chromium/content/app/strings/translations/content_strings_ar.xtb @@ -10,28 +10,34 @@ تكميلي صورة بها نص متحرك شريط تمرير +تعليق ختامي مربع حوار +إسهامات article إعلان بانر منطقة هذا الشهر اختيار الملفات +اقتباس مستمد يُرجى إدخال عنوان البريد الإلكتروني وعدم ترك الحق فارغًا. آخر... مربع حوار_للتنبيه شبكة متفرعة صباحًا/مساءً تشغيل على جهاز بعيد +مثال يوم الحالة يجب أن تكون القيمة أو قبل ذلك. تم التبديل إلى النسخ المطابق. تم استخدام "" في موضع خاطئ في "". اليوم +قائمة صفحات يُرجى اختصار هذا النص حتى يصل إلى من الحروف أو أقل (أنت الآن تستخدم من الحروف). الأسبوع ، المقطع الصوتي مربع اختيار +فاصل صفحة منتقي التاريخ والوقت يوم يُرجى ملء هذا الحقل. @@ -40,6 +46,7 @@ تشغيل يُرجى إدخال قيمة صالحة. علمًا بأن أقرب قيمة صالحة هي . إرسال +كائن رسومي تذييل الملء التلقائي الصوت @@ -70,30 +77,41 @@ يُرجى إطالة هذا النص إلى من الحروف أو أكثر (أنت تستخدم حاليًا من الحروف). الجزء المتبوع بالعلامة "" يجب ألا يشتمل على الرمز "". خروج من عرض ملء الشاشة +خاتمة التطبيق +فاتحة شريط التمرير مللي ثانية الرسم +مقدمة عرض الشهر السابق علبة الألوان شريط قوائم معلومات المحتوى يُرجى إدخال قيمة صالحة. علمًا بأن القيم الصالحة تتراوح بين و. +سين وجيم زر القائمة +مرجع مسرد مصطلحات التفاصيل نموذج متفرع يجب ألا يشتمل الجزء الذي يلي العلامة "" على الرمز "". +ملحق +غلاف لوحة علامة التبويب فيديو بحث يُرجى إدخال عدد. +إدخال قائمة مراجع محددة إعادة الصوت يُرجى إدخال الجزء الذي يلي العلامة ""، حيث إن "" غير مكتمل. مؤشر التقدم +عبارة مقتبسة عرض الشهر التالي عنوان الصف +تتِّمة +يتم تشغيل هذا الفيديو في الوضع "نافذة ضمن النافذة". يجب أن تكون القيمة أكبر من أو تساوي . بيتابايت يُرجى إدخال جزء متبوع بعلامة ""، حيث إن "" غير مكتمل. @@ -106,9 +124,11 @@ إعادة الصوت الوقت انقر مرّتين جهة اليمين أو اليسار لتخطي 10 ثوانٍ. +نبذة مختصرة إيقاف التشغيل مؤقتًا زر خيارات إضافية +مقدمة كتم الصوت إيقاف ملء الشاشة @@ -119,12 +139,15 @@ عام مربع القائمة إعادة صوت المقطع الصوتي +قائمة مراجع متر +كلمة ختامية +مسرد مصطلحات عرض لوحة تحديد الشهر شريط تمرير وقت الصوت +شكر وتقدير منتقي الوقت خيارات -أنت الآن في وضع نافذة ضمن النافذة. تشغيل وضع ملء الشاشة البطاقات log @@ -135,22 +158,31 @@ تلميح التشغيل تعذر تحميل المكوّن الإضافي. +مرجع قائمة مراجع كتم الصوت إخفاء الترجمة والشرح الدليل +إشارة ناشر علامة اقتباس فقرة تنزيل وسائط يُرجى إطالة هذا النص إلى من الحروف أو أكثر (أنت الآن تستخدم حرفًا واحدًا). يُرجى مطابقة التنسيق المطلوب. +تمهيد المدة المنقضية يُرجى تحديد عنصر من القائمة. عنوان العمود +إسهام اختيار ملفّ آخر... +تعليقات ختامية تيرابايت علامة تبويب تنزيل +رابط رجوع +نصيحة +مستند رسومي ساعات +فهرس المدة المتبقية إيقاف عرض الترجمة والشرح ‏محتوى HTML @@ -160,24 +192,33 @@ أداة التقسيم ّلم يتمّ اختيار أيّ ملفّ الحقل النصي للبحث +ملاحظة +ملاحظة مرجعية بدء عرض الترجمة والشرح هذا الأسبوع تشغيل الفيلم في وضع ملء الشاشة سنة الترجمة والشرح قيمة غير صحيحة +جزء شريط الأدوات عدد الملفات: يُرجى تحديد ملف. زر الاختيار +إهداء +قائمة بالأخطاء دقائق التحكم في الوسائط +رمز رسومي قائمة تعريف العنوان +جدول المحتويات التحكم في التشغيل عن بعد +ترجمة مصاحِبة نافذة ضمن النافذة يُرجى اختيار أحد هذه الخيارات. بدء التشغيل +فصل يتم الآن الإرسال إلى جهاز التلفزيون main خطأ في تشغيل الفيديو. @@ -191,6 +232,7 @@ آخر... تنبيه ، ابتداء من +حاشية سُفلية خلية القائمة \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_bg.xtb b/chromium/content/app/strings/translations/content_strings_bg.xtb index c767e2faddf..14dc2f943d5 100644 --- a/chromium/content/app/strings/translations/content_strings_bg.xtb +++ b/chromium/content/app/strings/translations/content_strings_bg.xtb @@ -10,28 +10,34 @@ допълнение marquee плъзгач +бележка в края диалогов прозорец +признания за източника статия банер регион Този месец Избор на файлове +изнесен цитат Моля, въведете имейл адрес. Други... диалогов прозорец със сигнал дървовидна таблица AM/PM възпроизвеждане на отдалечено устройство +пример дд състояние Стойността трябва да е или по-рано. Превключихте към дублиране “ се използва на неправилна позиция в/ъв „“. Днес +списък със страниците Моля, съкратете този текст до знака или по-малко (понастоящем използвате знака). Седмица , г. Запис квадратче за отметка +разделител на страници инструмент за избор на дата и час Ден Моля, попълнете това поле. @@ -40,6 +46,7 @@ пускане Моля, въведете валидна стойност. Най-близката такава е . Изпращане +графичен обект долен колонтитул Автоматично попълване звук @@ -70,30 +77,41 @@ Моля, удължете този текст поне до знака (понастоящем използвате ). Текстът преди „“ не бива да съдържа символа „“. Изход от цял екран +заключение приложение +пролог лента за превъртане Милисекунди графика +предговор Показване на предишния месец инструмент за избор на цветове лента с менюта информация за съдържанието Моля, въведете валидна стойност. Двете най-близки такива са и . +Въпроси и отговори бутон за меню +препратка към определение в терминологичния речник Подробности формуляр tree Текстът след „“ не бива да съдържа символа „“. +приложение +корица панел с раздели видео search Моля, въведете номер. +библиографски запис Избрани: Включване на звука Моля, въведете текст след „“. „“ е непълно. индикатор за напредък +епиграф Показване на следващия месец заглавка на ред +епилог +Този видеоклип се възпроизвежда в режим „Картина в картината“ Стойността трябва да е по-голяма или равна на . ПБ Моля, въведете текст преди „“. „“ е непълно. @@ -106,9 +124,11 @@ пускане на звука time Докоснете два пъти стрелката наляво или надясно, за да пропуснете 10 сек +резюме поставяне на възпроизвеждането на пауза бутон още опции +въведение Заглушаване Изключено Цял екран @@ -119,12 +139,15 @@ Година списъчно поле пускане на аудиозаписа +библиография индикатор +послеслов +терминологичен речник Показване на панела за избиране на месец времеви плъзгач за аудиозаписа +признания инструмент за избор на час Опции -Гледате в режим „Картина в картината“ вход за цял екран емисия регистрационен файл @@ -135,22 +158,31 @@ подсказка Пускане Приставката не можа да се зареди. +препратка към библиографски запис заглушаване скриване на надписите директория +колофон блоков цитат изтегляне на мултимедията Моля, удължете този текст до поне знака (понастоящем използвате 1 знак). Моля, спазвайте изисквания формат. +предисловие изминало време Моля, изберете елемент в списъка. заглавка на колона +признание за източника Избор на файл Други... +бележки в края ТБ раздел Изтегляне +препратка +съвет +графичен документ Часове +показалец оставащо време спиране на показването на надписите HTML съдържание @@ -160,24 +192,33 @@ разделител Няма избран файл текстово поле за търсене +известие +препратка към бележка започване на показването на надписите Тази седмица пускане на филма в режим на цял екран гггг Надписи Невалидна стойност. +част лента с инструменти файла Моля, изберете файл. бутон за избор +посвещение +списък с печатните грешки Минути медийна контрола +графичен символ списък с дефиниции заглавие +съдържание управление на отдалеченото възпроизвеждане +подзаглавие Картина в картината Моля, изберете една от тези опции. начало на възпроизвеждането +глава В момента се предава към телевизора ви основен елемент Грешка при възпроизвеждането на видеоклипа @@ -191,6 +232,7 @@ Други... сигнал – започва от +бележка под линия клетка меню \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_bn.xtb b/chromium/content/app/strings/translations/content_strings_bn.xtb index d99fd33af1e..e0bf864e511 100644 --- a/chromium/content/app/strings/translations/content_strings_bn.xtb +++ b/chromium/content/app/strings/translations/content_strings_bn.xtb @@ -10,28 +10,34 @@ পরিপূরক marquee স্লাইডার +এন্ডনোট ডায়ালগ +ক্রেডিট নিবন্ধ ব্যানার অঞ্চল এই মাস ফাইল বেছে নিন +পুলকোট দয়া করে একটি খালি না থাকা ইমেল ঠিকানা লিখুন৷ অন্যান্য... সতর্কতার_ডায়ালগ ট্রি গ্রিড AM/PM রিমোট ডিভাইসে প্লে করুন +উদাহরণ dd স্থিতি মানকে অবশ্যই বা আগের হতে হবে৷ মিররিং এ পরিবর্তন করা হয়েছে '' এ একটি ভুল অবস্থানে '' ব্যবহৃত হয়েছে৷ আজ +পৃষ্ঠা তালিকা দয়া করে এই পাঠ্যটি টি অক্ষর বা তার কমে (আপনি বর্তমানে টি অক্ষর ব্যবহার করছেন) সংক্ষিপ্ত করুন৷ সপ্তাহ, ট্র্যাক চেকবাক্স +পৃষ্ঠা বিরতি তারিখ এবং সময় চয়নকারি দিন দয়া করে এই ক্ষেত্রটি পূরণ করুন৷ @@ -40,6 +46,7 @@ চালনা করুন দয়া করে একটি বৈধ মান লিখুন৷ কাছাকাছির বৈধ মান হল জমা দিন +গ্রাফিক্স অবজেক্ট পাদলেখ স্বয়ংপূরণ অডিও @@ -70,30 +77,41 @@ দয়া করে এই পাঠ্যকে ন্যূনতম অক্ষরের বা তার বেশি (আপনি বর্তমানে টি অক্ষর ব্যবহার করেছেন) দৈর্ঘের করুন। '' অনুসরণ করে এমন একটি অংশে '' চিহ্ন থাকা উচিত নয়৷ পূর্ণস্ক্রীন থেকে প্রস্থান করুন +সিদ্ধান্ত অ্যাপ্লিকেশান +ভূমিকা স্ক্রোল বার মিলিসেকেন্ড গ্রাফিক +মুখবন্ধ পূর্ববর্তী মাস দেখান রঙ চয়নকারী মেনু বার সামগ্রীর তথ্য দয়া করে একটি বৈধ মান লিখুন৷ দুটি কাছাকাছির বৈধ মান হল এবং +প্রশ্ন এবং উত্তর মেনু বোতাম +শব্দকোষের রেফারেন্স বিশদ বিবরণ ফর্ম tree '' অনুসরণ করে এমন একটি অংশে '' চিহ্ন থাকা উচিত নয়৷ +পরিশিষ্ট +কভার ট্যাব প্যানেল ভিডিও search দয়া করে একটি সংখ্যা লিখুন৷ +বিবলিওগ্রাফি এন্ট্রি টি নির্বাচিত সশব্দ করুন দয়া করে '' অনুসরণ করে একটি অংশ লিখুন৷ '' অসম্পূর্ণ৷ অগ্রগতি সূচক +এপিগ্রাফ পরবর্তী মাস দেখান সারি শিরোলেখ +উপসংহার +এই ভিডিওটি ছবির-মধ্যে-ছবিতে চালানো হচ্ছে মানটি অবশ্যই এর চেয়ে বেশি বা সমান হবে৷ PB '' অনুসরণ করে একটি অংশ লিখুন৷ '' অসম্পূর্ণ৷ @@ -106,9 +124,11 @@ সশব্দ time ১০ সেকেন্ড আগে পরে করার জন্য বাঁ অথবা ডান দিকে ডবল ট্যাপ করুন +বিমূর্ত প্লেব্যাক বিরতি বোতাম আরও বিকল্প +পরিচয় নিঃশব্দ করুন বন্ধ করুন সম্পূর্নস্ক্রীণ @@ -119,12 +139,15 @@ বছর তালিকা বাক্স অডিও ট্র্যাক সশব্দ করুন +বিবলিওগ্রাফি মিটার +পরিশিষ্ট +শব্দকোষ মাস নির্বাচনের প্যানেল দেখান অডিও সময় স্ক্রাবার +কৃতজ্ঞতা স্বীকার সময় চয়নকারি বিকল্পসমূহ -এখন ছবির-মধ্যে-ছবি মোডে পূর্ণ স্ক্রীনে প্রবেশ করুন ফিড লগ @@ -135,22 +158,31 @@ সরঞ্জামটিপ চালু করুন প্লাগ ইন লোড করা যায়নি। +বিবলিওগ্রাফি রেফারেন্স নিঃশব্দ বন্ধ করা পরিচয়লিপিগুলি লুকান ডিরেক্টরি +বইটির সম্পর্কে ব্লক উদ্ধৃতি মিডিয়া ডাউনলোড করুন দয়া করে এই পাঠ্যকে ন্যূনতম অক্ষরের বা তার বেশি দৈর্ঘ্যের করুন (আপনি বর্তমানে ১টি অক্ষর ব্যবহার করেছেন)। দয়া করে অনুরোধ হওয়া বিন্যাসটি মেলান৷ +মুখবন্ধ অতিবাহিত সময় তালিকা থেকে একটি আইটেম নির্বাচন করুন৷ কলাম শিরোলেখ +স্বীকৃতি ফাইল বেছে নিন অন্যান্য... +এন্ডনোট TB ট্যাব ডাউনলোড করুন +ফিরে যাওয়ার লিঙ্ক +পরামর্শ +গ্রাফিক্স ডকুমেন্ট ঘণ্টা +সূচি অবশিষ্ট সময় বদ্ধ পরিচয়লিপিগুলির প্রদর্শন থামান HTML সামগ্রী @@ -159,25 +191,34 @@ ইমেল ঠিনাকাটিতে দয়া করে একটি '' অন্তর্ভুক্ত করুন৷ '' এ একটি '' অনুপস্থিত৷ স্প্লিটার কোনও ফাইল চয়ন করা হয় নি -অনুসন্ধান পাঠ্য ফিল্ড +সার্চ পাঠ্য ফিল্ড +বিজ্ঞপ্তি +নোটের রেফারেন্স বন্ধ পরিচয়লিপিগুলির প্রদর্শন শুরু করুন এই সপ্তাহ পূর্ণ স্ক্রিন মোডে চলচ্চিত্র চালান yyyy পরিচয়লিপিগুলি অকার্যকর মান৷ +অংশ টুল দণ্ড টি ফাইল দয়া করে একটি ফাইল নির্বাচন করুন৷ রেডিও বোতাম +উৎসর্গ +ত্রুটি-বিচ্যুতি মিনিট মিডিয়া নিয়ন্ত্রণ +গ্রাফিক্স চিহ্ন সংজ্ঞার তালিকা শিরোনাম +সূচিপত্র রিমোট প্লেব্যাক নিয়ন্ত্রণ করুন +সাবটাইটেল ছবির-মধ্যে-ছবি দয়া করে বিকল্পগুলির একটি নির্বাচন করুন৷ প্লেব্যাক শুরু করুন +অধ্যায় এখন আপনার TV তে কাস্ট করা হচ্ছে প্রধান ভিডিও প্লেব্যাকে সমস্যা @@ -191,6 +232,7 @@ অন্যান্য... সতর্কতা , এ শুরু +ফুটনোট কক্ষ মেনু \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ca.xtb b/chromium/content/app/strings/translations/content_strings_ca.xtb index 3dc39f7b671..fd56238d889 100644 --- a/chromium/content/app/strings/translations/content_strings_ca.xtb +++ b/chromium/content/app/strings/translations/content_strings_ca.xtb @@ -10,28 +10,34 @@ complementari marquee control lliscant +nota final quadre de diàleg +crèdits article bàner regió Aquest mes Trieu els fitxers +citació extreta Introduïu una adreça electrònica. Altres... alert_dialog quadrícula d'arbre a. m./p. m. reprodueix al dispositiu remot +exemple dd estat El valor ha de ser o anterior. S'ha canviat a projecció "" s'ha utilitzat en una posició incorrecta a "". Avui +llista de pàgines Escurceu aquest text a un màxim de caràcters (ara n'esteu utilitzant ). Setmana , Pista casella de selecció +salt de pàgina selector de data i hora Dia Empleneu aquest camp. @@ -40,6 +46,7 @@ reprodueix Introduïu un valor vàlid. El valor vàlid més proper és . Envia +objecte gràfic peu Emplenament autom. àudio @@ -70,30 +77,41 @@ Allargueu aquest text fins a un mínim de caràcters (ara n'utilitzeu ). Un nom d'usuari seguit de "" no pot contenir el símbol "". Surt de la pantalla completa +conclusió aplicació +pròleg barra de desplaçament Mil·lisegons gràfic +preàmbul Mostra el mes anterior selector de color barra de menús informació sobre el contingut Introduïu un valor vàlid. Els valors vàlids més propers són i . +Preguntes botó de menú +referència del glossari Detalls formulari arbre Un domini precedit per "" no pot contenir el símbol "". +apèndix +portada tauler de pestanyes vídeo cerca Introduïu un número. +entrada bibliogràfica Elements seleccionats: Activa el so Introduïu un domini precedit per "". "" no és una adreça electrònica completa. indicador de progrés +epígraf Mostra el mes següent capçalera de la fila +epíleg +Aquest vídeo s'està reproduint en mode de pantalla en pantalla El valor ha de ser més gran o igual que . PB Introduïu un nom d'usuari seguit de "". "" no és una adreça completa. @@ -106,9 +124,11 @@ activa el so time Fes doble toc a l'esquerra o a la dreta per saltar 10 s +resum pausa la reproducció botó més opcions +introducció Silencia Desactivat Pantalla completa @@ -119,12 +139,15 @@ Any quadre de llista activa el so de la pista d'àudio +bibliografia comptador +cloenda +glossari Mostra el tauler de la selecció de mes barra de moment de l'àudio +agraïments selector d'hora Opcions -Ara en el mode de pantalla en pantalla passa a pantalla completa El meu tauler registre @@ -135,22 +158,31 @@ descripció emergent Reprodueix El connector no s'ha pogut carregar. +referència bibliogràfica silencia amaga els subtítols ocults directori +colofó cita en bloc baixa els fitxers multimèdia Allarga aquest text fins a caràcters o més (ara n'utilitzes 1). Feu servir el format sol·licitat. +prefaci temps transcorregut Seleccioneu un element de la llista. capçalera de columna +crèdit Tria un fitxer Altres... +notes finals TB pestanya Baixa +enllaç d'entrada +consell +document gràfic Hores +índex temps restant deixa de mostrar subtítols ocults Contingut HTML @@ -160,24 +192,33 @@ divisor No s'ha triat cap fitxer camp de text de la cerca +avís +referència de la nota comença a mostrar subtítols ocults Aquesta setmana reprodueix la pel·lícula en mode de pantalla completa aaaa Subtítols Valor no vàlid. +part barra d'eines fitxers Seleccioneu un fitxer. botó d'opció +dedicatòria +errata Minuts control de mitjans +símbol gràfic llista de definicions Encapçalament +taula de continguts reproducció amb comandament +subtítol Pantalla en pantalla Seleccioneu una d'aquestes opcions. inicia la reproducció +capítol S'està emetent al televisor principal Error de reproducció del vídeo @@ -191,6 +232,7 @@ Altres... alerta , a partir del dia +nota al peu cel·la menú \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_cs.xtb b/chromium/content/app/strings/translations/content_strings_cs.xtb index bc5dc6c1164..f037cfd0ffe 100644 --- a/chromium/content/app/strings/translations/content_strings_cs.xtb +++ b/chromium/content/app/strings/translations/content_strings_cs.xtb @@ -10,28 +10,34 @@ doplňkové běžící text posuvník +koncová poznámka dialogové okno +poděkování čl banner oblast Tento měsíc Zvolit soubory +citace Vyplňte e-mailovou adresu. Jiné… dialog upozornění stromová mřížka AM/PM přehrát ve vzdáleném zařízení +příklad dd stav Datum musí být nebo dříve. Přepnuto na zrcadlení Znak v doméně není použitý správně. Dnes +seznam stránek Zkraťte prosím tento text na znaků nebo méně. (Aktuálně má znaků.) . týden, Stopa zaškrtávací políčko +konec stránky výběr data a času Den Vyplňte prosím toto pole. @@ -40,6 +46,7 @@ přehrát Zadejte platnou hodnotu. Nejbližší platná hodnota je . Odeslat +grafický objekt zápatí Automatické vyplňování zvuk @@ -70,30 +77,41 @@ Prodlužte prosím tento text na či více znaků. (Aktuálně má znaků.) Část před znakem nesmí obsahovat znak . Ukončit režim na celou obrazovku +závěr aplikace +prolog posuvník Milisekundy obrázek +předmluva Zobrazit předchozí měsíc výběr barev panel nabídky informace o obsahu Zadejte platnou hodnotu. Dvě nejbližší hodnoty jsou a . +Dotazy tlačítko nabídky +odkaz na glosář Podrobnosti formulář strom Část za znakem nesmí obsahovat znak . +příloha +titulní strana panel karty video search Zadejte prosím číslo. +bibliografická položka Vybráno: Zapnout zvuk Zadejte část za znakem . Adresa není úplná. indikátor průběhu +nápis Zobrazit další měsíc záhlaví řádku +epilog +Toto video se přehrává v režimu obrazu v obraze Hodnota musí být větší nebo rovna . PB Zadejte část před znakem . Adresa není úplná. @@ -106,9 +124,11 @@ zapnout zvuk čas Dvojitým klepnutím na šipku vlevo nebo vpravo přeskočíte o 10 s +abstrakt pozastavit přehrávání tlačítko další možnosti +úvod Ztlumit Vypnuto Celá obrazovka @@ -119,12 +139,15 @@ Rok seznam zapnout zvukovou stopu +bibliografie měřič +doslov +glosář Zobrazit panel pro výběr měsíců posuvník času zvuku +poděkování výběr času Možnosti -Je aktivní režim obrazu v obraze přejít do režimu celé obrazovky zdroj protokol @@ -135,22 +158,31 @@ popisek Přehrát Plugin se nepodařilo načíst. +bibliografický odkaz ztlumit skrýt titulky adresář +kolofon bloková citace stáhnout média Prodlužte prosím tento text na či více znaků. (Aktuálně má 1 znak.) Zadejte hodnotu, která odpovídá požadovanému formátu. +předmluva přehraný čas Vyberte prosím v seznamu některou položku. záhlaví sloupce +poděkování Vybrat soubor Jiné… +koncové poznámky TB karta Stáhnout +zpětný odkaz +tip +grafický dokument Hodiny +rejstřík zbývající čas ukončit zobrazování titulků Obsah ve formátu HTML @@ -160,24 +192,33 @@ rozdělovač Soubor nevybrán pole pro vyhledání textu +oznámení +odkaz na poznámku zahájit zobrazování titulků Tento týden přehrát film v režimu celé obrazovky rrrr Titulky Neplatná hodnota. +část lišta Počet souborů: Vyberte prosím soubor. přepínač +věnování +errata Minuty ovládání médií +grafický symbol seznam definic záhlaví +obsah ovládání vzdáleného přehrávání +titulek Obraz v obraze Vyberte jednu z těchto možností. zahájit přehrávání +kapitola Odesílání do televize hlavní Chyba přehrávání videa @@ -191,6 +232,7 @@ Jiné… upozornění , začíná +poznámka pod čarou buňka nabídka \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_da.xtb b/chromium/content/app/strings/translations/content_strings_da.xtb index b8a7ed0cf04..b2b772fcf80 100644 --- a/chromium/content/app/strings/translations/content_strings_da.xtb +++ b/chromium/content/app/strings/translations/content_strings_da.xtb @@ -10,28 +10,34 @@ supplerende marquee skyder +slutnote dialogboks +anerkendelser artikel banner område Denne måned Vælg filer +opmærksomhedsskabende citat Angiv en mailadresse, der ikke er tom. Andet... alert_dialog trægitter f.m./e.m. afspil på en enhed via fjernadgang +eksempel dd status Værdien må ikke være senere end . Ændret til spejling "" er placeret forkert i "". I dag +sideliste Forkort denne tekst til tegn eller færre (du bruger i øjeblikket tegn). Uge , Spor afkrydsningsfelt +sideskift dato- og tidsvælger Dag Udfyld dette felt. @@ -40,6 +46,7 @@ afspil Angiv en gyldig værdi. Den nærmeste gyldige værdi er . Indsend +grafikobjekt sidefod AutoFyld lyd @@ -70,30 +77,41 @@ Forlæng denne tekst til eller flere tegn (du bruger i øjeblikket tegn). Den del, der kommer før "", må ikke indeholde "". Afslut fuld skærm +konklusion applikation +prolog rullepanel Millisekunder grafik +forord Vis den foregående måned farvevælger menulinje indholdsoplysninger Angiv en gyldig værdi. De to nærmeste gyldige værdier er og . +Spørgsmål og svar menuknap +reference på ordliste Detaljer formular træ Den del, der kommer efter "", må ikke indeholde symbolet "". +bilag +forside fanepanel video søg Angiv et nummer. +bibliografipost er valgt Slå lyden til Angiv den del, der kommer efter "". "" er ufuldstændig. statusindikator +motto Vis næste måned rækkeoverskrift +epilog +Denne video afspilles som integreret billede Værdien skal være større end eller lig med . PB Angiv den del, der kommer før "". "" er ufuldstændig. @@ -106,9 +124,11 @@ slå lyd til tidspunkt Tryk to gange til venstre eller højre for at springe ti sekunder over +abstrakt pause knap flere valgmuligheder +indledning Slå lyden fra Fra Fuld skærm @@ -119,12 +139,15 @@ År listefelt slå lydspor til +bibliografi måler +efterskrift +ordliste Vis panel til valg af måned afspilningsbjælke for lyd +tak tidsvælger Valgmuligheder -I øjeblikket i tilstanden Integreret billede åbn fuld skærm feed log @@ -135,22 +158,31 @@ værktøjstip Afspil Pluginnet kunne ikke indlæses. +reference i bibliografi slå lyden fra skjul undertekster indeks +slutskrift blockquote download medier Forlæng denne tekst til eller flere tegn (du bruger i øjeblikket ét tegn). Find et match til det anmodede format. +forord forløbet tid Vælg et punkt på listen. kolonneoverskrift +anerkendelse Vælg fil Andet... +slutnoter TB fane Download +backlink +tip +grafikdokument Timer +indeks resterende tid stop visning af undertekster HTML-indhold @@ -160,24 +192,33 @@ splitter Der er ikke valgt nogen fil tekstfelt til søgning +meddelelse +reference i noter start visning af undertekster Denne uge afspil film i fuld skærm åååå Undertekster Ugyldig værdi. +del værktøjslinje filer Vælg en fil. alternativknap +dedikation +rettelser Minutter mediekontrol +grafiksymbol liste over definitioner overskrift +indholdsfortegnelse kontrollér afspilning via fjernadgang +undertitel Integreret billede Vælg en af disse muligheder. start afspilning +kapitel Caster nu til dit fjernsyn hovd Fejl under videoafspilning @@ -191,6 +232,7 @@ Andet... underretning , med start +fodnote celle menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_de.xtb b/chromium/content/app/strings/translations/content_strings_de.xtb index 5a7c5bef4e7..72ba8f81710 100644 --- a/chromium/content/app/strings/translations/content_strings_de.xtb +++ b/chromium/content/app/strings/translations/content_strings_de.xtb @@ -10,28 +10,34 @@ ergänzend marquee Schieberegler +Endnote Kleines Fenster +Mitwirkende article Banner Region Aktueller Monat Dateien auswählen +Textzitat Geben Sie eine E-Mail-Adresse ein. Andere... alert_dialog Baumraster AM/PM auf Remote-Gerät wiedergeben +Beispiel tt Status Verwenden Sie oder einen früheren Wert. Zu 1:1-Wiedergabe gewechselt Das Punktzeichen "" steht in "" an einer falschen Stelle. Heute +Seitenliste Kürzen Sie diesen Text auf max.  Zeichen. Zurzeit verwenden Sie  Zeichen. Woche , Titel Kästchen +Seitenumbruch Datums- und Uhrzeitauswahl Tag Füllen Sie dieses Feld aus. @@ -40,6 +46,7 @@ Wiedergeben Geben Sie einen gültigen Wert ein. Der nächstliegende gültige Wert ist . Senden +Grafikobjekt Fußzeile AutoFill Audio @@ -70,30 +77,41 @@ Verlängern Sie diesen Text auf mindestens Zeichen. Derzeit verwenden Sie Zeichen. Vor dem -Zeichen darf das Zeichen "" nicht verwendet werden. Vollbildmodus beenden +Fazit Anwendung +Prolog Bildlaufleiste Millisekunden Grafik +Vorwort Vorherigen Monat anzeigen color picker Menüleiste Inhaltsinformationen Geben Sie einen gültigen Wert ein. Die zwei nächstliegenden gültigen Werte sind und . +Fragen und Antworten Menüschaltfläche +Glossarreferenz Details Formular tree Nach dem -Zeichen darf das Zeichen "" nicht verwendet werden. +Anhang +Titelseite Tabsteuerfeld Video Suchen Geben Sie eine Nummer ein. +Bibliografieeintrag ausgewählt Stummschaltung aufheben Geben Sie etwas nach dem -Zeichen ein. Die Angabe "" ist unvollständig. Fortschrittsanzeige +Motto Nächsten Monat anzeigen Zeilenüberschrift +Epilog +Dieses Video wird im Bild-in-Bild-Modus wiedergegeben Wert muss größer als oder gleich sein. PB Geben Sie etwas vor dem -Zeichen ein. Die Angabe "" ist unvollständig. @@ -106,9 +124,11 @@ Ton an time Tippen Sie links oder rechts doppelt, um 10 s zu überspringen +Zusammenfassung Wiedergabe anhalten Schaltfläche weitere Optionen +Einleitung Stummschalten Aus Vollbild @@ -119,12 +139,15 @@ Jahr Listenfeld Ton anschalten +Bibliografie Messinstrument +Nachwort +Glossar Auswahlbereich für Monatsanzeige Audio-Zeitachse +Danksagung Uhrzeitauswahl Optionen -Jetzt im Bild-in-Bild-Modus Vollbildmodus aktivieren Feed log @@ -135,22 +158,31 @@ Kurzinfo Wiedergabe Plug-in konnte nicht geladen werden. +Bibliografiereferenz Stumm Untertitel ausblenden Verzeichnis +Kolophon blockquote Medien herunterladen Verlängern Sie diesen Text auf mindestens  Zeichen. Derzeit verwenden Sie 1 Zeichen. Ihre Eingabe muss mit dem geforderten Format übereinstimmen. +Vorbemerkung Vergangene Zeit Wählen Sie ein Element in der Liste aus. Spaltenüberschrift +Danksagung Datei auswählen Andere... +Endnoten TB tab Herunterladen +Rückverweis +Tipp +Grafikdokument Stunden +Index Verbleibende Zeit Keine Untertitel mehr anzeigen HTML-Inhalte @@ -160,24 +192,33 @@ Teilungsfunktion Keine ausgewählt Feld für den Suchtext +Bemerkung +Hinweisreferenz Untertitel ab sofort anzeigen Diese Woche Film im Vollbildmodus ansehen jjjj Untertitel Ungültiger Wert. +Teil Symbolleiste Dateien Wählen Sie eine Datei aus. Optionsfeld +Widmung +Druckfehler Minuten Mediensteuerung +Grafiksymbol Definitionsliste Kopfzeile +Inhaltsverzeichnis Remote-Wiedergabe steuern +Untertitel Bild im Bild Wählen Sie eine dieser Optionen aus. Wiedergabe starten +Kapitel Wird gerade auf Ihren Fernseher gestreamt main Videowiedergabefehler @@ -191,6 +232,7 @@ Andere... Benachrichtigung ab dem +Fußnote Zelle Menü \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_el.xtb b/chromium/content/app/strings/translations/content_strings_el.xtb index 5d27f5de436..e80b9556f8b 100644 --- a/chromium/content/app/strings/translations/content_strings_el.xtb +++ b/chromium/content/app/strings/translations/content_strings_el.xtb @@ -10,28 +10,34 @@ συμπληρωματικό μαρκίζα ρυθμιστικό +σημείωση τέλους παράθυρο διαλόγου +συντελεστές άρθρο banner περιοχή Αυτόν το μήνα Επιλογή αρχείων +ελκυστική φράση Καταχωρίστε μια διεύθυνση ηλεκτρονικού ταχυδρομείου. Άλλες… παράθυρο διαλόγου ειδοποιήσεων πλέγμα δέντρου Π.Μ./Μ.Μ. αναπαραγωγή σε απομακρυσμένη συσκευή +παράδειγμα ηη κατάσταση Η τιμή πρέπει να είναι ή προγενέστερη. Εναλλαγή σε κατοπτρισμό Το σύμβολο "" χρησιμοποιείται σε λάθος θέση στη διεύθυνση "". Σήμερα +λίστα σελίδων Κάντε πιο σύντομο αυτό το κείμενο ώστε να έχει το πολύ χαρακτήρες (αυτήν τη στιγμή χρησιμοποιείτε χαρακτήρες). Εβδομάδα , Κομμάτι πλαίσιο ελέγχου +αλλαγή σελίδας εργαλείο επιλογής ημερομηνίας και ώρας Ημέρα Συμπληρώστε αυτό το πεδίο. @@ -40,6 +46,7 @@ αναπαραγωγή Καταχωρίστε μια έγκυρη τιμή. Η κοντινότερη έγκυρη τιμή είναι . Υποβολή +αντικείμενο γραφικών υποσέλιδο Αυτόματη συμπλήρωση ήχος @@ -70,30 +77,41 @@ Αυξήστε την έκταση αυτού του κειμένου στους χαρακτήρες ή περισσότερο (αυτήν τη στιγμή χρησιμοποιείτε χαρακτήρες). Το τμήμα της διεύθυνσης πριν το σύμβολο "" δεν πρέπει να περιέχει το σύμβολο "". Έξοδος από πλήρη οθόνη +συμπέρασμα εφαρμογή +πρόλογος γραμμή κύλισης Χιλιοστά του δευτερολέπτου γραφικό +εισαγωγή Εμφάνιση προηγούμενου μήνα επιλογέας χρώματος γραμμή μενού πληροφορίες περιεχομένου Καταχωρίστε μια έγκυρη τιμή. Οι δύο πιο κοντινές έγκυρες τιμές είναι και . +Ερωτήσεις και απαντήσεις κουμπί μενού +αναφορά στο γλωσσάρι Λεπτομέρειες φόρμα δέντρο Το τμήμα της διεύθυνσης μετά το σύμβολο "" δεν πρέπει να περιέχει το σύμβολο "". +παράρτημα +εξώφυλλο παράθυρο καρτέλας βίντεο search Εισαγάγετε έναν αριθμό. +βιβλιογραφική καταχώριση Επιλέχτηκαν Κατάργηση σίγασης Καταχωρίστε το τμήμα της διεύθυνσης μετά το σύμβολο "". Η διεύθυνση "" δεν είναι πλήρης. ένδειξη προόδου +επιγραφή Εμφάνιση επόμενου μήνα κεφαλίδα σειράς +επίλογος +Η αναπαραγωγή αυτού του βίντεο γίνεται σε λειτουργία Picture-in-Picture Η τιμή πρέπει να είναι μεγαλύτερη ή ίση του . PB Καταχωρίστε το τμήμα της διεύθυνσης πριν το σύμβολο "". Η διεύθυνση "" δεν είναι πλήρης. @@ -106,9 +124,11 @@ κατάργηση σίγασης ώρα Πατήστε δύο φορές αριστερά ή δεξιά, για να παραβλέψετε 10 δευτ. +περίληψη παύση αναπαραγωγής κουμπί περισσότερες επιλογές +εισαγωγή Σίγαση Απενεργοποιημένη Πλήρης οθόνη @@ -119,12 +139,15 @@ Έτος πλαίσιο λίστας κατάργηση σίγασης ήχου +βιβλιογραφία μετρητής +επίλογος +γλωσσάρι Εμφάνιση παραθύρου επιλογής μήνα δείκτης χρόνου ήχου +ευχαριστίες εργαλείο επιλογής ώρας Επιλογές -Η αναπαραγωγή γίνεται σε λειτουργία παράθεσης εικόνων ενεργοποίηση πλήρους οθόνης ροή αρχείο καταγραφής @@ -135,22 +158,31 @@ επεξήγηση εργαλείου Αναπαραγωγή Δεν είναι δυνατή η φόρτωση της προσθήκης. +βιβλιογραφική αναφορά σίγαση απόκρυψη υπότιτλων κατάλογος +εκδοτικό σήμα blockquote λήψη μέσων Αυξήστε την έκταση αυτού του κειμένου στους χαρακτήρες ή περισσότερο (αυτήν τη στιγμή χρησιμοποιείτε 1 χαρακτήρα). Αντιστοιχίστε τη ζητούμενη μορφή. +πρόλογος χρόνος που παρήλθε Επιλέξτε ένα στοιχείο από τη λίστα. κεφαλίδα στήλης +εύσημα Επιλογή αρχείου Άλλες… +σημειώσεις τέλους TB καρτέλα Λήψη +σύνδεσμος επιστροφής +συμβουλή +έγγραφο γραφικών Ώρες +ευρετήριο χρόνος που απομένει διακοπή προβολής υπότιτλων Περιεχόμενο HTML @@ -160,24 +192,33 @@ διαχωριστής Δεν επιλέχθηκε κανένα αρχείο. αναζήτηση πεδίου κειμένου +ειδοποίηση +παραπομπή έναρξη προβολής υπότιτλων Αυτήν την εβδομάδα αναπαραγωγή ταινίας σε λειτουργία πλήρους οθόνης εεεε Υπότιτλοι Μη έγκυρη τιμή. +τμήμα γραμμή εργαλείων αρχεία Επιλέξτε ένα αρχείο. κουμπί επιλογής +αφιέρωση +τυπογραφικά λάθη Λεπτά έλεγχος μέσων +σύμβολο γραφικών λίστα ορισμών επικεφαλίδα +πίνακας περιεχομένων έλεγχος απομακρυσμένης αναπαραγωγής +υπότιτλος Picture-in-picture Ορίστε μία από αυτές τις επιλογές. έναρξη αναπαραγωγής +κεφάλαιο Γίνεται μετάδοση στην TV κύριο Σφάλμα αναπαραγωγής βίντεο @@ -191,6 +232,7 @@ Άλλες… ειδοποίηση , από τις +υποσημείωση κελί μενού \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_en-GB.xtb b/chromium/content/app/strings/translations/content_strings_en-GB.xtb index 615e3a4c5f0..30e75ecd7ae 100644 --- a/chromium/content/app/strings/translations/content_strings_en-GB.xtb +++ b/chromium/content/app/strings/translations/content_strings_en-GB.xtb @@ -10,28 +10,34 @@ complementary marquee slider +endnote dialogue +credits article banner region This month Choose Files +pullquote Please enter a non-empty email address. Other... alert_dialogue tree grid AM/PM play on remote device +example dd status Value must be or earlier. Switched to mirroring '' is used at a wrong position in ''. Today +page list Please shorten this text to characters or less (you are currently using characters). Week , Track Tick box +page break date and time picker Day Please fill in this field. @@ -40,6 +46,7 @@ play Please enter a valid value. The nearest valid value is . Submit +graphics object footer Auto-fill audio @@ -70,30 +77,41 @@ Please lengthen this text to characters or more (you are currently using characters). A part followed by '' should not contain the symbol ''. Exit full screen +conclusion application +prologue scroll bar Milliseconds graphic +foreword Show previous month colour picker menu bar content info Please enter a valid value. The two nearest valid values are and . +Q&A menu button +glossary reference Details form tree A part following '' should not contain the symbol ''. +appendix +cover tab panel video search Please enter a number. +bibliography entry selected Unmute Please enter a part following ''. '' is incomplete. progress indicator +epigraph Show next month row header +epilogue +This video is playing in Picture-in-Picture Value must be greater than or equal to . PB Please enter a part followed by ''. '' is incomplete. @@ -106,9 +124,11 @@ un-mute time Double-tap left or right to skip 10s +abstract pause playback button more options +introduction Mute Off Full screen @@ -119,12 +139,15 @@ Year list box un-mute audio track +bibliography meter +afterword +glossary Show month selection panel audio time scrubber +acknowledgements time picker Options -Now in picture-in-picture mode enter full screen feed log @@ -135,22 +158,31 @@ tooltip Play Couldn't load plug-in. +bibliography reference mute hide closed captions directory +colophon blockquote download media Please lengthen this text to characters or more (you are currently using 1 character). Please match the format requested. +preface elapsed time Please select an item in the list. column header +credit Choose file Other... +endnotes TB tab Download +back link +tip +graphics document Hours +index remaining time stop displaying closed captions HTML content @@ -160,24 +192,33 @@ splitter No file chosen search text field +notice +note reference start displaying closed captions This week play film in full screen mode yyyy Captions Invalid value. +part toolbar files Please select a file. radio button +dedication +errata Minutes media control +graphics symbol definition list heading +table of contents control remote playback +subtitle Picture-in-picture Please select one of these options. begin playback +chapter Now casting to your TV main Video playback error @@ -191,6 +232,7 @@ Other... alert , starting on +footnote cell menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_es-419.xtb b/chromium/content/app/strings/translations/content_strings_es-419.xtb index 78a087575a5..b65c8e31e1c 100644 --- a/chromium/content/app/strings/translations/content_strings_es-419.xtb +++ b/chromium/content/app/strings/translations/content_strings_es-419.xtb @@ -10,28 +10,34 @@ complementario marquee control deslizante +nota final diálogo +créditos artículo banner región Este mes Elegir archivos +cita inicial Ingresa una dirección de correo electrónico que no esté vacía. Otra... alert_dialog cuadrícula de árbol AM/PM jugar en el dispositivo remoto +ejemplo dd estado El valor debe ser igual o anterior a . Se cambió a duplicación El signo "" está colocado en una posición incorrecta en "". Hoy +listado de páginas Acorta este texto a caracteres o menos (actualmente estás usando caracteres). Semana , Pista casilla de verificación +salto de página selector de fecha y hora Día Completa este campo @@ -40,6 +46,7 @@ reproducir Ingresa un valor válido. El valor válido más aproximado es . Enviar +objeto gráfico pie de página Autocompletar audio @@ -70,30 +77,41 @@ Alarga el texto a o más caracteres (actualmente, usas  caracteres). El texto antes del signo "" no debe incluir el símbolo "". Salir de pantalla completa +conclusión aplicación +prólogo barra de desplazamiento Milisegundos gráfico +prefacio Mostrar el mes anterior selector de color barra de menús información del contenido Ingresa un valor válido. Los dos valores válidos más aproximados son y . +Preguntas y respuestas botón de menú +referencia de glosario Detalles formulario tree El texto después del signo "" no debe incluir el símbolo "". +apéndice +portada panel de pestañas video search Debes ingresar un número. +entrada de bibliografía elementos seleccionados Dejar de silenciar Ingresa texto después del signo "". La dirección "" está incompleta. indicador de progreso +epígrafe Mostrar el mes siguiente encabezado de fila +epílogo +Este video se reproduce en el modo de pantalla en pantalla El valor debe ser mayor de o igual a PB Ingresa texto antes del signo "". La dirección "" está incompleta. @@ -106,9 +124,11 @@ desactivar silencio time Presiona dos veces a la derecha o izquierda para omitir 10 s +resumen pausar reproducción botón más opciones +introducción Silenciar Desactivado Pantalla completa @@ -119,12 +139,15 @@ Año cuadro de lista desactivar silencio de la pista de audio +bibliografía medidor +posfacio +glosario Mostrar el panel de selección de meses control deslizante de duración del audio +reconocimientos selector de hora Opciones -Ahora en modo de pantalla en pantalla ingresar a pantalla completa feed rgstr @@ -135,22 +158,31 @@ información sobre la herramienta Reproducir No se pudo cargar el complemento. +referencia bibliográfica silencio ocultar los subtítulos directorio +colofón bloque entrecomillado descargar medios Extiende este texto para que tenga caracteres o más (actualmente usas 1 carácter). Haz coincidir el formato solicitado. +prefacio tiempo transcurrido Selecciona un elemento de la lista encabezado de columna +crédito Seleccionar archivo Otra... +notas finales TB tab Descargar +retrovínculo +sugerencia +documento gráfico Horas +índice tiempo restante dejar de mostrar subtítulos Contenido HTML @@ -160,24 +192,33 @@ separador No se eligió archivo campo de texto de búsqueda +aviso +nota de referencia empezar a mostrar subtítulos Esta semana reproducir la película en modo de pantalla completa aaaa Subtítulos Valor no válido. +parte barra de herramientas archivos Selecciona un archivo. botón de selección +dedicatoria +errata Minutos control de medios +símbolo gráfico lista de definiciones cabecera +índice controlar la reproducción remota +subtítulo Pantalla en pantalla Selecciona una de estas opciones. comenzar la reproducción +capítulo Transmitiendo a tu TV ppal Error de reproducción de video @@ -191,6 +232,7 @@ Otra... alerta , a partir del +nota a pie de página celda menú \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_es.xtb b/chromium/content/app/strings/translations/content_strings_es.xtb index ee4b514dd41..01abb818539 100644 --- a/chromium/content/app/strings/translations/content_strings_es.xtb +++ b/chromium/content/app/strings/translations/content_strings_es.xtb @@ -10,28 +10,34 @@ complementario marquee control deslizante +nota final cuadro de diálogo +créditos artículo banner región Este mes Elegir archivos +cita Introduce una dirección de correo electrónico que no esté vacía. Otra... alert_dialog cuadrícula de árbol A.M./P.M. reproducir en dispositivo remoto +ejemplo dd estado El valor debe ser igual o anterior a . Se ha cambiado a proyección El signo "" está colocado en una posición incorrecta en la dirección "". Hoy +lista de páginas Reduce la longitud de este texto a caracteres o menos (actualmente, el texto tiene caracteres) Semana , Pista casilla de verificación +salto de página selector de fecha y hora Día Completa este campo @@ -40,6 +46,7 @@ reproducir Introduce un valor válido. El valor válido más aproximado es . Enviar +objeto gráfico pie de página Autocompletar audio @@ -70,30 +77,41 @@ Aumenta la longitud del texto a caracteres como mínimo (actualmente, el texto tiene caracteres). El texto seguido del signo "" no debe incluir el símbolo "". Salir de pantalla completa +conclusión aplicación +prólogo barra de desplazamiento Millisegundos gráfico +prefacio Mostrar mes anterior selector de color barra de menús información del contenido Introduce un valor válido. Los dos valores válidos más aproximados son y . +Preguntas botón de menú +referencia de glosario Detalles formulario tree El texto detrás del signo "" no debe incluir el símbolo "". +apéndice +portada panel de pestaña vídeo buscar Debes introducir un número. +entrada bibliográfica seleccionados Activar sonido Introduce texto detrás del signo "". La dirección "" está incompleta. indicador de progreso +epígrafe Mostrar mes siguiente encabezado de fila +epílogo +Este vídeo se está reproduciendo en modo imagen en imagen El valor debe ser superior o igual a PB Introduce texto seguido del signo "". La dirección "" está incompleta. @@ -106,9 +124,11 @@ activar sonido time Toca dos veces a la derecha o a la izquierda para saltar 10 segundos +resumen pausar reproducción botón más opciones +introducción Silenciar No Pantalla completa @@ -119,12 +139,15 @@ Año cuadro de lista activar sonido de la pista de audio +bibliografía medidor +epílogo +glosario Mostrar panel para seleccionar el mes control deslizante de duración de audio +agradecimientos selector de hora Configuración -Modo de imagen en imagen activar pantalla completa feed registro @@ -135,22 +158,31 @@ descripción emergente Reproducir No se ha podido cargar el complemento. +referencia bibliográfica silenciar ocultar subtítulos directorio +colofón blockquote descargar archivos multimedia Aumenta la longitud de este texto a caracteres o más (actualmente, el texto tiene 1 carácter). Utiliza un formato que coincida con el solicitado +prefacio tiempo transcurrido Selecciona un elemento de la lista encabezado de columna +crédito Seleccionar archivo Otra... +notas finales TB tabulador Descargar +enlace de retroceso +consejo +documento gráfico Horas +índice tiempo restante dejar de mostrar subtítulos Contenido HTML @@ -160,24 +192,33 @@ divisor Ningún archivo seleccionado campo para buscar texto +aviso +referencia de nota iniciar la visualización de subtítulos Esta semana reproducir la película en modo de pantalla completa aaaa Subtítulos Valor no válido +parte barra de herramientas archivos Selecciona un archivo botón de selección +dedicatoria +errata Minutos control de medios +símbolo gráfico lista de definiciones cabecera +índice reproducción de control remoto +subtítulo Imagen en imagen Selecciona una de estas opciones iniciar reproducción +capítulo Se está enviando contenido a tu TV principal No se puede reproducir el vídeo @@ -191,6 +232,7 @@ Otra... alerta , a partir del +nota al pie celda menú \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_et.xtb b/chromium/content/app/strings/translations/content_strings_et.xtb index 33cf5d25364..c5ea1a2fef5 100644 --- a/chromium/content/app/strings/translations/content_strings_et.xtb +++ b/chromium/content/app/strings/translations/content_strings_et.xtb @@ -10,28 +10,34 @@ täiendav marquee liugur +järelmärkus dialoog +tiitrid artikkel bänner piirkond See kuu Vali failid +tsitaat Sisestage mittetühi e-posti aadress. Muu ... hoiatusdialoog puuruudustik AM/PM kaugseadmes esitamine +näide pp olek Väärtus peab olema või varasem. Lülitatud peegeldamisele Tähist „” on aadressis „” valesti kasutatud. Täna +lehtede loend Lühendage seda teksti tähemärgini või rohkem (praegu kasutate tähemärki). Nädal , . lugu märkeruut +leheküljepiir kuupäeva ja kellaaja valija päev Täitke see väli. @@ -40,6 +46,7 @@ esitus Sisestage kehtiv väärtus. Lähim kehtiv väärtus on . Esita +graafika objekt jalus Automaatne täitmine heli @@ -70,30 +77,41 @@ Pikendage teksti vähemalt tähemärgini (kasutate praegu tähemärki). Märgile „” eelnev osa ei tohi sisaldada sümbolit „”. Täisekraanilt väljumine +kokkuvõte rakendus +proloog kerimisriba Millisekundid graafika +eessõna Eelmise kuu kuvamine värvivalija menüüriba sisu teave Sisestage kehtiv väärtus. Kaks lähimat kehtivat väärtust on ja . +Küsimused ja vastused menüünupp +sõnastiku viide Üksikasjad vorm tree Märgile „” järgnev osa ei tohi sisaldada sümbolit „”. +lisa +kaas vahelehepaneel video otsing Sisestage arv. +bibliograafia kirje Valitud on üksust Tühista vaigistus Sisestage märgile „” järgnev osa. Aadress „” pole täielik. edenemise näidik +epigraaf Järgmise kuu kuvamine rea päis +epiloog +Videot esitatakse pilt-pildis-režiimis Väärtus peab olema suurem või võrdne -ga. PB Sisestage märgile „” eelnev osa. Aadress „” pole täielik. @@ -106,9 +124,11 @@ vaigistuse tühistamine time 10 sekundi võrra kerimiseks topeltpuudutage vasakul või paremal +abstraktne taasesituse peatamine nupp rohkem valikuid +sissejuhatus Vaigista Väljas Täisekraan @@ -119,12 +139,15 @@ Aasta loendikast heliraja vaigistamise tühistamine +bibliograafia mõõdik +järelsõna +sõnastik Kuu valikupaneeli kuvamine heli ajamõõdik +tunnustused kellaaja valija Valikud -Nüüd režiimis Pilt pildis kuvamine täisekraanil voog logi @@ -135,22 +158,31 @@ tööriistavihje Esita Pistikprogrammi ei saanud laadida. +kasutatud kirjandus vaigista subtiitrite peitmine kataloog +kolofoon plokktsitaat laadi meedia alla Pikendage teksti vähemalt tähemärgini (kasutate praegu ühte tähemärki). Vastendage nõutav vorming. +eessõna möödunud aeg Valige loendist element. veeru päis +autor(id) Vali fail Muu ... +järelmärkused TB Tabulaator Laadi alla +tagasilink +nõuanne +graafika dokument Tunnid +register järelejäänud aeg subtiitrite kuvamise peatamine HTML-sisu @@ -160,24 +192,33 @@ jagaja Pole valitud otsinguteksti väli +teatis +märkuse viide subtiitrite kuvamise alustamine See nädal video esitus täisekraani režiimis aaaa Subtiitrid Kehtetu väärtus. +osa tööriistariba faili Valige üks fail. raadionupp +pühendus +veaparandused Minutid meedia juhtimine +graafika sümbol definitsioonide loend pealkiri +sisukord kaugesituse juhtimine +alapealkiri Pilt pildis Tehke üks nendest valikutest. taasesituse alustamine +peatükk Kantakse nüüd üle telerisse pea Video taasesituse viga @@ -191,6 +232,7 @@ Muu ... hoiatus alates kuupäevast +allmärkus lahter menüü \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_fa.xtb b/chromium/content/app/strings/translations/content_strings_fa.xtb index e28aa3f0514..5f84dfcb4fd 100644 --- a/chromium/content/app/strings/translations/content_strings_fa.xtb +++ b/chromium/content/app/strings/translations/content_strings_fa.xtb @@ -5,33 +5,39 @@ بیصداکردن تراک صوتی دکمه تغییر حالت ثانیه -لطفاً یک نشانی رایانامه وارد کنید. +لطفاً یک نشانی ایمیل وارد کنید. مکث تکمیلی نوشتار متحرک روی صفحه لغزنده +ته‌نویس کادر گفتگو +دست‌اندرکاران article برنما منطقه این ماه انتخاب فایل‌ها -لطفاً یک نشانی رایانامه غیرخالی وارد کنید. +نقل‌قول +لطفاً یک نشانی ایمیل غیرخالی وارد کنید. موارد دیگر... alert_dialog شبکه درختی ق.ظ/ب.ظ پخش در دستگاه راه دور +مثال dd وضعیت مقدار باید یا قبل از آن باشد. به نمایش صفحه‌نمایش روی دستگاه دیگر تغییر یافت «» در «» در محل اشتباهی قرار دارد. امروز +فهرست صفحه لطفاً این متن را به اندازه نویسه یا کمتر کوتاه کنید (شما در حال حاضر از نویسه استفاده می‌کنید). هفته ، آهنگ کادر تأیید +جداساز صفحه انتخابگر تاریخ و زمان روز لطفاً این قسمت را تکمیل کنید. @@ -40,6 +46,7 @@ پخش لطفاً یک مقدار معتبر وارد کنید. نزدیک‌ترین مقدار معتبر است. ارائه +شیء گرافیکی پانویس تکمیل خودکار صدا @@ -47,7 +54,7 @@ دکمه بازشو درصورتی‌که می‌خواهید ادامه دهید، این کادر را انتخاب کنید. گروه رادیویی -لطفاً لیستی از آدرس‌های رایانامه که با کاما از هم جدا شده‌اند را وارد کنید. +لطفاً لیستی از آدرس‌های ایمیل که با کاما از هم جدا شده‌اند را وارد کنید. شیء محتوای برجسته شده پیوند @@ -70,30 +77,41 @@ لطفاً این نوشتار را به نویسه یا بیشتر افزایش دهید (درحال حاضر از نویسه استفاده می‌کنید). قسمت قبل از «» نباید حاوی نماد «» باشد. خروج از حالت تمام صفحه +نتیجه‌گیری برنامه +گفتار آغازین نوار پیمایش میلی‌ ثانیه گرافیک +پیش‌گفتار نمایش ماه قبلی انتخابگر رنگ نوار منو اطلاعات محتوا لطفاً یک مقدار معتبر وارد کنید. نزدیک‌ترین مقادیر معتبر و هستند. +پرسش و پاسخ دکمه منو +مرجع واژه‌نامه جزئیات فرم درخت قسمت بعد از «» نباید حاوی نماد «» باشد. +ضمیمه +جلد پانل برگه ویدئو جستجو لطفاً شماره‌ای را وارد کنید. +ورودی فهرست منابع انتخاب شد باصدا کردن لطفاً قسمت بعد از «» را وارد کنید. «» ناقص است. نشانگر پیشرفت +نقل‌قول آغازین نمایش ماه بعدی عنوان ردیف +گفتار پایانی +این ویدیو درحال پخش شدن در حالت «تصویر در تصویر» است مقدار باید بیشتر یا مساوی با باشد. پتابایت لطفاً قسمت قبل از «» را وارد کنید. «» ناقص است. @@ -106,9 +124,11 @@ صدادارکردن زمان روی سمت راست یا چپ دو ضربه سریع بزنید تا ۱۰ ثانیه رد شود +انتزاعی مکث بازپخش دکمه گزینه‌های بیشتر +مقدمه بی‌صدا کردن خاموش تمام صفحه @@ -119,12 +139,15 @@ سال کادر فهرست صدادارکردن تراک صوتی +فهرست منابع متر +سخن پایانی +واژه‌نامه نمایش پانل انتخاب ماه انتخابگر زمان صدا +قدردانی انتخابگر زمان گزینه‌ها -اکنون درحالت تصویردرتصویر رفتن به حالت تمام صفحه خبرمایه log @@ -135,49 +158,67 @@ نکته ابزار پخش افزایه بارگیری نشد. +مرجع فهرست منابع بیصداکردن پنهان کردن زیرنویس ناشنوایان فهرست راهنما +انجامه نقل‌قول بارگیری رسانه لطفاً این نوشتار را به نویسه یا بیشتر افزایش دهید (درحال‌حاضر از ۱ نویسه استفاده می‌کنید). لطفاً با قالب درخواستی مطابقت دهید. +پیش‌گفتار مدت سپری شده لطفاً یک مورد را در فهرست انتخاب کنید. عنوان ستون +تأیید اعتبار انتخاب فایل موارد دیگر... +ته‌نویس‌ها ترابایت برگه بارگیری +پیوند برگشت +نکته +سند گرافیکی ساعت +فهرست موضوعی زمان باقی‌مانده توقف نمایش زیرنویس ناشنوایان ‏محتوای HTML مکث مثلث افشا -لطفاً نماد «» را به نشانی رایانامه اضافه کنید. «» در «» موجود نیست. +لطفاً نماد «» را به نشانی ایمیل اضافه کنید. «» در «» موجود نیست. تقسیم‌کننده فایلی انتخاب نشده است فیلد نوشتاری جستجو +اطلاعیه +مرجع یادداشت شروع به نمایش زیرنویس ناشنوایان این هفته پخش فیلم در حالت تمام صفحه yyyy زیرنویس‌ها مقدار نامعتبر. +بخش نوار ابزار فایل لطفاً یک فایل انتخاب کنید. دکمه رادیو +تقدیم‌نامه +فهرست اشتباهات دقیقه کنترل رسانه +نماد گرافیکی فهرست معنی‌ها عنوان +فهرست مطالب کنترل بازپخش راه دور +زیرنویس تصویردرتصویر لطفاً یکی از این گزینه‌ها را انتخاب کنید. شروع بازپخش +فصل درحال ارسال محتوا به تلویزیون اصلی خطا در بازپخش ویدئو @@ -191,6 +232,7 @@ موارد دیگر... هشدار ، شروع از +پانویس سلول منو \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_fi.xtb b/chromium/content/app/strings/translations/content_strings_fi.xtb index 4c444fa40fd..ffe7d7fb4e5 100644 --- a/chromium/content/app/strings/translations/content_strings_fi.xtb +++ b/chromium/content/app/strings/translations/content_strings_fi.xtb @@ -10,28 +10,34 @@ täydentävä marquee liukusäädin +loppuhuomautus valintaikkuna +tunnustukset artikkeli banneri alue Tässä kuussa Valitse tiedostot +erotettu lainaus Kirjoita ei-tyhjä sähköpostiosoite. Muu... ilmoitusvalintaikkuna puuruudukko AP/IP toista etälaitteella +esimerkki pp tila Arvon on oltava tai aiempi. Peilaus käynnissä -merkkiä on käytetty väärässä kohdassa osoitteessa . Tänään +sivuluettelo Lyhennä tämä teksti alle merkkiin (tällä hetkellä käytössä merkkiä). Viikko , Kappale valintaruutu +sivunvaihto päivämäärän ja ajan valitsin Päivä Täytä tämä kenttä. @@ -40,6 +46,7 @@ toista Syötä kelvollinen arvo. Lähin kelvollinen arvo on . Lähetä +grafiikkaobjekti alaviite Automaattinen täyttö ääni @@ -70,30 +77,41 @@ Pidennä tämä teksti yli merkkiin (tällä hetkellä käytössä merkkiä). -osaa ennen tulevassa osassa ei pitäisi olla merkkiä . Sulje koko näytön tila. +johtopäätös sovellus +prologi vierityspalkki Millisekuntia kuva +esipuhe Näytä edellinen kuukausi värinvalitsin valikkopalkki sisällön tiedot Syötä kelvollinen arvo. Kaksi lähintä kelvollista arvoa ovat ja . +K & V valikkopainike +sanastoviittaus Tiedot lomake tree -osan jälkeen tulevassa osassa ei pitäisi olla merkkiä . +liite +kansi välilehtipaneeli video haku Anna numero. +lähdeluettelomerkintä valittu Poista mykistys Kirjoita loppuun asti osoite, joka alkaa . on vielä kesken. etenemisen osoitin +epigrafi Näytä seuraava kuukausi rivin otsikko +epilogi +Video toistetaan Kuva kuvassa ‑tilassa Arvon tulee olla suurempi tai yhtä suuri kuin . Pt Lisää -osaa ennen tuleva osa. on puutteellinen. @@ -106,9 +124,11 @@ peruuta mykistys time Ohita 10 sekuntia kaksoisnapauttamalla vasemmalle tai oikealle. +tiivistelmä keskeytä toisto painike lisäasetukset +johdanto Mykistä Pois käytöstä Koko ruutu @@ -119,12 +139,15 @@ Vuosi luetteloruutu peruuta ääniraidan mykistys +lähdeluettelo mittari +jälkipuhe +sanasto Näytä kuukaudenvalintapaneeli äänen ajan liukusäädin +kiitokset ajan valitsin Asetukset -Kuva kuvassa ‑tila nyt käytössä siirry koko näytön tilaan fiidi loki @@ -135,22 +158,31 @@ työkaluvinkki Toista Laajennuksen lataaminen epäonnistui. +lähdeluetteloviittaus äänetön piilota tekstitykset hakemisto +kolofoni muotoiltu lainaus lataa media Pidennä tämä teksti vähintään merkkiin (tällä hetkellä käytössä 1 merkki). Käytä pyydettyä muotoilua. +johdanto kulunut aika Valitse kohde luettelosta. sarakkeen otsikko +tunnustus Valitse tiedosto Muu... +loppuhuomautukset Tt sarkain Lataa +viittaajan linkki +vinkki +grafiikkadokumentti Tuntia +hakemisto jäljellä oleva aika älä näytä tekstityksiä HTML-sisältö @@ -160,24 +192,33 @@ jakaja Ei valittua tiedostoa hakutekstikenttä +ilmoitus +muistiinpanoviittaus näytä tekstitykset Tällä viikolla toista elokuva koko näytön tilassa vvvv Tekstitykset Virheellinen arvo. +osa työkalupalkki tiedostoa Valitse tiedosto. valintapainike +omistuskirjoitus +oikaisut Minuuttia median hallinta +grafiikkasymboli määritelmäluettelo otsikko +sisällysluettelo hallinnoi etätoistoa +alaotsikko Kuva kuvassa Valitse yksi vaihtoehdoista. aloita toisto +luku Suoratoistetaan televisioosi pää Videon toistovirhe @@ -191,6 +232,7 @@ Muu... ilmoitus , alkupäivä: +alaviite solu valikko \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_fil.xtb b/chromium/content/app/strings/translations/content_strings_fil.xtb index 0226e24ca57..7687fc29e30 100644 --- a/chromium/content/app/strings/translations/content_strings_fil.xtb +++ b/chromium/content/app/strings/translations/content_strings_fil.xtb @@ -10,28 +10,34 @@ complementary marquee slider +endnote dialog +mga credit article banner rehiyon Buwang ito Pumili ng Mga File +pullquote Mangyaring maglagay ng isang non-empty na email address. Iba pa... alert_dialog tree grid AM/PM i-play sa malayuang device +halimbawa dd katayuan Dapat o mas nauna ang value. Lumipat sa pag-mirror Ginamit ang '' sa maling posisyon sa '.' Ngayon +listahan ng page Mangyaring paikliin ang tekstong ito ng (na) character o mas mababa (kasalukuyan kang gumagamit ng (na) character). Linggo , Track checkbox +page break picker ng petsa at oras Araw Pakipunan ang field na ito. @@ -40,6 +46,7 @@ i-play Mangyaring maglagay ng isang wastong value. Ang pinakamalapit na wastong value ay . Isumite +object ng graphics footer AutoFill audio @@ -70,30 +77,41 @@ Pakihabaan ang text na ito hanggang (na) character o higit pa (kasalukuyan kang gumagamit ng (na) character). Hindi dapat naglalaman ng simbolong '' ang bahagi bago ang '.' Lumabas sa fullscreen +konklusyon application +prologue scroll bar Milliseconds graphic +foreword Ipakita ang nakaraang buwan tagapili ng kulay menu bar impormasyon ng content Mangyaring maglagay ng isang wastong value. Ang dalawang pinakamalapit na wastong value ay at . +Q&A button ng menu +sanggunian sa glosaryo Mga Detalye form tree Hindi dapat naglalaman ng simbolong '' ang bahagi pagkatapos ng '.' +appendix +cover panel ng tab video search Mangyaring maglagay ng numero. +entry sa bibliograpiya ang napili I-unmute Mangyaring maglagay ng isang bahagi pagkatapos ng '.' Hindi kumpleto ang '.' indicator ng pag-usad +epigraph Ipakita ang susunod na buwan header ng row +epilogue +Nagpe-play ang video na ito sa Picture-in-Picture Dapat mas mataas kaysa sa o katumbas ng ang halaga. (na) PB Mangyaring maglagay ng isang bahagi na sinusundan ng '.' Hindi kumpleto ang '.' @@ -106,9 +124,11 @@ i-unmute oras Mag-double tap sa kaliwa o kanan upang lumaktaw nang 10s +abstract i-pause ang pag-playback button higit pang opsyon +panimula I-mute Naka-off Fullscreen @@ -119,12 +139,15 @@ Taon kahon ng listahan i-unmute ang audio track +bibliograpiya metro +afterword +glosaryo Ipakita ang panel ng pagpipilian ng buwan scrubber ng oras ng audio +mga pagkilala picker ng petsa Mga Pagpipilian -Nasa Picture-in-Picture mode ngayon mag-full screen feed log @@ -135,22 +158,31 @@ tooltip I-play Hindi ma-load ang plugin. +sanggunian sa bibliograpiya i-mute itago ang mga nakasarang caption direktoryo +colophon blockquote i-download ang media Pakidagdagan ang text na ito nang hanggang (na) character o higit pa (kasalukuyan kang gumagamit ng 1 character). Pakitugma ang hiniling na format. +preface lumipas na oras Mangyaring pumili ng item sa listahan. header ng column +credit Pumili ng File Iba pa... +mga endnote (na) TB tab I-download +back link +tip +dokumento ng graphics Oras +index natitirang oras ihinto ang pagpapakita ng mga nakasarang caption HTML na nilalaman @@ -160,24 +192,33 @@ splitter Walang napiling file text field ng paghahanap +paunawa +sanggunian sa tala simulan ang pagpapakita ng mga nakasarang caption Linggong ito i-play ang pelikula sa full screen mode yyyy Mga Caption Di-wastong halaga. +bahagi toolbar mga file Mangyaring pumili ng file. radio button +dedikasyon +mga erratum Minuto kontrol sa media +simbolo ng graphics listahan ng kahulugan heading +talaan ng nilalaman kontrolin ang malayuang pag-playback +subtitle Picture-in-Picture Mangyaring pumili ng isa sa mga opsyong ito. simulan ang pag-playback +kabanata Ikina-cast ngayon sa iyong TV main Error sa pag-playback ng video @@ -191,6 +232,7 @@ Iba pa... alerto , na magsisimula sa +footnote cell menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_fr.xtb b/chromium/content/app/strings/translations/content_strings_fr.xtb index ef80e46d2c4..8e4af484697 100644 --- a/chromium/content/app/strings/translations/content_strings_fr.xtb +++ b/chromium/content/app/strings/translations/content_strings_fr.xtb @@ -10,28 +10,34 @@ complémentaire marquee curseur +note de fin boîte de dialogue +crédits article bannière région Ce mois Sélect. fichiers +témoignage Veuillez saisir une adresse e-mail dans le champ correspondant. Autre… alert_dialog arborescence AM/PM lire sur un appareil à distance +exemple jj état La date ou l'heure doit être égale ou antérieure à "". Passage à la duplication d'écran L'emplacement du caractère "" est incorrect dans "". Aujourd'hui +liste des pages Veuillez réduire ce texte à  caractères maximum (il compte actuellement  caractères). Semaine , Piste  case à cocher +saut de page outil de sélection de la date et de l'heure Jour Veuillez renseigner ce champ. @@ -40,6 +46,7 @@ lire Veuillez saisir une valeur valide. La valeur valide la plus proche est "". Valider +objet graphique pied de page Saisie automatique audio @@ -70,30 +77,41 @@ Veuillez allonger ce texte pour qu'il comporte au moins  caractères. Il en compte actuellement . La partie suivie du symbole "" ne doit pas contenir le caractère "". Quitter le mode plein écran +conclusion application +prologue barre de défilement Millisecondes élément graphique +avant-propos Afficher le mois précédent palette couleurs barre de menu infos sur le contenu Veuillez saisir une valeur valide. Les deux valeurs valides les plus proches sont "" et "". +Questions/Réponses bouton de menu +référence de glossaire Détails formulaire tree La partie précédée du symbole "" ne doit pas contenir le caractère "". +annexe +couverture panneau des onglets vidéo rechercher Veuillez saisir un nombre. +entrée bibliographique  élément(s) sélectionné(s) Réactiver le son Veuillez saisir la partie manquante après le symbole "". L'adresse "" est incomplète. indicateur de progression +épigraphe Afficher le mois suivant en-tête de ligne +épilogue +Cette vidéo est diffusée en mode PIP (Picture-in-Picture) Cette valeur doit être supérieure ou égale à .  Po Veuillez saisir la partie manquante avant le caractère "". L'adresse "" est incomplète. @@ -106,9 +124,11 @@ réactiver le son horodatage Appuyez deux fois à gauche/droite pour reculer/avancer de 10 s +résumé interrompre la lecture bouton plus d'options +introduction Couper le son Désactivé Plein écran @@ -119,12 +139,15 @@ Année zone de liste réactiver le son de la piste audio +bibliographie outil de mesure +postface +glossaire Afficher le panneau de sélection du mois curseur durée audio +remerciements outil de sélection de l'heure Options -Mode PIP (Picture-in-Picture) activé activer le mode plein écran flux journal @@ -135,22 +158,31 @@ info-bulle Lire Impossible de charger le plug-in. +référence bibliographique muet masquer les sous-titres annuaire +achevé d'imprimer bloc de citation télécharger des contenus multimédias Veuillez allonger ce texte pour qu'il comporte au moins  caractères. Il en compte actuellement un seul. Veuillez respecter le format requis. +préface temps écoulé Sélectionnez un élément dans la liste. en-tête de colonne +crédit Choisir un fichier Autre… +notes de fin  To tabulation Télécharger +lien entrant +conseil +document graphique Heures +index temps restant ne plus afficher les sous-titres Contenu HTML @@ -160,24 +192,33 @@ séparateur Aucun fichier choisi champ de recherche de texte +notification +référence de note commencer à afficher les sous-titres Cette semaine lire le film en mode plein écran aaaa Sous-titres Valeur incorrecte +partie barre d'outils  fichiers Veuillez sélectionner un fichier. case d'option +dédicace +errata Minutes commande multimédia +symbole graphique liste de définitions en-tête +sommaire contrôler la lecture à distance +sous-titre Picture-in-Picture Veuillez sélectionner l'une de ces options. commencer la lecture +chapitre En cours de diffusion sur votre téléviseur principal Erreur lors de la lecture de la vidéo @@ -191,6 +232,7 @@ Autre… alerte (premier jour de la semaine : ) +note de bas de page cellule menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_gu.xtb b/chromium/content/app/strings/translations/content_strings_gu.xtb index bac0dca9195..52c5bc2b23d 100644 --- a/chromium/content/app/strings/translations/content_strings_gu.xtb +++ b/chromium/content/app/strings/translations/content_strings_gu.xtb @@ -10,28 +10,34 @@ પૂરક માર્કી સ્લાઇડર +અંતિમ નોંધ સંવાદ +ક્રેડિટ લેખ બેનર પ્રદેશ આ મહિને ફાઇલો પસંદ કરો +પુલક્વોટ કૃપા કરીને એક બિન-ખાલી ઇમેઇલ સરનામું દાખલ કરો. અન્ય... ચેતવણી_સંવાદ ટ્રી ગ્રિડ AM/PM રિમોટ ઉપકરણ પર ચલાવો +ઉદાહરણ dd સ્થિતિ મૂલ્ય અથવા પહેલાંનું હોવું આવશ્યક છે. મીરરીંગ પર સ્વિચ કર્યુંં '' નો ઉપયોગ '' માં ખોટી જગ્યાએ થયો છે. આજે +પેજ સૂચિ કૃપા કરીને આ ટેક્સ્ટને અક્ષર અથવા તેથી ઓછા સુધી નાનો કરો (તમે હાલમાં અક્ષરોનો ઉપયોગ કરી રહ્યા છો). અઠવાડિયું , ટ્રૅક ચેકબોક્સ +પેજ વિભાજન તારીખ અને સમય પીકર દિવસ કૃપા કરીને આ ફીલ્ડ ભરો. @@ -40,6 +46,7 @@ ચલાવો કૃપા કરીને એક માન્ય મૂલ્ય દાખલ કરો. નિકટતમ માન્ય મૂલ્ય છે. સબમિટ કરો +ગ્રાફિક્સ ઑબ્જેક્ટ ફૂટર સ્વતઃભરો ઑડિઓ @@ -70,30 +77,41 @@ કૃપા કરીને આ ટેક્સ્ટને અક્ષર અથવા તેથી વધુ સુધી લંબાવો (તમે હાલમાં અક્ષરોનો ઉપયોગ કરી રહ્યાં છો). '' દ્વારા અનુસરાઈ રહેલા ભાગમાં '' પ્રતીક શામેલ હોવું જોઈએ નહીં. પૂર્ણસ્ક્રીનથી બહાર નીકળો +સારાંશ ઍપ્લિકેશન +પ્રસ્તાવના સ્ક્રોલ બાર મીલીસેકન્ડ ગ્રાફિક +આમુખ પહેલાનો મહિનો દર્શાવો રંગ ચૂંટનાર મેનૂ બાર સામગ્રી માહિતી કૃપા કરીને એક માન્ય મૂલ્ય દાખલ કરો. બે નિકટતમ માન્ય મૂલ્યો અને છે. +પ્રશ્ન અને જવાબ મેનૂ બટન +શબ્દાવલીનો સંદર્ભ વિગતો ફોર્મ ટ્રી '' ને અનુસરી રહેલા ભાગમાં '' પ્રતીક શામેલ હોવું જોઈએ નહીં. +જોડાણ +કવર ટેબ પેનલ વિડિઓ search કૃપા કરીને એક નંબર દાખલ કરો. +ગ્રંથસૂચિની એન્ટ્રી પસંદ કર્યા અનમ્યૂટ કરો કૃપા કરીને '' ને અનુસરી રહેલો ભાગ દાખલ કરો. '' અપૂર્ણ છે. પ્રગતિ સૂચક +શિલાલેખ આગલો મહિનો દર્શાવો પંક્તિ હેડર +ઉપસંહાર +આ વીડિઓ ચિત્ર-માં-ચિત્ર મોડમાં ચાલી રહ્યો છે મૂલ્ય જેટલું અથવા આનાથી વધુ હોવું આવશ્યક છે. PB કૃપા કરીને '' ની આગળનો ભાગ દાખલ કરો. '' અપૂર્ણ છે. @@ -106,9 +124,11 @@ અનમ્યૂટ કરો સમય 10 સેકન્ડ છોડવા માટે ડાબે અથવા જમણે બે વાર ટૅપ કરો +અમૂર્ત પ્લેબેક થોભાવો બટન વધુ વિકલ્પો +પ્રસ્તાવના અવાજ બંધ કરો બંધ પૂર્ણસ્ક્રીન @@ -119,12 +139,15 @@ વર્ષ સૂચિ બૉક્સ ઑડિઓ ટ્રેક અનમ્યૂટ કરો +ગ્રંથસૂચિ મીટર +સમાપન ભાષણ +શબ્દાવલી મહિના પસંદગી પેનલ દર્શાવો ઑડિઓ સમય સ્ક્રબર +સ્વીકૃતિઓ સમય પીકર વિકલ્પો -હાલમાં ચિત્ર-માં-ચિત્ર મોડમાં પૂર્ણ સ્ક્રીનમાં દાખલ થાઓ ફીડ લૉગ @@ -135,22 +158,31 @@ ટૂલટીપ ચલાવો પ્લગિન લોડ કરી શક્યાં નથી. +ગ્રંથસૂચિનો સંદર્ભ બંધ કરો ઉપશીર્ષક છુપાવો નિર્દેશિકા +પ્રકાશકની માહિતી બ્લૉકક્વોટ મીડિયા ડાઉનલોડ કરો કૃપા કરીને આ ટેક્સ્ટને અથવા તેથી વધુ અક્ષર સુધી લંબાવો (તમે હાલમાં 1 અક્ષરનો ઉપયોગ કરી રહ્યાં છો). કૃપા કરીને વિનંતી કરેલા ફોર્મેટ સાથે મેળ કરો. +પ્રસ્તાવના વીતેલો સમય કૃપા કરીને સૂચિમાંથી એક આઇટમ પસંદ કરો. કૉલમ હેડર +ક્રેડિટ ફાઇલ પસંદ કરો અન્ય... +અંતિમ નોંધ TB ટેબ ડાઉનલોડ કરો +બૅક લિંક +ટિપ +ગ્રાફિક્સ દસ્તાવેજ કલાક +અનુક્રમણિકા બાકીનો સમય વિગતવાર ઉપશીર્ષકનું પ્રદર્શન અટકાવો HTML સામગ્રી @@ -160,24 +192,33 @@ વિભાજનકર્તા કોઈ ફાઇલ પસંદ કરેલી નથી ટેક્સ્ટ ફીલ્ડ શોધો +સૂચના +નોંધ સંદર્ભ ઉપશીર્ષક પ્રદર્શન પ્રારંભ કરો આ અઠવાડિયે પૂર્ણ સ્ક્રીન મોડમાં મૂવી ચલાવો yyyy કૅપ્શન્સ અમાન્ય મૂલ્ય. +ભાગ ટુલબાર ફાઇલો કૃપા કરીને કોઈ ફાઇલ પસંદ કરો. રેડિઓ બટન +સમર્પણ +ભૂલ સુધારાની સૂચિ મિનિટ મીડિયાનું નિયંત્રણ +ગ્રાફિક્સ પ્રતીક વિવરણ સૂચિ મથાળું +અનુક્રમણિકા રિમોટ પ્લેબેકનું નિયંત્રણ કરો +(સબટાઇટલ) ચિત્ર-માં-ચિત્ર કૃપા કરીને આ વિકલ્પોમાંથી કોઈ એક પસંદ કરો. પ્લેબૅક શરૂ કરો +પ્રકરણ હમણાં તમારા TV પર કાસ્ટ કરી રહ્યાં છીએ મુખ્ય વીડિઓ પ્લેબૅક ભૂલ @@ -191,6 +232,7 @@ અન્ય... ચેતવણી , થી શરૂ કરીને +ફૂટનોટ કોષ મેનૂ \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_hi.xtb b/chromium/content/app/strings/translations/content_strings_hi.xtb index bc6bdfe1537..422a5cf250b 100644 --- a/chromium/content/app/strings/translations/content_strings_hi.xtb +++ b/chromium/content/app/strings/translations/content_strings_hi.xtb @@ -10,28 +10,34 @@ पूरक मार्की स्लाइडर +एंड नोट संवाद +क्रेडिट लेख बैनर क्षेत्र इस माह फ़ाइलें चुनें +पुलकोट कृपया गैर-खाली ईमेल पता डालें. अन्य... सूचना संवाद ट्री ग्रिड पूर्वाह्न/अपराह्न दूरस्थ डिवाइस पर चलाएं +उदाहरण dd स्थिति मान या पहले का होना चाहिए. स्क्रीन शेयर करने पर स्विच किया गया '' का '' में गलत स्थान पर उपयोग किया गया है. आज +पेज सूची कृपया इस टेक्स्ट को वर्णों या कम तक छोटा करें (वर्तमान में आप वर्णों का उपयोग कर रहे हैं). सप्ताह , ट्रैक चेकबॉक्स +नया पेज दिनांक और समय पिकर दिन कृपया इस फ़ील्ड को भरें. @@ -40,6 +46,7 @@ चलाएं कृपया कोई मान्य मान डालें. निकटतम मान्य मान है. सबमिट करें +ग्राफ़िक्स ऑब्जेक्ट पाद लेख ऑटोमैटिक भरना ऑडियो @@ -51,11 +58,11 @@ ऑब्जेक्ट हाइलाइट की गई सामग्री संपर्क - MB -कृपया मान्य मान डालें. फ़ील्ड अधूरी है या उसमें एक अमान्य दिनांक है. + एमबी +कृपया सही मान डालें. फ़ील्ड अधूरी है या उसमें डाली गई तारीख गलत है. तालिका नोट -कृपया URL लिखें. +कृपया यूआरएल डालें. टाइमर कॉम्बो बॉक्स मान से कम या इसके बराबर होना चाहिए. @@ -64,36 +71,47 @@ फ़िल्म के शेष सेकंड स्विच करें mm - GB + जीबी फ़िल्म समय स्क्रबर शब्द कृपया इस लेख को वर्णों या अधिक तक बढ़ाएं (वर्तमान में आप वर्णों का उपयोग कर रहे हैं). '' के बाद आने वाले भाग में '' प्रतीक शामिल नहीं होना चाहिए. पूर्णस्क्रीन से बाहर निकलें +निष्कर्ष ऐप्लिकेशन +प्रस्तावना स्क्रॉल बार मिलीसेकंड ग्राफ़‍िक +भूमिका पिछला माह दिखाएं रंग पिकर मेनू बार सामग्री की जानकारी कृपया कोई मान्य मान डालें. दो निकटतम मान्य मान और हैं. +सवाल और जवाब मेनू बटन +संदर्भ के लिए शब्दावली विवरण फ़ॉर्म ट्री '' के बाद आने वाले भाग में '' प्रतीक शामिल नहीं होना चाहिए. +परिशिष्ट +कवर टैब फलक वीडियो search कृपया कोई संख्या डालें. +संदर्भ सूची प्रविष्टि चुने गए अनम्यूट करें कृपया '' के बाद आने वाला भाग डालें. '' अधूरा है. प्रगति संकेतक +इपिग्राफ़ अगला माह दिखाएं पंक्ति शीर्षलेख +उपसंहार +यह वीडियो पिक्चर में पिक्चर मोड में चल रहा है मान से कम या इसके बराबर होना चाहिए. PB कृपया '' के पहले वाला भाग डालें. '' अधूरा है. @@ -106,9 +124,11 @@ अनम्यूट करें समय 10 सेकंड आगे या पीछे जाने के लिए बाएं या दाएं पर दो बार टैप करें +संक्षेप प्लेबैक रोकें बटन अधिक विकल्प +परिचय म्यूट करें बंद पूर्णस्‍क्रीन @@ -119,12 +139,15 @@ वर्ष सूची बॉक्स ऑडियो ट्रैक अनम्यूट करें +संदर्भ सूची मीटर +उपसंहार +शब्दावली माह चयन फलक दिखाएं ऑडियो समय स्क्रबर +धन्यवाद समय पिकर विकल्प -अब पिक्चर में पिक्चर मोड में पूर्ण स्क्रीन में प्रवेश करें फ़ीड लॉग @@ -135,22 +158,31 @@ टूलटिप चलाएं प्लग इन लोड नहीं किया जा सका. +संदर्भ सूची का संदर्भ म्यूट करें बंद कैप्शन छिपाएं निर्देशिका +कॉलफ़न ब्लॉककोट मीडिया डाउनलोड करें कृपया इस लेख को या उससे ज़्यादा वर्णों तक बढ़ाएं (आप इस समय 1 वर्ण का उपयोग कर रहे हैं). कृपया अनुरोधित प्रारूप का मिलान करें. +आमुख बीता हुआ समय कृपया सूची में किसी आइटम को चुनें. स्तंभ शीर्षलेख +क्रेडिट फ़ाइल चुनें अन्य... +एंडनोट TB टैब डाउनलोड करें +बैक लिंक +सलाह +ग्राफ़िक्स दस्तावेज़ घंटे +इंडेक्स शेष समय बंद कैप्शन दिखाना रोकें HTML सामग्री @@ -160,28 +192,37 @@ विभाजक कोई फाइल नहीं चुनी गई लेख फ़ील्ड खोजें +सूचना +नोट संदर्भ बंद कैप्शन दिखाना प्रारंभ करें इस सप्ताह फ़िल्म को पूर्ण स्क्रीन मोड में चलाएं yyyy कैप्शन अमान्य मान. +भाग टूलबार फ़ाइल कृपया किसी फ़ाइल को चुनें. रेडियो बटन +लगन +गलतियां मिनट मीडिया नियंत्रण +ग्राफ़िक्स चिह्न परिभाषा सूची हेडिंग +विषय सूची दूरस्थ प्लेबैक नियंत्रित करें +सबटाइटल पिक्चर में पिक्चर कृपया इनमें से कोई विकल्प चुनें. प्लेबैक शुरू करें +पाठ अब आपके टीवी पर कास्ट हो रहा है मुख्य वीडियो चलाने में गड़बड़ी - KB + केबी माह वर्तमान समय, सेकंड में मार्गदर्शक @@ -191,6 +232,7 @@ अन्य... सूचना , से प्रारंभ हो रहा है +फ़ुटनोट सेल मेनू \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_hr.xtb b/chromium/content/app/strings/translations/content_strings_hr.xtb index b2d751d3dfb..1f23cd9eec2 100644 --- a/chromium/content/app/strings/translations/content_strings_hr.xtb +++ b/chromium/content/app/strings/translations/content_strings_hr.xtb @@ -10,28 +10,34 @@ dopunski pomični tekst klizač +završna napomena dijalog +zasluge članak natpis regija Ovaj mjesec Odabir datoteka +izdvojeno Unesite e-adresu koja nije prazna vrijednost. Drugo... dijaloški okvir upozorenja rešetka u obliku stabla prijepodne/poslijepodne reproduciraj na udaljenom uređaju +primjer dd status Vrijednost mora biti ili prije toga. Prebačeno na zrcaljenje Znak "" upotrebljava se na pogrešnom položaju u domeni "". Danas +popis stranica Skratite taj tekst na znakova ili manje (trenutačno upotrebljavate znakova). . tjedan, . godina Zapis potvrdni okvir +prijelom stranice alat za odabir datuma i vremena Dan Ispunite ovo polje. @@ -40,6 +46,7 @@ reprodukcija Unesite važeću vrijednost. Najbliža je važeća vrijednost . Pošalji +grafički objekt podnožje Automatsko popunjavanje zvuk @@ -70,30 +77,41 @@ Produljite broj znakova u tekstu na minimalno . Trenutačno imate premalo znakova (). Dio adrese ispred znaka "" ne smije sadržavati simbol "". Izlaz iz punog zaslona +zaključak aplikacija +prolog klizač Milisekunde slika +predgovor Prikaži prethodni mjesec odabir boja traka izbornika informacije o sadržaju Unesite važeću vrijednost. Dvije su najbliže važeće vrijednosti i . +Pitanja i odgovori gumb izbornika +referenca iz pojmovnika Detalji obrazac stablo Dio adrese iza znaka "" ne smije sadržavati simbol "". +dodatak +naslovnica ploča kartice videozapis search Unesite broj. +bibliografski unos Odabrano: Uključi zvuk Unesite dio adrese iza znaka "". "" nije potpuna e-adresa. pokazivač napretka +epigraf Prikaži sljedeći mjesec zaglavlje retka +epilog +Ovaj se videozapis prikazuje u značajci slike u slici Vrijednost mora biti ili veća. PB Unesite dio adrese ispred znaka "". "" nije potpuna e-adresa. @@ -106,9 +124,11 @@ uključivanje zvuka vrijeme Dvaput dodirnite lijevo ili desno za preskakanje od 10 s +sažetak pauziranje reprodukcije gumb više opcija +uvod Isključi zvuk Isključeno Puni zaslon @@ -119,12 +139,15 @@ Godina okvir s popisom uključivanje zvučnog zapisa +bibliografija mjerač +pogovor +pojmovnik Prikaži ploču za odabir mjeseca klizač vremena audiozapisa +zahvale alat za odabir vremena Opcije -Sada ste u načinu slike u slici otvaranje na cijelom zaslonu feed zap @@ -135,22 +158,31 @@ opis Reproduciraj Nije bilo moguće učitati dodatak. +bibliografska referenca isključi ton sakrivanje titlova direktorij +kolofon uvučeni citat preuzmi medij Produljite broj znakova u tekstu na minimalno (trenutačno imate 1 znak). Udovoljite zadanom formatu. +predgovor proteklo vrijeme Odaberite stavku s popisa. zaglavlje stupca +zasluga Odaberi datoteku Drugo... +završne napomene TB kart Preuzmi +povratna veza +savjet +grafički dokument Sati +indeks preostalo vrijeme zaustavljanje prikazivanja titlova HTML sadržaj @@ -160,24 +192,33 @@ razdjelnik Nije odabrana niti jedna datoteka. pretraži tekstno polje +obavijest +referenca bilješke početak prikazivanja titlova Ovaj tjedan reprodukcija filma na cijelom zaslonu gggg Titlovi Nevažeća vrijednost. +dio alatna traka Broj datoteka: Odaberite datoteku. izborni gumb +posveta +errata Minute kontrola medija +grafički simbol popis definicija naslov +sadržaj upravljaj daljinskom reprodukcijom +titl Slika u slici Izaberite jednu od tih opcija. početak reprodukcije +poglavlje Emitira se na televizoru glav Pogreška pri reprodukciji videozapisa @@ -191,6 +232,7 @@ Drugo... upozorenje , počevši od +fusnota ćelija izbornik \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_hu.xtb b/chromium/content/app/strings/translations/content_strings_hu.xtb index d8512555347..00378120757 100644 --- a/chromium/content/app/strings/translations/content_strings_hu.xtb +++ b/chromium/content/app/strings/translations/content_strings_hu.xtb @@ -10,28 +10,34 @@ kiegészítő fényújság csúszka +záró jegyzet párbeszédpanel +készítők cikk szalaghirdetés régió Ebben a hónapban Fájlok kiválasztása +kiemelt idézet Kérjük, ne hagyja üresen az e-mail-cím mezőjét. Más... értesítő párbeszédpanel farács de./du. lejátszás távoli eszközön +példa nn állapot Az érték vagy azt megelőző kell, hogy legyen. Átváltott tükrözésre A „” rossz helyen van a(z) „” címben. Ma +oldallista Kérjük, rövidítse le a szöveget legfeljebb karakterre (jelenleg karaktert használ). . hét, . szám jelölőnégyzet +oldaltörés dátum- és időválasztó nap Kérjük, töltse ki ezt a mezőt. @@ -40,6 +46,7 @@ lejátszás Kérjük, érvényes értéket adjon meg. A legközelebbi érvényes érték . Elküldés +grafikus objektum lábléc Automatikus kitöltés audio @@ -70,30 +77,41 @@ Kérjük, karakter hosszú vagy annál hosszabb szöveget adjon meg (jelenleg karaktert használ). A „” előtti rész nem tartalmazhat „” karaktert. Teljes képernyő – ki +összefoglalás alkalmazás +prológus görgetősáv Ezredmásodperc grafika +előszó Az előző hónap megjelenítése színválasztó menüsor tartalominformáció Kérjük, érvényes értéket adjon meg. A két legközelebbi érvényes érték és . +Kérdések és válaszok menügomb +szószedet-hivatkozás Részletek űrlap fa A „” utáni rész nem tartalmazhat „” karaktert. +függelék +borító lappanel video search Kérjük, adjon meg egy számot. +bibliográfiai bejegyzés kiválasztva Némítás feloldása Kérjük, adja meg a „” utáni részt is. A(z) „” cím nem teljes. folyamatjelző +mottó A következő hónap megjelenítése sorfejléc +végszó +A videó lejátszása kép a képben módban történik Az érték legyen nagyobb vagy egyenlő, mint . PB Kérjük, adja meg a „” előtti részt is. A „” cím nem teljes. @@ -106,9 +124,11 @@ némítás feloldása idő A jobb/bal oldalon duplán koppintva ugorhat előre/vissza 10 másodpercet +absztrakt lejátszás szüneteltetése gomb további beállítások +bevezetés Némítás Kikapcsolva Teljes képernyő @@ -119,12 +139,15 @@ Év listamező hangsáv némításának feloldása +bibliográfia mérő +utószó +szószedet A hónapválasztási panel megjelenítése hang idővonalának vezérlője +köszönetnyilvánítás időválasztó Beállítások -Most kép a képben módban van teljes képernyős nézet hírcsatorna napló @@ -135,22 +158,31 @@ elemleírás Játék Nem sikerült betölteni a beépülő modult. +bibliográfiai hivatkozás némítás feliratok elrejtése címtár +kolofon bekezdésszintű idézet médiafájlok letöltése Legalább karakter hosszú szöveget adjon meg (jelenleg 1 karaktert használ). Kérjük, tartsa magát a kívánt formátumhoz. +bevezető eltelt idő Kérjük, válasszon egyet a lista elemei közül. oszlopfejléc +elismerés Fájl kiválasztása Más... +záró jegyzetek TB tab Letöltés +visszamutató link +tipp +grafikus dokumentum Óra +tárgymutató hátralévő idő feliratok elrejtése HTML-tartalom @@ -160,24 +192,33 @@ felosztó Nincs fájl kiválasztva keresés a szövegmezőben +közlés +jegyzethivatkozás feliratok megjelenítése Ezen a héten film lejátszása teljes képernyős nézetben éééé Feliratok Érvénytelen érték. +rész eszköztár fájl Válasszon egy fájlt. választógomb +ajánlás +hibajegyzék Perc médiavezérlő +grafikus szimbólum definíciós lista fejléc +tartalomjegyzék távoli lejátszás kezelése +felirat Kép a képben Kérjük, válassza ki az egyik opciót. lejátszás indítása +fejezet Átküldés a tévére folyamatban Videólejátszási hiba @@ -191,6 +232,7 @@ Más... értesítés . hét (-i dátummal kezdődik) +lábjegyzet cella menü \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_id.xtb b/chromium/content/app/strings/translations/content_strings_id.xtb index e2289b71cfb..e0da8db2aec 100644 --- a/chromium/content/app/strings/translations/content_strings_id.xtb +++ b/chromium/content/app/strings/translations/content_strings_id.xtb @@ -10,28 +10,34 @@ komplementer marquee penggeser +catatan akhir dialog +daftar penghargaan article spanduk wilayah Bulan ini Pilih File +pullquote Jangan mengosongkan bidang alamat email. Lainnya... alert_dialog kisi pohon AM/PM putar di perangkat jarak jauh +contoh hh status Tanggal harus atau lebih awal. Dialihkan ke pencerminan '' digunakan pada posisi yang salah di ''. Hari ini +daftar halaman Pendekkan teks ini menjadi karakter atau kurang (saat ini Anda menggunakan karakter). Minggu , Lagu kotak centang +batas halaman pemilih tanggal dan waktu Hari Harap isi bidang ini. @@ -40,6 +46,7 @@ main Masukkan nilai yang valid. Nilai valid terdekatnya adalah . Kirim +objek grafis footer Isi-Otomatis audio @@ -70,30 +77,41 @@ Perpanjang teks ini hingga karakter atau lebih (saat ini Anda menggunakan karakter). Bagian sebelum '' tidak boleh berisi simbol ''. Keluar dari layar penuh +kesimpulan aplikasi +prolog bilah gulir Milidetik grafis +kata pengantar Tampilkan bulan sebelumnya pemilih warna bilah menu info konten Masukkan nilai yang valid. Dua nilai valid terdekat adalah dan . +Tanya Jawab tombol menu +referensi glosarium Detail formulir tree Bagian setelah '' tidak boleh berisi simbol ''. +lampiran +sampul panel tab video search Masukkan nomor. +entri bibliografi dipilih Bunyikan Masukkan bagian setelah ''. '' tidak lengkap. indikator kemajuan +epigraf Tampilkan bulan berikutnya judul baris +epilog +Video ini diputar dalam Picture-in-Picture Nilai harus lebih besar daripada atau sama dengan . PB Masukkan bagian yang diikuti dengan ''. '' tidak lengkap. @@ -106,9 +124,11 @@ suarakan waktu Tap dua kali ke kiri atau kanan untuk melewati 10 detik +abstrak jeda pemutaran tombol opsi lainnya +pendahuluan Bisukan Nonaktif Layar Penuh @@ -119,12 +139,15 @@ Tahun kotak daftar suarakan trek audio +bibliografi pengukur +penutup +glosarium Tampilkan panel pilihan bulan scrubber waktu audio +ucapan terima kasih pemilih waktu Opsi -Kini dalam mode Picture-in-Picture masuk layar penuh feed log @@ -135,22 +158,31 @@ keterangan alat Putar Tidak dapat memuat plugin. +referensi bibliografi bisukan sembunyikan teks direktori +kolofon blockquote download media Perpanjang teks ini menjadi karakter atau lebih (saat ini Anda menggunakan 1 karakter). Sesuaikan dengan format yang diminta. +kata pengantar waktu berlalu Pilih item pada daftar. judul kolom +daftar penghargaan Pilih File Lainnya... +catatan akhir TB tab Download +back link +tips +dokumen grafis Jam +indeks sisa waktu berhenti menampilkan teks Konten HTML @@ -160,24 +192,33 @@ pemisah Tidak ada file yang dipilih bidang teks penelusuran +pemberitahuan +referensi catatan mulai menampilkan teks Minggu ini putar film dalam mode layar penuh tttt Teks Nilai tidak valid. +bagian bilah alat file Pilih file. tombol radio +persembahan +erratum Menit kontrol media +simbol grafis daftar definisi kepala +daftar isi kontrol pemutaran jarak jauh +subjudul Picture-in-Picture Pilih salah satu opsi berikut. mulai pemutaran +bab Sekarang sedang mentransmisi ke TV Anda main Error pemutaran video @@ -191,6 +232,7 @@ Lainnya... notifikasi , dimulai pada +catatan kaki sel menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_it.xtb b/chromium/content/app/strings/translations/content_strings_it.xtb index 305c8e96347..72572258b83 100644 --- a/chromium/content/app/strings/translations/content_strings_it.xtb +++ b/chromium/content/app/strings/translations/content_strings_it.xtb @@ -10,28 +10,34 @@ elementi complementari marquee dispositivo di scorrimento +nota finale finestra di dialogo +ringraziamenti articolo banner regione Questo mese Scegli file +citazione Inserisci un indirizzo email valido. Altro... alert_dialog griglia ad albero AM/PM riproduci su dispositivo remoto +esempio gg stato Il valore deve essere o precedente. Sei passato a mirroring Il segno "" è utilizzato in una posizione errata in "". Oggi +elenco pagine Riduci questo testo a caratteri o meno (attualmente stai utilizzando caratteri). Settimana , Traccia casella di controllo +interruzione di pagina selettore di data e ora Giorno Compila questo campo. @@ -40,6 +46,7 @@ riproduci Inserisci un valore valido. Il valore valido più vicino è . Invia +oggetto grafico piè di pagina Compilazione automatica audio @@ -70,30 +77,41 @@ Prolunga questo testo a o più caratteri (al momento stai utilizzando caratteri). Una parte seguita da "" non deve contenere il simbolo "". Esci da schermo intero +conclusioni applicazione +prologo barra di scorrimento Millisecondi immagine +premessa Mostra mese precedente selettore colori barra dei menu informazioni sui contenuti Inserisci un valore valido. I due valori validi più vicini sono e . +Domande e risposte pulsante di menu +riferimento glossario Dettagli modulo tree Una parte che segue "" non deve contenere il simbolo "". +appendice +copertina riquadro a schede video ricerca Inserisci un numero. +voce bibliografica Elementi selezionati: Riattiva audio Inserisci una parte dopo "". Il valore "" è incompleto. indicatore di avanzamento +epigrafe Mostra mese successivo intestazione di riga +epilogo +Questo video è riprodotto in modalità picture in picture Il valore deve essere superiore o uguale a . PB Inserisci una parte seguita da "". Il valore "" è incompleto. @@ -106,9 +124,11 @@ riattiva audio time Tocca due volte a sinistra o destra per saltare di 10 secondi +abstract sospendi riproduzione pulsante altre opzioni +introduzione Disattiva audio Off Schermo intero @@ -119,12 +139,15 @@ Anno casella di riepilogo riattiva traccia audio +bibliografia indicatore +postfazione +glossario Mostra il riquadro di selezione del mese dispositivo di scorrimento durata audio +riconoscimenti selettore di ora Opzioni -Ora in modalità picture in picture passa a schermo intero feed log @@ -135,22 +158,31 @@ descrizione comando Play Impossibile caricare il plug-in +riferimento bibliografico disattiva audio nascondi sottotitoli directory +colophon citazione lunga scarica contenuti multimediali Prolunga questo testo a o più caratteri (al momento stai utilizzando 1 carattere). Rispetta il formato richiesto. +prefazione tempo trascorso Seleziona un elemento nell'elenco. intestazione di colonna +ringraziamento Scegli file Altro... +note finali TB Tab Scarica +backlink +suggerimento +documento grafico Orario +indice tempo rimanente interrompi la visualizzazione dei sottotitoli Contenuti HTML @@ -160,24 +192,33 @@ barra di divisione Nessun file selezionato campo di testo della ricerca +informativa +riferimento note avvia la visualizzazione dei sottotitoli Questa settimana riproduci filmato in modalità a schermo intero aaaa Sottotitoli Valore non valido. +parte barra degli strumenti file Seleziona un file. pulsante di opzione +dedica +errata corrige Minuti controllo contenuti multimediali +simbolo grafico elenco di definizioni intestazione +sommario gestisci riproduzione remota +sottotitolo Picture in picture Seleziona una di queste opzioni. inizia riproduzione +capitolo Ora in fase di trasmissione alla TV main Errore di riproduzione video @@ -191,6 +232,7 @@ Altro... avviso , a partire dal giorno +piè di pagina cella menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_iw.xtb b/chromium/content/app/strings/translations/content_strings_iw.xtb index bd625140753..3a5a640f164 100644 --- a/chromium/content/app/strings/translations/content_strings_iw.xtb +++ b/chromium/content/app/strings/translations/content_strings_iw.xtb @@ -10,28 +10,34 @@ משלים marquee מחוון +הערת סיום דו-שיח +קרדיטים article מודעת באנר אזור החודש בחר קבצים +ציטוט הזן ערך נדרש בשדה של כתובת האימייל. אחר... alert_dialog רשת של עץ AM/PM הפעלה במכשיר מרוחק +דוגמה dd מצב על הערך להיות או מוקדם יותר. הועבר לשיקוף מסך נעשה שימוש ב-'' במיקום שגוי ב-''. היום +רשימת דפים קצר טקסט זה ל- תווים או פחות (אתה משתמש כעת ב- תווים). שבוע ‏, רצועה תיבת סימון +מעבר דף בוחר תאריך ושעה יום מלא שדה זה. @@ -40,6 +46,7 @@ הפעל הזן ערך חוקי. הערך החוקי הקרוב ביותר הוא שלח +אובייקט גרפי כותרת תחתונה מילוי אוטומטי אודיו @@ -70,30 +77,41 @@ הארך טקסט זה ל- תווים או יותר (אתה משתמש כרגע ב- תווים). חלק ולאחריו '' לא אמור לכלול את הסמל ''. צא ממסך מלא +סיכום יישום +פרולוג סרגל גלילה אלפיות שנייה פריט גרפי +פתח דבר הצג חודש קודם color picker שורת תפריטים פרטי תוכן הזן ערך חוקי. שני הערכים החוקיים הקרובים ביותר הם ו-. +שאלות ותשובות לחצן תפריט +הפניה למילון מונחים פרטים טופס tree חלק ולאחריו '' לא אמור לכלול את הסמל ''. +נספח +שער חלונית כרטיסיות סרטוני וידאו Search יש להזין מספר. +ערך ביבליוגרפי נבחרו בטל השתקה הזן חלק ולאחריו ''‏. השדה '' אינו מלא. סימן ויזואלי להתקדמות +ציטוט בראש ספר הצג את החודש הבא כותרת שורה +אפילוג +הסרטון הזה פועל במצב תמונה בתוך תמונה הערך חייב להיות גדול מ- או שווה לו. PB הזן חלק ולאחריו ''‏. השדה '' אינו מלא. @@ -106,9 +124,11 @@ בטל השתקה time הקשה כפולה בצד ימין או שמאל מדלגת על 10 שניות +תקציר השהה הפעלה לחצן אפשרויות נוספות +מבוא השתק כבוי מסך מלא @@ -119,12 +139,15 @@ שנה תיבת רשימה בטל השתקה של רצועת אודיו +ביבליוגרפיה מד +אחרית דבר +מילון מונחים הצג חלונית לבחירת חודש מסתיר משך אודיו +תודות בוחר שעות אפשרויות -עכשיו במצב 'תמונה בתוך תמונה' היכנס למסך מלא פיד log @@ -135,22 +158,31 @@ הסבר קצר הפעל לא ניתן היה לטעון את הפלאגין. +הפניה לביבליוגרפיה השתק הסתר כתוביות סגורות ספריה +קולופון ‏רכיב blockquote הורד מדיה עליך להאריך את הטקסט ל- תווים או יותר (אתה משתמש כרגע בתו אחד). התאם את הפורמט המבוקש. +הקדמה זמן שחלף בחר פריט מהרשימה. כותרת עמודה +קרדיט בחירת קובץ אחר... +הערות סיום TB tab הורד +קישור לאחור +טיפ +מסמך גרפי שעות +אינדקס זמן שנותר הפסק להציג כתוביות סגורות ‏תוכן HTML @@ -160,24 +192,33 @@ מפצל לא נבחר קובץ שדה טקסט לחיפוש +הודעה +הפניה להערה התחל להציג כתוביות סגורות השבוע הפעל את הסרט במצב מסך מלא yyyy כתוביות ערך לא חוקי. +חלק סרגל כלים קבצים בחר קובץ. לחצן בחירה +הקדשה +שגיאת דפוס דקות שליטה במדיה +סמל גרפי רשימת הגדרות כותרת +תוכן העניינים שליטה בהפעלה מרחוק +כותרת משנה תמונה בתוך תמונה בחר אחת מהאפשרויות הבאות. התחל בהפעלה +פרק מעביר תוכן לטלוויזיה main שגיאה בהפעלת הסרטון @@ -191,6 +232,7 @@ אחר... התראה , שמתחיל בתאריך +הערת שוליים תא תפריט \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ja.xtb b/chromium/content/app/strings/translations/content_strings_ja.xtb index 39ba9808af5..314178fe319 100644 --- a/chromium/content/app/strings/translations/content_strings_ja.xtb +++ b/chromium/content/app/strings/translations/content_strings_ja.xtb @@ -10,28 +10,34 @@ 捕捉 マーキー スライダ +巻末注 ダイアログ +クレジット 記事 バナー 地域 今月 ファイル選択 +引用 メール アドレスを入力してください。 その他... アラート ダイアログ ツリーグリッド AM/PM リモート デバイスで再生 + ステータス 以前の値を指定する必要があります。 ミラーリングに切り替わりました 」内の「」の位置が間違っています。 今日 +ページリスト このテキストを半角 文字以下にしてください(現時点で半角 文字です)。 年第 トラック チェックボックス +改ページ 日時選択ツール このフィールドを入力してください。 @@ -40,6 +46,7 @@ 再生 有効な値を入力してください。有効な値として最も近いのは です。 送信 +図形オブジェクト フッター 自動入力 音声 @@ -70,30 +77,41 @@ このテキストは 文字以上で指定してください(現在は 文字です)。 」の前の文字列に記号「」を使用しないでください。 全画面表示を終了 +まとめ アプリケーション +プロローグ スクロールバー ミリ秒 グラフィック +前書き 前の月を表示 色の選択 メニューバー コンテンツ情報 有効な値を入力してください。有効な値として最も近いのは です。 +Q&A メニューボタン +用語参照 詳細 フォーム ツリー 」に続く文字列に記号「」を使用しないでください。 +付録 +表紙 タブパネル 動画 検索 数字を入力してください。 +文献情報 件選択 ミュートを解除 」は完全なメールアドレスではありません。「」に続く文字列を入力してください。 プログレスバー +題辞 次の月を表示 行見出し +エピローグ +この動画はピクチャー イン ピクチャーで再生されています 値は 以上にする必要があります。 PB 」の前の文字列を入力してください。「」は完全なメール アドレスではありません。 @@ -106,9 +124,11 @@ ミュートを解除 日時 左または右をダブルタップして 10 秒スキップします +概要 再生を一時停止 ボタン その他のオプション +序論 ミュート オフ 全画面表示 @@ -119,12 +139,15 @@ リストボックス 音声トラックのミュートを解除 +参考文献 メーター +後書き +用語集 月選択パネルを表示 オーディオ再生バー +謝辞 時間選択ツール オプション -ピクチャー イン ピクチャー モードで表示中です 全画面表示 フィード ログ @@ -135,22 +158,31 @@ ツールチップ 再生 プラグインを読み込むことができませんでした。 +文献参照 ミュート クローズド キャプションを非表示 ディレクトリ +奥付 引用 メディアをダウンロード このテキストは 文字以上で指定してください(現在 1 文字で指定されています)。 指定されている形式で入力してください。 +序文 経過時間 リスト内の項目を選択してください。 列見出し +クレジット ファイルを選択 その他... +巻末注 TB タブ ダウンロード +言及リンク +ヒント +図形ドキュメント 時間 +索引 残り時間 クローズド キャプションの表示を終了 HTML コンテンツ @@ -160,24 +192,33 @@ 分割バー 選択されていません 検索テキスト欄 +注記 +備考 クローズド キャプションの表示を開始 今週 全画面表示モードで再生 字幕 値が無効です。 + ツールバー ファイル ファイルを選択してください。 ラジオボタン +献辞 +正誤表 メディア管理 +図形記号 定義リスト 見出し +目次 リモート再生をコントロール +副題 ピクチャー イン ピクチャー これらのオプションから 1 つ選択してください。 再生を開始 + テレビにキャストしています 本文 動画再生エラー @@ -191,6 +232,7 @@ その他... アラート から始まる +脚注 セル メニュー \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_kn.xtb b/chromium/content/app/strings/translations/content_strings_kn.xtb index ba142f30101..02c6cdc5517 100644 --- a/chromium/content/app/strings/translations/content_strings_kn.xtb +++ b/chromium/content/app/strings/translations/content_strings_kn.xtb @@ -10,28 +10,34 @@ ಪೂರಕವಾಗಿ ಮಾರ್ಕ್ಯೂ ಸ್ಲೈಡರ್ +ಮುಕ್ತಾಯ ಟಿಪ್ಪಣಿ ಸಂವಾದ +ಕ್ರೆಡಿಟ್‌ಗಳು ಲೇಖನ ಬ್ಯಾನರ್ ಪ್ರದೇಶ ಈ ತಿಂಗಳು ಫೈಲ್‌ಗಳನ್ನು ಆರಿಸಿ +ಪ್ರಮುಖ ಉಲ್ಲೇಖ ಖಾಲಿ-ಅಲ್ಲದ ಇಮೇಲ್ ವಿಳಾಸವನ್ನು ನಮೂದಿಸಿ. ಇತರೆ... alert_dialog ಟ್ರೀ ಗ್ರಿಡ್ AM/PM ರಿಮೋಟ್ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡಿ +ಉದಾಹರಣೆ ದಿದಿ ಸ್ಥಿತಿ ಮೌಲ್ಯವು ಅಥವಾ ಹಿಂದಿನದ್ದಾಗಿರಬೇಕು. ಪ್ರತಿಬಿಂಬಿಸುವಿಕೆಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ '' ಅನ್ನು '' ನಲ್ಲಿ ತಪ್ಪಾದ ಸ್ಥಾನದಲ್ಲಿ ಬಳಸಲಾಗಿದೆ. ಇಂದು +ಪುಟ ಪಟ್ಟಿ ದಯವಿಟ್ಟು ಈ ಪಠ್ಯವನ್ನು ಅಕ್ಷರಗಳಿಗೆ ಅಥವಾ ಅದಕ್ಕಿಂತಲೂ ಕಡಿಮೆಗೆ ಸೀಮಿತಗೊಳಿಸಿ (ನೀವು ಪ್ರಸ್ತುತ ಅಕ್ಷರಗಳನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ). ವಾರ , ಟ್ರ್ಯಾಕ್ ಚೆಕ್‌ಬಾಕ್ಸ್ +ಪುಟ ವಿಭಜನೆ ದಿನಾಂಕ ಮತ್ತು ಸಮಯ ಪಿಕರ್ ದಿನ ದಯವಿಟ್ಟು ಈ ಕ್ಷೇತ್ರವನ್ನು ಭರ್ತಿ ಮಾಡಿ. @@ -40,6 +46,7 @@ ಪ್ಲೇ ಮಾಡಿ ಮಾನ್ಯ ಮೌಲ್ಯವನ್ನು ನಮೂದಿಸಿ. ಹತ್ತಿರದ ಮಾನ್ಯ ಮೌಲ್ಯವು ಆಗಿದೆ. ಸಲ್ಲಿಸು +ಗ್ರಾಫಿಕ್ಸ್ ವಸ್ತು ಅಡಿಟಿಪ್ಪಣಿ ಸ್ವಯಂತುಂಬುವಿಕೆ ಆಡಿಯೋ @@ -70,30 +77,41 @@ ಈ ಪಠ್ಯವನ್ನು ಅಕ್ಷರಗಳಿಗೆ ಅಥವಾ ಅದಕ್ಕಿಂತಲೂ ಹೆಚ್ಚಿಗೆ ಸೀಮಿತಗೊಳಿಸಿ (ನೀವು ಪ್ರಸ್ತುತವಾಗಿ ಅಕ್ಷರಗಳನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ). '' ನಂತರದ ಭಾಗವು '' ಚಿಹ್ನೆಯನ್ನು ಒಳಗೊಂಡಿರಬಾರದು. ಪೂರ್ಣಪರದೆಯಿಂದ ನಿರ್ಗಮಿಸಿ +ತೀರ್ಮಾನ ಅಪ್ಲಿಕೇಶನ್ +ಪೀಠಿಕೆ ಸ್ಕ್ರಾಲ್ ಪಟ್ಟಿ ಮಿಲಿಸೆಕೆಂಡುಗಳು ಗ್ರಾಫಿಕ್ +ಮುನ್ನುಡಿ ಹಿಂದಿನ ತಿಂಗಳು ತೋರಿಸು ಬಣ್ಣದ ಆಯ್ಕೆಮಾಡುವಿಕೆ ಮೆನು ಬಾರ್‌ ವಿಷಯ ಮಾಹಿತಿ ಮಾನ್ಯವಾದ ಮೌಲ್ಯವನ್ನು ನಮೂದಿಸಿ. ಮತ್ತು ಎರಡು ಹತ್ತಿರದ ಮಾನ್ಯ ಮೌಲ್ಯಗಳಾಗಿವೆ. +ಪ್ರಶ್ನೋತ್ತರ ಮೆನು ಬಟನ್ +ಪದಕೋಶ ಉಲ್ಲೇಖ ವಿವರಗಳು ಫಾರ್ಮ್ ಮರ '' ನಂತರದ ಭಾಗವು '' ಚಿಹ್ನೆಯನ್ನು ಒಳಗೊಂಡಿರಬಾರದು. +ಅನುಬಂಧ +ಕವರ್ ಟ್ಯಾಬ್ ಫಲಕ ವೀಡಿಯೊ search ದಯವಿಟ್ಟು ಸಂಖ್ಯೆಯನ್ನು ನಮೂದಿಸಿ. +ಗ್ರಂಥಸೂಚಿ ನಮೂದು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ ಅನ್‌ಮ್ಯೂಟ್ '' ನಂತರದ ಒಂದು ಭಾಗವನ್ನು ನಮೂದಿಸಿ. '' ಅಪೂರ್ಣವಾಗಿದೆ. ಪ್ರಗತಿ ಸೂಚಕ +ಶಿಲಾಶಾಸನ ಮುಂದಿನ ತಿಂಗಳು ತೋರಿಸಿ ಸಾಲಿನ ಶಿರೋನಾಮೆ +ಹಿನ್ನುಡಿ +ಚಿತ್ರದಲ್ಲಿನ ಚಿತ್ರದಲ್ಲಿ ಈ ವೀಡಿಯೊ ಪ್ಲೇ ಆಗುತ್ತಿದೆ ಮೌಲ್ಯವು ಕ್ಕಿಂತಲೂ ಹೆಚ್ಚಾಗಿರಬೇಕು ಅಥವಾ ಸಮನಾಗಿರಬೇಕು. PB '' ನಂತರದ ಭಾಗವನ್ನು ನಮೂದಿಸಿ. '' ಅಪೂರ್ಣವಾಗಿದೆ. @@ -106,9 +124,11 @@ ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡು ಸಮಯ 10ಸೆ ಸ್ಕಿಪ್ ಮಾಡಲು ಎಡಕ್ಕೆ ಅಥವಾ ಬಲಕ್ಕೆ ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ +ಸಾರಾಂಶ ಪ್ಲೇಬ್ಯಾಕ್ ವಿರಾಮಗೊಳಿಸು ಬಟನ್ ಇನ್ನಷ್ಟು ಆಯ್ಕೆಗಳು +ಪರಿಚಯ ಮ್ಯೂಟ್ ಆಫ್ ಪೂರ್ಣಪರದೆ @@ -119,12 +139,15 @@ ವರ್ಷ ಪಟ್ಟಿಯ ಬಾಕ್ಸ್ ಆಡಿಯೋ ಟ್ರ್ಯಾಕ್ ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡು +ಗ್ರಂಥಸೂಚಿ ಮೀಟರ್ +ನಂತರದ +ಪದಕೋಶ ತಿಂಗಳ ಆಯ್ಕೆ ಪ್ಯಾನಲ್ ತೋರಿಸಿ ಆಡಿಯೊ ಸಮಯ ಸ್ಕ್ರಬ್ಬರ್ +ಅಂಗೀಕಾರಗಳು ಸಮಯ ಪಿಕರ್ ಆಯ್ಕೆಗಳು -ಈಗ ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ ಮೋಡ್‌ನಲ್ಲಿ ಇದೆ ಪೂರ್ಣ ಪರದೆ ನಮೂದಿಸು ಫೀಡ್‌ ಲಾಗ್ @@ -135,22 +158,31 @@ ಟೂಲ್‌ ಟಿಪ್‌ ಪ್ಲೇ ಮಾಡು ಪ್ಲಗ್-ಇನ್ ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. +ಗ್ರಂಥಸೂಚಿ ಉಲ್ಲೇಖ ಮ್ಯೂಟ್ ಮುಚ್ಚಿರುವ ಶೀರ್ಷಿಕೆಗಳನ್ನು ಮರೆಮಾಡಿ ಡೈರೆಕ್ಟರಿ +ಕೊಲೊಫೋನ್ ಬ್ಲಾಕ್‌ಕೋಟ್ ಮಾಧ್ಯಮ ಡೌನ್‌ಲೋಡ್ ಈ ಪಠ್ಯವನ್ನು ಅಥವಾ ಇದಕ್ಕಿಂತಲೂ ಹೆಚ್ಚಿನ ಅಕ್ಷರಗಳಿಗೆ ಉದ್ದಗೊಳಿಸಿ (ನೀವು ಪ್ರಸ್ತುತ 1 ಅಕ್ಷರಗಳನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ). ದಯವಿಟ್ಟು ವಿನಂತಿಸಿದ ಸ್ವರೂಪವನ್ನು ಹೊಂದಿಸಿ. +ಮುನ್ನುಡಿ ಕಳೆದುಹೋದ ಸಮಯ ಪಟ್ಟಿಯಲ್ಲಿನ ಐಟಂ ಅನ್ನು ದಯವಿಟ್ಟು ಆಯ್ಕೆ ಮಾಡಿ. ಕಾಲಮ್ ಶಿರೋನಾಮೆ +ಗೌರವ ಫೈಲ್ ಆಯ್ಕೆ ಮಾಡಿ ಇತರೆ... +ಅಂತಿಮ ಟಿಪ್ಪಣಿಗಳು TB ಟ್ಯಾಬ್ ಡೌನ್‌ಲೋಡ್ +ಬ್ಯಾಕ್‌ಲಿಂಕ್ +ಸಲಹೆ +ಗ್ರಾಫಿಕ್ಸ್ ಡಾಕ್ಯುಮೆಂಟ್ ಗಂಟೆಗಳು +ಸೂಚಿಕೆ ಉಳಿದಿರುವ ಸಮಯ ಮುಚ್ಚಲಾಗಿರುವ ಶೀರ್ಷಿಕೆಗಳ ಪ್ರದರ್ಶಿಸುವಿಕೆಯನ್ನು ನಿಲ್ಲಿಸಿ HTML ವಿಷಯ @@ -160,24 +192,33 @@ ಛೇದಕ ಯಾವುದೇ ಫೈಲ್ ಆಯ್ಕೆ ಮಾಡಿಲ್ಲ ಪಠ್ಯ ಕ್ಷೇತ್ರವನ್ನು ಹುಡುಕಿ +ಸೂಚನೆ +ಸೂಚನೆ ಉಲ್ಲೇಖ ಮುಚ್ಚಲಾಗಿರುವ ಶೀರ್ಷಿಕೆಗಳ ಪ್ರದರ್ಶಿಸುವಿಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ ಈ ವಾರ ಪೂರ್ಣ ಪರದೆಯ ಮೋಡ್‌‌ನಲ್ಲಿ ಚಲನಚಿತ್ರವನ್ನು ಪ್ಲೇ ಮಾಡಿ ವವವವ ಶೀರ್ಷಿಕೆಗಳು ಅಮಾನ್ಯ ಮೌಲ್ಯ. +ಭಾಗ ಟೂಲ್‌ಬಾರ್ ಫೈಲ್‌ಗಳು ದಯವಿಟ್ಟು ಫೈಲ್ ಆಯ್ಕೆಮಾಡಿ. ರೇಡಿಯೋ ಬಟನ್ +ಸಮರ್ಪಣೆ +ಎರ್ರಾಟಾ ನಿಮಿಷಗಳು ಮಾಧ್ಯಮ ನಿಯಂತ್ರಣ +ಗ್ರಾಫಿಕ್ಸ್ ಚಿಹ್ನೆ ವಿವರಣೆ ಪಟ್ಟಿ ಶೀರ್ಷಿಕೆ +ಪರಿವಿಡಿ ರಿಮೋಟ್ ಪ್ಲೇಬ್ಯಾಕ್ ನಿಯಂತ್ರಿಸಿ +ಉಪಶೀರ್ಷಿಕೆ ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ ದಯವಿಟ್ಟು ಈ ಕೆಳಗಿನ ಆಯ್ಕೆಗಳಲ್ಲಿ ಒಂದನ್ನು ಆರಿಸಿ. ಪ್ಲೇಬ್ಯಾಕ್ ಪ್ರಾರಂಭಿಸಿ +ಅಧ್ಯಾಯ ಈಗ ನಿಮ್ಮ ಟಿವಿಯನ್ನು ಬಿತ್ತರಿಸಲಾಗುತ್ತಿದೆ ಮುಖ್ಯ ವೀಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್ ದೋಷ @@ -191,6 +232,7 @@ ಇತರೆ... ಎಚ್ಚರಿಕೆ , ರಂದು ಪ್ರಾರಂಭವಾಗುತ್ತದೆ +ಅಡಿಟಿಪ್ಪಣಿ ಸೆಲ್ ಮೆನು \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ko.xtb b/chromium/content/app/strings/translations/content_strings_ko.xtb index 4fe6f5a5c08..0a7e051195e 100644 --- a/chromium/content/app/strings/translations/content_strings_ko.xtb +++ b/chromium/content/app/strings/translations/content_strings_ko.xtb @@ -10,28 +10,34 @@ 상호 보완 marquee 슬라이더 +미주 대화상자 +저작권 표시 article 배너 지역 이번 달 파일 선택 +인용문 비어 있지 않은 이메일 주소를 입력해 주세요. 다른 일자... 알림 대화상자 트리 격자 오전/오후 원격 기기에서 재생 +예시 상태 값은 이전이어야 합니다. 미러링으로 전환됨 ''에서 ''의 위치가 잘못되었습니다. 오늘 +페이지 목록 이 텍스트를 자 이하로 줄이세요(현재 자 사용 중). , 번째 주 번 트랙 체크박스 +페이지 나누기 날짜 및 시간 선택기 이 입력란을 작성하세요. @@ -40,6 +46,7 @@ 재생 유효한 값을 입력해 주세요. 가장 근접한 유효 값은 입니다. 제출 +그래픽 객체 바닥글 자동 완성 오디오 @@ -70,30 +77,41 @@ 이 텍스트를 자 이상으로 늘리세요(현재 자 사용 중). '' 앞 부분에 '' 기호가 포함되면 안됩니다. 전체화면 종료 +결론 애플리케이션 +프롤로그 스크롤바 밀리초 그래픽 +서문 이전 달 표시 color picker 메뉴 표시줄 콘텐츠 정보 유효한 값을 입력해 주세요. 가장 근접한 유효 값 2개는 입니다. +Q&A 메뉴 버튼 +용어 참조 세부정보 양식 tree '' 다음 부분에 '' 기호가 포함되면 안됩니다. +부록 +표지 탭 패널 동영상 검색 숫자를 입력하세요. +참고문헌 항목 개 선택됨 음소거 해제 '' 뒷 부분을 입력해 주세요. ''(이)가 완전하지 않습니다. 진행률 표시기 +제명 다음 달 표시 행 헤더 +에필로그 +이 동영상은 PIP 모드에서 재생 중입니다. 값은 이상이어야 합니다. PB '' 앞 부분을 입력해 주세요. ''(이)가 완전하지 않습니다. @@ -106,9 +124,11 @@ 음소거 해제 time 10초를 건너뛰려면 왼쪽이나 오른쪽을 두 번 탭하세요. +초록 재생 일시중지 버튼 옵션 더보기 +소개 음소거 사용 안함 전체화면 @@ -119,12 +139,15 @@ 연도 목록 상자 오디오 트랙 음소거 해제 +참고문헌 미터 +후기 +용어설명 월 선택 패널 표시 오디오 시간 스크러버 +감사의 말 시간 선택기 옵션 -PIP 모드입니다. 전체화면 열기 피드 log @@ -135,22 +158,31 @@ 도움말 재생 플러그인을 로드할 수 없습니다. +참고문헌 참조 음소거 캡션 숨기기 디렉토리 +출판사 이름 인용구 미디어 다운로드 이 텍스트를 자 이상으로 늘리세요(현재 1자 사용 중). 요청한 형식과 일치시키세요. +머리말 경과 시간 목록에서 항목을 선택하세요. 열 헤더 +크레딧 파일 선택 다른 주... +미주 TB tab 다운로드 +뒤로 링크 +도움말 +그래픽 문서 시간 +색인 남은 시간 캡션 표시 중지 HTML 콘텐츠 @@ -160,24 +192,33 @@ 분할선 선택된 파일 없음 검색어 입력란 +안내문 +참고 자료 캡션 표시 시작 이번 주 전체화면 모드로 영화 재생 연도 자막 값이 잘못되었습니다. + 툴바 파일 파일을 선택하세요. 라디오 버튼 +헌정사 +정오표 미디어 컨트롤 +그래픽 기호 정의 목록 항목 +목차 원격 재생 제어 +부제 PIP 모드 다음 옵션 중 하나를 선택하세요. 재생 시작 + TV로 전송 중 main 동영상 재생 오류 @@ -191,6 +232,7 @@ 다른 달... 알림 (에 시작) +각주 메뉴 \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_lt.xtb b/chromium/content/app/strings/translations/content_strings_lt.xtb index 495c954d071..7ce8525f36b 100644 --- a/chromium/content/app/strings/translations/content_strings_lt.xtb +++ b/chromium/content/app/strings/translations/content_strings_lt.xtb @@ -10,28 +10,34 @@ papildomas marquee šliaužiklis +išnaša dialogo langas +titrai artikelis reklamjuostė regionas Šis mėnuo Pasirinkti failus +citata Įveskite el. pašto adresą (nepalikite lauko tuščio). Kita... alert_dialog medžio tinklelis iki pietų / po pietų leisti naudojant nuotolinį įrenginį +pavyzdys dd būsena Vertė turi būti ar ankstesnė data. Perjungta į ekrano vaizdo bendrinimą “ naudojamas netinkamoje „“ vietoje. Šiandien +puslapių sąrašas Sutrumpinkite šį tekstą iki simb. ar mažiau (šiuo metu naudojate simb.). m. sav. takelis žymimasis laukelis +puslapio lūžis datos ir laiko rinkiklis Diena Užpildykite šį lauką. @@ -40,6 +46,7 @@ paleisti Įveskite tinkamą vertę. Artimiausia tinkama vertė yra . Pateikti +grafikos objektas poraštė Automatinis pildymas garso įrašas @@ -70,30 +77,41 @@ Pailginkite šį tekstą iki simb. ar daugiau (šiuo metu naudojate simb.). Prieš „“ esančioje dalyje neturėtų būti simbolio „“. Išjungti viso ekrano režimą +išvada programa +prologas slinkties juosta Milisekundės grafinis elementas +pratarmė Rodyti ankstesnį mėnesį spalvos parinkiklis meniu juosta turinio informacija Įveskite galiojančią vertę. Dvi artimiausios vertės yra ir . +Klausimai ir atsakymai meniu mygtukas +aiškinamojo terminų žodyno nuoroda Išsami informacija forma tree Po „“ esančioje dalyje neturėtų būti simbolio „“. +priedas +viršelis skirtuko skydelis vaizdo įrašas ieškoti Įveskite skaičių. +bibliografijos įrašas Pasirinkta: Įjungti garsą Įveskite dalį po „“. „“ nėra visas el. pašto adresas. eigos indikatorius +epigrafas Rodyti kitą mėnesį eilutės antraštė +epilogas +Šis vaizdo įrašas leidžiamas kaip vaizdas vaizde Vertė turi būti arba didesnė. PB Įveskite el. pašto adreso dalį iki „“. „“ nėra visas el. pašto adresas. @@ -106,9 +124,11 @@ įjungti garsą time Dukart palieskite kairėn arba dešinėn, kad praleistumėte 10 sek. +santrauka pristabdyti atkūrimą mygtukas daugiau parinkčių +įžanga Nutildyti Išjungta Visas ekranas @@ -119,12 +139,15 @@ Metai sąrašo laukelis įjungti garso įrašo takelio garsą +bibliografija matuoklis +baigiamasis žodis +aiškinamasis terminų žodynas Rodyti mėnesio pasirinkimo skydelį garso laiko valdiklis +padėka laiko rinkiklis Parinktys -Dabar įjungtas vaizdo vaizde režimas įjungti viso ekrano režimą sklaidos kanalas log @@ -135,22 +158,31 @@ patarimas Žaisti Nepavyko įkelti papildinio. +bibliografijos nuoroda nutildyti slėpti subtitrus katalogas +informacija apie leidimą citata atsisiųsti mediją Pailginkite šį tekstą iki simb. ar daugiau (šiuo metu naudojate 1 simbolį). Priderinkite reikalaujamą formatą. +įvadas praėjęs laikas Pasirinkite sąraše pateiktą elementą. stulpelio antraštė +padėka Pasirinkti failą Kita... +išnašos TB Tabuliavimo klavišas Atsisiųsti +atgalinė nuoroda +patarimas +grafikos dokumentas Valandos +rodyklė likęs laikas nebepateikti subtitrų HTML turinys @@ -160,24 +192,33 @@ skaidiklis Nepasirinktas joks failas paieškos teksto laukas +pranešimas +pastabos nuoroda pateikti subtitrus Ši savaitė paleisti filmą viso ekrano režimu mmmm Subtitrai Neteisinga vertė. +dalis įrankių juosta failai (-ų) Pasirinkite failą. akutė +dedikacija +spaudos klaidų sąrašas Minutės medijos valdiklis +grafikos simbolis apibrėžimų sąrašas antraštė +turinys valdyti nuotolinį atkūrimą +paantraštė Vaizdas vaizde Pasirinkite vieną iš šių parinkčių. pradėti atkūrimą +skyrius Dabar perduodama į TV main Vaizdo įrašo atkūrimo klaida @@ -191,6 +232,7 @@ Kita... įspėjimas , nuo +išnaša langelis meniu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_lv.xtb b/chromium/content/app/strings/translations/content_strings_lv.xtb index 24cd767e5fa..2b2273786b5 100644 --- a/chromium/content/app/strings/translations/content_strings_lv.xtb +++ b/chromium/content/app/strings/translations/content_strings_lv.xtb @@ -10,28 +10,34 @@ papildu slīdošais teksts slīdnis +beigu vēre dialoglodziņš +pateicības raksts reklāmkarogs reģions Šis mēnesis Izvēlēties failus +izvilkuma citāts Lūdzu, ievadiet e-pasta adresi. Cits... brīdinājuma dialoglodziņš koka režģis priekšpusdienā/pēcpusdienā atskaņot attālinātā ierīcē +piemērs dd statuss Vērtībai ir jābūt “” vai agrākam datumam vai laikam. Mainīts uz spoguļošanu Punkts () atrodas nepareizā vietā (). Šodien +lapu saraksts Lūdzu, saīsiniet šo tekstu līdz vai mazāk zīmēm (pašreiz tas ietver rakstzīmes). . nedēļa, . gads . ieraksts izvēles rūtiņa +lappuses pārtraukums datuma un laika atlasītājs Diena Aizpildiet šo lauku. @@ -40,6 +46,7 @@ atskaņot Lūdzu, ievadiet derīgu vērtību. Tuvākā derīgā vērtība ir . Iesniegt +grafiskais objekts kājene Automātiskā aizpilde audio @@ -70,30 +77,41 @@ Lūdzu, papildiniet šo tekstu līdz vismaz  rakstzīmēm (pašlaik tas ietver  rakstzīmes). Daļā, kas atrodas pirms zīmes , nedrīkst būt ietverts simbols . Aizvērt pilnekrāna režīmu +nobeigums lietojumprogramma +prologs ritjosla Milisekundes grafiskais elements +priekšvārds Rādīt iepriekšējo mēnesi krāsu izvēle izvēļņu josla satura informācija Lūdzu, ievadiet derīgu vērtību. Divas tuvākās derīgās vērtības ir  un . +Jautājumi un atbildes izvēlnes poga +atsauce uz glosāriju Informācija veidlapa koks Daļā, kas atrodas aiz zīmes , nedrīkst būt ietverts simbols . +pielikums +vāks ciļņu panelis video meklēt Lūdzu, ievadiet skaitli. +bibliogrāfijas ieraksts Atlasīti  Ieslēgt skaņu Lūdzu, ievadiet daļu, kas atrodas aiz zīmes . “” ir nepilna adrese. norises indikators +epigrāfs Rādīt nākamo mēnesi rindas virsraksts +epilogs +Videoklips tiek atskaņots režīmā Attēls attēlā Vērtībai ir jābūt lielākai vai vienādai ar .  PB Lūdzu, ievadiet daļu, kas atrodas pirms zīmes . “” ir nepilna adrese. @@ -106,9 +124,11 @@ rādīt laiks Veiciet dubultskārienu pa labi vai pa kreisi, lai izlaistu 10 s. +kopsavilkums pauzēt atskaņošanu poga citas opcijas +ievads Izslēgt skaņu Izsl. Pilnekrāna @@ -119,12 +139,15 @@ Gads sarakstlodziņš ieslēgt audio ieraksta skaņu +bibliogrāfija mērītājs +pēcvārds +glosārijs Rādīt mēneša atlases paneli audio laika skalas slīdnis +pateicības laika atlasītājs Opcijas -Notiek atskaņošana režīmā “Attēls attēlā”. atvērt pilnekrāna režīmā plūsma žurnāls @@ -135,22 +158,31 @@ rīka padoms Atskaņot Nevarēja ielādēt spraudni. +bibliogrāfiskā atsauce izslēgt skaņu slēpt slēgtos parakstus katalogs +izdošanas ziņas citāta bloks lejupielādēt multivides failus Lūdzu, papildiniet šo tekstu līdz vismaz  rakstzīmēm (pašlaik tas ietver 1 rakstzīmi). Pieskaņojiet vērtību prasītajam formātam. +ievadraksts pagājušais laiks Atlasiet vienumu sarakstā. slejas virsraksts +pateicība Izvēlēties failu Cits... +beigu vēres  TB cilne Lejupielādēt +atpakaļsaite +padoms +grafikas dokuments Stundas +rādītājs atlikušais laiks apturēt slēgto parakstu rādīšanu HTML saturs @@ -160,24 +192,33 @@ sadalītājs Nav izvēlēts neviens fails meklēšanas teksta lauks +paziņojums +vēres atsauce sākt slēgto parakstu rādīšanu Šī nedēļa atskaņot filmu pilnekrāna režīmā gggg Paraksti Nederīga vērtība. +daļa rīkjosla  faili Lūdzu, atlasiet failu. radiopoga +veltījums +iespiedkļūdu saraksts Minūtes multivides vadība +grafiskais simbols definīciju saraksts Virsraksts +satura rādītājs kontrolēt attālināto atskaņošanu +apakšvirsraksts Attēls attēlā Lūdzu, atlasiet vienu no šīm opcijām. sākt atskaņošanu +nodaļa Pašlaik notiek apraide uz jūsu TV galvenais Videoklipa atskaņošanas kļūda @@ -191,6 +232,7 @@ Cits... brīdinājums , sākot no: +vēre šūna izvēlne \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ml.xtb b/chromium/content/app/strings/translations/content_strings_ml.xtb index 66589c22bc4..5b07795a2db 100644 --- a/chromium/content/app/strings/translations/content_strings_ml.xtb +++ b/chromium/content/app/strings/translations/content_strings_ml.xtb @@ -10,28 +10,34 @@ കോംപ്ലിമെന്ററി മാർക്യൂ സ്ലൈഡർ +അന്തിമ കുറിപ്പ് ഡയലോഗ് +ക്രെഡിറ്റുകൾ ലേഖനം ബാനർ പ്രദേശം ഈ മാസം ഫയലുകൾ തിരഞ്ഞെടുക്കുക +പുൾക്വോട്ട് ശൂന്യമായിടാതെ, ഇമെയിൽ വിലാസം നൽകുക. മറ്റുള്ളവ... അലേർട്ട്_ഡയലോഗ് ട്രീ ഗ്രിഡ് AM/PM വിദൂര ഉപകരണത്തിൽ പ്ലേ ചെയ്യുക +ഉദാഹരണം തീയതി നില മൂല്യം എന്നതോ അതിനുമുമ്പുള്ള തീയതിയോ ആയിരിക്കണം. മിററിംഗിലേക്ക് മാറി '', '' എന്നതിൽ തെറ്റായ സ്ഥാനത്താണ് ഉപയോഗിച്ചിരിക്കുന്നത്. ഇന്ന് +പേജ് ലിസ്റ്റ് ഈ വാചകത്തെ അല്ലെങ്കില്‍ അതില്‍‌ക്കുറവ് പ്രതീകങ്ങളായി ദയവായി കുറയ്ക്കുക (നിങ്ങള്‍ നിലവില്‍ പ്രതീകങ്ങള്‍ ഉപയോഗിക്കുകയാണ്). ആഴ്‌ച , ട്രാക്ക് ചെക്ക്‌ബോക്‌സ് +പേജ് ബ്രേക്ക് തീയതിയും സമയ പിക്കറും ദിവസം ദയവായി ഈ ഫീല്‍ഡ് പൂരിപ്പിക്കുക. @@ -40,6 +46,7 @@ പ്ലേ ചെയ്യുക സാധുവായ ഒരു മൂല്യം നൽകുക. ഏറ്റവുമടുത്ത സാധുവായ മൂല്യം ആണ്. സമര്‍പ്പിക്കൂ +ഗ്രാഫിക്‌സ് ഒബ്‌ജക്‌റ്റ് അടിക്കുറിപ്പ് ഓട്ടോഫില്‍ ഓഡിയോ @@ -70,30 +77,41 @@ ഈ ടെക്‌സ്റ്റിന്റെ ദൈർഘ്യം അല്ലെങ്കിൽ അതിൽക്കൂടുതൽ പ്രതീകങ്ങൾ നൽകി കൂട്ടുക. (നിങ്ങൾ നിലവിൽ പ്രതീകങ്ങളാണ് ഉപയോഗിക്കുന്നത്). '' എന്നതിനുശേഷം വരുന്ന ഒരു ഭാഗത്തിൽ '' ചിഹ്നം ഉണ്ടാകരുത്. പൂർണ്ണ സ്‌ക്രീനിൽ നിന്ന് പുറത്തുകടക്കുക +ഉപസംഹാരം അപ്ലിക്കേഷൻ +ആമുഖം സ്‌ക്രോൾ ബാർ മില്ലിസെക്കൻഡ് ഗ്രാഫിക് +മുഖവുര മുൻ മാസം കാണിക്കുക വർണ്ണ പിക്കർ മെനു ബാർ ഉള്ളടക്ക വിവരം സാധുവായ മൂല്യം നൽകുക. സാധുവായ ഏറ്റവുമടുത്ത രണ്ട് മൂല്യങ്ങൾ , എന്നിവയാണ്. +ചോദ്യോത്തരം മെനു ബട്ടൺ +പദസഞ്ചയ റെഫറൻസ് വിശദാംശങ്ങൾ‌ ഫോം ട്രീ '' എന്നതിനുശേഷം വരുന്ന ഭാഗത്തിൽ '' ചിഹ്നം ഉണ്ടാകരുത്. +അനുബന്ധം +കവർ ടാബ് പാനൽ വീഡിയോ search ഒരു നമ്പർ നൽകുക. +ഗ്രന്ഥസൂചി എൻട്രി തിരഞ്ഞെടുത്തു അൺമ്യൂട്ടുചെയ്യുക '' എന്നതിനുശേഷം ഒരു ഭാഗം നൽകുക.'' അപൂർണ്ണമാണ്. 'പ്രോഗ്രസ്' ഇൻഡിക്കേറ്റർ +ശിലാലേഖ അടുത്ത മാസം കാണിക്കുക വരി ശീർഷകം +ഉപസംഹാരം +'ചിത്രത്തിനുള്ളിലെ ചിത്രം' മോഡിലാണ് ഈ വീഡിയോ പ്ലേ ചേയ്യുന്നത് മൂല്യം എന്നതില്‍ കൂടുതലോ സമമോ ആയിരിക്കണം. PB '' എന്നതിനുശേഷം ഒരു ഭാഗം നൽകുക.'' അപൂർണ്ണമാണ്. @@ -106,9 +124,11 @@ ശബ്‌ദമുള്ളതാക്കുക സമയം 10 സെക്കൻഡ് ഒഴിവാക്കാൻ ഇടത്തോട്ടോ വലത്തോട്ടോ രണ്ടുതവണ ടാപ്പ് ചെയ്യുക +സംക്ഷേപം പ്ലേബാക്ക് താൽക്കാലികമായി നിർത്തുക ബട്ടൺ കൂടുതൽ ഓപ്‌ഷനുകൾ +ആമുഖം മ്യൂട്ടുചെയ്യുക ഓഫാക്കുക പൂര്‍‌ണ്ണസ്‌ക്രീന്‍ @@ -119,12 +139,15 @@ വര്‍ഷം ലിസ്റ്റ് ബോക്‌സ് ഓഡിയോ ട്രാക്ക് ശബ്‌ദമുള്ളതാക്കുക +ഗ്രന്ഥസൂചി മീറ്റർ +പിൻ കുറിപ്പ് +പദസഞ്ചയം മാസം തിരഞ്ഞെടുക്കുന്ന പാനൽ കാണിക്കുക ഓഡിയോ സമയ സ്‌ക്രബർ +കടപ്പാട് സമയ പിക്കർ ഓപ്ഷനുകൾ -ഇപ്പോൾ ചിത്രത്തിനുള്ളിലെ ചിത്രം മോഡിൽ പൂർണ്ണ സ്‌ക്രീനിലേക്ക് പോവുക ഫീഡ് ലോഗ് @@ -135,22 +158,31 @@ ടൂൾടിപ്പ് പ്ലേചെയ്യുക പ്ലഗിൻ ലോഡുചെയ്യാനായില്ല. +ഗ്രന്ഥസൂചി റെഫറൻസ് നിശബ്‌ദമാക്കുക അടച്ച അടിക്കുറിപ്പുകൾ മറയ്‌ക്കുക ഡയറക്‌ടറി +അച്ചടിമുദ്ര ബ്ലോക്ക്‌കോട്ട് മീഡിയ ഡൗൺലോഡുചെയ്യുക ഈ ടെക്‌സ്റ്റിന്റെ ദൈർഘ്യം പ്രതീകങ്ങളോ അതിൽ കൂടുതലോ ആയി വർദ്ധിപ്പിക്കുക (നിങ്ങൾ നിലവിൽ ഒരു പ്രതീകം മാത്രമേ ഉപയോഗിക്കുന്നുള്ളൂ). അഭ്യര്‍ത്ഥി ച്ചഫോര്‍മാറ്റ് ദയവായി പൊരുത്തപ്പെടുത്തുക. +പീഠിക കഴിഞ്ഞ സമയം പട്ടികയിലെ ഒരു ഇനം ദയവായി തിരഞ്ഞെടുക്കുക കോളം പേര് +ക്രെഡിറ്റ് ഫയല്‍ തിരഞ്ഞെടുക്കൂ മറ്റുള്ളവ... +അന്തിമ കുറിപ്പുകൾ TB ടാബ് ഡൗൺലോഡുചെയ്യുക +ബാക്ക്‌ ലിങ്ക് +നുറുങ്ങ് +ഗ്രാഫിക്‌സ് ഡോക്യുമെന്റ് മണിക്കൂര്‍‌ +സൂചിക അവശേഷിക്കുന്ന സമയം അടച്ച അടിക്കുറിപ്പുകൾ പ്രദർശിപ്പിക്കുന്നത് നിർത്തുക HTML ഉള്ളടക്കം @@ -160,24 +192,33 @@ സ്‌പ്ലിറ്റർ ഒരു ഫയലും തിരഞ്ഞെടുത്തിട്ടില്ല തിരയൽ ടെക്‌സ്റ്റ് ഫീൽഡ് +അറിയിപ്പ് +കുറിപ്പ് റെഫറൻസ് അടച്ച അടിക്കുറിപ്പുകൾ പ്രദർശിപ്പിക്കുന്നത് ആരംഭിക്കുക ഈ ആഴ്‌ച പൂർണ്ണ സ്‌ക്രീൻ മോഡിൽ മൂവി പ്ലേ ചെയ്യുക വർഷം അടിക്കുറിപ്പുകൾ അസാധുവായ മൂല്യം. +ഭാഗം ടൂൾബാർ ഫയലുകള്‍ ദയവായി ഒരു ഫയല്‍ തരം തിരഞ്ഞെടുക്കുക. റേഡിയോ ബട്ടൺ +സമർപ്പണം +സംശോധന പട്ടിക മിനിറ്റ് മീഡിയ നിയന്ത്രണം +ഗ്രാഫിക്സ് ചിഹ്നം നിർവചന ലിസ്റ്റ് തലക്കെട്ട് +ഉള്ളടക്ക പട്ടിക വിദൂര പ്ലേബാക്ക് നിയന്ത്രിക്കുക +ഉപശീർഷകം ചിത്രത്തിനുള്ളിലെ ചിത്രം ഈ ഓപ്ഷനുകളിലൊന്ന് ദയവായി തിരഞ്ഞെടുക്കുക. പ്ലേബാക്ക് ആരംഭിക്കുക +അധ്യായം ഇപ്പോൾ നിങ്ങളുടെ ടിവിയിൽ കാസ്‌റ്റുചെയ്യുന്നു main വീഡിയോ പ്ലേബാക്ക് പിശക് @@ -191,6 +232,7 @@ മറ്റുള്ളവ... അലേർട്ട് , -ന് ആരംഭിക്കുന്നു +അടിക്കുറിപ്പ് സെൽ മെനു \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_mr.xtb b/chromium/content/app/strings/translations/content_strings_mr.xtb index a39b0cef88d..1f29d299817 100644 --- a/chromium/content/app/strings/translations/content_strings_mr.xtb +++ b/chromium/content/app/strings/translations/content_strings_mr.xtb @@ -10,28 +10,34 @@ पूरक marquee स्लायडर +तळटीप संवाद +श्रेय लेख बॅनर प्रदेश या महिन्यात फायली निवडा +पुलकोट कृपया एक रिक्त-नसलेला ईमेल अॅड्रेस एंटर करा. अन्य... सूचना_संवाद ट्री ग्रीड AM/PM दूरस्थ डिव्हाइसवर प्ले करा +उदाहरण dd स्थिती मूल्य किंवा आधीचे असणे आवश्यक आहे. मिररिंगवर स्विच केले '' '' मध्ये चुकीच्या स्थितीवर वापरले आहे. आज +पेज सूची कृपया हा मजकूर वर्ण लहान किंवा कमी करा (आपण सध्या वर्ण वापरत आहात). आठवडा , ट्रॅक चेकबॉक्‍स +पेज ब्रेक तारीख आणि वेळ निवडक दिवस कृपया हे फील्ड भरा. @@ -40,6 +46,7 @@ प्ले करा कृपया एक वैध मूल्य एंटर करा. जवळील वैध मूल्य आहे. सबमिट करा +ग्राफिक्स ऑब्जेक्ट अधोलेख ऑटोफिल ऑडिओ @@ -70,30 +77,41 @@ कृपया हा मजकूर वर्ण किंवा त्यापेक्षा अधिक मोठा करा (आपण सध्‍या वर्ण वापरत आहात). '' मागुन येणार्‍या भागामध्ये '' चिन्ह नसावे. पूर्णस्क्रीनमधून बाहेर पडा +निष्कर्ष अॅप्लिकेशन +उपोद्घात स्क्रोल बार मिलिसेकंद ग्राफिक +प्रस्तावना मागील महिना दर्शवा रंग निवडक मेनू बार सामग्री माहिती कृपया एक वैध मूल्य एंटर करा. दोन जवळील वैध मूल्ये आणि आहेत. +प्रश्नोत्तरे मेनू बटण +शब्दावली संदर्भ तपशील फॉर्म tree '' चे फॉलो करणार्‍या भागामध्ये '' चिन्ह नसावे. +परिशिष्ट +कव्हर टॅब पॅनेल व्हिडिओ search कृपया एक नंबर एंटर करा. +ग्रंथसूची नोंद निवडले सशब्द करा कृपया '' चे फॉलो करणारा भाग एंटर करा. '' अपूर्ण आहे. प्रगती सूचक +बोधवाक्य पुढील महिना दर्शवा पंक्ती शीर्षलेख +उपसंहार +हा व्हिडिओ पिक्चर-इन-पिक्चर मध्ये सुरू आहे मूल्य पेक्षा मोठे किंवा समान असावे. PB कृपया '' मागुन येणारा भाग एंटर करा. '' अपूर्ण आहे. @@ -106,9 +124,11 @@ सशब्द करा time 10 सेकंद वगळण्यासाठी डावीकडे किंवा उजवीकडे दोनदा टॅप करा +अमूर्त प्लेबॅकला विराम द्या बटण अधिक पर्याय +परिचय निःशब्द करा बंद पूर्णस्क्रीन @@ -119,12 +139,15 @@ वर्ष सूची बॉक्स ऑडिओ ट्रॅक सशब्द करा +ग्रंथसूची मीटर +अंतिम शब्द +शब्दावली महिना निवड पॅनेल दर्शवा ऑडिओ वेळ स्क्रबर +श्रेयनिर्देश वेळ निवडक पर्याय -आता चित्रात-चित्र मोडमध्ये पूर्ण स्क्रीन एंटर करा फीड लॉग @@ -135,22 +158,31 @@ टूलटिप प्ले करा प्लगिन लोड करणे शक्य झाले नाही. +ग्रंथसूची संदर्भ नि:शब्द करा बंद मथळा लपवा निर्देशिका +बोधचिन्ह ब्लॉककोट मीडिया डाउनलोड करा कृपया हा मजकूर वर्ण किंवा त्यापेक्षा अधिक मोठा करा (आपण सध्‍या 1 वर्ण वापरत आहात). कृपया विनंती केलेले स्वरूपन जुळवा. +प्रस्तावना लोटलेला अवधी कृपया सूचीमधील आयटम निवडा. स्तंभ शीर्षलेख +श्रेय फाइल निवडा अन्य... +अंतिम नोंदी TB टॅब डाउनलोड करा +मागील लिंक +टीप +ग्राफिक्स दस्तऐवज तास +अनुक्रमणिका शिल्लक वेळ बंद मथळा प्रदर्शित करणे थांबवा HTML सामुग्री @@ -160,24 +192,33 @@ विभाजक कोणतीही फाइल निवडलेली नाही मजकूर फील्ड दर्शवा +सूचना +टीप संदर्भ बंद मथळा प्रदर्शित करणे प्रारंभ करा या आठवड्यात पूर्ण स्क्रीन मोडमध्ये चित्रपट प्ले करा yyyy मथळे अवैध मूल्य. +भाग टूलबार फायली कृपया एखादी फाइल निवडा. रेडिओ बटण +अर्पणपत्रिका +शुद्धिपत्रक मिनिटे माध्यम नियंत्रण +ग्राफिक्स चिन्ह परिभाषा सूची शीर्षलेख +अनुक्रमणिका दूरस्थ प्लेबॅक नियंत्रित करा +उपशीर्षक चित्रात-चित्र कृपया या पर्यायांपैकी एक निवडा. प्लेबॅक आरंभ करा +प्रकरण आता तुमच्या टिव्हीवर कास्ट करत आहे मुख्य व्हिडिओ प्लेबॅक एरर @@ -191,6 +232,7 @@ अन्य... सूचना रोजी प्रारंभ होणारा, +तळटीप सेल मेनू \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ms.xtb b/chromium/content/app/strings/translations/content_strings_ms.xtb index 2ffcf8d1af9..2cad6383b2e 100644 --- a/chromium/content/app/strings/translations/content_strings_ms.xtb +++ b/chromium/content/app/strings/translations/content_strings_ms.xtb @@ -10,28 +10,34 @@ pelengkap marki peluncur +nota hujung dialog +kredit artikel sepanduk wilayah Bulan ini Pilih Fail +petikan tarik keluar Sila masukkan alamat e-mel bukan kosong. Lain-lain... dialog_makluman grid pohon PG / P/M main pada peranti jauh +contoh dd status Nilai mestilah atau lebih awal. Beralih kepada pencerminan '' digunakan pada kedudukan yang salah dalam ''. Hari ini +senarai halaman Sila pendekkan teks ini menjadi aksara atau kurang (anda kini menggunakan aksara). Minggu , Lagu kotak pilihan +pemisah halaman pemilih tarikh dan masa Hari Sila isikan medan ini. @@ -40,6 +46,7 @@ main Sila masukkan nilai yang sah. Nilai sah yang terdekat ialah . Serah +objek grafik pembawah Autoisi audio @@ -70,30 +77,41 @@ Sila panjangkan teks ini kepada aksara atau lebih (anda sedang menggunakan aksara). Bahagian yang diikuti oleh '' tidak boleh mengandungi simbol ''. Keluar dari skrin penuh +kesimpulan aplikasi +prolog bar tatal Milisaat grafik +kata pengantar Tunjukkan bulan sebelumnya pemilih warna bar menu maklumat kandungan Sila masukkan nilai yang sah. Dua nilai sah yang terdekat ialah dan . +Soal Jawab butang menu +rujukan glosari Butiran borang pohon Bahagian selepas '' tidak boleh mengandungi simbol ''. +lampiran +muka depan panel tab video search Sila masukkan nombor. +masukan bibliografi dipilih Nyahredam Sila masukkan bahagian selepas ''. '' tidak lengkap. penunjuk kemajuan +epigraf Tunjukkan bulan seterusnya pengepala baris +epilog +Video ini dimainkan dalam Gambar dalam Gambar Nilai mesti lebih besar daripada atau sama dengan . PB Sila masukkan bahagian diikuti oleh ''. '' tidak lengkap. @@ -106,9 +124,11 @@ nyahredam masa Ketik dua kali ke kiri atau kanan untuk melangkau 10s +abstrak jeda main balik butang lagi pilihan +pengenalan Redam Dimatikan Skrin penuh @@ -119,12 +139,15 @@ Tahun kotak senarai nyahredam runut audio +bibliografi meter +kata hujungan +glosari Tunjukkan panel pilihan bulan pembersih masa audio +perakuan pemilih masa Pilihan -Sekarang dalam mod Gambar dalam Gambar memasuki skrin penuh suapan log @@ -135,22 +158,31 @@ tip alat Mainkan Tidak dapat memuatkan pemalam. +rujukan bibliografi redam sembunyikan kapsyen tertutup direktori +kolofon petikan blok muat turun media Sila panjangkan teks ini kepada aksara atau lebih (anda sedang menggunakan 1 aksara). Sila padankan dengan format yang diminta. +kata penghantar masa berlalu Sila pilih item dalam senarai. pengepala lajur +kredit Pilih Fail Lain-lain... +nota hujung TB tab Muat Turun +pautan balik +petua +dokumen grafik Jam +indeks masa yang tinggal berhenti memaparkan kapsyen tertutup Kandungan HTML @@ -160,24 +192,33 @@ pemisah Tiada fail dipilih medan teks carian +notis +rujukan nota mula memaparkan kapsyen tertutup Minggu ini mainkan filem dalam mod skrin penuh yyyy Kapsyen Nilai tidak sah. +bahagian bar alat fail Sila pilih fail. butang radio +dedikasi +ralat Minit kawalan media +simbol grafik senarai takrif tajuk +isi kandungan kawal main balik jauh +sari kata Gambar dalam Gambar Sila pilih salah satu pilihan ini. mulakan main balik +bab Menghantar ke TV anda sekarang utama Ralat main balik video @@ -191,6 +232,7 @@ Lain-lain... makluman , bermula pada +nota kaki sel menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_nl.xtb b/chromium/content/app/strings/translations/content_strings_nl.xtb index 256f9a270a2..96d17a835dc 100644 --- a/chromium/content/app/strings/translations/content_strings_nl.xtb +++ b/chromium/content/app/strings/translations/content_strings_nl.xtb @@ -10,28 +10,34 @@ aanvullend marquee schuifregelaar +eindnoot dialoogvenster +bronvermelding artikel banner regio Deze maand Bestanden kiezen +pullquote Geef een e-mailadres op. Anders... dialoogvenster voor meldingen boomstructuur a.m./p.m. afspelen op extern apparaat +voorbeeld dd status Waarde moet of eerder zijn. Overgeschakeld naar mirroring '' wordt op een verkeerde positie gebruikt in ''. Vandaag +paginalijst Kort deze tekst in tot tekens of minder (je gebruikt momenteel tekens). Week , Track selectievakje +pagina-einde datum- en tijdkiezer Dag Vul dit veld in. @@ -40,6 +46,7 @@ afspelen Geef een geldige waarde op. De dichtstbijzijnde geldige waarde is . Verzenden +grafisch object voettekst Automatisch aanvullen audio @@ -70,30 +77,41 @@ Breid deze tekst uit tot tekens of meer (je gebruikt momenteel tekens). Het naamgedeelte vóór '' mag niet het teken '' bevatten. Volledig scherm sluiten +conclusie app +proloog scrollbar Milliseconden afbeelding +voorwoord Vorige maand weergeven kleurkiezer menubalk informatie over content Geef een geldige waarde op. De twee dichtstbijzijnde geldige waarden zijn en . +Q&A menuknop +woordenlijstreferentie Details formulier structuur Het adresgedeelte na '' mag niet het teken '' bevatten. +bijlage +omslag deelvenster met tabblad video zoeken Voer een getal in. +bibliografievermelding geselecteerd Dempen opheffen Geef een adresgedeelte op na ''. '' is onvolledig. voortgangsindicator +epigraaf Volgende maand weergeven rijkop +epiloog +Deze video wordt in de scherm-in-scherm-modus afgespeeld Waarde moet groter dan of gelijk zijn aan . PB Geef een naamgedeelte op, gevolgd door ''. '' is onvolledig. @@ -106,9 +124,11 @@ dempen opheffen tijd Dubbeltik links of rechts om 10 seconden over te slaan +uittreksel afspelen onderbreken knop meer opties +inleiding Dempen Uit Volledig scherm @@ -119,12 +139,15 @@ Jaar keuzelijst dempen van audiotrack opheffen +bibliografie meter +nawoord +woordenlijst Deelvenster voor maandselectie weergeven scrollbar met audiotijd +dankwoord tijdkiezer Opties -Nu in scherm-in-scherm-modus volledig scherm openen feed logboek @@ -135,22 +158,31 @@ knopinfo Spelen Kan plug-in niet laden. +bibliografiereferentie dempen ondertiteling verbergen directory +colofon blok met geciteerde tekst media downloaden Breid deze tekst uit tot tekens of meer (je gebruikt momenteel één teken). Zorg dat de indeling voldoet aan de gevraagde indeling. +voorwoord verstreken tijd Selecteer een item in de lijst. kolomkop +bronvermelding Bestand kiezen Anders... +eindnoten TB tab Downloaden +back link +tip +grafisch document Uur +index resterende tijd het weergeven van ondertiteling stoppen HTML-content @@ -160,24 +192,33 @@ splitser Geen bestand gekozen zoektekstveld +kennisgeving +opmerkingsreferentie het weergeven van ondertiteling starten Deze week film afspelen op volledig scherm jjjj Ondertiteling Ongeldige waarde. +deel werkbalk bestanden Selecteer een bestand. keuzerondje +opgedragen aan +drukfouten Minuten mediacontrole +grafisch symbool definitielijst kop +inhoudsopgave afspelen bedienen op afstand +ondertitel Scherm-in-scherm Selecteer een van deze opties. afspelen starten +hoofdstuk Wordt nu gecast naar je tv hoofd Fout bij afspelen video @@ -191,6 +232,7 @@ Anders... melding , die begint op +voetnoot cel menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_no.xtb b/chromium/content/app/strings/translations/content_strings_no.xtb index 46de87f7862..b845d307239 100644 --- a/chromium/content/app/strings/translations/content_strings_no.xtb +++ b/chromium/content/app/strings/translations/content_strings_no.xtb @@ -10,28 +10,34 @@ komplementær marquee glidebryter +sluttnote dialog +anerkjennelser article banner område Denne måneden Velg filer +sitat Skriv inn en ikke-tom e-postadresse. Andre varseldialog trerutenett AM/PM spill på ekstern enhet +eksempel dd status Verdien må være eller før. Byttet til speiling «» er brukt på feil sted i «». I dag +sideliste Forkort denne teksten til tegn eller færre (for øyeblikket bruker du tegn). Uke , Spor avmerkingsboks +sideskift dato- og klokkeslettvelger Dag Vennligst fyll ut dette feltet. @@ -40,6 +46,7 @@ spill av Skriv inn en gyldig verdi. Den nærmeste, gyldige verdien er . Send +grafikkobjekt fotnote Autofyll lyd @@ -70,30 +77,41 @@ Du må forlenge denne teksten til tegn eller mer (for øyeblikket bruker du tegn). En del etterfulgt av «» kan ikke inneholde symbolet «». Avslutt fullskjermmodus +konklusjon program +prolog rullefelt Millisekunder grafikk +forord Se forrige måned fargevelger menyrad innholdsinformasjon Skriv inn en gyldig verdi. De to nærmeste, gyldige verdiene er og . +Spørsmål og svar menyknapp +ordlistereferanse Detaljer skjema tree En del etterfulgt av «» kan ikke inneholde symbolet «». +vedlegg +omslag fanepanel video søk Skriv inn et tall. +bibliografioppføring er valgt Slå på lyden Skriv inn en del etterfulgt av «». «» er ufullstendig. fremdriftsindikator +epigraf Se neste måned radoverskrift +epilog +Denne videoen spilles av i bilde-i-bilde Verdien må være større enn eller lik . PB Skriv inn en del etterfulgt av «». «» er ufullstendig. @@ -106,9 +124,11 @@ slå på lyden time Dobbelttrykk på venstre eller høyre side for å hoppe 10 sekunder +abstrakt stopp avspillingen midlertidig knapp flere alternativer +innledning Kutt lyden Av Fullskjerm @@ -119,12 +139,15 @@ År listefelt slå på lydsporet +bibliografi måler +etterord +ordliste Se panelet for valg av måned lydtidslinje +omtale klokkeslettvelger Alternativer -Nå i bilde-i-bilde-modus gå til fullskjermmodus feed logg @@ -135,22 +158,31 @@ verktøytips Spill av Kunne ikke laste inn programtillegget. +bibliografireferanse kutt lyd skjul teksting katalog +kolofon blokksitat last ned medier Øk lengden på denne teksten med minst tegn (du bruker for øyeblikket 1 tegn). Sørg for samsvar med det forespurte formatet. +forord brukt tid Velg en artikkel i listen. kolonneoverskrift +anerkjennelse Velg fil Andre +sluttnoter TB tab Last ned +tilbakelink +tips +grafikkdokument Timer +stikkordregister tid som gjenstår slå av tekstingen HTML-innhold @@ -160,24 +192,33 @@ vindusdeler Ingen fil valgt søketekstfelt +merknad +kommentarreferanse slå på tekstingen Denne uken spill av filmen i fullskjermmodus åååå Teksting Ugyldig verdi +del verktøyrad filer Velg en fil. alternativknapp +dedikasjon +errata Minutter mediekontroll +grafikksymbol definisjonsliste overskrift +innholdsfortegnelse kontrollér ekstern avspilling +undertittel Bilde-i-bilde Velg ett av følgende alternativer. start avspillingen +kapittel Caster til TV-en nå main Feil ved videoavspilling @@ -191,6 +232,7 @@ Andre varsel , med start +fotnote celle meny \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_pl.xtb b/chromium/content/app/strings/translations/content_strings_pl.xtb index 20bcb1ef174..acac3373159 100644 --- a/chromium/content/app/strings/translations/content_strings_pl.xtb +++ b/chromium/content/app/strings/translations/content_strings_pl.xtb @@ -10,28 +10,34 @@ pomocniczy marquee suwak +przypis końcowy okno dialogowe +autorzy article baner region W tym miesiącu Wybierz pliki +cytat Podaj adres e-mail. Inny... alert_dialog siatka drzewa rano/po południu odtwarzanie na urządzeniu zdalnym +przykład dd stan Musisz podać wartość lub wcześniejszą. Przełączono na odbicie lustrzane ” występuje w niewłaściwym miejscu w „”. Dzisiaj +lista stron Skróć ten tekst do maksymalnie znaków (w tej chwili korzystasz z znaków). Tydzień , Utwór pole wyboru +podział strony selektor daty i godziny Dzień Wypełnij to pole. @@ -40,6 +46,7 @@ odtwórz Podaj prawidłową wartość. Najbliższa prawidłowa wartość to . Prześlij +obiekt graficzny stopka Autouzupełnianie dźwięk @@ -70,30 +77,41 @@ Wydłuż ten tekst przynajmniej do znaków (teraz używasz znaków). Część przed znakiem „” nie może zawierać symbolu „”. Zamknij pełny ekran +podsumowanie aplikacja +prolog pasek przewijania Milisekundy grafika +przedmowa Pokaż poprzedni miesiąc selektor kolorów pasek menu informacje o zawartości Podaj prawidłową wartość. Dwie najbliższe prawidłowe wartości to i . +Pytania i odpowiedzi przycisk menu +odniesienie do słownika Szczegóły formularz tree Część po znaku „” nie może zawierać symbolu „”. +dodatek +okładka panel karty film search Wpisz liczbę. +pozycja bibliograficzna Wybrano: Wyłącz wyciszenie Podaj część po znaku „”. Adres „” jest niepełny. wskaźnik postępu +epigraf Pokaż przyszły miesiąc nagłówek wiersza +epilog +Ten film jest odtwarzany w trybie obrazu w obrazie Wartość nie może być mniejsza niż . PB Podaj część przed znakiem „”. Adres „” jest niepełny. @@ -106,9 +124,11 @@ wyłącz wyciszenie time Kliknij dwukrotnie z lewej lub prawej, by przewinąć o 10 s +streszczenie wstrzymaj odtwarzanie przycisk więcej opcji +wprowadzenie Wycisz Wyłączone Pełny ekran @@ -119,12 +139,15 @@ Rok pole listy wyłącz wyciszenie ścieżki dźwiękowej +bibliografia miernik +posłowie +słownik Pokaż panel wyboru miesiąca pasek czasu odtwarzania dźwięku +podziękowania selektor godziny Opcje -Teraz pracuję w trybie Obraz w obrazie przejdź do pełnego ekranu kanał dziennik @@ -135,22 +158,31 @@ etykietka Odtwórz Nie można załadować wtyczki. +odniesienie bibliograficzne wyciszenie ukryj napisy katalog +kolofon cytat blokowy pobierz multimedia Wydłuż ten tekst co najmniej do  znaków (teraz używasz jednego znaku). Podaj wartość w wymaganym formacie. +wstęp upłynęło czasu Wybierz element z listy. nagłówek kolumny +twórcy Wybierz plik Inny... +przypisy końcowe TB tab Pobierz +link zwrotny +Wskazówka +dokument graficzny Godziny +indeks pozostały czas przestań pokazywać napisy Treść HTML @@ -160,24 +192,33 @@ podział Nie wybrano pliku pole tekstowe wyszukiwania +uwaga +odsyłacz zacznij pokazywać napisy W tym tygodniu odtwórz film w trybie pełnoekranowym rrrr Napisy Nieprawidłowa wartość. +część pasek narzędzi Liczba plików: Wybierz plik. przycisk opcji +dedykacja +errata Minuty sterowanie multimediami +symbol graficzny lista definicji nagłówek +spis treści sterowanie zdalnym odtwarzaniem +podtytuł Obraz w obrazie Wybierz jedną z opcji. rozpocznij odtwarzanie +rozdział Przesyłam na telewizor główny Błąd odtwarzania filmu @@ -191,6 +232,7 @@ Inny... alert , początek +przypis komórka menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_pt-BR.xtb b/chromium/content/app/strings/translations/content_strings_pt-BR.xtb index 1da5a5819be..12943615149 100644 --- a/chromium/content/app/strings/translations/content_strings_pt-BR.xtb +++ b/chromium/content/app/strings/translations/content_strings_pt-BR.xtb @@ -10,28 +10,34 @@ complementar marquee controle deslizante +nota final caixa de diálogo +créditos article banner região Este mês Escolher arquivos +citação Insira um endereço de e-mail que não esteja vazio. Outras... alert_dialog grade de árvore AM/PM reproduzir em dispositivo remoto +exemplo dd status O valor deve ser ou anterior. Alterado para espelhamento "" está sendo usado em uma posição incorreta em "". Hoje +lista de páginas Reduza este texto para caracteres ou menos (você está usando caracteres). Semana , Faixa caixa de seleção +quebra de página seletor de data e hora Dia Preencha este campo. @@ -40,6 +46,7 @@ reproduzir Insira um valor válido. O valor válido mais próximo é . Enviar +objeto gráfico rodapé Preenchimento automático áudio @@ -70,30 +77,41 @@ Aumente este texto para caracteres ou mais. No momento, você está usando caracteres). Uma parte seguida por "" não deve conter o símbolo "". Sair da tela cheia +conclusão app +prólogo barra de rolagem Milésimos de segundo gráfico +prefácio Mostrar mês anterior Seletor de cores barra de menu informações sobre o conteúdo Insira um valor válido. Os dois valores válidos mais próximos são e . +Perguntas e respostas botão de menu +referência de glossário Detalhes formulário tree A parte depois de "" não deve conter o símbolo "". +apêndice +capa painel da guia vídeo pesquisar Insira um número. +entrada bibliográfica selecionados Ativar som Insira uma parte depois de "". "" está incompleto. indicador de progresso +epígrafe Mostrar próximo mês cabeçalho de linha +epílogo +Este vídeo está sendo reproduzido em picture-in-picture O valor deve ser maior ou igual a . PB Insira uma parte seguida por "". "" está incompleto. @@ -106,9 +124,11 @@ ativar som time Toque duas vezes na esquerda ou direita para pular 10 s +resumo pausar reprodução botão mais opções +introdução Desativar som Desativado Tela inteira @@ -119,12 +139,15 @@ Ano caixa de listagem ativar som da faixa de áudio +bibliografia medidor +posfácio +glossário Mostrar painel de seleção de mês barra de progressão do áudio +agradecimentos seletor de hora Opções -Agora no modo picture-in-picture entrar no modo tela cheia feed log @@ -135,22 +158,31 @@ dica Reproduzir Não foi possível carregar o plug-in. +referência bibliográfica sem som ocultar legendas ocultas diretório +colofão bloco de texto fazer o download da mídia Aumente esse texto para caracteres ou mais. No momento, você está usando 1 caractere. É preciso que o formato corresponda ao exigido. +prefácio tempo decorrido Selecione um item da lista. cabeçalho da coluna +crédito Escolher arquivo Outras... +notas finais TB tab Fazer o download +backlink +dica +documento gráfico Horas +índice tempo restante parar de exibir legendas ocultas Conteúdo HTML @@ -160,24 +192,33 @@ divisor Nenhum arquivo selecionado campo de texto da pesquisa +aviso +referência de nota começar a exibir legendas ocultas Esta semana reproduzir filme em tela cheia aaaa Legendas Valor inválido. +parte barra de ferramentas arquivos Selecione um arquivo. botão de opção +dedicatória +errata Minutos controle de mídia +símbolo gráfico lista de definições cabeçalho +sumário controlar reprodução remota +subtítulo Picture-in-picture Selecione uma das opções. iniciar reprodução +capítulo Reproduzindo agora na sua TV main Erro na reprodução do vídeo @@ -191,6 +232,7 @@ Outras... alerta , que começa em +nota de rodapé célula menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_pt-PT.xtb b/chromium/content/app/strings/translations/content_strings_pt-PT.xtb index 9b0712fc428..2c22789449d 100644 --- a/chromium/content/app/strings/translations/content_strings_pt-PT.xtb +++ b/chromium/content/app/strings/translations/content_strings_pt-PT.xtb @@ -10,28 +10,34 @@ complementar painel rolante controlo de deslize +nota final caixa de diálogo +créditos article banner região Este mês Escolher Ficheiros +trecho em destaque Introduza um endereço de email que não esteja vazio. Outra... alert_dialog grelha de árvore AM/PM reproduzir no dispositivo remoto +exemplo dd estado O valor tem de ser ou anterior. Alterado para espelhamento. "" está a ser utilizado numa posição errada em "". Hoje +lista de páginas Encurte este texto para caracteres ou menos (está atualmente a utilizar caracteres). Semana , de Faixa caixa de verificação +quebra de página selecionador de data e hora Dia Preencha este campo. @@ -40,6 +46,7 @@ reproduzir Introduza um valor válido. O valor válido mais próximo é . Submeter +objeto de gráficos rodapé Preenchimento automático áudio @@ -70,30 +77,41 @@ Aumente este texto para carateres ou mais (está atualmente a utilizar carateres). Uma parte seguida de "" não deve conter o símbolo "". Sair do modo de ecrã inteiro +conclusão aplicação +prólogo barra de deslocamento Milissegundos gráfico +prefácio Mostrar mês anterior seletor de cores barra de menu informações do conteúdo Introduza um valor válido. Os dois valores válidos mais próximos são e . +Perg e Resp botão de menu +referência do glossário Detalhes formulário árvore Uma parte a seguir a "" não deve conter o símbolo "". +apêndice +capa painel de separadores vídeo search Introduza um número. +entrada da bibliografia selecionados Reativar o som Introduza uma parte a seguir a "". "" está incompleto. indicador de progresso +epígrafe Mostrar mês seguinte cabeçalho da linha +epílogo +Este vídeo está a ser reproduzido no modo ecrã no ecrã O valor tem de ser superior ou igual a . PB Introduza uma parte seguida de "". "" está incompleto. @@ -106,9 +124,11 @@ reativar som hora Toque duas vezes à esquerda ou à direita para avançar 10 seg. +resumo interromper a reprodução botão mais opções +introdução Desativar som Desativado Ecrã inteiro @@ -119,12 +139,15 @@ Ano caixa de lista reativar a faixa de áudio +bibliografia contador +posfácio +glossário Mostrar painel de seleção do mês controlo de arrasto do tempo do áudio +agradecimentos selecionador de hora Opções -Agora no modo ecrã no ecrã entrar no modo de ecrã inteiro feed log @@ -135,22 +158,31 @@ sugestão Reproduzir Não foi possível carregar o plug-in. +referência bibliográfica desativar som esconder legendas ocultas diretório +colofão blockquote transferir multimédia Aumente este texto para ou mais carateres (atualmente, está a utilizar 1 caráter). Faça corresponder o formato pedido. -tempo decorrido +prefácio +timelapse Seleccione um item na lista. cabeçalho da coluna +crédito Escolher ficheiro Outra... +notas finais TB tab Transferir +backlink +sugestão +documento de gráficos Horas +índice tempo restante parar de apresentar legendas ocultas Conteúdo HTML @@ -160,24 +192,33 @@ divisor Nenhum ficheiro selecionado campo de texto de pesquisa +aviso +referência de nota apresentar legendas ocultas Esta semana reproduzir o filme no modo de ecrã inteiro aaaa Legendas Valor inválido. +parte barra de ferramentas ficheiros Seleccione um ficheiro. botão de opção +dedicatória +errata Minutos controlo multimédia +símbolo de gráficos lista de definições cabeçalho +índice controlar a reprodução remota +legenda Ecrã no ecrã Seleccione uma destas opções. iniciar reprodução +capítulo A transmitir na sua TV… main Erro de reprodução de vídeo. @@ -191,6 +232,7 @@ Outra... alerta , a partir de +nota de rodapé célula menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ro.xtb b/chromium/content/app/strings/translations/content_strings_ro.xtb index 59eedb6271a..4a14856b246 100644 --- a/chromium/content/app/strings/translations/content_strings_ro.xtb +++ b/chromium/content/app/strings/translations/content_strings_ro.xtb @@ -10,28 +10,34 @@ complementar text derulant glisor +notă de final dialog +mulțumiri articol banner regiune Luna aceasta Alege fișierele +citat Introduceți o adresă de e-mail concretă. Altele... dialog de alertă grilă arbore AM/PM redă pe dispozitiv la distanță +exemplu zz stare Valoarea pentru dată/oră trebuie să fie sau una anterioară. S-a comutat la oglindire Semnul „” apare poziționat greșit în „”. Astăzi +listă de pagini Micșorează acest text la cel mult caractere (în prezent utilizezi caractere). Săptămâna , Melodia casetă de selectare +sfârșit de pagină selector pentru dată și oră Zi Completează acest câmp. @@ -40,6 +46,7 @@ redați Introduceți o valoare validă. Cea mai apropiată valoare validă este . Trimite +obiect grafic notă de subsol Completare automată audio @@ -70,30 +77,41 @@ Mărește acest text la cel puțin caractere (în prezent folosești caractere). Valoarea urmată de semnul „” nu trebuie să conțină simbolul „”. Ieși din ecranul complet +concluzie aplicație +prolog bară de derulare Milisecunde element grafic +cuvânt înainte Afișează luna anterioară selector de culoare bară de meniu informații privind conținutul Introduceți o valoare validă. Cele mai apropiate valori valide sunt și . +Întrebări și răspunsuri buton de meniu +trimitere la glosar Detalii formular arbore Valoarea care urmează după semnul „” nu trebuie să conțină simbolul „”. +anexă +copertă panou de file video căutați Introduceți un număr. +intrare bibliografică selectate Activează sunetul Introduceți o valoare după semnul „”. Adresa „” nu este completă. indicator de progres +epigraf Afișează luna următoare antet rând +epilog +Videoclipul se redă în modul imagine în imagine Valoarea trebuie să fie mai mare sau egală cu . PB Introduceți o valoare urmată de semnul „”. Adresa „” nu este completă. @@ -106,9 +124,11 @@ activați sunetul oră Atinge de două ori spre stânga sau spre dreapta pentru a derula cu 10 secunde +rezumat întrerupeți redarea buton mai multe opțiuni +introducere Dezactivează sunetul Dezactivat Ecran complet @@ -119,12 +139,15 @@ An casetă listă activați sunetul înregistrării audio +bibliografie instrument de măsurare +postfață +glosar Afișează panoul de selectare a lunii glisor redare audio +lista colaboratorilor selector oră Opțiuni -Ești acum în modul picture-in-picture deschideți în ecran complet feed jurnal @@ -135,22 +158,31 @@ balon explicativ Redă Nu s-a putut încărca pluginul. +referință bibliografică dezactivați sunetul ascundeți subtitrările director +colofon blockquote descarcă conținut media Mărește acest text la cel puțin caractere (în prezent folosești 1 caracter). Respectă formatul solicitat. +prefață timp scurs Selectează un articol din listă. antet coloană +mulțumire Alege fișierul Altele... +note de final TB filă Descarcă +backlink +sfat +document grafic Ore +index timp rămas nu mai afișați subtitrările Conținut HTML @@ -160,24 +192,33 @@ element de divizare Nu ai ales niciun fișier câmp pentru căutarea textului +anunț +trimitere la notă începeți să afișați subtitrările Săptămâna aceasta redați filmul în modul ecran complet aaaa Subtitrări Valoare nevalidă. +parte bară de instrumente fișiere Selectează un fișier. buton radio +dedicație +erată Minute comandă media +simbol grafic listă de definiții antet +cuprins controlează redarea la distanță +subtitlu Picture-in-Picture Selectează una dintre aceste opțiuni. începeți redarea +capitol Acum se proiectează pe televizor principal Eroare de redare video @@ -191,6 +232,7 @@ Altele... alertă , începând de pe +notă de subsol celulă meniu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ru.xtb b/chromium/content/app/strings/translations/content_strings_ru.xtb index 4638df24a13..c1ba6cbaaf8 100644 --- a/chromium/content/app/strings/translations/content_strings_ru.xtb +++ b/chromium/content/app/strings/translations/content_strings_ru.xtb @@ -10,28 +10,34 @@ дополнительно marquee ползунок +концевая сноска диалоговое окно +выходные сведения article баннер регион В этом месяце Выбрать файлы +цитата в виде врезки Введите адрес электронной почты. Другое… диалоговое окно оповещения сетка в виде дерева AM/PM воспроизвести на удаленном устройстве +пример дд статус Максимальное значение должно быть . Включено дублирование экрана Недопустимое положение символа "" в адресе "". Сегодня +список страниц Длина текста не должна превышать симв. (сейчас симв.). Неделя , Трек флажок +разрыв страницы окно выбора даты и времени День Заполните это поле. @@ -40,6 +46,7 @@ воспроизведение Введите допустимое значение. Ближайшее допустимое значение: . Отправить +графический объект нижний колонтитул Автозаполнение аудио @@ -70,30 +77,41 @@ Минимально допустимое количество символов: . Длина текста сейчас: . Часть адреса до символа "" не должна содержать символ "". Обычный режим +заключение приложение +пролог полоса прокрутки Миллисекунды изображение +предисловие Показать предыдущий месяц color picker панель меню сведения о контенте Введите допустимое значение. Ближайшие допустимые значения: и . +Вопросы и ответы кнопка меню +ссылка на словарную статью Подробнее форма tree Часть адреса после символа "" не должна содержать символ "". +приложение +обложка панель вкладок видео поиск Введите число. +запись в библиографии Выбрано: Включить звук Введите часть адреса после символа "". Адрес "" неполный. индикатор хода выполнения +эпиграф Показать следующий месяц заголовок строки +эпилог +Видео воспроизводится в режиме "Картинка в картинке" Значение должно быть больше или равно . ПБ Введите часть адреса до символа "". Адрес "" неполный. @@ -106,9 +124,11 @@ включение звука time Чтобы пропустить 10 секунд, нажмите дважды слева или справа. +аннотация пауза кнопка дополнительные параметры +введение Отключить звук Выкл. Полноэкранный режим @@ -119,12 +139,15 @@ Год список включение звуковой дорожки +библиография счетчик +послесловие +словарь Показать панель выбора месяца полоса воспроизведения +благодарности окно выбора даты Параметры -В режиме "Картинка в картинке" полноэкранный режим лента log @@ -135,22 +158,31 @@ подсказка Воспроизвести Не удалось загрузить плагин +ссылка на библиографию отключить звук скрыть субтитры каталог +выпускные данные цитата скачать файл Текст должен быть не короче  симв. Длина текста сейчас: 1 символ. Введите данные в указанном формате. +вступление прошедшее время Выберите один из пунктов списка. заголовок столбца +упоминание Выберите файл Другое… +концевые сноски ТБ tab Скачать +обратная ссылка +совет +графический документ Часы +указатель оставшееся время скрыть субтитры HTML-содержание @@ -160,24 +192,33 @@ разделитель Файл не выбран поле поиска +примечание +ссылка на примечание показать субтитры На этой неделе воспроизведение в полноэкранном режиме гггг Титры Недопустимые данные. +часть панель инструментов Число файлов: Выберите файл. переключатель +посвящение +список опечаток Минуты управление мультимедиа +графический символ список описаний заголовок +оглавление управлять воспроизведением на удаленных устройствах +подзаголовок Картинка в картинке Выберите один из вариантов. начать воспроизведение +глава Транслируется на телевизор main Ошибка при воспроизведении видео @@ -191,6 +232,7 @@ Другое… оповещение , начинается +сноска ячейка меню \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_sk.xtb b/chromium/content/app/strings/translations/content_strings_sk.xtb index 8d3c842429c..ce521185669 100644 --- a/chromium/content/app/strings/translations/content_strings_sk.xtb +++ b/chromium/content/app/strings/translations/content_strings_sk.xtb @@ -10,28 +10,34 @@ doplnkové pohyblivý prvok posúvač +vysvetlivka dialóg +titulky článok banner oblasť Tento mesiac Vybrať súbory +obsiahnutý citát Zadajte e-mailovú adresu, ktorá nie je prázdna hodnota. Iné... dialóg_upozornenia stromová mriežka AM / PM prehrať na vzdialenom zariadení +príklad dd stav Hodnota musí byť alebo skôr. Prepnuté na zrkadlenie Znak je v doméne použitý na nesprávnej pozícii. Dnes +zoznam strán Tento text musíte skrátiť na znakov alebo menej (súčasný počet znakov: ). . týždeň, Skladba začiarkavacie políčko +zlom strany výber dátumu a času Deň Vyplňte toto pole. @@ -40,6 +46,7 @@ prehrať Zadajte platnú hodnotu. Najbližšia platná hodnota je . Odoslať +grafický objekt päta Automatické dopĺňanie zvuk @@ -70,30 +77,41 @@ Predĺžte tento text aspoň na alebo viac znakov (momentálny počet znakov je ). Časť pred znakom by nemala obsahovať symbol . Ukončiť režim celej obrazovky +záver aplikácia +prológ posúvač Milisekundy grafika +predhovor Zobraziť predchádzajúci mesiac výber farieb panel s ponukami informácie o obsahu Zadajte platnú hodnotu. Najbližšie platné hodnoty sú a . +Otázky a odpovede tlačidlo ponuky +glosárový termín Podrobnosti formulár strom Časť za znakom by nemala obsahovať symbol . +príloha +titulná strana panel karty video search Zadajte číslo. +bibliografický záznam Počet vybraných položiek: Zapnúť zvuk Zadajte časť za znakom . Adresa je neúplná. indikátor priebehu +epigraf Zobraziť ďalší mesiac hlavička riadka +epilóg +Toto video sa prehráva v režime Obraz v obraze Hodnota musí byť väčšia alebo rovná hodnote . PB Zadajte časť pred znakom . Adresa je neúplná. @@ -106,9 +124,11 @@ obnoviť zvuk čas Dvojitým klepnutím doľava alebo doprava preskočíte o 10 s +abstrakt pozastaviť prehrávanie tlačidlo ďalšie možnosti +úvod Vypnúť zvuk Vypnuté Celá obrazovka @@ -119,12 +139,15 @@ Rok pole s ponukou obnoviť zvukovú stopu +bibliografia meter +doslov +glosár Zobraziť panel na výber mesiaca posúvač časovej osi zvuku +poďakovanie výber času Možnosti -Je aktívny režim Obraz v obraze prejsť do režimu celej obrazovky informačný kanál denník @@ -135,22 +158,31 @@ popis Prehrať Doplnok sa nepodarilo načítať. +bibliografický odkaz stlmiť skryť skryté titulky adresár +kolofón značka blockquote stiahnuť médiá Predĺžte tento text aspoň na alebo viac znakov (momentálne používate jeden znak). Zadajte hodnotu zodpovedajúcu požadovanému formátu. +predslov uplynutý čas Vyberte položku zo zoznamu. hlavička stĺpca +poďakovanie Vybrať súbor Iné... +vysvetlivky TB karta Stiahnuť +spätný odkaz +tip +grafický dokument Hodiny +index zostávajúci čas ukončiť zobrazovanie skrytých titulkov Obsah HTML @@ -160,24 +192,33 @@ rozdeľovač Nie je vybratý žiadny súbor textové pole pre vyhľadávanie +upozornenie +odkaz na poznámku zobrazovať skryté titulky Tento týždeň prehrať film v režime celej obrazovky rrrr Titulky Neplatná hodnota. +časť panel s nástrojmi Počet súborov: Vyberte súbor. prepínač +venovanie +erráta Minúty ovládanie médií +grafický symbol zoznam definícií nadpis +obsah ovládať vzdialené prehrávanie +podnadpis Obraz v obraze Vyberte jednu z týchto možností. začať prehrávanie +kapitola Prenáša sa do televízora hlavné Chyba prehrávania videa @@ -191,6 +232,7 @@ Iné... upozornenie začínajúci +poznámka pod čiarou bunka ponuka \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_sl.xtb b/chromium/content/app/strings/translations/content_strings_sl.xtb index af6716e2b55..e21b1f5e558 100644 --- a/chromium/content/app/strings/translations/content_strings_sl.xtb +++ b/chromium/content/app/strings/translations/content_strings_sl.xtb @@ -10,28 +10,34 @@ dopolnilno potujoči napis drsnik +končna opomba pogovorno okno +seznam sodelujočih člnk pasica območje Ta mesec Izberi datoteke +pojavni citat Vnesite e-poštni naslov (polje ne sme biti prazno). Drugo ... pogovorno okno z opozorilom drevesna mreža Dopoldne/popoldne predvajanje v oddaljeni napravi +primer dd stanje Vrednost mora biti ali prej. Preklopljeno na zrcaljenje »« je na napačnem mestu v »«. Danes +seznam strani Skrajšajte to besedilo na znakov ali manj (trenutno uporabljate znakov). . teden, Besedilni posnetek potrditveno polje +prelom strani izbirnik datuma in ure Dan Izpolnite to polje @@ -40,6 +46,7 @@ predvajanje Vnesite veljavno vrednost. Najbližja veljavna vrednost je . Pošlji +grafični predmet noga Samodejno izpolnjevanje zvok @@ -70,30 +77,41 @@ Podaljšajte to besedilo na toliko znakov ali več: (trenutno uporabljate toliko znakov: ). Del pred »« ne sme vsebovati znaka »«. Zapri celozaslonski način +sklep aplikacija +uvod drsni trak Milisekunde grafični element +predgovor Prikaz prejšnjega meseca izbirnik barve menijska vrstica podatki o vsebini Vnesite veljavno vrednost. Najbližji veljavni vrednosti sta in . +Vprašanja menijski gumb +vnos v glosarju Podrobnosti obrazec drevo Del po »« ne sme vsebovati znaka »«. +dodatek +naslovnica podokno z zavihki video search Vnesite številko. +bibliografski vnos Št. izbranih: Vklopi zvok Vnesite nekaj po znaku »«. Naslov »« je nepopoln. kazalnik poteka +epigraf Prikaz naslednjega meseca glava vrstice +epilog +Ta videoposnetek se predvaja v načinu slike v sliki Vrednost mora biti večja od ali enaka. PB Vnesite nekaj in nato . Naslov »« je nepopoln. @@ -106,9 +124,11 @@ vklop zvoka čas Dvakrat se dotaknite levo ali desno, da preskočite 10 s +izvleček začasna ustavitev predvajanja gumb več možnosti +uvod Izklopi zvok Izklopljeno Celozaslonsko @@ -119,12 +139,15 @@ Leto polje s seznamom vklop zvoka za zvočni posnetek +bibliografija merilnik +spremna beseda +glosar Prikaz podokna za izbiro meseca časovni krmilnik za predvajanje zvoka +zahvala izbirnik ure Možnosti -Izbran je način slike v sliki prehod v celozaslonski način vir dnevn @@ -135,22 +158,31 @@ opis orodja Predvajanje Vtičnika ni bilo mogoče naložiti. +bibliografski viri nemo skrivanje podnapisov imenik +kolofon daljši citat prenos predstavnosti Podaljšajte to besedilo na toliko znakov ali več: (trenutno uporabljate en znak). Poskrbite za ujemanje z zahtevano obliko. +predgovor pretečeni čas Izberite element s seznama. glava stolpca +zasluga Izberite datoteko Drugo ... +končne opombe TB tabulatorka Prenos +povratna povezava +namig +grafični dokument Ure +kazalo preostali čas ustavitev prikazovanja podnapisov Vsebina HTML @@ -160,24 +192,33 @@ razdelilnik Nobena datoteka ni izbrana besedilno polje za iskanje +kritika +sklicevanje na opombo začetek predvajanja podnapisov Ta teden predvajanje filma v celozaslonskem načinu llll Napisi Neveljavna vrednost. +del orodna vrstica Število datotek: Izberite datoteko. izbirni gumb +posvetilo +popravki Minute nadziranje predstavnosti +grafični simbol seznam opredelitev naslov +kazalo upravljanje oddaljenega predvajanja +podnaslov Slika v sliki Izberite eno od teh možnosti. začetek predvajanja +poglavje Predvajanje na televizorju glavn Napaka pri predvajanju videoposnetka @@ -191,6 +232,7 @@ Drugo ... opozorilo , začne se +sprotna opomba celica meni \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_sr.xtb b/chromium/content/app/strings/translations/content_strings_sr.xtb index 9893f4f40be..80a786e21be 100644 --- a/chromium/content/app/strings/translations/content_strings_sr.xtb +++ b/chromium/content/app/strings/translations/content_strings_sr.xtb @@ -10,28 +10,34 @@ комплементарно покретни текст клизач +завршна напомена дијалог +заслуге article банер регион Овај месец Избор датотека +кратки цитат Унесите имејл адресу која није празна. Друго... дијалог обавештења мрежа стабла пре подне/по подне пуштајте на удаљеном уређају +пример дд статус Вредност мора да буде или старија. Пребачено је на пресликавање “ је стављено на погрешно место у „“. Данас +листа страница Скратите овај текст на знак(ов)а или мање (тренутно користите знак(ов)а). . недеља, . . песма поље за потврду +прелом странице бирач датума и времена Дан Попуните ово поље. @@ -40,6 +46,7 @@ пустите Унесите важећу вредност. Најближа важећа вредност је . Пошаљи +графички објекат подножје Аутоматско попуњавање аудио @@ -70,30 +77,41 @@ Продужите овај текст на знак(ов)а или више (тренутно користите знак(ов)а). Део пре „“ не треба да садржи симбол „“. Изађи из целог екрана +закључак апликација +пролог трака за померање Милисекунде графички елемент +предговор Прикажи претходни месец бирач боја трака са менијима информације о садржају Унесите важећу вредност. Две најближе важеће вредности су и . +Питања и одговори дугме менија +референца речника термина Детаљи образац стабло Део после „“ не треба да садржи симбол „“. +додатак +корицe табла са картицама видео search Унесите број. +библиографска ставка Изабрано: Укључи звук Унесите неки део после „“. Адреса „“ је непотпуна. индикатор напретка +натпис Прикажи следећи месец заглавље реда +епилог +Видео се репродукује у режиму слике у слици Вредност сме да буде најмање . PB Унесите неки део пре „“. Адреса „“ је непотпуна. @@ -106,9 +124,11 @@ укључите звук време Двапут додирните лево или десно да бисте прескочили 10 сек +сажетак паузирајте репродукцију дугме још опција +увод Искључи звук Искључено Цеo екран @@ -119,12 +139,15 @@ Година оквир са листом укључите звук аудио снимка +библиографија мерач +поговор +речник термина Прикажи таблу за избор месеца клизач за трајање аудио-садржаја +признања бирач времена Опције -Сада у режиму Слика у слици пређите на режим целог екрана фид log @@ -135,22 +158,31 @@ објашњење Пусти Учитавање додатне компоненте није успело. +референца библиографије искључи звук сакријте опционални титл каталог +знак издавача издвојени цитат преузми медије Продужите овај текст на бар знак(ов)а (тренутно користите 1 знак). Изаберите захтевани формат. +предговор протекло време Изаберите ставку са листе. заглавље колоне +заслуга Одабери датотеку Друго... +завршне напомене TB tab Преузми +повратни линк +савет +графички документ Сати +индекс преостало време зауставите приказивање опционалног титла HTML садржај @@ -160,24 +192,33 @@ разделник Није одабрано поље за текст претраге +обавештење +референца напомене започните приказивање опционалног титла Ова недеља пустите филм у режиму целог екрана гггг Титл Неважећа вредност. +део трака с алаткама датотеке(а) Изаберите датотеку. дугме за избор +посвета +исправке и допуне Минути контрола за медије +графички симбол листа дефиниција наслов +садржај контролишите репродукцију на удаљеном уређају +титл Слика у слици Изаберите неку од ових опција. започните репродукцију +поглавље Тренутно се пребацује на ТВ main Грешка при пуштању видео снимка @@ -191,6 +232,7 @@ Друго... обавештење , од +фуснота ћелија мени \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_sv.xtb b/chromium/content/app/strings/translations/content_strings_sv.xtb index 9bd0e0679b0..570a7a56037 100644 --- a/chromium/content/app/strings/translations/content_strings_sv.xtb +++ b/chromium/content/app/strings/translations/content_strings_sv.xtb @@ -10,28 +10,34 @@ kompletterande markör skjutreglage +slutnot dialogruta +medverkande artikel banner område Den här månaden Välj filer +utvalt citat Ange en e-postadress som inte är tom. Annat ... varningsdialogruta träddiagram FM/EM spela på en fjärrenhet +exempel dd status Värdet måste vara eller tidigare. Bytte till spegling används på fel plats i . Idag +sidlista Förkorta texten till tecken eller mindre (nu är texten tecken). Vecka Spår kryssruta +sidbrytning datum- och tidsväljare Dag Fyll i det här fältet. @@ -40,6 +46,7 @@ spela upp Ange ett giltigt värde. Det närmast giltiga värdet är . Skicka +grafiskt objekt sidfot Autofyll ljud @@ -70,30 +77,41 @@ Lägg till minst tecken (för närvarande har du angett tecken). En del följt av får inte innehålla symbolen . Avsluta helskärmsläge +resultat program +prolog rullningslist Millisekunder bild +förord Visa föregående månad färgval menyfält innehållsinformation Ange ett giltigt värde. De två närmaste giltiga värdena är och . +Frågor och svar menyknapp +hänvisning till ordlista Info formulär träd En del efter får inte innehålla symbolen . +bilaga +omslag flikpanel video sök Ange ett nummer. +bibliografipost valda Slå på ljudet Ange en del följt av . är ofullständig. förloppsindikator +epigraf Visa nästa månad radrubrik +epilog +Videon spelas upp i läget bild-i-bild Värdet måste vara större än eller lika med . PB Ange en del följt av . är ofullständig. @@ -106,9 +124,11 @@ visa tid Hoppa över 10 s genom att trycka två gånger till vänster/höger +abstract pausa uppspelning knapp fler alternativ +inledning Stäng av ljudet Av Helskärm @@ -119,12 +139,15 @@ År listruta spela upp ljudspåret +bibliografi mätare +efterord +ordlista Visa panelen för val av månad tidsreglage för ljud +författarens tack tidsväljare Alternativ -Visas i läget bild-i-bild visa i helskärm flöde logg @@ -135,22 +158,31 @@ beskrivning Spela Det gick inte att läsa in plugin-programmet. +bibliografisk referens ljud av dölj textning katalog +kolofon citatblock ladda ned media Lägg till minst tecken (för närvarande har du angett 1 tecken). Matcha det format som anges. +företal förfluten tid Välj ett alternativ i listan. kolumnrubrik +erkännande Välj fil Annat ... +slutnoter TB tabb Ladda ned +bakåtlänk +tips +grafiskt dokument Timmar +index återstående tid sluta visa textning HTML-innehåll @@ -160,24 +192,33 @@ delare Ingen fil har valts fält för söktext +anmärkning +referensnot börja visa textning Den här veckan spela upp filmen i helskärmsläge åååå Textning Ogiltigt värde. +del verktygsfält filer Välj en fil. alternativknapp +tillägnan +errata Minuter mediekontroll +grafisk symbol definitionslista rubrik +innehållsförteckning styra fjärruppspelning +undertext Bild i bild Välj ett av följande alternativ. starta uppspelning +kapitel Castar nu till TV:n huvud Videouppspelningsfel @@ -191,6 +232,7 @@ Annat ... varning som börjar den +fotnot cell meny \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_sw.xtb b/chromium/content/app/strings/translations/content_strings_sw.xtb index f21490de4fd..810a58adb8b 100644 --- a/chromium/content/app/strings/translations/content_strings_sw.xtb +++ b/chromium/content/app/strings/translations/content_strings_sw.xtb @@ -10,28 +10,34 @@ nyongeza marquee kitelezi +maelezo ya mwisho wa kitabu kidirisha +walioshiriki makala bango eneo Mwezi huu Chagua Faili +nukuu muhimu Tafadhali jaza sehemu ya anwani ya barua pepe. Nyingine... kidirisha cha arifa gridi ya mti AM / PM cheza kwenye kifaa cha mbali +mfano dd hali Thamani lazima iwe au mapema. Inatumia hali ya kuakisi ' 'imetumika kwenye nafasi isiyostahili katika ''. Leo +orodha ya kurasa Tafadhali fupisha maandishi haya hadi vibambo au chini (kwa sasa unatumia vibambo ). Wiki , Wimbo wa kisanduku cha kuteua +nafasi ya kugawa kurasa mchumaji tarehe na wakati Siku Tafadhali jaza sehemu hii. @@ -40,6 +46,7 @@ cheza Tafadhali ingiza thamani halali.Thamani halali ya karibu ni . Wasilisha +kipengee cha michoro kijachini Kujaza Kiotomatiki sauti @@ -70,30 +77,41 @@ Tafadhali refusha maandishi haya hadi herufi au zaidi (kwa sasa unatumia herufi ). Sehemu inayofuatwa na '' haipaswi kuwa na alama ya ''. Ondoka kwenye skrini nzima +hitimisho programu +shairi la utangulizi sehemu ya kusogeza nukta mchoro +dibaji Onyesha mwezi uliotangulia kiteua rangi upau wa menyu maelezo ya maudhui Tafadhali ingiza thamani halali. Thamani mbili halali za karibu ni na . +Maswali na Majibu kitufe cha menyu +marejeleo ya farahasa Maelezo fomu mti Sehemu inayofuata '' haipaswi kuwa na alama ya ''. +kiambatisho +jalada kisanduku cha kichupo video tafuta Tafadhali ingiza nambari. +maelezo ya bibliografia Vipengee vimechaguliwa Rejesha sauti Tafadhali ingiza sehemu inayofuatia ''. '' haijakamilika. kiashiria cha maendeleo +epigrafu Onyesha mwezi unaofuata kichwa cha safu mlalo +hitimisho +Video hii inacheza katika hali ya Picha Ndani ya Picha Lazima thamani iwe kubwa kuliko au sawa na . PB Tafadhali ingiza sehemu ikifuatiwa na ''. '' haijakamilika. @@ -106,9 +124,11 @@ washa sauti wakati Gusa mara mbili kushoto au kulia ili uruke kwa sekunde 10 +ikisiri sitisha kucheza kitufe chaguo zaidi +utangulizi Zima sauti Kimezimwa Skrini nzima @@ -119,12 +139,15 @@ Mwaka kikasha cha orodha washa sauti ya wimbo +bibliografia mita +maelezo kuhusu kitabu +faharasa Onyesha kisanduku cha uchaguzi wa mwezi kitelezi cha muda cha sauti +shukrani Kiteua wakati Chaguo -Sasa iko Katika Hali ya Picha Ndani ya Picha ingia skrini kamili mipasho kumbukumbu @@ -135,22 +158,31 @@ kidirisha cha vidokezo Cheza Haikuweza kupakia programu-jalizi. +marejeleo ya bibliografia nyamazisha ficha manukuu yanayoweza kuonyeshwa saraka +kolofoni nukuu la msingi pakua maudhui Tafadhali refusha maandishi haya hadi herufi au zaidi (kwa sasa unatumia herufi 1). Tafadhali linganisha umbizo lililoombwa. +dibaji muda uliokwisha Tafadhali chagua kipengee katika orodha. kijajuu cha safu wima +aliyeshiriki Chagua Faili Nyingine... +maelezo ya mwisho wa kitabu TB kichupo Pakua +kiungo rejeshi +kidokezo +hati ya picha Saa +faharasa muda unaosalia koma kuonyesha manukuu yanaweza kufichwa Maudhui ya HTML @@ -160,24 +192,33 @@ kitenganishi Hakuna faili iliyochaguliwa sehemu ya maandishi ya utafutaji +ilani +marejeleo ya dokezo anza kuonyesha manukuu yaliyofungwa Wiki hii cheza filamu katika hali ya skrini kamili yyyy Manukuu Thamani batili. +sehemu upau wa vidhibiti faili Tafadhali chagua faili. kitufe cha mviringo +tabaruku +hitilafu katika uchapishaji Dakika udhibiti wa vyombo vya habari +ishara ya picha orodha ya ufafanuzi kichwa +jedwali la yaliyomo dhibiti kucheza kwa mbali +kichwa kidogo Picha ndani ya picha Tafadhali chagua moja wapo ya chaguo hizi. anza kucheza +sura Sasa inatuma kwenye TV yako kuu Hitilafu ya kucheza video @@ -191,6 +232,7 @@ Nyingine... arifa , itaanza tarehe +tanbihi kisanduku menyu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_ta.xtb b/chromium/content/app/strings/translations/content_strings_ta.xtb index 5373e1415a4..450fea855b3 100644 --- a/chromium/content/app/strings/translations/content_strings_ta.xtb +++ b/chromium/content/app/strings/translations/content_strings_ta.xtb @@ -10,28 +10,34 @@ ஈடுசெய்யக்கூடியது மார்கியூ ஸ்லைடர் +முடிவுக்குறிப்பு உரையாடல் +பங்களித்தவர்கள் article பேனர் மண்டலம் இந்த மாதம் கோப்புகளைத் தேர்வுசெய்க +pullquote காலி அல்லாத மின்னஞ்சல் முகவரியை உள்ளிடவும். மற்றவை… விழிப்பூட்டல்_உரையாடல் ட்ரீ கிரிட் AM/PM தொலைநிலைச் சாதனத்தில் இயக்கு +உதாரணம் dd நிலை மதிப்பு அல்லது அதற்கு முன்பு இருக்க வேண்டும். பிரதிபலித்தலுக்கு மாற்றியது இல் தவறான இடத்தில் '' பயன்படுத்தபட்டுள்ளது. இன்று +பக்கப் பட்டியல் இந்த உரையை எழுத்துக்குறிகள் அல்லது அதற்கும் குறைவாக சுருக்கிடுங்கள் (நீங்கள் தற்போது எழுத்துக்குறிகளைப் பயன்படுத்துகிறீர்கள்). வாரம் , ட்ராக் செக்பாக்ஸ் +பக்க முறிப்பு தேதி மற்றும் நேரம் தேர்ந்தெடுப்பான் நாள் இந்தப் புலத்தை நிரப்புக. @@ -40,6 +46,7 @@ இயக்கு சரியான மதிப்பை உள்ளிடவும். என்பது நெருக்கமாக உள்ள சரியான மதிப்பாகும். சமர்ப்பி +கிராஃபிக்ஸ் பொருள் அடிக்குறிப்பு தானாகநிரப்பு ஆடியோ @@ -70,30 +77,41 @@ இந்த உரையை எழுத்துக்குறிகள் அல்லது அதற்கும் அதிகமாக (தற்போது எழுத்துக்குறிகளைப் பயன்படுத்துகிறீர்கள்) நீட்டிக்கவும். '' ஐத் தொடர்ந்து வரும் பகுதியில் '' சின்னம் இருக்கக்கூடாது. முழுத்திரை வேண்டாம் +முடிவு பயன்பாடு +முன்னுரை உருட்டல் பட்டி மில்லிவினாடிகள் கிராஃபிக் +அறிமுகம் முந்தைய மாதத்தைக் காட்டு வண்ணத் தேர்வி மெனுப் பட்டி உள்ளடக்கத் தகவல் சரியான மதிப்பை உள்ளிடவும். மற்றும் ஆகியவை மிக நெருக்கமான சரியான இரண்டு மதிப்புகளாகும். +கேள்வி-பதில் மெனு பொத்தான் +அருஞ்சொல் திரட்டு மேற்கோள் விவரங்கள் படிவம் tree '' ஐத் தொடரும் பகுதியில், '' சின்னம் இருக்கக்கூடாது. +பிற்சேர்க்கை +அட்டை தாவல் பலகம் வீடியோ Search எண்ணை உள்ளிடுக. +நூல்விவர அட்டவணை உள்ளீடு தேர்ந்தெடுக்கப்பட்டன ஒலி இயக்கு '' ஐத் தொடர்ந்து ஒரு பகுதியை உள்ளிடவும். '' முழுமைப்பெறாமல் உள்ளது. செயல்நிலை காட்டி +கல்வெட்டு அடுத்த மாதத்தைக் காட்டு வரிசை மேற்தலைப்பு +முடிவுரை +பிக்ச்சர்-இன்-பிக்ச்சர் பயன்முறையில் இந்த வீடியோ இயக்கப்படுகிறது மதிப்பானது, கண்டிப்பாக ஐ விட அதிகமாக அல்லது அதற்குச் சமமாக இருக்க வேண்டும். பெ.பை '' ஐத் தொடர்ந்து ஒரு பகுதியை உள்ளிடவும். '' முழுமைப் பெறாமல் உள்ளது. @@ -106,9 +124,11 @@ ஒலி இயக்கு நேரம் 10வி தவிர்க்க, இடது அல்லது வலதுபுறம் இருமுறை தட்டவும் +சுருக்கம் மறுஇயக்கத்தை இடைநிறுத்து பொத்தான் கூடுதல் விருப்பங்கள் பொத்தான் +அறிமுகம் ஒலியடக்கு ஆஃப் முழுத்திரை @@ -119,12 +139,15 @@ ஆண்டு பட்டியல் பெட்டி ஆடியோ டிராக்கை இயக்கு +நூல்விவர அட்டவணை மீட்டர் +பின்னுரை +அருஞ்சொல் திரட்டு மாதம் தேர்ந்தெடுப்புப் பலகத்தைக் காட்டு ஆடியோ நேர ஸ்கிரப்பர் +அங்கீகாரங்கள் நேரம் தேர்ந்தெடுப்பான் விருப்பத்தேர்வுகள் -இப்போது பிக்ச்சர்-இன்-பிக்ச்சர் பயன்முறையில் உள்ளது முழுத்திரைக்குச் செல் ஊட்டம் பதிவு @@ -135,22 +158,31 @@ உதவிக்குறிப்பு இயக்கு செருகுநிரல் ஏற்றப்படவில்லை. +நூல்விவர அட்டவணை மேற்கோள் ஒலியடக்கு மூடப்பட்ட தலைப்புகளை மறை கோப்பகம் +முத்திரை பிளாக்கோட் மீடியாவைப் பதிவிறக்கு இந்த உரையில் அல்லது அதற்கு மேற்பட்ட எழுத்துக்குறிகளைப் பயன்படுத்தவும் (தற்போது 1 எழுத்துக்குறியைப் பயன்படுத்துகிறீர்கள்). கோரிய வடிவமைப்பில் தருக. +முன்னுரை முடிவடைந்த நேரம் பட்டியலிலிருந்து ஒரு உருப்படியைத் தேர்ந்தெடுங்கள். நெடுவரிசை மேற்தலைப்பு +பங்களித்தவர் கோப்பைத் தேர்வு செய்க மற்றவை… +முடிவுக்குறிப்புகள் டெ.பை tab பதிவிறக்கு +பின்னிணைப்பு +உதவிக்குறிப்பு +கிராஃபிக்ஸ் ஆவணம் மணிநேரம் +பொருளடக்கம் மீதமுள்ள நேரம் மூடப்பட்ட தலைப்புகளைக் காட்டுவதை நிறுத்து HTML உள்ளடக்கம் @@ -160,24 +192,33 @@ பிரிப்பான் எந்த கோப்பும் தேர்ந்தெடுக்கப்படவில்லை தேடல் உரைப் புலம் +அறிவிப்பு +குறிப்பு மேற்கோள் மூடப்பட்ட தலைப்புகளைக் காட்டுவதைத் தொடங்கு இந்த வாரம் மூவியை முழுத்திரைப் பயன்முறையில் இயக்கு yyyy வசனங்கள் செல்லாத மதிப்பு. +பகுதி கருவிப்பட்டி கோப்புகள் ஒரு கோப்பை தேர்ந்தெடுக்கவும். ரேடியோ பொத்தான் +அர்ப்பணம் +பிழைகள் நிமிடங்கள் மீடியா கட்டுப்பாடு +கிராஃபிக்ஸ் சின்னம் விளக்கப் பட்டியல் தலைப்பு +உள்ளடக்க அட்டவணை தொலைநிலை இயக்கத்தைக் கட்டுப்படுத்தவும் +துணை தலைப்பு பிக்ச்சர்-இன்-பிக்ச்சர் தயவுசெய்து இந்த விருப்பங்களில் ஒன்றைத் தேர்ந்தெடுங்கள். மறுஇயக்கத்தைத் தொடங்கு +அத்தியாயம் டிவிக்கு அனுப்புகிறது முதன்மை வீடியோ இயக்கப் பிழை @@ -191,6 +232,7 @@ மற்றவை… விழிப்பூட்டல் , அன்று தொடங்குவது +அடிக்குறிப்பு கலம் மெனு \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_te.xtb b/chromium/content/app/strings/translations/content_strings_te.xtb index 1c5d44f8602..ece107bc6e9 100644 --- a/chromium/content/app/strings/translations/content_strings_te.xtb +++ b/chromium/content/app/strings/translations/content_strings_te.xtb @@ -10,28 +10,34 @@ బహుమానపూర్వకం marquee స్లయిడర్ +ఎండ్‌నోట్ డైలాగ్ +సహకారులు కథనం బ్యానర్ ప్రాంతం ఈ నెల ఫైల్‌లను ఎంచుకోండి +పుల్‌కోట్ దయచేసి ఖాళీ-కాని ఇమెయిల్ చిరునామాని నమోదు చేయండి. ఇతర... alert_dialog వృక్షాంశ గ్రిడ్ AM/PM రిమోట్ పరికరంలో ప్లే చేస్తుంది +ఉదాహరణ dd స్థితి విలువ తప్పనిసరిగా లేదా అంతకంటే మునుపటిది అయి ఉండాలి. మిర్రరింగ్‌కు మార్చబడింది ''లో '' తప్పు స్థానంలో ఉపయోగించబడింది. ఈ రోజు +పేజీ జాబితా దయచేసి ఈ వచనాన్ని అక్షరాలకు లేదా అంతకంటే తక్కువ (మీరు ప్రస్తుతం అక్షరాలను ఉపయోగిస్తున్నారు)కు తగ్గించండి. వ వారం, ట్రాక్ తనిఖీపెట్టె +పేజీ విభజన తేదీ మరియు సమయం ఎంపిక రోజు దయచేసి ఈ ఫీల్డ్‌ని పూర్తి చెయ్యండి. @@ -40,6 +46,7 @@ ప్లే చేయి దయచేసి చెల్లుబాటు అయ్యే విలువను నమోదు చేయండి. అనేది సమీప చెల్లుబాటు విలువ. సమర్పించు +గ్రాఫిక్స్ ఆబ్జెక్ట్ ఫుటర్ స్వయంపూర్తి ఆడియో @@ -70,30 +77,41 @@ దయచేసి ఈ వచనాన్ని లేదా అంతకంటే ఎక్కువ అక్షరాలకు పొడిగించండి (ప్రస్తుతం మీరు అక్షరాలను ఉపయోగిస్తున్నారు). ''కి ముందు ఉన్న భాగంలో '' చిహ్నం ఉండకూడదు. పూర్తి స్క్రీన్ నుండి నిష్క్రమించు +ముగింపు అనువర్తనం +ప్రారంభం స్క్రోల్ పట్టీ మిల్లీసెకన్లు గ్రాఫిక్ +ముందుమాట మునుపటి నెలను చూపుతుంది రంగు ఎంపిక మెను పట్టీ కంటెంట్ సమాచారం దయచేసి చెల్లుబాటు అయ్యే విలువను నమోదు చేయండి. మరియు అనేవి రెండు సమీప చెల్లుబాటు విలువలు. +Q&A మెను బటన్ +పదకోశ సూచన వివరాలు ఫారమ్ tree ''కి తర్వాత ఉన్న భాగంలో '' చిహ్నం ఉండకూడదు. +అనుబంధం +ముఖచిత్రం ట్యాబ్ ప్యానెల్ వీడియో search దయచేసి సంఖ్యను నమోదు చేయండి. +వివరణపట్టి నమోదు ఎంచుకోబడ్డాయి అన్‌మ్యూట్ చేయి దయచేసి ''కి తర్వాత ఉన్న భాగాన్ని నమోదు చేయండి. '' అసంపూర్ణంగా ఉంది. ప్రోగ్రెస్ సూచిక +పరిచయ వాక్యం తదుపరి నెలను చూపుతుంది అడ్డు వరుస శీర్షిక +ఉపసంహారం +ఈ వీడియో చిత్రంలో-చిత్రంలో ప్లే అవుతోంది విలువ ఖచ్చితంగా కంటే ఎక్కువగా లేదా సమానంగా ఉండాలి. PB దయచేసి ''కి ముందు ఉన్న భాగాన్ని నమోదు చేయండి. '' అసంపూర్ణంగా ఉంది. @@ -106,9 +124,11 @@ అన్‌మ్యూట్ చేయి time 10సె దాటవేయడానికి రెండుసార్లు ఎడమ లేదా కుడివైపుకి నొక్కండి +సంక్షేపం ప్లేబ్యాక్‌ను పాజ్ చేయి బటన్ మరిన్ని ఎంపికలు +పరిచయం మ్యూట్ చేయి ఆఫ్ అయ్యింది పూర్తితెర @@ -119,12 +139,15 @@ సంవత్సరం జాబితా పెట్టె ఆడియో ట్రాక్‌ను అన్‌మ్యూట్ చేయి +వివరణపట్టి మీటర్ +చివరిమాట +పదకోశం నెల ఎంపిక ప్యానెల్‌ను చూపుతుంది ఆడియో సమయ స్క్రబ్బర్ +గుర్తింపులు సమయం ఎంపిక ఎంపికలు -ఇప్పుడు చిత్రంలో చిత్రం మోడ్‌లో ఉంది పూర్తి స్క్రీన్‌లోకి ప్రవేశించు ఫీడ్ log @@ -135,22 +158,31 @@ సాధన చిట్కా ప్లే చేయి ప్లగిన్‌ను లోడ్ చేయడం సాధ్యపడలేదు. +వివరణపట్టి సూచన మ్యూట్ చేయి సంవృత శీర్షికలను దాచు డైరెక్టరీ +ఆఖరిమాట బ్లాక్‌కోట్ మీడియా అంశం డౌన్‌లోడ్ చేయి దయచేసి ఈ వచనాన్ని లేదా అంతకంటే ఎక్కువ అక్షరాలకు పొడిగించండి (ప్రస్తుతం మీరు 1 అక్షరాన్ని ఉపయోగిస్తున్నారు). దయచేసి అభ్యర్థించిన ఆకృతీకరణను సరిపోల్చండి. +ముందుమాట గడిచిన సమయం దయచేసి జాబితాలోని ఒక అంశాన్ని ఎంచుకోండి. నిలువు వరుస శీర్షిక +సహకారి ఫైల్‌ను ఎంచుకోండి ఇతర... +ఎండ్‌నోట్‌లు TB tab డౌన్‌లోడ్ చేయి +వెనుకకు తీసుకువెళ్లే లింక్‌ +చిట్కా +గ్రాఫిక్స్ పత్రం గంటలు +సూచిక మిగిలి ఉన్న సమయం సంవృత శీర్షికలను ప్రదర్శించడం ఆపివేయి HTML కంటెంట్ @@ -160,24 +192,33 @@ విభజన ఫైల్ ఏదీ ఎంచుకోలేదు శోధన వచనం ఫీల్డ్ +గమనిక +గమనిక సూచన సంవృత శీర్షికలను ప్రదర్శించడం ప్రారంభించు ఈ వారం చలనచిత్రాన్ని పూర్తి స్క్రీన్ మోడ్‌లో ప్లే చేయి yyyy ఉపశీర్షికలు చెల్లని విలువ. +భాగం సాధన పట్టీ ఫైళ్ళు దయచేసి ఒక ఫైల్‌ని ఎంచుకోండి. రేడియో బటన్ +అంకితం +తప్పొప్పుల పట్టిక నిమిషాలు మీడియా నియంత్రణ +గ్రాఫిక్స్ చిహ్నం నిర్వచన జాబితా శీర్షిక +విషయ పట్టిక రిమోట్ ప్లేబ్యాక్‌ను నియంత్రిస్తుంది +ఉపశీర్షిక చిత్రంలో చిత్రం దయచేసి ఈ ఎంపికలలో ఒకదాన్ని ఎంచుకోండి. ప్లేబ్యాక్‌ను ప్రారంభించు +అధ్యాయం ఇప్పుడు మీ టీవీలో ప్రసారం చేస్తోంది main వీడియో ప్లేబ్యాక్ ఎర్రర్ @@ -191,6 +232,7 @@ ఇతర... హెచ్చరిక , నుండి ప్రారంభమవుతుంది +ఫుట్‌నోట్ గడి మెను \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_th.xtb b/chromium/content/app/strings/translations/content_strings_th.xtb index 3edfe6da9e4..b7ec11b7cc2 100644 --- a/chromium/content/app/strings/translations/content_strings_th.xtb +++ b/chromium/content/app/strings/translations/content_strings_th.xtb @@ -10,28 +10,34 @@ ส่วนเสริม ตัวอักษรวิ่ง แถบเลื่อน +อ้างอิงท้ายเรื่อง กล่องโต้ตอบ +เครดิต บทความ แบนเนอร์ ภูมิภาค เดือนนี้ เลือกไฟล์ +การเน้นข้อความ โปรดป้อนที่อยู่อีเมลที่ไม่ว่างเปล่า อื่นๆ... alert_dialog แผนผังต้นไม้ AM/PM เล่นในอุปกรณ์ระยะไกล +ตัวอย่าง วว สถานะ ค่าต้องเป็น หรือก่อนหน้านั้น เปลี่ยนเป็นการมิเรอร์ "" ถูกใช้งานในตำแหน่งที่ไม่ถูกต้องใน "" วันนี้ +รายการหน้า โปรดย่อข้อความนี้ให้เหลือไม่เกิน อักขระ (ขณะนี้ข้อความของคุณมี อักขระ) สัปดาห์ที่ , แทร็ก ช่องทำเครื่องหมาย +ตัวแบ่งหน้า เครื่องมือเลือกวันที่และเวลา วัน โปรดกรอกฟิลด์นี้ @@ -40,6 +46,7 @@ เล่น โปรดป้อนค่าที่ถูกต้อง ค่าใกล้เคียงที่สุดที่ถูกต้องคือ ส่ง +ออบเจ็กต์กราฟิก ส่วนท้าย ป้อนอัตโนมัติ เสียง @@ -66,34 +73,45 @@ ดด GB ตัวควบคุมเวลาของภาพยนตร์ -คำหลัก +คีย์เวิร์ด โปรดกรอกข้อความนี้ให้มีอักขระอย่างน้อย ตัว (ตอนนี้คุณมี ตัว) ส่วนที่ตามด้วย "" ต้องไม่มีสัญลักษณ์ "" ออกจากการแสดงเต็มหน้าจอ +บทสรุป แอปพลิเคชัน +อารัมภบท แถบเลื่อน มิลลิวินาที กราฟิก +คำนำ แสดงเดือนที่ผ่านมา ตัวเลือกสี แถบเมนู ข้อมูลเนื้อหา โปรดป้อนค่าที่ถูกต้อง ค่าใกล้เคียงที่สุดที่ถูกต้องสองรายการคือ และ +ถามและตอบ ปุ่มเมนู +ข้อมูลอ้างอิงอภิธานศัพท์ รายละเอียด ฟอร์ม แผนผัง ส่วนที่ต่อท้าย "" ต้องไม่มีสัญลักษณ์ "" +ภาคผนวก +หน้าปก แผงแท็บ วิดีโอ ค้นหา โปรดป้อนตัวเลข +รายการบรรณานุกรม เลือกไว้ รายการ เปิดเสียง โปรดป้อนส่วนที่ต่อท้าย "" "" นั้นไม่สมบูรณ์ ตัวบอกสถานะความคืบหน้า +คำจารึก แสดงเดือนถัดไป ส่วนหัวของแถว +ปัจฉิมบท +กำลังเล่นวิดีโอในโหมดการแสดงภาพซ้อนภาพ ค่าต้องมากกว่าหรือเท่ากับ PB โปรดป้อนส่วนหนึ่งโดยตามด้วย "" "" นั้นไม่สมบูรณ์ @@ -106,9 +124,11 @@ เปิดเสียง เวลา แตะ 2 ครั้งที่ด้านขวาหรือซ้ายเพื่อข้ามทีละ 10 วินาที +บทคัดย่อ หยุดเล่นชั่วคราว ปุ่ม ตัวเลือกเพิ่มเติม +บทนำ ปิดเสียง ปิด เต็มหน้าจอ @@ -119,12 +139,15 @@ ปี ช่องรายการ เปิดไฟล์เสียง +บรรณานุกรม เมตร +คำแถลงท้ายเล่ม +อภิธานศัพท์ แสดงแผงการเลือกเดือน ตัวควบคุมเวลาของเสียง +กิตติกรรมประกาศ เครื่องมือเลือกเวลา ตัวเลือก -ขณะนี้อยู่ในโหมดการแสดงภาพซ้อนภาพ เข้าสู่โหมดเต็มหน้าจอ ฟีด บันทึก @@ -135,22 +158,31 @@ เคล็ดลับเครื่องมือ เล่น ไม่สามารถโหลดปลั๊กอิน +ข้อมูลอ้างอิงทางบรรณานุกรม ปิดเสียง ซ่อนคำอธิบายภาพ ไดเรกทอรี +ข้อมูลทางบรรณานุกรม ข้อความที่ยกมา ดาวน์โหลดสื่อ โปรดกรอกข้อความนี้ให้มีอักขระอย่างน้อย ตัว (ตอนนี้คุณมี 1 ตัว) โปรดจับคู่รูปแบบที่ร้องขอ +ส่วนนำ เวลาที่ผ่านไป โปรดเลือกรายการจากหน้ารายการ ส่วนหัวคอลัมน์ +เครดิต เลือกไฟล์ อื่นๆ... +อ้างอิงท้ายเรื่อง TB แท็บ ดาวน์โหลด +ลิงก์กลับมาที่หน้า +เคล็ดลับ +เอกสารกราฟิก ชั่วโมง +ดัชนี เวลาที่เหลือ หยุดแสดงคำอธิบายภาพ เนื้อหา HTML @@ -160,24 +192,33 @@ ตัวแยก ไม่ได้เลือกไฟล์ใด ช่องข้อความค้นหา +ประกาศ +ข้อมูลอ้างอิงหมายเหตุ เริ่มแสดงคำอธิบายภาพ สัปดาห์นี้ เล่นภาพยนตร์ในโหมดเต็มหน้าจอ ปปปป คำบรรยาย ค่าไม่ถูกต้อง +ภาค แถบเครื่องมือ ไฟล์ โปรดเลือกไฟล์ ปุ่มตัวเลือก +คำอุทิศ +ข้อผิดพลาด นาที การควบคุมสื่อ +สัญลักษณ์กราฟิก รายการคำจำกัดความ ส่วนหัว +สารบัญ ควบคุมการเล่นระยะไกล +คำบรรยาย การแสดงภาพซ้อนภาพ โปรดเลือกตัวเลือกอย่างหนึ่งอย่างใดเหล่านี้ เริ่มเล่น +บท ตอนนี้กำลังแคสต์ไปยัง TV หลัก ข้อผิดพลาดในการเล่นวิดีโอ @@ -191,6 +232,7 @@ อื่นๆ... การแจ้งเตือน เริ่มวันที่ +เชิงอรรถ เซลล์ เมนู \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_tr.xtb b/chromium/content/app/strings/translations/content_strings_tr.xtb index cfe8f034e66..109fca3bd36 100644 --- a/chromium/content/app/strings/translations/content_strings_tr.xtb +++ b/chromium/content/app/strings/translations/content_strings_tr.xtb @@ -10,28 +10,34 @@ tamamlayıcı marquee kaydırma çubuğu +son not iletişim kutusu +katkıda bulunanlar article banner bölge Bu ay Dosyaları Seç +alıntı Lütfen e-posta adresini boş bırakmayın. Diğer... alert_dialog ağaç tablo AM/PM uzaktan cihazda oynat +örnek gg durum Değer veya daha geri olmalıdır. Yansıtmaya geçildi "", "" adı içinde yanlış bir konumda kullanılmış. Bugün +sayfa listesi Lütfen bu metni veya daha az karakter olacak şekilde kısaltın (şu anda karakter kullanıyorsunuz). . hafta, . parça onay kutusu +sayfa sonu tarih ve saat seçici Gün Lütfen bu alanı doldurun. @@ -40,6 +46,7 @@ oynat Lütfen geçerli bir değer girin. En yakın geçerli değer şudur: Gönder +grafik nesnesi altbilgi Otomatik doldurma ses @@ -70,30 +77,41 @@ Lütfen bu metni karakter veya daha fazla olacak şekilde uzatın (şu anda karakter kullanıyorsunuz). Başında "" bulunan kısımda "" simgesi bulunmamalıdır. Tam ekran modundan çık +sonuç uygulama +prolog kaydırma çubuğu Milisaniye grafik +önsöz Önceki ayı göster renk seçici menü çubuğu içerik bilgileri Lütfen geçerli bir değer girin. En yakın iki geçerli değer şunlardır: ve . +Soru-Cevap menü düğmesi +terimler sözlüğü referansı Ayrıntılar form tree Başında "" bulunan kısımda "" simgesi bulunmamalıdır. +ek +kapak sekme paneli video ara Lütfen bir sayı girin. +kaynakça girişi tane seçildi Sesi aç -Lütfen başına "" ekleyin. "" eksik. +Lütfen "" işaretinden sonra gelen kısmı ekleyin. "", tam bir adres değil. ilerleme durumu göstergesi +yazıt Sonraki ayı göster satır başlığı +son söz +Bu video Pencere İçinde Pencere modunda oynatılıyor Değer veya daha büyük olmalıdır. PB Lütfen başına "" ekleyin. "" adresi eksik. @@ -106,9 +124,11 @@ sesi aç time 10 sn. atlamak için sola veya sağa iki kez dokunun +özet oynatmayı duraklat düğme diğer seçenekler +giriş Sesi kapat Kapalı Tam ekran @@ -119,12 +139,15 @@ Yıl liste kutusu ses kanalını aç +kaynakça ölçüm aracı +sonsöz +terimler sözlüğü Ay seçim panelini göster ses zaman çizelgesi temizleyici +teşekkür zaman seçici Seçenekler -Şimdi Pencere içinde Pencere modunda tam ekrana geç kartlar log @@ -135,22 +158,31 @@ ipucu Oynat Eklenti yüklenemedi. +kaynakça referansı sesi kapat alt yazıları gizle dizin +künye blok alıntı medyayı indir Lütfen bu metni karakter veya daha fazla olacak şekilde uzatın (şu anda 1 karakter kullanıyorsunuz). Lütfen istenen biçimi eşleştirin. +önsöz geçen süre Lütfen listeden bir öğe seçin. sütun başlığı +katkıda bulunan Dosya Seç Diğer... +son notlar TB tab İndir +geri bağlantı +ipucu +grafik dokümanı Saat +dizin kalan süre altyazıların görüntülenmesini durdur HTML içeriği @@ -160,24 +192,33 @@ ayırıcı Dosya seçilmedi arama metni alanı +bildirim +not referansı altyazıları görüntülemeye başla Bu hafta filmi tam ekran modunda oynat yyyy Altyazılar Geçersiz değer. +bölüm araç çubuğu dosya Lütfen bir dosya seçin. radyo düğmesi +ithaf +yazım hataları Dakika medya kontrolü +grafik sembolü tanım listesi başlık +içindekiler uzaktan oynatmayı kontrol et +alt başlık Pencere İçinde Pencere Lütfen bu seçeneklerden birini belirleyin. oynatmayı başlat +bölüm Şimdi TV'nize yayınlanıyor main Video oynatma hatası @@ -191,6 +232,7 @@ Diğer... uyarı tarihinde başlayan +dipnot hücre menü \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_uk.xtb b/chromium/content/app/strings/translations/content_strings_uk.xtb index 79a10ad7290..1c2f2e07482 100644 --- a/chromium/content/app/strings/translations/content_strings_uk.xtb +++ b/chromium/content/app/strings/translations/content_strings_uk.xtb @@ -10,28 +10,34 @@ додатково область виділення повзунок +кінцева виноска діалогове вікно +подяки стаття банер регіон Цей місяць Вибрати файли +цитата Введіть електронну адресу. Інші... вікно сповіщення сітка дерева д.п./п.п. відтворити на віддаленому пристрої +приклад дд статус Має бути або раніша дата. Змінено на дзеркалювання Символ "" у "" використано неправильно. Сьогодні +список сторінок Скоротіть текст до такої кількості символів або менше: (наразі використано символів: ). Тиждень , р. Композиція прапорець +розрив сторінки засіб вибору дати й часу День Заповніть це поле. @@ -40,6 +46,7 @@ відтворити Введіть дійсне значення. Найближче дійсне значення: . Надіслати +графічний об’єкт нижній колонтитул Автозаповнення аудіо @@ -70,30 +77,41 @@ У тексті має бути на менше символів (ви ввели символів). Частина перед знаком "" не може містити символ "". Вийти з повноекранного режиму +висновок додаток +пролог смуга прокрутки Мілісекунди зображення +передмова Показати попередній місяць вибір кольору панель меню інформація про вміст Введіть дійсне значення. Два найближчі дійсні значення: і . +Запитання кнопка меню +посилання на глосарій Деталі форма дерево Частина після знака "" не може містити символ "". +додаток +обкладинка панель вкладок відео пошук Введіть число. +бібліографічний запис Вибрано Увімкнути звук Введіть частину електронної адреси після знака "". Електронна адреса "" неповна. індикатор перебігу +епіграф Показати наступний місяць заголовок рядка +епілог +Це відео відтворюється в режимі "Картинка в картинці" Значення має бути більшим або дорівнювати . Пб Введіть частину електронної адреси до знака "". Електронна адреса "" неповна. @@ -106,9 +124,11 @@ увімкнути звук час Двічі торкніться ліворуч або праворуч, щоб пропустити 10 с +автореферат призупинити відтворення кнопка більше опцій +вступ Вимкнути звук Вимк. Повноекранний режим @@ -119,12 +139,15 @@ Рік вікно списку увімкнути звукову доріжку +бібліографія вимірювач +післямова +глосарій Показати панель вибору місяців повзунок часу відтворення аудіо +слова подяки засіб вибору часу Параметри -Зараз у режимі "Картинка в картинці" увійти в повноекранний режим стрічка журнал @@ -135,22 +158,31 @@ підказка Відтворити Не вдалося завантажити плагін. +посилання на бібліографію вимкнути звук сховати приховані субтирти каталог +емблема видавництва цитата завантажити медіафайл У тексті має бути не менше стількох символів: . Наразі ви ввели 1 символ. Виберіть потрібний формат. +передмова минуло часу Виберіть елемент зі списку. заголовок стовпця +подяка Вибрати файл Інші... +кінцеві виноски ТБ вкладка Завантажити +зворотне посилання +порада +графічний документ Години +покажчик залишилось часу сховати приховані субтитри Вміст HTML @@ -160,24 +192,33 @@ розділювач Файл не вибрано текстове поле пошуку +примітка +посилання на примітку показати приховані субтитри Цей тиждень відтворити фільм у повноекранному режимі рррр Субтитри Недійсне значення +частина панель інструментів файлів: Виберіть файл. перемикач +присвята +перелік описок Хвилини елемент керування мультимедіа +графічний символ список визначень заголовок +зміст керувати віддаленим відтворенням +підзаголовок Картинка в картинці Виберіть один із запропонованих варіантів. розпочати відтворення +розділ Транслюється на телевізор головний Помилка відтворення відео @@ -191,6 +232,7 @@ Інші... сповіщення , починається +виноска клітинка меню \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_vi.xtb b/chromium/content/app/strings/translations/content_strings_vi.xtb index 7f5639ae1ac..92c65db3683 100644 --- a/chromium/content/app/strings/translations/content_strings_vi.xtb +++ b/chromium/content/app/strings/translations/content_strings_vi.xtb @@ -10,28 +10,34 @@ bổ sung bảng chữ chạy thanh trượt +chú thích cuối hộp thoại +ghi nhận tác giả bài viết biểu ngữ khu vực Tháng này Chọn tệp +đoạn trích dẫn Vui lòng nhập địa chỉ email không trống. Khác... alert_dialog lưới dạng cây SA/CH phát trên thiết bị từ xa +ví dụ dd trạng thái Giá trị phải là hoặc sớm hơn. Đã chuyển sang chế độ phản chiếu '' bị sử dụng sai vị trí trong ''. Hôm nay +danh sách trang Hãy cắt ngắn văn bản này thành ký tự hoặc ít hơn (bạn hiện đang sử dụng ký tự). Tuần , Bản nhạc hộp kiểm +ngắt trang bộ chọn ngày và giờ Ngày Vui lòng điền vào trường này. @@ -40,6 +46,7 @@ phát Vui lòng nhập giá trị hợp lệ. Giá trị hợp lệ gần nhất là . Gửi +đối tượng đồ họa chân trang Tự động điền âm thanh @@ -70,30 +77,41 @@ Hãy kéo dài văn bản này thành ký tự trở lên (bạn hiện đang sử dụng ký tự). Phần đứng trước '' không được chứa biểu tượng ''. Thoát chế độ toàn màn hình +kết luận ứng dụng +phần mở đầu thanh cuộn Mili giây hình ảnh +lời tựa Hiển thị tháng trước công cụ chọn màu thanh menu thông tin nội dung Vui lòng nhập giá trị hợp lệ. Hai giá trị hợp lệ gần nhất là . +Giải đáp nút menu +tham chiếu bảng thuật ngữ Chi tiết biểu mẫu cây Phần đứng sau '' không được chứa biểu tượng ''. +phụ lục +trang bìa bảng điều khiển tab video search Vui lòng nhập một số. +mục nhập danh mục Đã chọn Bật âm thanh Vui lòng nhập phần đứng sau ''. '' không hoàn chỉnh. chỉ báo tiến trình +lời đề từ Hiển thị tháng tiếp theo tiêu đề hàng +lời kết +Video này đang phát ở chế độ Hình trong hình Giá trị phải lớn hơn hoặc bằng . PB Vui lòng nhập phần đứng trước ''. '' không hoàn chỉnh. @@ -106,9 +124,11 @@ bật tiếng thời gian Nhấn đúp vào bên trái hoặc phải để bỏ qua sau 10 giây +bản tóm tắt tạm dừng phát lại nút tùy chọn khác +giới thiệu Tắt tiếng Tắt Toàn màn hình @@ -119,12 +139,15 @@ Năm hộp danh sách bật tiếng bản âm thanh +danh mục thước đo +lời bạt +bảng thuật ngữ Hiển thị bảng lựa chọn tháng trình kiểm soát thời gian âm thanh +lời cảm ơn bộ chọn giờ Tùy chọn -Hiện đang ở chế độ hình trong hình vào chế độ toàn màn hình nguồn cấp dữ liệu nhật ký @@ -135,22 +158,31 @@ chú giải công cụ Phát Không thể tải plugin. +tham chiếu danh mục tắt tiếng ẩn phụ đề chi tiết thư mục +lời ghi cuối sách khung trích dẫn tải xuống phương tiện Vui lòng kéo dài văn bản này thành ký tự trở lên (bạn hiện đang sử dụng 1 ký tự). Vui lòng khớp với định dạng được yêu cầu. +lời nói đầu thời gian trôi qua Vui lòng chọn một mục trong danh sách. tiêu đề cột +ghi nhận tác giả Chọn tệp Khác... +chú thích cuối TB tab Tải xuống +liên kết ngược +mẹo +tài liệu đồ họa Giờ +chú dẫn thời gian còn lại ngừng hiển thị phụ đề chi tiết Nội dung HTML @@ -160,24 +192,33 @@ bộ chia Không có tệp nào được chọn trường văn bản tìm kiếm +lưu ý +tham chiếu chú thích bắt đầu hiển thị phụ đề chi tiết Tuần này phát phim ở chế độ toàn màn hình yyyy Phụ đề Giá trị không hợp lệ. +phần thanh công cụ tệp Vui lòng chọn một tệp. nút radio +lời đề tặng +bản đính chính Phút kiểm soát phương tiện +biểu tượng đồ họa danh sách định nghĩa đầu đề +mục lục điều khiển phát lại từ xa +phụ đề Chế độ hình trong hình Vui lòng chọn một trong các tùy chọn sau. bắt đầu phát lại +chương Hiện đang truyền tới TV của bạn chính Lỗi phát lại video @@ -191,6 +232,7 @@ Khác... thông báo , bắt đầu vào +chú thích cuối trang ô menu \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_zh-CN.xtb b/chromium/content/app/strings/translations/content_strings_zh-CN.xtb index b582ad3abda..d6d7b75efc6 100644 --- a/chromium/content/app/strings/translations/content_strings_zh-CN.xtb +++ b/chromium/content/app/strings/translations/content_strings_zh-CN.xtb @@ -10,28 +10,34 @@ 补充内容 marquee 滑块 +尾注 对话框 +参与者名单 文章标记 横幅 区域 本月 选择文件 +重要引述 请输入有效的电子邮件地址。 其他... 提醒对话框 树状网格 上午/下午 在远程设备上播放 +示例 状态 指定的值不得晚于 已切换到镜像 ”中“”的位置不正确。 今天 +页面列表 请将该文本减少为 个字符或更少(您当前使用了 个字符)。 年第 字幕轨 复选框 +分页符 日期和时间选择器 请填写此字段。 @@ -40,6 +46,7 @@ 播放 请输入有效值。最接近的有效值为 提交 +图形对象 页脚 自动填充 音频 @@ -70,30 +77,41 @@ 请将该文本增加为 个字符或更多(您当前使用的是 个字符)。 ”前面的内容不应包含符号“”。 退出全屏模式 +总结 应用 +序言 滚动条 毫秒 图形 +前言 显示上一个月 颜色选择器 菜单栏 内容信息 请输入有效值。两个最接近的有效值分别为 +问答 菜单按钮 +词汇表参考资料 详细信息 表单 tree ”后面的内容不应包含符号“”。 +附录 +封面 标签面板 视频 搜索 请输入一个数字。 +参考书目 选择了 取消静音 请在“”后面输入内容。“”不完整。 进度指示器 +题词 显示下一个月 行标题 +结语 +此视频正在“画中画”模式中播放 值必须大于或等于 PB 请在“”前面输入内容。“”不完整。 @@ -106,9 +124,11 @@ 取消静音 time 在左侧或右侧点按两次即可跳过 10 秒 +摘要 暂停播放 按钮 更多选项 +简介 静音 关闭 全屏 @@ -119,12 +139,15 @@ 列表框 取消对音轨静音 +参考书目 计量条 +后记 +词汇表 显示月份选择面板 音频时间进度条 +致谢 时间选择器 选项 -现为画中画模式 进入全屏模式 Feed 日志 @@ -135,22 +158,31 @@ 提示 播放 无法加载插件。 +参考书目 静音 隐藏可选字幕 目录 +版权页 引用标记 下载媒体 请将该内容增加到 个或更多字符(目前您使用了 1 个字符)。 请与所请求的格式保持一致。 +序言 已播放时间 请在列表中选择一项。 列标题 +感谢 选择文件 其他... +尾注 TB 标签 下载 +返回链接 +提示 +图形文档 小时 +索引 剩余时间 停止显示可选字幕 HTML 内容 @@ -160,24 +192,33 @@ 分离器 未选择任何文件 搜索文本字段 +通知 +备注参考资料 开始显示可选字幕 本周 在全屏模式下播放电影 字幕 值无效。 +部分 工具栏 个文件 请选择一个文件。 单选按钮 +献辞 +勘误表 分钟 媒体控件 +图形符号 定义列表 标题 +目录 控制远程播放 +副标题 画中画 请从这些选项中选择一个。 开始播放 +章节 现在正投射到您的电视上 主体内容 视频播放出错 @@ -191,6 +232,7 @@ 其他... 提醒 ,从 开始 +脚注 单元格 菜单 \ No newline at end of file diff --git a/chromium/content/app/strings/translations/content_strings_zh-TW.xtb b/chromium/content/app/strings/translations/content_strings_zh-TW.xtb index 6b890983fe9..06fcccba466 100644 --- a/chromium/content/app/strings/translations/content_strings_zh-TW.xtb +++ b/chromium/content/app/strings/translations/content_strings_zh-TW.xtb @@ -10,28 +10,34 @@ 補充 marquee 滑桿 +章節附註 對話方塊 +參與名單 文章 橫幅 區域 本月 選擇檔案 +重要引述 請輸入電子郵件地址。 其他... 警示對話方塊 樹狀目錄網格 AM/PM 在遠端裝置上播放 +範例 狀態 必須輸入 或之前的值。 已切換至鏡像模式 」未放在「」的正確位置。 今天 +頁面清單 請將這段文字刪減至 個字元以下 (目前的字元數為 個)。 年,第 曲目 核取方塊 +分頁符號 日期和時間選擇器 請填寫這個欄位。 @@ -40,6 +46,7 @@ 播放 請輸入有效值。最接近的有效值是 提交 +圖形物件 頁尾 自動填入 音訊 @@ -70,30 +77,41 @@ 請將這段文字加長到 個字元以上 (目前使用字元數:)。 」後面的部分不應包含「」符號。 結束全螢幕 +結論 應用程式 +序言 捲軸 毫秒 圖形 +前言 顯示上一個月 顏色選擇器 選單列 內容資訊 請輸入有效值。最接近的兩個有效值分別是 +問與答 選單按鈕 +詞彙解釋參考資料 詳細資訊 表單 tree 」後面的部分不應包含「」符號。 +附錄 +封面 分頁面板 影片 search 請輸入一個數字。 +參考書目 已選取 個項目 取消靜音 請輸入「」後面的部分。「」不是完整值。 進度指標 +題詞 顯示下一個月 列標題 +結尾 +目前在子母畫面模式中播放這部影片 值必須大於或等於 PB 請輸入「」後面的部分。「」不是完整值。 @@ -106,9 +124,11 @@ 取消靜音 time 輕觸兩下左側或右側可跳過 10 秒 +摘要 暫停播放 按鈕 更多選項 +簡介 靜音 關閉 全螢幕 @@ -119,12 +139,15 @@ 清單方塊 取消音軌靜音 +參考書目 計量器 +後記 +詞彙解釋 顯示月份選取面板 音訊時間點拖曳工具 +鳴謝 時間選擇器 選項 -目前在子母畫面模式中 進入全螢幕 資訊提供 記錄 @@ -135,22 +158,31 @@ 工具提示 播放 無法載入外掛程式。 +參考書目資料 靜音 不顯示字幕 目錄 +版權頁標記 引用標記 下載媒體 請將這段文字加長到 個字元以上 (目前已有 1 個字元)。 請符合要求的格式。 +前言 已播放時間 請選取一個清單中的項目。 欄標題 +參與名單 選擇檔案 其他... +章節附註 TB 分頁 下載 +反向連結 +提示 +圖形文件 小時 +索引 剩餘時間 停止顯示字幕 HTML 內容 @@ -160,24 +192,33 @@ 分割器 未選擇任何檔案 搜尋文字欄位 +聲明 +附註參考資料 開始顯示字幕 本週 使用全螢幕模式播放電影 字幕 無效的值。 +分輯 工具列 個檔案 請選取檔案。 圓形按鈕 +獻辭 +勘誤表 分鐘 媒體控制 +圖形符號 定義清單 標題 +目錄 控制遠端播放 +副標題 子母畫面 請選取其中一個選項。 開始播放 +章節 正在投放到電視上 主要元素 影片播放錯誤 @@ -191,6 +232,7 @@ 其他... 警示 ,從 開始 +註腳 儲存格 選單 \ No newline at end of file diff --git a/chromium/content/browser/BUILD.gn b/chromium/content/browser/BUILD.gn index a967e1b15f3..5747496dc5f 100644 --- a/chromium/content/browser/BUILD.gn +++ b/chromium/content/browser/BUILD.gn @@ -7,7 +7,6 @@ import("//build/config/features.gni") import("//build/config/jumbo.gni") import("//build/config/linux/pangocairo/pangocairo.gni") import("//build/config/ui.gni") -import("//content/public/common/zygote_features.gni") import("//gpu/vulkan/features.gni") import("//media/media_options.gni") import("//net/features.gni") @@ -71,18 +70,19 @@ jumbo_source_set("browser") { "//content/browser/background_fetch:background_fetch_proto", "//content/browser/background_sync:background_sync_proto", "//content/browser/cache_storage:cache_storage_proto", + "//content/browser/cookie_store:cookie_store_proto", "//content/browser/devtools:devtools_resources", "//content/browser/devtools:protocol_sources", "//content/browser/dom_storage:local_storage_proto", "//content/browser/notifications:notification_proto", "//content/browser/payments:payment_app_proto", + "//content/browser/process_internals:mojo_bindings", "//content/browser/service_worker:service_worker_proto", "//content/browser/speech/proto", "//content/common", "//content/common:buildflags", "//content/common:mojo_bindings", "//content/public/common:common_sources", - "//content/public/common:zygote_buildflags", "//crypto", "//device/bluetooth", "//device/fido", @@ -97,6 +97,7 @@ jumbo_source_set("browser") { "//gpu/ipc/common:gpu_preferences_util", "//gpu/ipc/host", "//gpu/vulkan:buildflags", + "//jingle:jingle_glue", "//media", "//media:media_buildflags", "//media/capture", @@ -107,8 +108,6 @@ jumbo_source_set("browser") { "//media/mojo/interfaces", "//media/mojo/interfaces:constants", "//media/mojo/services", - "//mojo/common", - "//mojo/common:values_struct_traits", "//mojo/edk", "//mojo/public/cpp/bindings", "//mojo/public/js:resources", @@ -119,7 +118,7 @@ jumbo_source_set("browser") { "//printing/buildflags", "//services/audio:lib", "//services/audio/public/cpp", - "//services/audio/public/mojom:constants", + "//services/audio/public/mojom", "//services/catalog:constants", "//services/catalog/public/cpp", "//services/catalog/public/mojom:constants", @@ -139,11 +138,12 @@ jumbo_source_set("browser") { "//services/resource_coordinator:lib", "//services/resource_coordinator/public/cpp:resource_coordinator_cpp", "//services/service_manager", - "//services/service_manager/embedder", + "//services/service_manager/embedder:embedder_result_codes", "//services/service_manager/public/cpp", "//services/service_manager/public/mojom", "//services/service_manager/runner/common", "//services/service_manager/runner/host:lib", + "//services/service_manager/zygote:zygote_buildflags", "//services/shape_detection:lib", "//services/shape_detection/public/mojom", "//services/tracing:lib", @@ -172,6 +172,9 @@ jumbo_source_set("browser") { "//third_party/icu", "//third_party/libyuv", "//third_party/re2", + "//third_party/webrtc/media:rtc_media_base", + "//third_party/webrtc/modules/desktop_capture:primitives", + "//third_party/webrtc/rtc_base:rtc_base", "//third_party/zlib", "//third_party/zlib/google:compression_utils", "//third_party/zlib/google:zip", @@ -206,8 +209,8 @@ jumbo_source_set("browser") { ":accessibility_buildflags", "//ipc", "//media/mojo/interfaces:remoting", + "//third_party/blink/public:embedded_frame_sink_mojo_bindings", "//third_party/blink/public:media_devices_mojo_bindings", - "//third_party/blink/public:offscreen_canvas_mojo_bindings", "//third_party/leveldatabase", ] @@ -253,10 +256,6 @@ jumbo_source_set("browser") { "$target_gen_dir/devtools/protocol/tracing.h", "../common/service_manager/child_connection.cc", "../common/service_manager/child_connection.h", - "../zygote/zygote_linux.cc", - "../zygote/zygote_linux.h", - "../zygote/zygote_main.h", - "../zygote/zygote_main_linux.cc", "accessibility/accessibility_tree_formatter.cc", "accessibility/accessibility_tree_formatter.h", "accessibility/accessibility_tree_formatter_blink.cc", @@ -323,8 +322,6 @@ jumbo_source_set("browser") { "android/launcher_thread.h", "android/scoped_surface_request_manager.cc", "android/scoped_surface_request_manager.h", - "android/string_message_codec.cc", - "android/string_message_codec.h", "android/text_suggestion_host_android.cc", "android/text_suggestion_host_android.h", "android/text_suggestion_host_mojo_impl_android.cc", @@ -413,6 +410,8 @@ jumbo_source_set("browser") { "background_fetch/background_fetch_event_dispatcher.h", "background_fetch/background_fetch_job_controller.cc", "background_fetch/background_fetch_job_controller.h", + "background_fetch/background_fetch_metrics.cc", + "background_fetch/background_fetch_metrics.h", "background_fetch/background_fetch_registration_id.cc", "background_fetch/background_fetch_registration_id.h", "background_fetch/background_fetch_registration_notifier.cc", @@ -438,8 +437,16 @@ jumbo_source_set("browser") { "background_fetch/storage/get_developer_ids_task.h", "background_fetch/storage/get_metadata_task.cc", "background_fetch/storage/get_metadata_task.h", + "background_fetch/storage/get_num_requests_task.cc", + "background_fetch/storage/get_num_requests_task.h", + "background_fetch/storage/get_settled_fetches_task.cc", + "background_fetch/storage/get_settled_fetches_task.h", "background_fetch/storage/mark_registration_for_deletion_task.cc", "background_fetch/storage/mark_registration_for_deletion_task.h", + "background_fetch/storage/mark_request_complete_task.cc", + "background_fetch/storage/mark_request_complete_task.h", + "background_fetch/storage/start_next_pending_request_task.cc", + "background_fetch/storage/start_next_pending_request_task.h", "background_fetch/storage/update_registration_ui_task.cc", "background_fetch/storage/update_registration_ui_task.h", "background_sync/background_sync_context.cc", @@ -493,7 +500,8 @@ jumbo_source_set("browser") { "browser_main.h", "browser_main_loop.cc", "browser_main_loop.h", - "browser_main_runner.cc", + "browser_main_runner_impl.cc", + "browser_main_runner_impl.h", "browser_plugin/browser_plugin_embedder.cc", "browser_plugin/browser_plugin_embedder.h", "browser_plugin/browser_plugin_guest.cc", @@ -569,6 +577,14 @@ jumbo_source_set("browser") { # needed on all platforms. "compositor/surface_utils.cc", "compositor/surface_utils.h", + "cookie_store/cookie_change_subscription.cc", + "cookie_store/cookie_change_subscription.h", + "cookie_store/cookie_store_context.cc", + "cookie_store/cookie_store_context.h", + "cookie_store/cookie_store_host.cc", + "cookie_store/cookie_store_host.h", + "cookie_store/cookie_store_manager.cc", + "cookie_store/cookie_store_manager.h", "dedicated_worker/dedicated_worker_host.cc", "dedicated_worker/dedicated_worker_host.h", "devtools/browser_devtools_agent_host.cc", @@ -592,6 +608,12 @@ jumbo_source_set("browser") { "devtools/devtools_pipe_handler.h", "devtools/devtools_session.cc", "devtools/devtools_session.h", + "devtools/devtools_stream_blob.cc", + "devtools/devtools_stream_blob.h", + "devtools/devtools_stream_file.cc", + "devtools/devtools_stream_file.h", + "devtools/devtools_stream_pipe.cc", + "devtools/devtools_stream_pipe.h", "devtools/devtools_target_registry.cc", "devtools/devtools_target_registry.h", "devtools/devtools_traceable_screenshot.cc", @@ -684,12 +706,20 @@ jumbo_source_set("browser") { "dom_storage/local_storage_context_mojo.h", "dom_storage/session_storage_context_mojo.cc", "dom_storage/session_storage_context_mojo.h", + "dom_storage/session_storage_data_map.cc", + "dom_storage/session_storage_data_map.h", "dom_storage/session_storage_database.cc", "dom_storage/session_storage_database.h", "dom_storage/session_storage_database_adapter.cc", "dom_storage/session_storage_database_adapter.h", + "dom_storage/session_storage_leveldb_wrapper.cc", + "dom_storage/session_storage_leveldb_wrapper.h", + "dom_storage/session_storage_metadata.cc", + "dom_storage/session_storage_metadata.h", "dom_storage/session_storage_namespace_impl.cc", "dom_storage/session_storage_namespace_impl.h", + "dom_storage/session_storage_namespace_impl_mojo.cc", + "dom_storage/session_storage_namespace_impl_mojo.h", "download/blob_download_url_loader_factory_getter.cc", "download/blob_download_url_loader_factory_getter.h", "download/byte_stream_input_stream.cc", @@ -704,6 +734,8 @@ jumbo_source_set("browser") { "download/download_request_utils.cc", "download/download_resource_handler.cc", "download/download_resource_handler.h", + "download/download_url_loader_factory_getter_impl.cc", + "download/download_url_loader_factory_getter_impl.h", "download/download_utils.cc", "download/download_utils.h", "download/drag_download_file.cc", @@ -736,6 +768,8 @@ jumbo_source_set("browser") { "file_url_loader_factory.h", "fileapi/browser_file_system_helper.cc", "fileapi/browser_file_system_helper.h", + "fileapi/file_system_url_loader_factory.cc", + "fileapi/file_system_url_loader_factory.h", "fileapi/fileapi_message_filter.cc", "fileapi/fileapi_message_filter.h", "find_request_manager.cc", @@ -743,10 +777,10 @@ jumbo_source_set("browser") { "font_list_async.cc", "frame_host/ancestor_throttle.cc", "frame_host/ancestor_throttle.h", + "frame_host/blocked_scheme_navigation_throttle.cc", + "frame_host/blocked_scheme_navigation_throttle.h", "frame_host/cross_process_frame_connector.cc", "frame_host/cross_process_frame_connector.h", - "frame_host/data_url_navigation_throttle.cc", - "frame_host/data_url_navigation_throttle.h", "frame_host/debug_urls.cc", "frame_host/debug_urls.h", "frame_host/form_submission_throttle.cc", @@ -761,8 +795,6 @@ jumbo_source_set("browser") { "frame_host/frame_tree_node_blame_context.h", "frame_host/input/input_injector_impl.cc", "frame_host/input/input_injector_impl.h", - "frame_host/input/legacy_ipc_frame_input_handler.cc", - "frame_host/input/legacy_ipc_frame_input_handler.h", "frame_host/interstitial_page_impl.cc", "frame_host/interstitial_page_impl.h", "frame_host/interstitial_page_navigator_impl.cc", @@ -808,6 +840,8 @@ jumbo_source_set("browser") { "frame_host/render_frame_proxy_host.h", "frame_host/render_widget_host_view_guest.cc", "frame_host/render_widget_host_view_guest.h", + "frame_host/webui_navigation_throttle.cc", + "frame_host/webui_navigation_throttle.h", "generic_sensor/sensor_provider_proxy_impl.cc", "generic_sensor/sensor_provider_proxy_impl.h", "geolocation/geolocation_service_impl.cc", @@ -818,8 +852,8 @@ jumbo_source_set("browser") { "gpu/browser_gpu_memory_buffer_manager.h", "gpu/compositor_util.cc", "gpu/compositor_util.h", - "gpu/gpu_client.cc", - "gpu/gpu_client.h", + "gpu/gpu_client_impl.cc", + "gpu/gpu_client_impl.h", "gpu/gpu_data_manager_impl.cc", "gpu/gpu_data_manager_impl.h", "gpu/gpu_data_manager_impl_private.cc", @@ -836,13 +870,11 @@ jumbo_source_set("browser") { "gpu/shader_cache_factory.h", "histogram_controller.cc", "histogram_controller.h", - "histogram_internals_request_job.cc", - "histogram_internals_request_job.h", - "histogram_internals_url_loader.cc", - "histogram_internals_url_loader.h", "histogram_subscriber.h", "histogram_synchronizer.cc", "histogram_synchronizer.h", + "histograms_internals_ui.cc", + "histograms_internals_ui.h", "image_capture/image_capture_impl.cc", "image_capture/image_capture_impl.h", "indexed_db/cursor_impl.cc", @@ -925,12 +957,15 @@ jumbo_source_set("browser") { "indexed_db/leveldb/leveldb_write_batch.cc", "indexed_db/leveldb/leveldb_write_batch.h", "indexed_db/list_set.h", + "initiator_csp_context.cc", + "initiator_csp_context.h", "installedapp/installed_app_provider_impl_default.cc", "installedapp/installed_app_provider_impl_default.h", "interface_provider_filtering.cc", "interface_provider_filtering.h", "isolated_origin_util.cc", "isolated_origin_util.h", + "keyboard_lock/keyboard_lock_metrics.h", "keyboard_lock/keyboard_lock_service_impl.cc", "keyboard_lock/keyboard_lock_service_impl.h", "leveldb_wrapper_impl.cc", @@ -959,20 +994,14 @@ jumbo_source_set("browser") { "loader/mojo_async_resource_handler.h", "loader/navigation_loader_interceptor.cc", "loader/navigation_loader_interceptor.h", - "loader/navigation_metrics.cc", - "loader/navigation_metrics.h", - "loader/navigation_resource_handler.cc", - "loader/navigation_resource_handler.h", + "loader/navigation_loader_util.cc", + "loader/navigation_loader_util.h", "loader/navigation_url_loader.cc", "loader/navigation_url_loader.h", "loader/navigation_url_loader_delegate.h", "loader/navigation_url_loader_factory.h", "loader/navigation_url_loader_impl.cc", "loader/navigation_url_loader_impl.h", - "loader/navigation_url_loader_impl_core.cc", - "loader/navigation_url_loader_impl_core.h", - "loader/navigation_url_loader_network_service.cc", - "loader/navigation_url_loader_network_service.h", "loader/null_resource_controller.cc", "loader/null_resource_controller.h", "loader/prefetch_url_loader.cc", @@ -1040,6 +1069,16 @@ jumbo_source_set("browser") { "media/android/media_web_contents_observer_android.h", "media/audible_metrics.cc", "media/audible_metrics.h", + "media/audio_input_stream_broker.cc", + "media/audio_input_stream_broker.h", + "media/audio_loopback_stream_broker.cc", + "media/audio_loopback_stream_broker.h", + "media/audio_muting_session.cc", + "media/audio_muting_session.h", + "media/audio_output_stream_broker.cc", + "media/audio_output_stream_broker.h", + "media/audio_stream_broker.cc", + "media/audio_stream_broker.h", "media/audio_stream_monitor.cc", "media/audio_stream_monitor.h", "media/capture/audio_mirroring_manager.cc", @@ -1054,6 +1093,10 @@ jumbo_source_set("browser") { "media/capture/web_contents_tracker.h", "media/cdm_registry_impl.cc", "media/cdm_registry_impl.h", + "media/flinging_renderer.cc", + "media/flinging_renderer.h", + "media/forwarding_audio_stream_factory.cc", + "media/forwarding_audio_stream_factory.h", "media/media_devices_permission_checker.cc", "media/media_devices_permission_checker.h", "media/media_devices_util.cc", @@ -1146,8 +1189,6 @@ jumbo_source_set("browser") { "notifications/notification_event_dispatcher_impl.h", "notifications/notification_id_generator.cc", "notifications/notification_id_generator.h", - "notifications/notification_message_filter.cc", - "notifications/notification_message_filter.h", "notifications/platform_notification_context_impl.cc", "notifications/platform_notification_context_impl.h", "origin_manifest/origin_manifest_parser.cc", @@ -1176,6 +1217,10 @@ jumbo_source_set("browser") { "picture_in_picture/picture_in_picture_window_controller_impl.h", "presentation/presentation_service_impl.cc", "presentation/presentation_service_impl.h", + "process_internals/process_internals_handler_impl.cc", + "process_internals/process_internals_handler_impl.h", + "process_internals/process_internals_ui.cc", + "process_internals/process_internals_ui.h", "push_messaging/push_messaging_context.cc", "push_messaging/push_messaging_context.h", "push_messaging/push_messaging_manager.cc", @@ -1195,6 +1240,10 @@ jumbo_source_set("browser") { "renderer_host/display_util.h", "renderer_host/dwrite_font_proxy_message_filter_win.cc", "renderer_host/dwrite_font_proxy_message_filter_win.h", + "renderer_host/embedded_frame_sink_impl.cc", + "renderer_host/embedded_frame_sink_impl.h", + "renderer_host/embedded_frame_sink_provider_impl.cc", + "renderer_host/embedded_frame_sink_provider_impl.h", "renderer_host/event_with_latency_info.h", "renderer_host/file_utilities_host_impl.cc", "renderer_host/file_utilities_host_impl.h", @@ -1210,6 +1259,10 @@ jumbo_source_set("browser") { "renderer_host/frame_token_message_queue.h", "renderer_host/input/fling_controller.cc", "renderer_host/input/fling_controller.h", + "renderer_host/input/fling_scheduler.cc", + "renderer_host/input/fling_scheduler.h", + "renderer_host/input/fling_scheduler_mac.h", + "renderer_host/input/fling_scheduler_mac.mm", "renderer_host/input/gesture_event_queue.cc", "renderer_host/input/gesture_event_queue.h", "renderer_host/input/input_device_change_observer.cc", @@ -1221,10 +1274,6 @@ jumbo_source_set("browser") { "renderer_host/input/input_router_config_helper.h", "renderer_host/input/input_router_impl.cc", "renderer_host/input/input_router_impl.h", - "renderer_host/input/legacy_input_router_impl.cc", - "renderer_host/input/legacy_input_router_impl.h", - "renderer_host/input/legacy_ipc_widget_input_handler.cc", - "renderer_host/input/legacy_ipc_widget_input_handler.h", "renderer_host/input/motion_event_web.cc", "renderer_host/input/motion_event_web.h", "renderer_host/input/mouse_wheel_event_queue.cc", @@ -1278,7 +1327,6 @@ jumbo_source_set("browser") { "renderer_host/input/synthetic_touchscreen_pinch_gesture.h", "renderer_host/input/tap_suppression_controller.cc", "renderer_host/input/tap_suppression_controller.h", - "renderer_host/input/tap_suppression_controller_client.h", "renderer_host/input/timeout_monitor.cc", "renderer_host/input/timeout_monitor.h", "renderer_host/input/touch_action_filter.cc", @@ -1306,8 +1354,6 @@ jumbo_source_set("browser") { "renderer_host/media/audio_input_delegate_impl.h", "renderer_host/media/audio_input_device_manager.cc", "renderer_host/media/audio_input_device_manager.h", - "renderer_host/media/audio_input_renderer_host.cc", - "renderer_host/media/audio_input_renderer_host.h", "renderer_host/media/audio_input_stream_handle.cc", "renderer_host/media/audio_input_stream_handle.h", "renderer_host/media/audio_output_authorization_handler.cc", @@ -1316,8 +1362,8 @@ jumbo_source_set("browser") { "renderer_host/media/audio_output_delegate_impl.h", "renderer_host/media/audio_output_stream_observer_impl.cc", "renderer_host/media/audio_output_stream_observer_impl.h", - "renderer_host/media/audio_renderer_host.cc", - "renderer_host/media/audio_renderer_host.h", + "renderer_host/media/audio_service_listener.cc", + "renderer_host/media/audio_service_listener.h", "renderer_host/media/in_process_launched_video_capture_device.cc", "renderer_host/media/in_process_launched_video_capture_device.h", "renderer_host/media/in_process_video_capture_device_launcher.cc", @@ -1339,6 +1385,12 @@ jumbo_source_set("browser") { "renderer_host/media/media_stream_track_metrics_host.h", "renderer_host/media/media_stream_ui_proxy.cc", "renderer_host/media/media_stream_ui_proxy.h", + "renderer_host/media/old_render_frame_audio_input_stream_factory.cc", + "renderer_host/media/old_render_frame_audio_input_stream_factory.h", + "renderer_host/media/old_render_frame_audio_output_stream_factory.cc", + "renderer_host/media/old_render_frame_audio_output_stream_factory.h", + "renderer_host/media/peer_connection_tracker_host.cc", + "renderer_host/media/peer_connection_tracker_host.h", "renderer_host/media/render_frame_audio_input_stream_factory.cc", "renderer_host/media/render_frame_audio_input_stream_factory.h", "renderer_host/media/render_frame_audio_output_stream_factory.cc", @@ -1369,14 +1421,22 @@ jumbo_source_set("browser") { "renderer_host/media/video_capture_provider_switcher.h", "renderer_host/native_web_keyboard_event_aura.cc", "renderer_host/native_web_keyboard_event_mac.mm", - "renderer_host/offscreen_canvas_provider_impl.cc", - "renderer_host/offscreen_canvas_provider_impl.h", - "renderer_host/offscreen_canvas_surface_impl.cc", - "renderer_host/offscreen_canvas_surface_impl.h", "renderer_host/overscroll_configuration.cc", "renderer_host/overscroll_controller.cc", "renderer_host/overscroll_controller.h", "renderer_host/overscroll_controller_delegate.h", + "renderer_host/p2p/socket_dispatcher_host.cc", + "renderer_host/p2p/socket_dispatcher_host.h", + "renderer_host/p2p/socket_host.cc", + "renderer_host/p2p/socket_host.h", + "renderer_host/p2p/socket_host_tcp.cc", + "renderer_host/p2p/socket_host_tcp.h", + "renderer_host/p2p/socket_host_tcp_server.cc", + "renderer_host/p2p/socket_host_tcp_server.h", + "renderer_host/p2p/socket_host_throttler.cc", + "renderer_host/p2p/socket_host_throttler.h", + "renderer_host/p2p/socket_host_udp.cc", + "renderer_host/p2p/socket_host_udp.h", "renderer_host/render_frame_metadata_provider_impl.cc", "renderer_host/render_frame_metadata_provider_impl.h", "renderer_host/render_message_filter.cc", @@ -1459,6 +1519,8 @@ jumbo_source_set("browser") { "service_worker/service_worker_blob_reader.h", "service_worker/service_worker_cache_writer.cc", "service_worker/service_worker_cache_writer.h", + "service_worker/service_worker_client_info.cc", + "service_worker/service_worker_client_info.h", "service_worker/service_worker_client_utils.cc", "service_worker/service_worker_client_utils.h", "service_worker/service_worker_consts.cc", @@ -1664,6 +1726,8 @@ jumbo_source_set("browser") { "web_package/signed_exchange_certificate_chain.cc", "web_package/signed_exchange_certificate_chain.h", "web_package/signed_exchange_consts.h", + "web_package/signed_exchange_devtools_proxy.cc", + "web_package/signed_exchange_devtools_proxy.h", "web_package/signed_exchange_handler.cc", "web_package/signed_exchange_handler.h", "web_package/signed_exchange_header.cc", @@ -1684,6 +1748,13 @@ jumbo_source_set("browser") { "web_package/web_package_prefetch_handler.h", "web_package/web_package_request_handler.cc", "web_package/web_package_request_handler.h", + "webrtc/webrtc_internals.cc", + "webrtc/webrtc_internals.h", + "webrtc/webrtc_internals_message_handler.cc", + "webrtc/webrtc_internals_message_handler.h", + "webrtc/webrtc_internals_ui.cc", + "webrtc/webrtc_internals_ui.h", + "webrtc/webrtc_internals_ui_observer.h", "websockets/websocket_handshake_request_info_impl.cc", "websockets/websocket_handshake_request_info_impl.h", "websockets/websocket_manager.cc", @@ -1711,19 +1782,21 @@ jumbo_source_set("browser") { "webui/web_ui_message_handler.cc", "webui/web_ui_url_loader_factory.cc", "webui/web_ui_url_loader_factory_internal.h", - "zygote_host/zygote_communication_linux.cc", - "zygote_host/zygote_communication_linux.h", - "zygote_host/zygote_host_impl_linux.cc", - "zygote_host/zygote_host_impl_linux.h", ] if (toolkit_views) { deps += [ "//ui/events" ] } + if (is_linux) { + deps += [ "//services/service_manager/zygote" ] + } + # ChromeOS also defines linux but their memory-monitors conflict. if (is_chromeos) { sources += [ + "media/keyboard_mic_registration.cc", + "media/keyboard_mic_registration.h", "memory/memory_monitor_chromeos.cc", "memory/memory_monitor_chromeos.h", "tracing/cros_tracing_agent.cc", @@ -1736,6 +1809,10 @@ jumbo_source_set("browser") { ] } + if (is_chromeos || is_android || is_chromecast) { + defines += [ "ENABLE_PROTECTED_MEDIA_IDENTIFIER_PERMISSION" ] + } + if (is_chromecast && is_linux) { sources += [ "tracing/cast_tracing_agent.cc", @@ -1761,7 +1838,7 @@ jumbo_source_set("browser") { ] } - if (enable_basic_printing || enable_print_preview) { + if (enable_basic_printing) { deps += [ "//printing" ] } @@ -1782,39 +1859,6 @@ jumbo_source_set("browser") { ] } - if (enable_webrtc) { - sources += [ - "renderer_host/media/peer_connection_tracker_host.cc", - "renderer_host/media/peer_connection_tracker_host.h", - "renderer_host/p2p/socket_dispatcher_host.cc", - "renderer_host/p2p/socket_dispatcher_host.h", - "renderer_host/p2p/socket_host.cc", - "renderer_host/p2p/socket_host.h", - "renderer_host/p2p/socket_host_tcp.cc", - "renderer_host/p2p/socket_host_tcp.h", - "renderer_host/p2p/socket_host_tcp_server.cc", - "renderer_host/p2p/socket_host_tcp_server.h", - "renderer_host/p2p/socket_host_throttler.cc", - "renderer_host/p2p/socket_host_throttler.h", - "renderer_host/p2p/socket_host_udp.cc", - "renderer_host/p2p/socket_host_udp.h", - "webrtc/webrtc_internals.cc", - "webrtc/webrtc_internals.h", - "webrtc/webrtc_internals_message_handler.cc", - "webrtc/webrtc_internals_message_handler.h", - "webrtc/webrtc_internals_ui.cc", - "webrtc/webrtc_internals_ui.h", - "webrtc/webrtc_internals_ui_observer.h", - ] - - deps += [ - "//jingle:jingle_glue", - "//third_party/webrtc/media:rtc_media_base", - "//third_party/webrtc/modules/desktop_capture:primitives", - "//third_party/webrtc/rtc_base:rtc_base", - ] - } - # Desktop/Window/WebContents screen capture implementations, conditionally # built depending on the available implementations for each platform. if (is_linux || is_mac || is_win) { @@ -1822,6 +1866,8 @@ jumbo_source_set("browser") { sources += [ "media/capture/cursor_renderer.cc", "media/capture/cursor_renderer.h", + "media/capture/desktop_capture_device.cc", + "media/capture/desktop_capture_device.h", "media/capture/fake_webcontent_capture_machine.cc", "media/capture/fake_webcontent_capture_machine.h", "media/capture/frame_sink_video_capture_device.cc", @@ -1829,16 +1875,26 @@ jumbo_source_set("browser") { "media/capture/web_contents_video_capture_device.cc", "media/capture/web_contents_video_capture_device.h", ] + public_deps += [ "//third_party/webrtc_overrides:init_webrtc" ] + deps += [ "//third_party/webrtc/modules/desktop_capture" ] if (use_aura) { sources += [ "media/capture/aura_window_capture_machine.cc", "media/capture/aura_window_capture_machine.h", + "media/capture/aura_window_video_capture_device.cc", + "media/capture/aura_window_video_capture_device.h", "media/capture/cursor_renderer_aura.cc", "media/capture/cursor_renderer_aura.h", "media/capture/desktop_capture_device_aura.cc", "media/capture/desktop_capture_device_aura.h", ] } + if (is_chromeos) { + sources += [ + "media/capture/lame_window_capturer_chromeos.cc", + "media/capture/lame_window_capturer_chromeos.h", + ] + } if (is_mac) { sources += [ "media/capture/cursor_renderer_mac.h", @@ -1849,14 +1905,6 @@ jumbo_source_set("browser") { "//sandbox/mac:seatbelt_extension", ] } - if (enable_webrtc) { - sources += [ - "media/capture/desktop_capture_device.cc", - "media/capture/desktop_capture_device.h", - ] - deps += [ "//third_party/webrtc/modules/desktop_capture" ] - public_deps += [ "//third_party/webrtc_overrides:init_webrtc" ] - } } if (is_win) { @@ -2036,6 +2084,8 @@ jumbo_source_set("browser") { "accessibility/captioning_controller.h", "accessibility/web_contents_accessibility_android.cc", "accessibility/web_contents_accessibility_android.h", + "android/content_ui_event_handler.cc", + "android/content_ui_event_handler.h", "android/content_view_core.cc", "android/content_view_core.h", "android/content_view_render_view.cc", @@ -2047,8 +2097,6 @@ jumbo_source_set("browser") { "android/gesture_listener_manager.h", "android/ime_adapter_android.cc", "android/ime_adapter_android.h", - "android/interstitial_page_delegate_android.cc", - "android/interstitial_page_delegate_android.h", "android/java/gin_java_bound_object.cc", "android/java/gin_java_bound_object.h", "android/java/gin_java_bound_object_delegate.cc", @@ -2086,8 +2134,6 @@ jumbo_source_set("browser") { "android/selection/selection_popup_controller.h", "android/selection/smart_selection_client.cc", "android/selection/smart_selection_client.h", - "android/synchronous_compositor_browser_filter.cc", - "android/synchronous_compositor_browser_filter.h", "android/synchronous_compositor_host.cc", "android/synchronous_compositor_host.h", "android/synchronous_compositor_sync_call_bridge.cc", @@ -2098,6 +2144,8 @@ jumbo_source_set("browser") { "android/tracing_controller_android.h", "android/web_contents_observer_proxy.cc", "android/web_contents_observer_proxy.h", + "compositor/in_process_display_client.cc", + "compositor/in_process_display_client.h", "frame_host/render_frame_host_android.cc", "frame_host/render_frame_host_android.h", "media/capture/screen_capture_device_android.cc", @@ -2143,9 +2191,12 @@ jumbo_source_set("browser") { deps += [ ":reflection_jni_headers", "//content/public/android:jni", + "//device/gamepad/public/mojom", "//media", "//media/capture/content/android", "//media/capture/video/android", + "//ui/accessibility:ax_assistant", + "//ui/accessibility/mojom", "//ui/android", "//ui/compositor", ] @@ -2305,8 +2356,6 @@ jumbo_source_set("browser") { "context_factory.cc", "renderer_host/browser_compositor_view_mac.h", "renderer_host/browser_compositor_view_mac.mm", - "renderer_host/compositor_resize_lock.cc", - "renderer_host/compositor_resize_lock.h", "renderer_host/delegated_frame_host.cc", "renderer_host/delegated_frame_host.h", ] @@ -2325,6 +2374,7 @@ jumbo_source_set("browser") { "compositor/vulkan_browser_compositor_output_surface.cc", "compositor/vulkan_browser_compositor_output_surface.h", ] + deps += [ "//gpu/vulkan/init" ] } deps += [ "//components/viz/service", @@ -2351,10 +2401,6 @@ jumbo_source_set("browser") { ] } - if (use_zygote_handle) { - sources += [ "zygote_host/zygote_handle_linux.cc" ] - } - if (enable_reporting) { sources += [ "net/reporting_service_proxy.cc", diff --git a/chromium/content/browser/DEPS b/chromium/content/browser/DEPS index df2d78160a9..59cc7c497cd 100644 --- a/chromium/content/browser/DEPS +++ b/chromium/content/browser/DEPS @@ -53,7 +53,7 @@ include_rules = [ "+third_party/iaccessible2", "+third_party/isimpledom", "+third_party/khronos", # For enum definitions only - "+third_party/libaom/av1_features.h", + "+third_party/libaom/av1_buildflags.h", "+third_party/re2", "+third_party/zlib", @@ -67,10 +67,10 @@ include_rules = [ "-third_party/blink", "+third_party/blink/public/common", "+third_party/blink/public/mojom", + "+third_party/blink/public/platform/resource_request_blocked_reason.h", "+third_party/blink/public/platform/WebAddressSpace.h", "+third_party/blink/public/platform/web_content_security_policy.h", "+third_party/blink/public/platform/web_cursor_info.h", - "+third_party/blink/public/platform/web_display_mode.h", "+third_party/blink/public/platform/web_drag_operation.h", "+third_party/blink/public/platform/web_focus_type.h", "+third_party/blink/public/platform/web_fullscreen_video_status.h", @@ -87,6 +87,7 @@ include_rules = [ "+third_party/blink/public/platform/WebRemoteFrameProperties.h", "+third_party/blink/public/platform/web_screen_info.h", "+third_party/blink/public/platform/web_scroll_into_view_params.h", + "+third_party/blink/public/platform/web_scroll_types.h", "+third_party/blink/public/platform/web_security_style.h", "+third_party/blink/public/platform/web_sudden_termination_disabler_type.h", "+third_party/blink/public/platform/web_touch_event.h", @@ -96,19 +97,15 @@ include_rules = [ "+third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h", "+third_party/blink/public/platform/modules/indexeddb/web_idb_types.h", "+third_party/blink/public/platform/modules/notifications/web_notification_constants.h", - "+third_party/blink/public/platform/modules/screen_orientation/web_lock_orientation_error.h", - "+third_party/blink/public/platform/modules/screen_orientation/web_screen_orientation_lock_type.h", - "+third_party/blink/public/platform/modules/screen_orientation/web_screen_orientation_type.h", "+third_party/blink/public/platform/modules/serviceworker/web_service_worker_error.h", "+third_party/blink/public/public_buildflags.h", - "+third_party/blink/public/web/devtools_agent.mojom.h", - "+third_party/blink/public/web/devtools_frontend.mojom.h", "+third_party/blink/public/web/web_ax_enums.h", "+third_party/blink/public/web/web_console_message.h", "+third_party/blink/public/web/web_context_menu_data.h", "+third_party/blink/public/web/web_device_emulation_params.h", "+third_party/blink/public/web/web_drag_status.h", "+third_party/blink/public/web/web_find_options.h", + "+third_party/blink/public/web/web_fullscreen_options.h", "+third_party/blink/public/web/web_frame_serializer_cache_control_policy.h", "+third_party/blink/public/web/web_ime_text_span.h", "+third_party/blink/public/web/web_media_player_action.h", @@ -131,15 +128,15 @@ include_rules = [ "+third_party/blink/public/platform/modules/bluetooth/web_bluetooth.mojom.h", "+third_party/blink/public/platform/modules/broadcastchannel/broadcast_channel.mojom.h", "+third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom.h", + "+third_party/blink/public/platform/modules/cookie_store/cookie_store.mojom.h", "+third_party/blink/public/platform/modules/geolocation/geolocation_service.mojom.h", "+third_party/blink/public/platform/modules/installedapp/installed_app_provider.mojom.h", "+third_party/blink/public/platform/modules/installedapp/related_application.mojom.h", "+third_party/blink/public/platform/modules/keyboard_lock/keyboard_lock.mojom.h", - "+third_party/blink/public/platform/modules/manifest/manifest_manager.mojom.h", "+third_party/blink/public/platform/modules/mediasession/media_session.mojom.h", "+third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h", "+third_party/blink/public/platform/modules/notifications/notification_service.mojom.h", - "+third_party/blink/public/platform/modules/offscreencanvas/offscreen_canvas_surface.mojom.h", + "+third_party/blink/public/platform/modules/frame_sinks/embedded_frame_sink.mojom.h", "+third_party/blink/public/platform/modules/payments/payment_app.mojom.h", "+third_party/blink/public/platform/modules/permissions/permission.mojom.h", "+third_party/blink/public/platform/modules/permissions/permission_status.mojom.h", @@ -152,6 +149,8 @@ include_rules = [ "+third_party/blink/public/platform/reporting.mojom.h", "+third_party/blink/public/platform/web_feature.mojom.h", "+third_party/blink/public/web/commit_result.mojom.h", + "+third_party/blink/public/web/devtools_agent.mojom.h", + "+third_party/blink/public/web/devtools_frontend.mojom.h", # DO NOT ADD ANY CHROME OR COMPONENTS INCLUDES HERE!!! # See https://sites.google.com/a/chromium.org/dev/developers/content-module diff --git a/chromium/content/browser/OWNERS b/chromium/content/browser/OWNERS index c78c8cee12b..76478df0021 100644 --- a/chromium/content/browser/OWNERS +++ b/chromium/content/browser/OWNERS @@ -31,5 +31,5 @@ per-file sandbox_ipc_linux.*=jorgelo@chromium.org # LevelDBWrapperImpl used in LocalStorage per-file leveldb_wrapper_impl*=mek@chromium.org -per-file leveldb_wrapper_impl*=michaeln@chromium.org +per-file leveldb_wrapper_impl*=dmurph@chromium.org per-file leveldb_wrapper_impl*=pwnall@chromium.org diff --git a/chromium/content/browser/accessibility/accessibility_action_browsertest.cc b/chromium/content/browser/accessibility/accessibility_action_browsertest.cc index 96a46abd1e5..cad16f8da69 100644 --- a/chromium/content/browser/accessibility/accessibility_action_browsertest.cc +++ b/chromium/content/browser/accessibility/accessibility_action_browsertest.cc @@ -164,12 +164,12 @@ IN_PROC_BROWSER_TEST_F(AccessibilityActionBrowserTest, CanvasGetImage) { " c.beginPath();\n" " c.moveTo(0, 0.5);\n" " c.lineTo(4, 0.5);\n" - " c.strokeStyle = '#ff0000';\n" + " c.strokeStyle = '%23ff0000';\n" " c.stroke();\n" " c.beginPath();\n" " c.moveTo(0, 1.5);\n" " c.lineTo(4, 1.5);\n" - " c.strokeStyle = '#0000ff';\n" + " c.strokeStyle = '%230000ff';\n" " c.stroke();\n" "" ""); @@ -211,9 +211,9 @@ IN_PROC_BROWSER_TEST_F(AccessibilityActionBrowserTest, CanvasGetImageScale) { "" "" ""); diff --git a/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc index 569515a57cb..50f1d01015b 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_blink.cc @@ -71,6 +71,8 @@ std::string AccessibilityTreeFormatterBlink::IntAttrToString( return ui::ToString(static_cast(value)); case ax::mojom::IntAttribute::kDescriptionFrom: return ui::ToString(static_cast(value)); + case ax::mojom::IntAttribute::kHasPopup: + return ui::ToString(static_cast(value)); case ax::mojom::IntAttribute::kInvalidState: return ui::ToString(static_cast(value)); case ax::mojom::IntAttribute::kNameFrom: @@ -81,6 +83,8 @@ std::string AccessibilityTreeFormatterBlink::IntAttrToString( return ui::ToString(static_cast(value)); case ax::mojom::IntAttribute::kTextDirection: return ui::ToString(static_cast(value)); + case ax::mojom::IntAttribute::kTextPosition: + return ui::ToString(static_cast(value)); // No pretty printing necessary for these: case ax::mojom::IntAttribute::kActivedescendantId: case ax::mojom::IntAttribute::kAriaCellColumnIndex: 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 9d32091c650..f5281e28968 100644 --- a/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc +++ b/chromium/content/browser/accessibility/accessibility_tree_formatter_utils_win.cc @@ -129,7 +129,9 @@ CONTENT_EXPORT base::string16 IAccessible2RoleToString(int32_t ia2_role) { QUOTE(IA2_ROLE_TOGGLE_BUTTON), QUOTE(IA2_ROLE_UNKNOWN), QUOTE(IA2_ROLE_VIEW_PORT), - }; + QUOTE(IA2_ROLE_COMPLEMENTARY_CONTENT), + QUOTE(IA2_ROLE_LANDMARK), + QUOTE(IA2_ROLE_LEVEL_BAR)}; return GetNameForPlatformConstant(ia2_table, arraysize(ia2_table), ia2_role); } diff --git a/chromium/content/browser/accessibility/accessibility_win_browsertest.cc b/chromium/content/browser/accessibility/accessibility_win_browsertest.cc index 5d928bb9524..089946e6c50 100644 --- a/chromium/content/browser/accessibility/accessibility_win_browsertest.cc +++ b/chromium/content/browser/accessibility/accessibility_win_browsertest.cc @@ -14,6 +14,7 @@ #include "base/process/process_handle.h" #include "base/strings/pattern.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" #include "base/win/scoped_bstr.h" #include "base/win/scoped_variant.h" @@ -48,14 +49,14 @@ namespace content { namespace { - -const char INPUT_CONTENTS[] = "Moz/5.0 (ST 6.x; WWW33) " +constexpr char kInputContents[] = + "Moz/5.0 (ST 6.x; WWW33) " "WebKit \"KHTML, like\"."; -const char TEXTAREA_CONTENTS[] = "Moz/5.0 (ST 6.x; WWW33)\n" +constexpr char kTextareaContents[] = + "Moz/5.0 (ST 6.x; WWW33)\n" "WebKit \n\"KHTML, like\"."; -const LONG CONTENTS_LENGTH = static_cast( - (sizeof(INPUT_CONTENTS) - 1) / sizeof(char)); - +constexpr LONG kContentsLength = + static_cast((sizeof(kInputContents) - 1) / sizeof(char)); // AccessibilityWinBrowserTest ------------------------------------------------ @@ -70,18 +71,22 @@ class AccessibilityWinBrowserTest : public ContentBrowserTest { const std::string& html, ui::AXMode accessibility_mode = ui::kAXModeComplete); IAccessible* GetRendererAccessible(); + base::string16 PrintAXTree() const; void ExecuteScript(const std::wstring& script); void SetUpInputField(Microsoft::WRL::ComPtr* input_text); + void SetUpScrollableInputField( + Microsoft::WRL::ComPtr* input_text); void SetUpTextareaField( Microsoft::WRL::ComPtr* textarea_text); void SetUpSampleParagraph( Microsoft::WRL::ComPtr* accessible_text, ui::AXMode accessibility_mode = ui::kAXModeComplete); - void SetUpSampleParagraphWithScroll( + void SetUpSampleParagraphInScrollableDocument( + Microsoft::WRL::ComPtr* accessible_text, + ui::AXMode accessibility_mode = ui::kAXModeComplete); + void SetUpSampleParagraphInScrollableEditable( Microsoft::WRL::ComPtr* accessible_text, ui::AXMode accessibility_mode = ui::kAXModeComplete); - void SetUpSampleParagraphHelper( - Microsoft::WRL::ComPtr* accessible_text); static Microsoft::WRL::ComPtr GetAccessibleFromVariant( IAccessible* parent, @@ -103,6 +108,11 @@ class AccessibilityWinBrowserTest : public ContentBrowserTest { IAccessible* element); private: + void SetUpInputFieldHelper( + Microsoft::WRL::ComPtr* input_text); + void SetUpSampleParagraphHelper( + Microsoft::WRL::ComPtr* accessible_text); + DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest); }; @@ -118,11 +128,29 @@ void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml( AccessibilityNotificationWaiter waiter(shell()->web_contents(), accessibility_mode, ax::mojom::Event::kLoadComplete); - GURL html_data_url("data:text/html," + html); + GURL html_data_url("data:text/html," + + net::EscapeQueryParamValue(html, false)); NavigateToURL(shell(), html_data_url); waiter.WaitForNotification(); } +base::string16 AccessibilityWinBrowserTest::PrintAXTree() const { + std::unique_ptr formatter( + AccessibilityTreeFormatter::Create()); + DCHECK(formatter); + formatter->set_show_ids(true); + formatter->SetFilters({AccessibilityTreeFormatter::Filter( + L"*", AccessibilityTreeFormatter::Filter::ALLOW)}); + + base::string16 str; + formatter->FormatAccessibilityTree( + static_cast(shell()->web_contents()) + ->GetRootBrowserAccessibilityManager() + ->GetRoot(), + &str); + return str; +} + // Retrieve the MSAA client accessibility object for the Render Widget Host View // of the selected tab. IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() { @@ -135,35 +163,66 @@ void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) { } // Loads a page with an input text field and places sample text in it. Also, -// places the caret on the last character. +// places the caret before the last character. void AccessibilityWinBrowserTest::SetUpInputField( Microsoft::WRL::ComPtr* input_text) { ASSERT_NE(nullptr, input_text); - LoadInitialAccessibilityTreeFromHtml(std::string("" - "
" - "
")); + LoadInitialAccessibilityTreeFromHtml(std::string( + R"HTML( + + +
+ + +
+ + )HTML")); + + SetUpInputFieldHelper(input_text); +} + +// Loads a page with an input text field and places sample text in it that +// overflows its width. Also, places the caret before the last character. +void AccessibilityWinBrowserTest::SetUpScrollableInputField( + Microsoft::WRL::ComPtr* input_text) { + ASSERT_NE(nullptr, input_text); + LoadInitialAccessibilityTreeFromHtml(std::string( + R"HTML( + + + + + )HTML")); - // Retrieve the IAccessible interface for the web page. + SetUpInputFieldHelper(input_text); +} + +void AccessibilityWinBrowserTest::SetUpInputFieldHelper( + Microsoft::WRL::ComPtr* input_text) { Microsoft::WRL::ComPtr document(GetRendererAccessible()); std::vector document_children = GetAllAccessibleChildren(document.Get()); ASSERT_EQ(1u, document_children.size()); - Microsoft::WRL::ComPtr form; + Microsoft::WRL::ComPtr div; ASSERT_HRESULT_SUCCEEDED(QueryIAccessible2( GetAccessibleFromVariant(document.Get(), document_children[0].AsInput()) .Get(), - form.GetAddressOf())); - std::vector form_children = - GetAllAccessibleChildren(form.Get()); - ASSERT_EQ(2u, form_children.size()); + div.GetAddressOf())); + std::vector div_children = + GetAllAccessibleChildren(div.Get()); + ASSERT_LT(0u, div_children.size()); - // Find the input text field. + // The input field is always the last child. Microsoft::WRL::ComPtr input; ASSERT_HRESULT_SUCCEEDED(QueryIAccessible2( - GetAccessibleFromVariant(form.Get(), form_children[1].AsInput()).Get(), + GetAccessibleFromVariant(div.Get(), + div_children[div_children.size() - 1].AsInput()) + .Get(), input.GetAddressOf())); LONG input_role = 0; ASSERT_HRESULT_SUCCEEDED(input->role(&input_role)); @@ -172,29 +231,32 @@ void AccessibilityWinBrowserTest::SetUpInputField( // Retrieve the IAccessibleText interface for the field. ASSERT_HRESULT_SUCCEEDED(input.CopyTo(input_text->GetAddressOf())); - // Set the caret on the last character. + // Set the caret before the last character. AccessibilityNotificationWaiter waiter( shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kTextSelectionChanged); - std::wstring caret_offset = base::UTF16ToWide(base::IntToString16( - static_cast(CONTENTS_LENGTH - 1))); - ExecuteScript(std::wstring( - L"var textField = document.getElementById('textField');" - L"textField.focus();" - L"textField.setSelectionRange(") + - caret_offset + L"," + caret_offset + L");"); + std::wstring caret_offset = base::UTF16ToWide( + base::IntToString16(static_cast(kContentsLength - 1))); + ExecuteScript(std::wstring(L"let textField = document.querySelector('input');" + L"textField.focus();" + L"textField.setSelectionRange(") + + caret_offset + L"," + caret_offset + L");"); waiter.WaitForNotification(); } // Loads a page with a textarea text field and places sample text in it. Also, -// places the caret on the last character. +// places the caret before the last character. void AccessibilityWinBrowserTest::SetUpTextareaField( Microsoft::WRL::ComPtr* textarea_text) { ASSERT_NE(nullptr, textarea_text); - LoadInitialAccessibilityTreeFromHtml(std::string("" - "")); + LoadInitialAccessibilityTreeFromHtml(std::string(R"HTML( + + + + + )HTML")); // Retrieve the IAccessible interface for the web page. Microsoft::WRL::ComPtr document(GetRendererAccessible()); @@ -224,16 +286,16 @@ void AccessibilityWinBrowserTest::SetUpTextareaField( // Retrieve the IAccessibleText interface for the field. ASSERT_HRESULT_SUCCEEDED(textarea.CopyTo(textarea_text->GetAddressOf())); - // Set the caret on the last character. + // Set the caret before the last character. AccessibilityNotificationWaiter waiter( shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kTextSelectionChanged); - std::wstring caret_offset = base::UTF16ToWide(base::IntToString16( - static_cast(CONTENTS_LENGTH - 1))); - ExecuteScript(std::wstring( - L"var textField = document.getElementById('textField');" - L"textField.focus();" - L"textField.setSelectionRange(") + + std::wstring caret_offset = base::UTF16ToWide( + base::IntToString16(static_cast(kContentsLength - 1))); + ExecuteScript( + std::wstring(L"var textField = document.querySelector('textarea');" + L"textField.focus();" + L"textField.setSelectionRange(") + caret_offset + L"," + caret_offset + L");"); waiter.WaitForNotification(); } @@ -243,12 +305,16 @@ void AccessibilityWinBrowserTest::SetUpSampleParagraph( Microsoft::WRL::ComPtr* accessible_text, ui::AXMode accessibility_mode) { LoadInitialAccessibilityTreeFromHtml( - "" - "" - "

Game theory is \"the study of " - "mathematical models " - "of conflict and
cooperation between intelligent rational " - "decision-makers.\"

", + R"HTML( + + +

Game theory is "the study of + mathematical models + of conflict and
cooperation between intelligent rational + decision-makers." +

+ + )HTML", accessibility_mode); SetUpSampleParagraphHelper(accessible_text); @@ -256,21 +322,55 @@ void AccessibilityWinBrowserTest::SetUpSampleParagraph( // Loads a page with a paragraph of sample text which is below the // bottom of the screen. -void AccessibilityWinBrowserTest::SetUpSampleParagraphWithScroll( +void AccessibilityWinBrowserTest::SetUpSampleParagraphInScrollableDocument( Microsoft::WRL::ComPtr* accessible_text, ui::AXMode accessibility_mode) { LoadInitialAccessibilityTreeFromHtml( - "" - "" - "

Game theory is \"the study of " - "mathematical models " - "of conflict and
cooperation between intelligent rational " - "decision-makers.\"

", + R"HTML( + + +

Game theory is "the study of + mathematical models + of conflict and
cooperation between intelligent rational + decision-makers." +

+ + )HTML", accessibility_mode); SetUpSampleParagraphHelper(accessible_text); } +// Loads a page with a content editable whose text overflows its height. +// Places the caret at the beginning of the editable's last line but doesn't +// scroll the editable. +void AccessibilityWinBrowserTest::SetUpSampleParagraphInScrollableEditable( + Microsoft::WRL::ComPtr* accessible_text, + ui::AXMode accessibility_mode) { + LoadInitialAccessibilityTreeFromHtml( + R"HTML(

+ hello


hello +

)HTML", + accessibility_mode); + + AccessibilityNotificationWaiter selection_waiter( + shell()->web_contents(), ui::kAXModeComplete, + ax::mojom::Event::kTextSelectionChanged); + ExecuteScript( + L"let selection=document.getSelection();" + L"let range=document.createRange();" + L"let editable=document.querySelector('p[contenteditable=\"true\"]');" + L"editable.focus();" + L"range.setStart(editable.lastChild, 0);" + L"range.setEnd(editable.lastChild, 0);" + L"selection.removeAllRanges();" + L"selection.addRange(range);"); + selection_waiter.WaitForNotification(); + + SetUpSampleParagraphHelper(accessible_text); +} + void AccessibilityWinBrowserTest::SetUpSampleParagraphHelper( Microsoft::WRL::ComPtr* accessible_text) { ASSERT_NE(nullptr, accessible_text); @@ -286,9 +386,11 @@ void AccessibilityWinBrowserTest::SetUpSampleParagraphHelper( GetAccessibleFromVariant(document.Get(), document_children[0].AsInput()) .Get(), paragraph.GetAddressOf())); + LONG paragraph_role = 0; ASSERT_HRESULT_SUCCEEDED(paragraph->role(¶graph_role)); ASSERT_EQ(IA2_ROLE_PARAGRAPH, paragraph_role); + ASSERT_HRESULT_SUCCEEDED(paragraph.CopyTo(accessible_text->GetAddressOf())); } @@ -417,9 +519,9 @@ AccessibilityWinBrowserTest::GetAllAccessibleChildren( std::vector children( static_cast(child_count)); - for (size_t i = 0; i < children.size(); i++) { + for (size_t i = 0; i < children.size(); i++) children[i].Reset(children_array[i]); - } + return children; } @@ -1085,17 +1187,18 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, EXPECT_EQ(-1, height); } -IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestCharacterExtents) { +IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, + TestCharacterExtentsInEditable) { Microsoft::WRL::ComPtr paragraph_text; SetUpSampleParagraph(¶graph_text); - const LONG newline_offset = 46; + constexpr LONG newline_offset = 46; LONG n_characters; ASSERT_HRESULT_SUCCEEDED(paragraph_text->get_nCharacters(&n_characters)); - ASSERT_LT(0, n_characters); + ASSERT_EQ(105, n_characters); LONG x, y, width, height; - LONG previous_x, previous_y; + LONG previous_x, previous_y, previous_height; for (int coordinate = IA2_COORDTYPE_SCREEN_RELATIVE; coordinate <= IA2_COORDTYPE_PARENT_RELATIVE; ++coordinate) { auto coordinate_type = static_cast(coordinate); @@ -1103,19 +1206,20 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestCharacterExtents) { 0, coordinate_type, &x, &y, &width, &height)); EXPECT_LT(0, x) << "at offset 0"; EXPECT_LT(0, y) << "at offset 0"; - EXPECT_LT(0, width) << "at offset 0"; - EXPECT_LT(0, height) << "at offset 0"; + EXPECT_LT(1, width) << "at offset 0"; + EXPECT_LT(1, height) << "at offset 0"; for (LONG offset = 1; offset < newline_offset; ++offset) { previous_x = x; previous_y = y; + previous_height = height; EXPECT_HRESULT_SUCCEEDED(paragraph_text->get_characterExtents( offset, coordinate_type, &x, &y, &width, &height)); EXPECT_LT(previous_x, x) << "at offset " << offset; EXPECT_EQ(previous_y, y) << "at offset " << offset; - EXPECT_LT(0, width) << "at offset " << offset; - EXPECT_LT(0, height) << "at offset " << offset; + EXPECT_LT(1, width) << "at offset " << offset; + EXPECT_EQ(previous_height, height) << "at offset " << offset; } EXPECT_HRESULT_SUCCEEDED(paragraph_text->get_characterExtents( @@ -1123,19 +1227,160 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestCharacterExtents) { EXPECT_LE(0, x) << "at offset " << newline_offset + 1; EXPECT_GT(previous_x, x) << "at offset " << newline_offset + 1; EXPECT_LT(previous_y, y) << "at offset " << newline_offset + 1; - EXPECT_LT(0, width) << "at offset " << newline_offset + 1; - EXPECT_LT(0, height) << "at offset " << newline_offset + 1; + EXPECT_LT(1, width) << "at offset " << newline_offset + 1; + EXPECT_EQ(previous_height, height) << "at offset " << newline_offset + 1; for (LONG offset = newline_offset + 2; offset < n_characters; ++offset) { previous_x = x; previous_y = y; + previous_height = height; EXPECT_HRESULT_SUCCEEDED(paragraph_text->get_characterExtents( offset, coordinate_type, &x, &y, &width, &height)); EXPECT_LT(previous_x, x) << "at offset " << offset; EXPECT_EQ(previous_y, y) << "at offset " << offset; - EXPECT_LT(0, width) << "at offset " << offset; - EXPECT_LT(0, height) << "at offset " << offset; + EXPECT_LT(1, width) << "at offset " << offset; + EXPECT_EQ(previous_height, height) << "at offset " << offset; + } + } +} + +IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, + TestCharacterExtentsInScrollableEditable) { + Microsoft::WRL::ComPtr editable_container; + // By construction, only the first line of the content editable is visible. + SetUpSampleParagraphInScrollableEditable(&editable_container); + + constexpr LONG first_line_end = 5; + constexpr LONG last_line_start = 8; + LONG n_characters; + ASSERT_HRESULT_SUCCEEDED(editable_container->get_nCharacters(&n_characters)); + ASSERT_EQ(13, n_characters); + LONG caret_offset; + ASSERT_HRESULT_SUCCEEDED(editable_container->get_caretOffset(&caret_offset)); + ASSERT_EQ(last_line_start, caret_offset); + + LONG x, y, width, height; + LONG previous_x, previous_y, previous_height; + for (int coordinate = IA2_COORDTYPE_SCREEN_RELATIVE; + coordinate <= IA2_COORDTYPE_PARENT_RELATIVE; ++coordinate) { + auto coordinate_type = static_cast(coordinate); + + // Test that non offscreen characters have increasing x coordinates and a + // height that is greater than 1px. + EXPECT_HRESULT_SUCCEEDED(editable_container->get_characterExtents( + 0, coordinate_type, &x, &y, &width, &height)); + EXPECT_LT(0, x) << "at offset 0"; + EXPECT_LT(0, y) << "at offset 0"; + EXPECT_LT(1, width) << "at offset 0"; + EXPECT_LT(1, height) << "at offset 0"; + + for (LONG offset = 1; offset < first_line_end; ++offset) { + previous_x = x; + previous_y = y; + previous_height = height; + + EXPECT_HRESULT_SUCCEEDED(editable_container->get_characterExtents( + offset, coordinate_type, &x, &y, &width, &height)); + EXPECT_LT(previous_x, x) << "at offset " << offset; + EXPECT_EQ(previous_y, y) << "at offset " << offset; + EXPECT_LT(1, width) << "at offset " << offset; + EXPECT_EQ(previous_height, height) << "at offset " << offset; + } + + // Vertically offscreen objects should have a height of 1px so that if an + // assistive aid ignores the offscreen state, they will still be too small + // to be visible and thus not appear outside the window. Note that a height + // of 0 is not used because it signifies an invalid size. + EXPECT_HRESULT_SUCCEEDED(editable_container->get_characterExtents( + last_line_start, coordinate_type, &x, &y, &width, &height)); + EXPECT_LT(0, x) << "at offset " << last_line_start; + EXPECT_LT(previous_y, y) << "at offset " << last_line_start; + EXPECT_LT(1, width) << "at offset " << last_line_start; + EXPECT_EQ(1, height) << "at offset " << last_line_start; + + for (LONG offset = last_line_start + 1; offset < n_characters; ++offset) { + previous_x = x; + previous_y = y; + + EXPECT_HRESULT_SUCCEEDED(editable_container->get_characterExtents( + offset, coordinate_type, &x, &y, &width, &height)); + EXPECT_LT(previous_x, x) << "at offset " << offset; + EXPECT_EQ(previous_y, y) << "at offset " << offset; + EXPECT_LT(1, width) << "at offset " << offset; + EXPECT_EQ(1, height) << "at offset " << offset; + } + } +} + +IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, + DISABLED_TestCharacterExtentsInScrollableInputField) { + Microsoft::WRL::ComPtr input_text; + SetUpScrollableInputField(&input_text); + + constexpr LONG visible_characters_start = 20; + LONG n_characters; + ASSERT_HRESULT_SUCCEEDED(input_text->get_nCharacters(&n_characters)); + ASSERT_EQ(kContentsLength, n_characters); + LONG caret_offset; + ASSERT_HRESULT_SUCCEEDED(input_text->get_caretOffset(&caret_offset)); + ASSERT_EQ(kContentsLength - 1, caret_offset); + + LONG x, y, width, height; + LONG previous_x, previous_y, previous_height; + for (int coordinate = IA2_COORDTYPE_SCREEN_RELATIVE; + coordinate <= IA2_COORDTYPE_PARENT_RELATIVE; ++coordinate) { + auto coordinate_type = static_cast(coordinate); + + // Horizontally offscreen objects should have a width of 1px so that if an + // assistive aid ignores the offscreen state, they will still be too small + // to be visible and thus not appear outside the window. Note that a width + // of 0 is not used because it signifies an invalid size. + EXPECT_HRESULT_SUCCEEDED(input_text->get_characterExtents( + 0, coordinate_type, &x, &y, &width, &height)); + EXPECT_LT(0, x) << "at offset 0"; + EXPECT_LT(0, y) << "at offset 0"; + EXPECT_EQ(1, width) << "at offset 0"; + EXPECT_LT(1, height) << "at offset 0"; + + // Test that characters at the start of the input field are offscreen by + // checking that their x coordinate is at the start of the field and their + // width is 1. + for (LONG offset = 1; offset < visible_characters_start; ++offset) { + previous_x = x; + previous_y = y; + previous_height = height; + + EXPECT_HRESULT_SUCCEEDED(input_text->get_characterExtents( + offset, coordinate_type, &x, &y, &width, &height)); + EXPECT_EQ(previous_x, x) << "at offset " << offset; + EXPECT_EQ(previous_y, y) << "at offset " << offset; + EXPECT_EQ(1, width) << "at offset " << offset; + EXPECT_LT(previous_height, height) << "at offset " << offset; + } + + // Test that non offscreen characters have increasing x coordinates and a + // width that is greater than 1px. + EXPECT_HRESULT_SUCCEEDED(input_text->get_characterExtents( + visible_characters_start, coordinate_type, &x, &y, &width, &height)); + EXPECT_EQ(previous_x, x) << "at offset " << visible_characters_start; + EXPECT_EQ(previous_y, y) << "at offset " << visible_characters_start; + EXPECT_LT(1, width) << "at offset " << visible_characters_start; + EXPECT_EQ(previous_height, height) + << "at offset " << visible_characters_start; + + for (LONG offset = visible_characters_start + 1; offset < n_characters; + ++offset) { + previous_x = x; + previous_y = y; + previous_height = height; + + EXPECT_HRESULT_SUCCEEDED(input_text->get_characterExtents( + offset, coordinate_type, &x, &y, &width, &height)); + EXPECT_LT(previous_x, x) << "at offset " << offset; + EXPECT_EQ(previous_y, y) << "at offset " << offset; + EXPECT_LT(1, width) << "at offset " << offset; + EXPECT_EQ(previous_height, height) << "at offset " << offset; } } } @@ -1148,11 +1393,11 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, ui::AXMode::kScreenReader); LONG x, y, width, height; - AccessibilityNotificationWaiter waiter(shell()->web_contents(), - ui::AXMode::kNativeAPIs | - ui::AXMode::kWebContents | - ui::AXMode::kScreenReader, - ax::mojom::Event::kLoadComplete); + AccessibilityNotificationWaiter waiter( + shell()->web_contents(), + ui::AXMode::kNativeAPIs | ui::AXMode::kWebContents | + ui::AXMode::kScreenReader | ui::AXMode::kInlineTextBoxes, + ax::mojom::Event::kLoadComplete); EXPECT_HRESULT_SUCCEEDED(paragraph_text->get_characterExtents( 0, IA2_COORDTYPE_SCREEN_RELATIVE, &x, &y, &width, &height)); // X and y coordinates should be available without @@ -1168,13 +1413,13 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 0, IA2_COORDTYPE_SCREEN_RELATIVE, &x, &y, &width, &height)); EXPECT_LT(0, x); EXPECT_LT(0, y); - EXPECT_LT(0, width); - EXPECT_LT(0, height); + EXPECT_LT(1, width); + EXPECT_LT(1, height); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestScrollToPoint) { Microsoft::WRL::ComPtr accessible_text; - SetUpSampleParagraphWithScroll(&accessible_text); + SetUpSampleParagraphInScrollableDocument(&accessible_text); Microsoft::WRL::ComPtr paragraph; ASSERT_HRESULT_SUCCEEDED( accessible_text.CopyTo(IID_PPV_ARGS(¶graph))); @@ -1213,7 +1458,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestSetCaretOffset) { LONG caret_offset = 0; HRESULT hr = input_text->get_caretOffset(&caret_offset); EXPECT_EQ(S_OK, hr); - EXPECT_EQ(CONTENTS_LENGTH - 1, caret_offset); + EXPECT_EQ(kContentsLength - 1, caret_offset); AccessibilityNotificationWaiter waiter( shell()->web_contents(), ui::kAXModeComplete, @@ -1236,7 +1481,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, LONG caret_offset = 0; HRESULT hr = textarea_text->get_caretOffset(&caret_offset); EXPECT_EQ(S_OK, hr); - EXPECT_EQ(CONTENTS_LENGTH - 1, caret_offset); + EXPECT_EQ(kContentsLength - 1, caret_offset); AccessibilityNotificationWaiter waiter( shell()->web_contents(), ui::kAXModeComplete, @@ -1266,7 +1511,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestSetSelection) { shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kTextSelectionChanged); start_offset = 0; - end_offset = CONTENTS_LENGTH; + end_offset = kContentsLength; EXPECT_HRESULT_FAILED(input_text->setSelection(1, start_offset, end_offset)); EXPECT_HRESULT_SUCCEEDED( input_text->setSelection(0, start_offset, end_offset)); @@ -1275,9 +1520,9 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestSetSelection) { hr = input_text->get_selection(0, &start_offset, &end_offset); EXPECT_EQ(S_OK, hr); EXPECT_EQ(0, start_offset); - EXPECT_EQ(CONTENTS_LENGTH, end_offset); + EXPECT_EQ(kContentsLength, end_offset); - start_offset = CONTENTS_LENGTH; + start_offset = kContentsLength; end_offset = 1; EXPECT_HRESULT_SUCCEEDED( input_text->setSelection(0, start_offset, end_offset)); @@ -1287,7 +1532,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestSetSelection) { EXPECT_EQ(S_OK, hr); // Start and end offsets are always swapped to be in ascending order. EXPECT_EQ(1, start_offset); - EXPECT_EQ(CONTENTS_LENGTH, end_offset); + EXPECT_EQ(kContentsLength, end_offset); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineSetSelection) { @@ -1305,7 +1550,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineSetSelection) { shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kTextSelectionChanged); start_offset = 0; - end_offset = CONTENTS_LENGTH; + end_offset = kContentsLength; EXPECT_HRESULT_FAILED( textarea_text->setSelection(1, start_offset, end_offset)); EXPECT_HRESULT_SUCCEEDED( @@ -1315,9 +1560,9 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineSetSelection) { hr = textarea_text->get_selection(0, &start_offset, &end_offset); EXPECT_EQ(S_OK, hr); EXPECT_EQ(0, start_offset); - EXPECT_EQ(CONTENTS_LENGTH, end_offset); + EXPECT_EQ(kContentsLength, end_offset); - start_offset = CONTENTS_LENGTH - 1; + start_offset = kContentsLength - 1; end_offset = 0; EXPECT_HRESULT_SUCCEEDED( textarea_text->setSelection(0, start_offset, end_offset)); @@ -1327,7 +1572,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineSetSelection) { EXPECT_EQ(S_OK, hr); // Start and end offsets are always swapped to be in ascending order. EXPECT_EQ(0, start_offset); - EXPECT_EQ(CONTENTS_LENGTH - 1, end_offset); + EXPECT_EQ(kContentsLength - 1, end_offset); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, @@ -1388,7 +1633,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); - invalid_offset = CONTENTS_LENGTH + 1; + invalid_offset = kContentsLength + 1; hr = input_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_WORD, &start_offset, &end_offset, text.Receive()); @@ -1399,7 +1644,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, // According to the IA2 Spec, only line boundaries should succeed when // the offset is one past the end of the text. - invalid_offset = CONTENTS_LENGTH; + invalid_offset = kContentsLength; hr = input_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_CHAR, &start_offset, &end_offset, text.Receive()); @@ -1481,7 +1726,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); - invalid_offset = CONTENTS_LENGTH + 1; + invalid_offset = kContentsLength + 1; hr = textarea_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_WORD, &start_offset, &end_offset, text.Receive()); @@ -1492,7 +1737,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, // According to the IA2 Spec, only line boundaries should succeed when // the offset is one past the end of the text. - invalid_offset = CONTENTS_LENGTH; + invalid_offset = kContentsLength; hr = textarea_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_CHAR, &start_offset, &end_offset, text.Receive()); @@ -1558,40 +1803,40 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestTextAtOffsetWithBoundaryCharacter) { Microsoft::WRL::ComPtr input_text; SetUpInputField(&input_text); - for (LONG offset = 0; offset < CONTENTS_LENGTH; ++offset) { - std::wstring expected_text(1, INPUT_CONTENTS[offset]); + for (LONG offset = 0; offset < kContentsLength; ++offset) { + std::wstring expected_text(1, kInputContents[offset]); LONG expected_start_offset = offset; LONG expected_end_offset = offset + 1; CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_CHAR, expected_start_offset, expected_end_offset, expected_text); } - for (LONG offset = CONTENTS_LENGTH - 1; offset >= 0; --offset) { - std::wstring expected_text(1, INPUT_CONTENTS[offset]); + for (LONG offset = kContentsLength - 1; offset >= 0; --offset) { + std::wstring expected_text(1, kInputContents[offset]); LONG expected_start_offset = offset; LONG expected_end_offset = offset + 1; CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_CHAR, expected_start_offset, expected_end_offset, expected_text); } - CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, - IA2_TEXT_BOUNDARY_CHAR, CONTENTS_LENGTH - 1, CONTENTS_LENGTH, L"."); + CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_CHAR, + kContentsLength - 1, kContentsLength, L"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineTextAtOffsetWithBoundaryCharacter) { Microsoft::WRL::ComPtr textarea_text; SetUpTextareaField(&textarea_text); - for (LONG offset = 0; offset < CONTENTS_LENGTH; ++offset) { - std::wstring expected_text(1, TEXTAREA_CONTENTS[offset]); + for (LONG offset = 0; offset < kContentsLength; ++offset) { + std::wstring expected_text(1, kTextareaContents[offset]); LONG expected_start_offset = offset; LONG expected_end_offset = offset + 1; CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_CHAR, expected_start_offset, expected_end_offset, expected_text); } - for (LONG offset = CONTENTS_LENGTH - 1; offset >= 0; --offset) { - std::wstring expected_text(1, TEXTAREA_CONTENTS[offset]); + for (LONG offset = kContentsLength - 1; offset >= 0; --offset) { + std::wstring expected_text(1, kTextareaContents[offset]); LONG expected_start_offset = offset; LONG expected_end_offset = offset + 1; CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_CHAR, @@ -1599,7 +1844,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, } CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, - IA2_TEXT_BOUNDARY_CHAR, CONTENTS_LENGTH - 1, CONTENTS_LENGTH, L"."); + IA2_TEXT_BOUNDARY_CHAR, kContentsLength - 1, + kContentsLength, L"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, @@ -1664,14 +1910,14 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 33, 40, L"KHTML, "); // Trailing final punctuation should be part of the last word. - CheckTextAtOffset(input_text, 41, IA2_TEXT_BOUNDARY_WORD, - 40, CONTENTS_LENGTH, L"like\"."); - CheckTextAtOffset(input_text, 45, IA2_TEXT_BOUNDARY_WORD, - 40, CONTENTS_LENGTH, L"like\"."); + CheckTextAtOffset(input_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, kContentsLength, + L"like\"."); + CheckTextAtOffset(input_text, 45, IA2_TEXT_BOUNDARY_WORD, 40, kContentsLength, + L"like\"."); // Test special offsets. - CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, - IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\"."); + CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_WORD, + 40, kContentsLength, L"like\"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, @@ -1736,14 +1982,14 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 33, 40, L"KHTML, "); // Trailing final punctuation should be part of the last word. - CheckTextAtOffset(textarea_text, 41, IA2_TEXT_BOUNDARY_WORD, - 40, CONTENTS_LENGTH, L"like\"."); - CheckTextAtOffset(textarea_text, 45, IA2_TEXT_BOUNDARY_WORD, - 40, CONTENTS_LENGTH, L"like\"."); + CheckTextAtOffset(textarea_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, + kContentsLength, L"like\"."); + CheckTextAtOffset(textarea_text, 45, IA2_TEXT_BOUNDARY_WORD, 40, + kContentsLength, L"like\"."); // Test special offsets. CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, - IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\"."); + IA2_TEXT_BOUNDARY_WORD, 40, kContentsLength, L"like\"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, @@ -1822,14 +2068,14 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, SetUpInputField(&input_text); // Single line text fields should return the whole text. - CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_LINE, - 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS)); + CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_LINE, 0, kContentsLength, + base::SysUTF8ToWide(kInputContents)); // Test special offsets. CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_LINE, - 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS)); + 0, kContentsLength, base::SysUTF8ToWide(kInputContents)); CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_LINE, - 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS)); + 0, kContentsLength, base::SysUTF8ToWide(kInputContents)); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, @@ -1845,18 +2091,20 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 24, 32, L"WebKit \n"); // Last line does not have a trailing newline. - CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_LINE, - 32, CONTENTS_LENGTH, L"\"KHTML, like\"."); + CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_LINE, 32, + kContentsLength, L"\"KHTML, like\"."); // An offset one past the last character should return the last line. - CheckTextAtOffset(textarea_text, CONTENTS_LENGTH, IA2_TEXT_BOUNDARY_LINE, - 32, CONTENTS_LENGTH, L"\"KHTML, like\"."); + CheckTextAtOffset(textarea_text, kContentsLength, IA2_TEXT_BOUNDARY_LINE, 32, + kContentsLength, L"\"KHTML, like\"."); // Test special offsets. CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_LENGTH, - IA2_TEXT_BOUNDARY_LINE, 32, CONTENTS_LENGTH, L"\"KHTML, like\"."); + IA2_TEXT_BOUNDARY_LINE, 32, kContentsLength, + L"\"KHTML, like\"."); CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, - IA2_TEXT_BOUNDARY_LINE, 32, CONTENTS_LENGTH, L"\"KHTML, like\"."); + IA2_TEXT_BOUNDARY_LINE, 32, kContentsLength, + L"\"KHTML, like\"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, @@ -1892,8 +2140,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, Microsoft::WRL::ComPtr input_text; SetUpInputField(&input_text); - CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_ALL, - 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS)); + CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_ALL, 0, kContentsLength, + base::SysUTF8ToWide(kInputContents)); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, @@ -1901,16 +2149,19 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, Microsoft::WRL::ComPtr textarea_text; SetUpTextareaField(&textarea_text); - CheckTextAtOffset(textarea_text, CONTENTS_LENGTH - 1, IA2_TEXT_BOUNDARY_ALL, - 0, CONTENTS_LENGTH, base::SysUTF8ToWide(TEXTAREA_CONTENTS)); + CheckTextAtOffset(textarea_text, kContentsLength - 1, IA2_TEXT_BOUNDARY_ALL, + 0, kContentsLength, base::SysUTF8ToWide(kTextareaContents)); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestIAccessibleAction) { LoadInitialAccessibilityTreeFromHtml( - "" - "\"image\"" - ""); + R"HTML( + + + image + + )HTML"); // Retrieve the IAccessible interface for the web page. Microsoft::WRL::ComPtr document(GetRendererAccessible()); @@ -2104,13 +2355,16 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestAccNavigateInTables) { IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestScrollTo) { LoadInitialAccessibilityTreeFromHtml( - "" - "
" - "Target1" - "
" - "Target2" - "
" - ""); + R"HTML( + + +
+ Target1 +
+ Target2 +
+ + )HTML"); // Retrieve the IAccessible interface for the document node. Microsoft::WRL::ComPtr document(GetRendererAccessible()); diff --git a/chromium/content/browser/accessibility/browser_accessibility.cc b/chromium/content/browser/accessibility/browser_accessibility.cc index bfd96ed1eb6..64868046c9a 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.cc +++ b/chromium/content/browser/accessibility/browser_accessibility.cc @@ -16,6 +16,7 @@ #include "content/browser/accessibility/browser_accessibility_state_impl.h" #include "content/common/accessibility_messages.h" #include "ui/accessibility/ax_role_properties.h" +#include "ui/accessibility/ax_table_info.h" #include "ui/accessibility/ax_text_utils.h" #include "ui/accessibility/platform/ax_unique_id.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -58,6 +59,8 @@ bool BrowserAccessibility::PlatformIsLeaf() const { // (Note that whilst ARIA buttons can have only presentational children, HTML5 // buttons are allowed to have content.) switch (GetRole()) { + case ax::mojom::Role::kDocCover: + case ax::mojom::Role::kGraphicsSymbol: case ax::mojom::Role::kImage: case ax::mojom::Role::kMeter: case ax::mojom::Role::kScrollBar: @@ -955,6 +958,83 @@ BrowserAccessibility::GetTargetForNativeAccessibilityEvent() { return root_delegate->AccessibilityGetAcceleratedWidget(); } +int BrowserAccessibility::GetTableRowCount() const { + ui::AXTableInfo* table_info = manager()->ax_tree()->GetTableInfo(node()); + if (!table_info) + return 0; + + return table_info->row_count; +} + +int BrowserAccessibility::GetTableColCount() const { + ui::AXTableInfo* table_info = manager()->ax_tree()->GetTableInfo(node()); + if (!table_info) + return 0; + + return table_info->col_count; +} + +std::vector BrowserAccessibility::GetColHeaderNodeIds( + int32_t col_index) const { + ui::AXTableInfo* table_info = manager()->ax_tree()->GetTableInfo(node()); + if (!table_info) + return std::vector(); + + if (col_index < 0 || col_index >= table_info->col_count) + return std::vector(); + + return table_info->col_headers[col_index]; +} + +std::vector BrowserAccessibility::GetRowHeaderNodeIds( + int32_t row_index) const { + ui::AXTableInfo* table_info = manager()->ax_tree()->GetTableInfo(node()); + if (!table_info) + return std::vector(); + + if (row_index < 0 || row_index >= table_info->row_count) + return std::vector(); + + return table_info->row_headers[row_index]; +} + +int32_t BrowserAccessibility::GetCellId(int32_t row_index, + int32_t col_index) const { + ui::AXTableInfo* table_info = manager()->ax_tree()->GetTableInfo(node()); + if (!table_info) + return -1; + + if (row_index < 0 || row_index >= table_info->row_count || col_index < 0 || + col_index >= table_info->col_count) + return -1; + + return table_info->cell_ids[row_index][col_index]; +} + +int32_t BrowserAccessibility::CellIdToIndex(int32_t cell_id) const { + ui::AXTableInfo* table_info = manager()->ax_tree()->GetTableInfo(node()); + if (!table_info) + return -1; + + const auto& iter = table_info->cell_id_to_index.find(cell_id); + if (iter != table_info->cell_id_to_index.end()) + return iter->second; + + return -1; +} + +int32_t BrowserAccessibility::CellIndexToId(int32_t cell_index) const { + ui::AXTableInfo* table_info = manager()->ax_tree()->GetTableInfo(node()); + if (!table_info) + return -1; + + if (cell_index < 0 || + cell_index >= static_cast(table_info->unique_cell_ids.size())) + return -1; + + return table_info->unique_cell_ids[cell_index]; +} + bool BrowserAccessibility::AccessibilityPerformAction( const ui::AXActionData& data) { switch (data.action) { diff --git a/chromium/content/browser/accessibility/browser_accessibility.h b/chromium/content/browser/accessibility/browser_accessibility.h index 2f74f6021c5..83f78a21604 100644 --- a/chromium/content/browser/accessibility/browser_accessibility.h +++ b/chromium/content/browser/accessibility/browser_accessibility.h @@ -341,6 +341,13 @@ class CONTENT_EXPORT BrowserAccessibility : public ui::AXPlatformNodeDelegate { ui::AXPlatformNode* GetFromNodeID(int32_t id) override; int GetIndexInParent() const override; gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override; + int GetTableRowCount() const override; + int GetTableColCount() const override; + std::vector GetColHeaderNodeIds(int32_t col_index) const override; + std::vector GetRowHeaderNodeIds(int32_t row_index) const override; + int32_t GetCellId(int32_t row_index, int32_t col_index) const override; + int32_t CellIdToIndex(int32_t cell_id) const override; + int32_t CellIndexToId(int32_t cell_index) const override; bool AccessibilityPerformAction(const ui::AXActionData& data) override; bool ShouldIgnoreHoveredStateForTesting() override; bool IsOffscreen() const override; diff --git a/chromium/content/browser/accessibility/browser_accessibility_android.cc b/chromium/content/browser/accessibility/browser_accessibility_android.cc index 01a094c10bc..0bce59cad23 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_android.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_android.cc @@ -16,9 +16,9 @@ #include "content/common/accessibility_messages.h" #include "content/public/common/content_client.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/accessibility/ax_assistant_structure.h" #include "ui/accessibility/ax_role_properties.h" #include "ui/accessibility/platform/ax_android_constants.h" -#include "ui/accessibility/platform/ax_snapshot_node_android_platform.h" #include "ui/accessibility/platform/ax_unique_id.h" namespace { @@ -253,9 +253,7 @@ bool BrowserAccessibilityAndroid::IsHeading() const { if (parent && parent->IsHeading()) return true; - return (GetRole() == ax::mojom::Role::kColumnHeader || - GetRole() == ax::mojom::Role::kHeading || - GetRole() == ax::mojom::Role::kRowHeader); + return ui::IsHeadingOrTableHeader(GetRole()); } bool BrowserAccessibilityAndroid::IsHierarchical() const { @@ -265,7 +263,7 @@ bool BrowserAccessibilityAndroid::IsHierarchical() const { } bool BrowserAccessibilityAndroid::IsLink() const { - return ui::AXSnapshotNodeAndroid::AXRoleIsLink(GetRole()); + return ui::IsLink(GetRole()); } bool BrowserAccessibilityAndroid::IsMultiLine() const { @@ -301,8 +299,10 @@ bool BrowserAccessibilityAndroid::IsVisibleToUser() const { } bool BrowserAccessibilityAndroid::IsInterestingOnAndroid() const { + // The root is not interesting if it doesn't have a title, even + // though it's focusable. if (GetRole() == ax::mojom::Role::kRootWebArea && GetText().empty()) - return true; + return false; // Focusable nodes are always interesting. Note that IsFocusable() // already skips over things like iframes and child frames that are @@ -366,12 +366,12 @@ bool BrowserAccessibilityAndroid::AreInlineTextBoxesLoaded() const { } bool BrowserAccessibilityAndroid::CanOpenPopup() const { - return HasState(ax::mojom::State::kHaspopup); + return HasIntAttribute(ax::mojom::IntAttribute::kHasPopup); } const char* BrowserAccessibilityAndroid::GetClassName() const { - return ui::AXSnapshotNodeAndroid::AXRoleToAndroidClassName( - GetRole(), PlatformGetParent() != nullptr); + return ui::AXRoleToAndroidClassName(GetRole(), + PlatformGetParent() != nullptr); } base::string16 BrowserAccessibilityAndroid::GetText() const { @@ -418,10 +418,10 @@ base::string16 BrowserAccessibilityAndroid::GetText() const { } } - if (text.empty() && (IsLink() || GetRole() == ax::mojom::Role::kImage) && + if (text.empty() && (ui::IsLink(GetRole()) || ui::IsImage(GetRole())) && !HasExplicitlyEmptyName()) { base::string16 url = GetString16Attribute(ax::mojom::StringAttribute::kUrl); - text = ui::AXSnapshotNodeAndroid::AXUrlBaseText(url); + text = ui::AXUrlBaseText(url); } return text; @@ -572,6 +572,123 @@ base::string16 BrowserAccessibilityAndroid::GetRoleDescription() const { case ax::mojom::Role::kDisclosureTriangle: message_id = IDS_AX_ROLE_DISCLOSURE_TRIANGLE; break; + case ax::mojom::Role::kDocAbstract: + message_id = IDS_AX_ROLE_DOC_ABSTRACT; + break; + case ax::mojom::Role::kDocAcknowledgments: + message_id = IDS_AX_ROLE_DOC_ACKNOWLEDGMENTS; + break; + case ax::mojom::Role::kDocAfterword: + message_id = IDS_AX_ROLE_DOC_AFTERWORD; + break; + case ax::mojom::Role::kDocAppendix: + message_id = IDS_AX_ROLE_DOC_APPENDIX; + break; + case ax::mojom::Role::kDocBackLink: + message_id = IDS_AX_ROLE_DOC_BACKLINK; + break; + case ax::mojom::Role::kDocBiblioEntry: + message_id = IDS_AX_ROLE_DOC_BIBLIO_ENTRY; + break; + case ax::mojom::Role::kDocBibliography: + message_id = IDS_AX_ROLE_DOC_BIBLIOGRAPHY; + break; + case ax::mojom::Role::kDocBiblioRef: + message_id = IDS_AX_ROLE_DOC_BIBLIO_REF; + break; + case ax::mojom::Role::kDocChapter: + message_id = IDS_AX_ROLE_DOC_CHAPTER; + break; + case ax::mojom::Role::kDocColophon: + message_id = IDS_AX_ROLE_DOC_COLOPHON; + break; + case ax::mojom::Role::kDocConclusion: + message_id = IDS_AX_ROLE_DOC_CONCLUSION; + break; + case ax::mojom::Role::kDocCover: + message_id = IDS_AX_ROLE_DOC_COVER; + break; + case ax::mojom::Role::kDocCredit: + message_id = IDS_AX_ROLE_DOC_CREDIT; + break; + case ax::mojom::Role::kDocCredits: + message_id = IDS_AX_ROLE_DOC_CREDITS; + break; + case ax::mojom::Role::kDocDedication: + message_id = IDS_AX_ROLE_DOC_DEDICATION; + break; + case ax::mojom::Role::kDocEndnote: + message_id = IDS_AX_ROLE_DOC_ENDNOTE; + break; + case ax::mojom::Role::kDocEndnotes: + message_id = IDS_AX_ROLE_DOC_ENDNOTES; + break; + case ax::mojom::Role::kDocEpigraph: + message_id = IDS_AX_ROLE_DOC_EPIGRAPH; + break; + case ax::mojom::Role::kDocEpilogue: + message_id = IDS_AX_ROLE_DOC_EPILOGUE; + break; + case ax::mojom::Role::kDocErrata: + message_id = IDS_AX_ROLE_DOC_ERRATA; + break; + case ax::mojom::Role::kDocExample: + message_id = IDS_AX_ROLE_DOC_EXAMPLE; + break; + case ax::mojom::Role::kDocFootnote: + message_id = IDS_AX_ROLE_DOC_FOOTNOTE; + break; + case ax::mojom::Role::kDocForeword: + message_id = IDS_AX_ROLE_DOC_FOREWORD; + break; + case ax::mojom::Role::kDocGlossary: + message_id = IDS_AX_ROLE_DOC_GLOSSARY; + break; + case ax::mojom::Role::kDocGlossRef: + message_id = IDS_AX_ROLE_DOC_GLOSS_REF; + break; + case ax::mojom::Role::kDocIndex: + message_id = IDS_AX_ROLE_DOC_INDEX; + break; + case ax::mojom::Role::kDocIntroduction: + message_id = IDS_AX_ROLE_DOC_INTRODUCTION; + break; + case ax::mojom::Role::kDocNoteRef: + message_id = IDS_AX_ROLE_DOC_NOTE_REF; + break; + case ax::mojom::Role::kDocNotice: + message_id = IDS_AX_ROLE_DOC_NOTICE; + break; + case ax::mojom::Role::kDocPageBreak: + message_id = IDS_AX_ROLE_DOC_PAGE_BREAK; + break; + case ax::mojom::Role::kDocPageList: + message_id = IDS_AX_ROLE_DOC_PAGE_LIST; + break; + case ax::mojom::Role::kDocPart: + message_id = IDS_AX_ROLE_DOC_PART; + break; + case ax::mojom::Role::kDocPreface: + message_id = IDS_AX_ROLE_DOC_PREFACE; + break; + case ax::mojom::Role::kDocPrologue: + message_id = IDS_AX_ROLE_DOC_PROLOGUE; + break; + case ax::mojom::Role::kDocPullquote: + message_id = IDS_AX_ROLE_DOC_PULLQUOTE; + break; + case ax::mojom::Role::kDocQna: + message_id = IDS_AX_ROLE_DOC_QNA; + break; + case ax::mojom::Role::kDocSubtitle: + message_id = IDS_AX_ROLE_DOC_SUBTITLE; + break; + case ax::mojom::Role::kDocTip: + message_id = IDS_AX_ROLE_DOC_TIP; + break; + case ax::mojom::Role::kDocToc: + message_id = IDS_AX_ROLE_DOC_TOC; + break; case ax::mojom::Role::kDocument: message_id = IDS_AX_ROLE_DOCUMENT; break; @@ -596,6 +713,15 @@ base::string16 BrowserAccessibilityAndroid::GetRoleDescription() const { case ax::mojom::Role::kGenericContainer: // No role description. break; + case ax::mojom::Role::kGraphicsDocument: + message_id = IDS_AX_ROLE_GRAPHICS_DOCUMENT; + break; + case ax::mojom::Role::kGraphicsObject: + message_id = IDS_AX_ROLE_GRAPHICS_OBJECT; + break; + case ax::mojom::Role::kGraphicsSymbol: + message_id = IDS_AX_ROLE_GRAPHICS_SYMBOL; + break; case ax::mojom::Role::kGrid: message_id = IDS_AX_ROLE_TABLE; break; @@ -757,6 +883,9 @@ base::string16 BrowserAccessibilityAndroid::GetRoleDescription() const { case ax::mojom::Role::kScrollBar: message_id = IDS_AX_ROLE_SCROLL_BAR; break; + case ax::mojom::Role::kScrollView: + // No role description. + break; case ax::mojom::Role::kSearch: message_id = IDS_AX_ROLE_SEARCH; break; @@ -769,9 +898,6 @@ base::string16 BrowserAccessibilityAndroid::GetRoleDescription() const { case ax::mojom::Role::kSliderThumb: // No role description. break; - case ax::mojom::Role::kSpinButtonPart: - // No role description. - break; case ax::mojom::Role::kSpinButton: message_id = IDS_AX_ROLE_SPIN_BUTTON; break; @@ -1419,7 +1545,7 @@ bool BrowserAccessibilityAndroid::HasOnlyTextAndImageChildren() const { for (uint32_t i = 0; i < InternalChildCount(); i++) { BrowserAccessibility* child = InternalGetChild(i); if (child->GetRole() != ax::mojom::Role::kStaticText && - child->GetRole() != ax::mojom::Role::kImage) { + !ui::IsImage(child->GetRole())) { return false; } } diff --git a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm index 2fba090c047..bc4b972f9c7 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/chromium/content/browser/accessibility/browser_accessibility_cocoa.mm @@ -29,6 +29,7 @@ #include "third_party/skia/include/core/SkColor.h" #include "ui/accessibility/ax_range.h" #include "ui/accessibility/ax_role_properties.h" +#include "ui/accessibility/ax_table_info.h" #import "ui/accessibility/platform/ax_platform_node_mac.h" @@ -866,19 +867,36 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; return nil; NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; - const std::vector& uniqueCellIds = - table->GetIntListAttribute(ax::mojom::IntListAttribute::kUniqueCellIds); - for (size_t i = 0; i < uniqueCellIds.size(); ++i) { - int id = uniqueCellIds[i]; - BrowserAccessibility* cell = - browserAccessibility_->manager()->GetFromID(id); - if (cell && cell->GetRole() == ax::mojom::Role::kColumnHeader) { - // Expose all column headers on table object. - // Expose only relevant column headers on cell object. - if (is_table_like || [self isColumnHeaderForCurrentCell:cell]) + + if (is_table_like) { + // If this is a table, return all column headers. + std::set headerIds; + for (int i = 0; i < table->GetTableColCount(); i++) { + std::vector colHeaderIds = table->GetColHeaderNodeIds(i); + std::copy(colHeaderIds.begin(), colHeaderIds.end(), + std::inserter(headerIds, headerIds.end())); + } + for (int32_t id : headerIds) { + BrowserAccessibility* cell = + browserAccessibility_->manager()->GetFromID(id); + if (cell) + [ret addObject:ToBrowserAccessibilityCocoa(cell)]; + } + } else { + // Otherwise this is a cell, return the column headers for this cell. + int column = -1; + browserAccessibility_->GetIntAttribute( + ax::mojom::IntAttribute::kTableCellColumnIndex, &column); + + std::vector colHeaderIds = table->GetColHeaderNodeIds(column); + for (int32_t id : colHeaderIds) { + BrowserAccessibility* cell = + browserAccessibility_->manager()->GetFromID(id); + if (cell) [ret addObject:ToBrowserAccessibilityCocoa(cell)]; } } + return [ret count] ? ret : nil; } @@ -1115,7 +1133,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; - (NSNumber*)hasPopup { if (![self instanceActive]) return nil; - return browserAccessibility_->HasState(ax::mojom::State::kHaspopup) ? @YES + return browserAccessibility_->HasState(ax::mojom::State::kHasPopup) ? @YES : @NO; } @@ -1768,6 +1786,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; - (NSArray*)rowHeaders { if (![self instanceActive]) return nil; + bool is_cell_or_table_header = ui::IsCellOrTableHeaderRole(browserAccessibility_->GetRole()); bool is_table_like = ui::IsTableLikeRole(browserAccessibility_->GetRole()); @@ -1778,17 +1797,36 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; return nil; NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; - const std::vector& uniqueCellIds = - table->GetIntListAttribute(ax::mojom::IntListAttribute::kUniqueCellIds); - for (size_t i = 0; i < uniqueCellIds.size(); ++i) { - int id = uniqueCellIds[i]; - BrowserAccessibility* cell = - browserAccessibility_->manager()->GetFromID(id); - if (cell && cell->GetRole() == ax::mojom::Role::kRowHeader) { - if (is_table_like || [self isRowHeaderForCurrentCell:cell]) + + if (is_table_like) { + // If this is a table, return all row headers. + std::set headerIds; + for (int i = 0; i < table->GetTableRowCount(); i++) { + std::vector rowHeaderIds = table->GetRowHeaderNodeIds(i); + for (int32_t id : rowHeaderIds) + headerIds.insert(id); + } + for (int32_t id : headerIds) { + BrowserAccessibility* cell = + browserAccessibility_->manager()->GetFromID(id); + if (cell) + [ret addObject:ToBrowserAccessibilityCocoa(cell)]; + } + } else { + // Otherwise this is a cell, return the row headers for this cell. + int row = -1; + browserAccessibility_->GetIntAttribute( + ax::mojom::IntAttribute::kTableCellRowIndex, &row); + + std::vector rowHeaderIds = table->GetRowHeaderNodeIds(row); + for (int32_t id : rowHeaderIds) { + BrowserAccessibility* cell = + browserAccessibility_->manager()->GetFromID(id); + if (cell) [ret addObject:ToBrowserAccessibilityCocoa(cell)]; } } + return [ret count] ? ret : nil; } @@ -2208,12 +2246,16 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; - (NSArray*)visibleCells { if (![self instanceActive]) return nil; + + ui::AXTableInfo* table_info = + browserAccessibility_->manager()->ax_tree()->GetTableInfo( + browserAccessibility_->node()); + if (!table_info) + return nil; + NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; - const std::vector& uniqueCellIds = - browserAccessibility_->GetIntListAttribute( - ax::mojom::IntListAttribute::kUniqueCellIds); - for (size_t i = 0; i < uniqueCellIds.size(); ++i) { - int id = uniqueCellIds[i]; + for (size_t i = 0; i < table_info->unique_cell_ids.size(); ++i) { + int id = table_info->unique_cell_ids[i]; BrowserAccessibility* cell = browserAccessibility_->manager()->GetFromID(id); if (cell) @@ -3029,7 +3071,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; [ret addObjectsFromArray:@[ NSAccessibilityGrabbedAttribute ]]; } - if (browserAccessibility_->HasState(ax::mojom::State::kHaspopup)) { + if (browserAccessibility_->HasState(ax::mojom::State::kHasPopup)) { [ret addObjectsFromArray:@[ NSAccessibilityHasPopupAttribute ]]; } diff --git a/chromium/content/browser/accessibility/browser_accessibility_com_win.cc b/chromium/content/browser/accessibility/browser_accessibility_com_win.cc index a0738c7e24b..0cd7fd5a2d0 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_com_win.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_com_win.cc @@ -1724,7 +1724,10 @@ void BrowserAccessibilityComWin::ComputeStylesIfNeeded() { // style span. std::vector previous_attributes = attributes_map.rbegin()->second; - if (!std::equal(attributes.begin(), attributes.end(), + // Must check the size, otherwise if attributes is a subset of + // prev_attributes, they would appear to be equal. + if (attributes.size() != previous_attributes.size() || + !std::equal(attributes.begin(), attributes.end(), previous_attributes.begin())) { attributes_map[start_offset] = attributes; } @@ -1913,6 +1916,52 @@ void BrowserAccessibilityComWin::Init(ui::AXPlatformNodeDelegate* delegate) { AXPlatformNodeWin::Init(delegate); } +base::string16 BrowserAccessibilityComWin::GetInvalidValue() const { + const BrowserAccessibilityWin* target = owner(); + // The aria-invalid=spelling/grammar need to be exposed as text attributes for + // a range matching the visual underline representing the error. + if (static_cast( + target->GetIntAttribute(ax::mojom::IntAttribute::kInvalidState)) == + ax::mojom::InvalidState::kNone && + target->IsTextOnlyObject() && target->PlatformGetParent()) { + // Text nodes need to reflect the invalid state of their parent object, + // otherwise spelling and grammar errors communicated through aria-invalid + // won't be reflected in text attributes. + target = static_cast(target->PlatformGetParent()); + } + + base::string16 invalid_value; + // Note: spelling+grammar errors case is disallowed and not supported. It + // could possibly arise with aria-invalid on the ancestor of a spelling error, + // but this is not currently described in any spec and no real-world use cases + // have been found. + switch (static_cast( + target->GetIntAttribute(ax::mojom::IntAttribute::kInvalidState))) { + case ax::mojom::InvalidState::kNone: + case ax::mojom::InvalidState::kFalse: + break; + case ax::mojom::InvalidState::kTrue: + return invalid_value = L"true"; + case ax::mojom::InvalidState::kSpelling: + return invalid_value = L"spelling"; + case ax::mojom::InvalidState::kGrammar: + return base::ASCIIToUTF16("grammar"); + case ax::mojom::InvalidState::kOther: { + base::string16 aria_invalid_value; + if (target->GetString16Attribute( + ax::mojom::StringAttribute::kAriaInvalidValue, + &aria_invalid_value)) { + SanitizeStringAttributeForIA2(aria_invalid_value, &aria_invalid_value); + invalid_value = aria_invalid_value; + } else { + // Set the attribute to L"true", since we cannot be more specific. + invalid_value = L"true"; + } + } + } + return invalid_value; +} + std::vector BrowserAccessibilityComWin::ComputeTextAttributes() const { std::vector attributes; @@ -1922,38 +1971,37 @@ std::vector BrowserAccessibilityComWin::ComputeTextAttributes() // TODO(nektar): Compute what objects are auto-generated in Blink. if (owner()->GetRole() == ax::mojom::Role::kListMarker) attributes.push_back(L"auto-generated:true"); - else - attributes.push_back(L"auto-generated:false"); int color; - base::string16 color_value(L"transparent"); if (owner()->GetIntAttribute(ax::mojom::IntAttribute::kBackgroundColor, &color)) { unsigned int alpha = SkColorGetA(color); unsigned int red = SkColorGetR(color); unsigned int green = SkColorGetG(color); unsigned int blue = SkColorGetB(color); - if (alpha) { - color_value = L"rgb(" + base::UintToString16(red) + L',' + - base::UintToString16(green) + L',' + - base::UintToString16(blue) + L')'; + // Don't expose default value of pure white. + if (alpha && (red != 255 || green != 255 || blue != 255)) { + base::string16 color_value = L"rgb(" + base::UintToString16(red) + L',' + + base::UintToString16(green) + L',' + + base::UintToString16(blue) + L')'; + SanitizeStringAttributeForIA2(color_value, &color_value); + attributes.push_back(L"background-color:" + color_value); } } - SanitizeStringAttributeForIA2(color_value, &color_value); - attributes.push_back(L"background-color:" + color_value); if (owner()->GetIntAttribute(ax::mojom::IntAttribute::kColor, &color)) { unsigned int red = SkColorGetR(color); unsigned int green = SkColorGetG(color); unsigned int blue = SkColorGetB(color); - color_value = L"rgb(" + base::UintToString16(red) + L',' + - base::UintToString16(green) + L',' + - base::UintToString16(blue) + L')'; - } else { - color_value = L"rgb(0,0,0)"; + // Don't expose default value of black. + if (red || green || blue) { + base::string16 color_value = L"rgb(" + base::UintToString16(red) + L',' + + base::UintToString16(green) + L',' + + base::UintToString16(blue) + L')'; + SanitizeStringAttributeForIA2(color_value, &color_value); + attributes.push_back(L"color:" + color_value); + } } - SanitizeStringAttributeForIA2(color_value, &color_value); - attributes.push_back(L"color:" + color_value); base::string16 font_family(owner()->GetInheritedString16Attribute( ax::mojom::StringAttribute::kFontFamily)); @@ -1975,117 +2023,56 @@ std::vector BrowserAccessibilityComWin::ComputeTextAttributes() L"pt"); } + // TODO(nektar): Add Blink support for the following attributes: + // text-line-through-mode, text-line-through-width, text-outline:false, + // text-position:baseline, text-shadow:none, text-underline-mode:continuous. + int32_t text_style = owner()->GetIntAttribute(ax::mojom::IntAttribute::kTextStyle); - if (text_style == static_cast(ax::mojom::TextStyle::kNone)) { - attributes.push_back(L"font-style:normal"); - attributes.push_back(L"font-weight:normal"); - } else { + if (text_style != static_cast(ax::mojom::TextStyle::kNone)) { if (text_style & static_cast(ax::mojom::TextStyle::kTextStyleItalic)) { attributes.push_back(L"font-style:italic"); - } else { - attributes.push_back(L"font-style:normal"); } if (text_style & static_cast(ax::mojom::TextStyle::kTextStyleBold)) { attributes.push_back(L"font-weight:bold"); - } else { - attributes.push_back(L"font-weight:normal"); } - } - int32_t invalid_state = - owner()->GetIntAttribute(ax::mojom::IntAttribute::kInvalidState); - switch (static_cast(invalid_state)) { - case ax::mojom::InvalidState::kNone: - case ax::mojom::InvalidState::kFalse: - attributes.push_back(L"invalid:false"); - break; - case ax::mojom::InvalidState::kTrue: - attributes.push_back(L"invalid:true"); - break; - case ax::mojom::InvalidState::kSpelling: - case ax::mojom::InvalidState::kGrammar: { - base::string16 spelling_grammar_value; - if (invalid_state & - static_cast(ax::mojom::InvalidState::kSpelling)) - spelling_grammar_value = L"spelling"; - else if (invalid_state & - static_cast(ax::mojom::InvalidState::kGrammar)) - spelling_grammar_value = L"grammar"; - else - spelling_grammar_value = L"spelling,grammar"; - attributes.push_back(L"invalid:" + spelling_grammar_value); - break; + if (text_style & + static_cast(ax::mojom::TextStyle::kTextStyleLineThrough)) { + // TODO(nektar): Figure out a more specific value. + attributes.push_back(L"text-line-through-style:solid"); } - case ax::mojom::InvalidState::kOther: { - base::string16 aria_invalid_value; - if (owner()->GetString16Attribute( - ax::mojom::StringAttribute::kAriaInvalidValue, - &aria_invalid_value)) { - SanitizeStringAttributeForIA2(aria_invalid_value, &aria_invalid_value); - attributes.push_back(L"invalid:" + aria_invalid_value); - } else { - // Set the attribute to L"true", since we cannot be more specific. - attributes.push_back(L"invalid:true"); - } - break; + + if (text_style & + static_cast(ax::mojom::TextStyle::kTextStyleUnderline)) { + // TODO(nektar): Figure out a more specific value. + attributes.push_back(L"text-underline-style:solid"); } } + // Screen readers look at the text attributes to determine if something is + // misspelled, so we need to propagate any spelling attributes from immediate + // parents of text-only objects. + base::string16 invalid_value = GetInvalidValue(); + if (!invalid_value.empty()) + attributes.push_back(L"invalid:" + invalid_value); + base::string16 language(owner()->GetInheritedString16Attribute( ax::mojom::StringAttribute::kLanguage)); - // Default value should be L"en-US". - if (language.empty()) { - attributes.push_back(L"language:en-US"); - } else { + // Don't expose default value should of L"en-US". + if (!language.empty() && language != L"en-US") { SanitizeStringAttributeForIA2(language, &language); attributes.push_back(L"language:" + language); } - // TODO(nektar): Add Blink support for the following attributes. - // Currently set to their default values as dictated by the IA2 Spec. - attributes.push_back(L"text-line-through-mode:continuous"); - if (text_style & - static_cast(ax::mojom::TextStyle::kTextStyleLineThrough)) { - // TODO(nektar): Figure out a more specific value. - attributes.push_back(L"text-line-through-style:solid"); - } else { - attributes.push_back(L"text-line-through-style:none"); - } - // Default value must be the empty string. - attributes.push_back(L"text-line-through-text:"); - if (text_style & - static_cast(ax::mojom::TextStyle::kTextStyleLineThrough)) { - // TODO(nektar): Figure out a more specific value. - attributes.push_back(L"text-line-through-type:single"); - } else { - attributes.push_back(L"text-line-through-type:none"); - } - attributes.push_back(L"text-line-through-width:auto"); - attributes.push_back(L"text-outline:false"); - attributes.push_back(L"text-position:baseline"); - attributes.push_back(L"text-shadow:none"); - attributes.push_back(L"text-underline-mode:continuous"); - if (text_style & - static_cast(ax::mojom::TextStyle::kTextStyleUnderline)) { - // TODO(nektar): Figure out a more specific value. - attributes.push_back(L"text-underline-style:solid"); - attributes.push_back(L"text-underline-type:single"); - } else { - attributes.push_back(L"text-underline-style:none"); - attributes.push_back(L"text-underline-type:none"); - } - attributes.push_back(L"text-underline-width:auto"); - auto text_direction = static_cast( owner()->GetIntAttribute(ax::mojom::IntAttribute::kTextDirection)); switch (text_direction) { case ax::mojom::TextDirection::kNone: case ax::mojom::TextDirection::kLtr: - attributes.push_back(L"writing-mode:lr"); break; case ax::mojom::TextDirection::kRtl: attributes.push_back(L"writing-mode:rl"); @@ -2099,6 +2086,19 @@ std::vector BrowserAccessibilityComWin::ComputeTextAttributes() break; } + auto text_position = static_cast( + owner()->GetIntAttribute(ax::mojom::IntAttribute::kTextPosition)); + switch (text_position) { + case ax::mojom::TextPosition::kNone: + break; + case ax::mojom::TextPosition::kSubscript: + attributes.push_back(L"text-position:sub"); + break; + case ax::mojom::TextPosition::kSuperscript: + attributes.push_back(L"text-position:super"); + break; + } + return attributes; } @@ -2125,10 +2125,8 @@ BrowserAccessibilityComWin::GetSpellingAttributes() { int end_offset = marker_ends[i]; std::vector start_attributes; start_attributes.push_back(L"invalid:spelling"); - std::vector end_attributes; - end_attributes.push_back(L"invalid:false"); spelling_attributes[start_offset] = start_attributes; - spelling_attributes[end_offset] = end_attributes; + spelling_attributes[end_offset] = std::vector(); } } if (owner()->IsPlainTextField()) { @@ -2193,6 +2191,16 @@ HRESULT BrowserAccessibilityComWin::GetStringAttributeAsBstr( return S_OK; } +// Pass in prefix with ":" included at the end, e.g. "invalid:". +bool HasAttribute(std::vector& existing_attributes, + base::string16 prefix) { + for (base::string16& attr : existing_attributes) { + if (base::StartsWith(attr, prefix, base::CompareCase::SENSITIVE)) + return true; + } + return false; +} + // static void BrowserAccessibilityComWin::MergeSpellingIntoTextAttributes( const std::map>& spelling_attributes, @@ -2212,16 +2220,14 @@ void BrowserAccessibilityComWin::MergeSpellingIntoTextAttributes( std::vector& existing_attributes = iterator->second; // There might be a spelling attribute already in the list of text // attributes, originating from "aria-invalid", that is being overwritten - // by a spelling marker. - auto existing_spelling_attribute = - std::find(existing_attributes.begin(), existing_attributes.end(), - L"invalid:false"); - if (existing_spelling_attribute != existing_attributes.end()) - existing_attributes.erase(existing_spelling_attribute); - - existing_attributes.insert(existing_attributes.end(), - spelling_attribute.second.begin(), - spelling_attribute.second.end()); + // by a spelling marker. If it already exists, prefer it over this + // automatically computed attribute. + if (!HasAttribute(existing_attributes, L"invalid:")) { + // Does not exist -- insert our own. + existing_attributes.insert(existing_attributes.end(), + spelling_attribute.second.begin(), + spelling_attribute.second.end()); + } } } } diff --git a/chromium/content/browser/accessibility/browser_accessibility_com_win.h b/chromium/content/browser/accessibility/browser_accessibility_com_win.h index 4d6ce30ac99..debd107f060 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_com_win.h +++ b/chromium/content/browser/accessibility/browser_accessibility_com_win.h @@ -483,6 +483,8 @@ class __declspec(uuid("562072fe-3390-43b1-9e2c-dd4118f5ac79")) HRESULT GetStringAttributeAsBstr(ax::mojom::StringAttribute attribute, BSTR* value_bstr); + base::string16 GetInvalidValue() const; + // Merges the given spelling attributes, i.e. document marker information, // into the given text attributes starting at the given character offset. This // is required for two reasons: 1. Document markers that are present on text diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.cc b/chromium/content/browser/accessibility/browser_accessibility_manager.cc index bf2680cbbf0..f15f0305256 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_manager.cc @@ -711,6 +711,28 @@ void BrowserAccessibilityManager::LoadInlineTextBoxes( delegate_->AccessibilityPerformAction(action_data); } +void BrowserAccessibilityManager::SetAccessibilityFocus( + const BrowserAccessibility& node) { + if (!delegate_) + return; + + ui::AXActionData action_data; + action_data.action = ax::mojom::Action::kSetAccessibilityFocus; + action_data.target_node_id = node.GetId(); + delegate_->AccessibilityPerformAction(action_data); +} + +void BrowserAccessibilityManager::ClearAccessibilityFocus( + const BrowserAccessibility& node) { + if (!delegate_) + return; + + ui::AXActionData action_data; + action_data.action = ax::mojom::Action::kClearAccessibilityFocus; + action_data.target_node_id = node.GetId(); + delegate_->AccessibilityPerformAction(action_data); +} + void BrowserAccessibilityManager::HitTest(const gfx::Point& point) { if (!delegate_) return; diff --git a/chromium/content/browser/accessibility/browser_accessibility_manager.h b/chromium/content/browser/accessibility/browser_accessibility_manager.h index 276eb429ba0..b1b47d25878 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_manager.h +++ b/chromium/content/browser/accessibility/browser_accessibility_manager.h @@ -203,6 +203,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXEventGenerator { // by sending a message to the renderer to perform the respective action // on the given node. See the definition of |ui::AXActionData| for more // information about each of these actions. + void ClearAccessibilityFocus(const BrowserAccessibility& node); void Decrement(const BrowserAccessibility& node); void DoDefaultAction(const BrowserAccessibility& node); void GetImageData(const BrowserAccessibility& node, @@ -214,6 +215,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXEventGenerator { const BrowserAccessibility& node, gfx::Rect subfocus); void ScrollToPoint( const BrowserAccessibility& node, gfx::Point point); + void SetAccessibilityFocus(const BrowserAccessibility& node); void SetFocus(const BrowserAccessibility& node); void SetScrollOffset(const BrowserAccessibility& node, gfx::Point offset); void SetValue( @@ -341,7 +343,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXEventGenerator { // Accessors. ui::AXTreeIDRegistry::AXTreeID ax_tree_id() const { return ax_tree_id_; } float device_scale_factor() const { return device_scale_factor_; } - const ui::AXTree* ax_tree() const { return tree_.get(); } + ui::AXTree* ax_tree() const { return tree_.get(); } // AXTreeDelegate implementation. void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; diff --git a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc index 2a7a4c49a34..f81b59ee00a 100644 --- a/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc +++ b/chromium/content/browser/accessibility/browser_accessibility_win_unittest.cc @@ -1788,16 +1788,15 @@ TEST_F(BrowserAccessibilityTest, TestTextAttributesInContentEditables) { EXPECT_EQ(3, end_offset); EXPECT_NE(base::string16::npos, base::string16(text_attributes).find(L"font-family:Helvetica")); - EXPECT_NE(base::string16::npos, - base::string16(text_attributes).find(L"font-weight:normal")); - EXPECT_NE(base::string16::npos, - base::string16(text_attributes).find(L"font-style:normal")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"font-weight:")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"font-style:")); EXPECT_NE( base::string16::npos, base::string16(text_attributes).find(L"text-underline-style:solid")); - EXPECT_NE( - base::string16::npos, - base::string16(text_attributes).find(L"text-underline-type:single")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"text-underline-type:")); // For compatibility with Firefox, spelling attributes should also be // propagated to the parent of static text leaves. EXPECT_NE(base::string16::npos, @@ -1811,16 +1810,15 @@ TEST_F(BrowserAccessibilityTest, TestTextAttributesInContentEditables) { EXPECT_EQ(3, end_offset); EXPECT_NE(base::string16::npos, base::string16(text_attributes).find(L"font-family:Helvetica")); - EXPECT_NE(base::string16::npos, - base::string16(text_attributes).find(L"font-weight:normal")); - EXPECT_NE(base::string16::npos, - base::string16(text_attributes).find(L"font-style:normal")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"font-weight:")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"font-style:")); EXPECT_NE( base::string16::npos, base::string16(text_attributes).find(L"text-underline-style:solid")); - EXPECT_NE( - base::string16::npos, - base::string16(text_attributes).find(L"text-underline-type:single")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"text-underline-type:")); EXPECT_NE(base::string16::npos, base::string16(text_attributes).find(L"invalid:spelling")); text_attributes.Reset(); @@ -1834,14 +1832,13 @@ TEST_F(BrowserAccessibilityTest, TestTextAttributesInContentEditables) { EXPECT_EQ(15, end_offset); base::string16 attributes(text_attributes); EXPECT_NE(base::string16::npos, attributes.find(L"font-family:Helvetica")); - EXPECT_NE(base::string16::npos, attributes.find(L"font-weight:normal")); - EXPECT_NE(base::string16::npos, attributes.find(L"font-style:normal")); - EXPECT_NE( - base::string16::npos, - base::string16(text_attributes).find(L"text-underline-style:none")); - EXPECT_NE( + EXPECT_EQ(base::string16::npos, attributes.find(L"font-weight:")); + EXPECT_EQ(base::string16::npos, attributes.find(L"font-style:")); + EXPECT_EQ( base::string16::npos, - base::string16(text_attributes).find(L"text-underline-type:none")); + base::string16(text_attributes).find(L"text-underline-style:solid")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"text-underline-type:")); EXPECT_EQ(base::string16::npos, attributes.find(L"invalid:spelling")); text_attributes.Reset(); } @@ -1869,14 +1866,14 @@ TEST_F(BrowserAccessibilityTest, TestTextAttributesInContentEditables) { EXPECT_EQ(7, end_offset); EXPECT_NE(base::string16::npos, base::string16(text_attributes).find(L"font-family:Helvetica")); - EXPECT_NE(base::string16::npos, - base::string16(text_attributes).find(L"font-weight:normal")); - EXPECT_NE(base::string16::npos, - base::string16(text_attributes).find(L"font-style:normal")); - EXPECT_NE(base::string16::npos, - base::string16(text_attributes).find(L"text-underline-style:none")); - EXPECT_NE(base::string16::npos, - base::string16(text_attributes).find(L"text-underline-type:none")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"font-weight:")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"font-style:")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"text-underline-style:")); + EXPECT_EQ(base::string16::npos, + base::string16(text_attributes).find(L"text-underline-type:")); EXPECT_EQ(base::string16::npos, base::string16(text_attributes).find(L"invalid:spelling")); text_attributes.Reset(); @@ -1965,11 +1962,9 @@ TEST_F(BrowserAccessibilityTest, TestExistingMisspellingsInSimpleTextFields) { for (LONG offset = 0; offset < value1_length; ++offset) { hr = ax_combo_box->GetCOM()->get_attributes( offset, &start_offset, &end_offset, text_attributes.Receive()); - EXPECT_EQ(S_OK, hr); + EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(value1_length, end_offset); - EXPECT_EQ(base::string16::npos, - base::string16(text_attributes).find(L"invalid:spelling")); text_attributes.Reset(); } @@ -1990,11 +1985,9 @@ TEST_F(BrowserAccessibilityTest, TestExistingMisspellingsInSimpleTextFields) { ++offset) { hr = ax_combo_box->GetCOM()->get_attributes( offset, &start_offset, &end_offset, text_attributes.Receive()); - EXPECT_EQ(S_OK, hr); + EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(value1_length + 4, start_offset); EXPECT_EQ(combo_box_value_length, end_offset); - EXPECT_EQ(base::string16::npos, - base::string16(text_attributes).find(L"invalid:spelling")); text_attributes.Reset(); } @@ -2068,11 +2061,9 @@ TEST_F(BrowserAccessibilityTest, TestNewMisspellingsInSimpleTextFields) { for (LONG offset = 0; offset < combo_box_value_length; ++offset) { hr = ax_combo_box->GetCOM()->get_attributes( offset, &start_offset, &end_offset, text_attributes.Receive()); - EXPECT_EQ(S_OK, hr); + EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(combo_box_value_length, end_offset); - EXPECT_EQ(base::string16::npos, - base::string16(text_attributes).find(L"invalid:spelling")); text_attributes.Reset(); } @@ -2095,11 +2086,9 @@ TEST_F(BrowserAccessibilityTest, TestNewMisspellingsInSimpleTextFields) { for (LONG offset = 0; offset < value1_length; ++offset) { hr = ax_combo_box->GetCOM()->get_attributes( offset, &start_offset, &end_offset, text_attributes.Receive()); - EXPECT_EQ(S_OK, hr); + EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(value1_length, end_offset); - EXPECT_EQ(base::string16::npos, - base::string16(text_attributes).find(L"invalid:spelling")); text_attributes.Reset(); } @@ -2120,11 +2109,9 @@ TEST_F(BrowserAccessibilityTest, TestNewMisspellingsInSimpleTextFields) { ++offset) { hr = ax_combo_box->GetCOM()->get_attributes( offset, &start_offset, &end_offset, text_attributes.Receive()); - EXPECT_EQ(S_OK, hr); + EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(value1_length + 4, start_offset); EXPECT_EQ(combo_box_value_length, end_offset); - EXPECT_EQ(base::string16::npos, - base::string16(text_attributes).find(L"invalid:spelling")); text_attributes.Reset(); } diff --git a/chromium/content/browser/accessibility/captioning_controller.cc b/chromium/content/browser/accessibility/captioning_controller.cc index 3e1967f9565..f0511fc03ae 100644 --- a/chromium/content/browser/accessibility/captioning_controller.cc +++ b/chromium/content/browser/accessibility/captioning_controller.cc @@ -24,7 +24,7 @@ int GetRenderProcessIdFromRenderViewHost(RenderViewHost* host) { RenderProcessHost* render_process = host->GetProcess(); DCHECK(render_process); if (render_process->HasConnection()) - return render_process->GetHandle(); + return render_process->GetProcess().Handle(); return 0; } diff --git a/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc index 14d88ae68bd..6a812bdaa34 100644 --- a/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc +++ b/chromium/content/browser/accessibility/cross_platform_accessibility_browsertest.cc @@ -430,18 +430,6 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, const ui::AXNode* cell3 = table->ChildAtIndex(1)->ChildAtIndex(0); const ui::AXNode* cell4 = table->ChildAtIndex(1)->ChildAtIndex(1); - ASSERT_EQ(ax::mojom::IntListAttribute::kCellIds, - table->data().intlist_attributes[0].first); - const std::vector& table_cell_ids = - 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(0, GetIntAttr(cell1, ax::mojom::IntAttribute::kTableCellColumnIndex)); EXPECT_EQ(0, GetIntAttr(cell1, ax::mojom::IntAttribute::kTableCellRowIndex)); diff --git a/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc index 09c6ba4cdf1..4d30edf5a69 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc +++ b/chromium/content/browser/accessibility/dump_accessibility_events_browsertest.cc @@ -189,9 +189,8 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, RunEventTest(FILE_PATH_LITERAL("aria-treeitem-focus.html")); } -// http:/crbug.com/791268 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest, - DISABLED_AccessibilityEventsAriaComboBoxFocus) { + AccessibilityEventsAriaComboBoxFocus) { RunEventTest(FILE_PATH_LITERAL("aria-combo-box-focus.html")); } diff --git a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index 41d6b68e6ff..263cc2387f5 100644 --- a/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/chromium/content/browser/accessibility/dump_accessibility_tree_browsertest.cc @@ -282,6 +282,11 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityCSSLanguage) { RunCSSTest(FILE_PATH_LITERAL("language.html")); } +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, + AccessibilityCSSTableIncomplete) { + RunCSSTest(FILE_PATH_LITERAL("table-incomplete.html")); +} + IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityA) { RunHtmlTest(FILE_PATH_LITERAL("a.html")); } @@ -370,6 +375,11 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, } #endif +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, + AccessibilityAriaAnyUnignored) { + RunAriaTest(FILE_PATH_LITERAL("aria-any-unignored.html")); +} + IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaApplication) { RunAriaTest(FILE_PATH_LITERAL("aria-application.html")); @@ -1060,6 +1070,14 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityDt) { RunHtmlTest(FILE_PATH_LITERAL("dt.html")); } +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityDpubRoles) { + RunAriaTest(FILE_PATH_LITERAL("dpub-roles.html")); +} + +IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityGraphicsRoles) { + RunAriaTest(FILE_PATH_LITERAL("graphics-roles.html")); +} + #if defined(OS_ANDROID) || defined(OS_MACOSX) // Flaky failures: http://crbug.com/445929. // Mac failures: http://crbug.com/571712. diff --git a/chromium/content/browser/accessibility/fullscreen_browsertest.cc b/chromium/content/browser/accessibility/fullscreen_browsertest.cc index dbc3358655b..aff774b3a62 100644 --- a/chromium/content/browser/accessibility/fullscreen_browsertest.cc +++ b/chromium/content/browser/accessibility/fullscreen_browsertest.cc @@ -51,7 +51,9 @@ class FakeFullscreenDelegate : public WebContentsDelegate { FakeFullscreenDelegate() = default; ~FakeFullscreenDelegate() override = default; - void EnterFullscreenModeForTab(WebContents*, const GURL&) override { + void EnterFullscreenModeForTab(WebContents*, + const GURL&, + const blink::WebFullscreenOptions&) override { is_fullscreen_ = true; } diff --git a/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc b/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc index 88c6d110407..a088ec91be7 100644 --- a/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc +++ b/chromium/content/browser/accessibility/one_shot_accessibility_tree_search.cc @@ -270,7 +270,7 @@ bool AccessibilityControlPredicate( if (node->HasState(ax::mojom::State::kFocusable) && node->GetRole() != ax::mojom::Role::kIframe && node->GetRole() != ax::mojom::Role::kIframePresentational && - node->GetRole() != ax::mojom::Role::kLink && + !ui::IsLink(node->GetRole()) && node->GetRole() != ax::mojom::Role::kWebArea && node->GetRole() != ax::mojom::Role::kRootWebArea) { return true; @@ -292,52 +292,52 @@ bool AccessibilityFocusablePredicate( bool AccessibilityGraphicPredicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return node->GetRole() == ax::mojom::Role::kImage; + return ui::IsImage(node->GetRole()); } bool AccessibilityHeadingPredicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return (node->GetRole() == ax::mojom::Role::kHeading); + return ui::IsHeading(node->GetRole()); } bool AccessibilityH1Predicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return (node->GetRole() == ax::mojom::Role::kHeading && + return (ui::IsHeading(node->GetRole()) && node->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel) == 1); } bool AccessibilityH2Predicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return (node->GetRole() == ax::mojom::Role::kHeading && + return (ui::IsHeading(node->GetRole()) && node->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel) == 2); } bool AccessibilityH3Predicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return (node->GetRole() == ax::mojom::Role::kHeading && + return (ui::IsHeading(node->GetRole()) && node->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel) == 3); } bool AccessibilityH4Predicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return (node->GetRole() == ax::mojom::Role::kHeading && + return (ui::IsHeading(node->GetRole()) && node->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel) == 4); } bool AccessibilityH5Predicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return (node->GetRole() == ax::mojom::Role::kHeading && + return (ui::IsHeading(node->GetRole()) && node->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel) == 5); } bool AccessibilityH6Predicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return (node->GetRole() == ax::mojom::Role::kHeading && + return (ui::IsHeading(node->GetRole()) && node->GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel) == 6); } @@ -381,21 +381,17 @@ bool AccessibilityLandmarkPredicate( bool AccessibilityLinkPredicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return node->GetRole() == ax::mojom::Role::kLink; + return ui::IsLink(node->GetRole()); } bool AccessibilityListPredicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return (node->GetRole() == ax::mojom::Role::kListBox || - node->GetRole() == ax::mojom::Role::kList || - node->GetRole() == ax::mojom::Role::kDescriptionList); + return ui::IsList(node->GetRole()); } bool AccessibilityListItemPredicate( BrowserAccessibility* start, BrowserAccessibility* node) { - return (node->GetRole() == ax::mojom::Role::kListItem || - node->GetRole() == ax::mojom::Role::kDescriptionListTerm || - node->GetRole() == ax::mojom::Role::kListBoxOption); + return ui::IsListItem(node->GetRole()); } bool AccessibilityLiveRegionPredicate( diff --git a/chromium/content/browser/accessibility/web_contents_accessibility_android.cc b/chromium/content/browser/accessibility/web_contents_accessibility_android.cc index 5e7ccb79c98..c1be0a5d63f 100644 --- a/chromium/content/browser/accessibility/web_contents_accessibility_android.cc +++ b/chromium/content/browser/accessibility/web_contents_accessibility_android.cc @@ -551,6 +551,7 @@ bool WebContentsAccessibilityAndroid::OnHoverEvent( root_manager_) { gfx::PointF point = IsUseZoomForDSFEnabled() ? event.GetPointPix() : event.GetPoint(); + point.Scale(1 / page_scale_); root_manager_->HitTest(gfx::ToFlooredPoint(point)); } return true; @@ -1042,15 +1043,25 @@ jboolean WebContentsAccessibilityAndroid::PreviousAtGranularity( return false; } -void WebContentsAccessibilityAndroid::SetAccessibilityFocus( +void WebContentsAccessibilityAndroid::MoveAccessibilityFocus( JNIEnv* env, const JavaParamRef& obj, - jint unique_id) { + jint old_unique_id, + jint new_unique_id) { + BrowserAccessibilityAndroid* old_node = GetAXFromUniqueID(old_unique_id); + if (old_node) + old_node->manager()->ClearAccessibilityFocus(*old_node); + + BrowserAccessibilityAndroid* node = GetAXFromUniqueID(new_unique_id); + if (!node) + return; + node->manager()->SetAccessibilityFocus(*node); + // When Android sets accessibility focus to a node, we load inline text // boxes for that node so that subsequent requests for character bounding - // boxes will succeed. - BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id); - if (node) + // boxes will succeed. However, don't do that for the root of the tree, + // as that will result in loading inline text boxes for the whole tree. + if (node != node->manager()->GetRoot()) node->manager()->LoadInlineTextBoxes(*node); } @@ -1196,7 +1207,8 @@ BrowserAccessibilityAndroid* WebContentsAccessibilityAndroid::GetAXFromUniqueID( BrowserAccessibilityAndroid::GetFromUniqueId(unique_id)); } -void WebContentsAccessibilityAndroid::UpdateFrameInfo() { +void WebContentsAccessibilityAndroid::UpdateFrameInfo(float page_scale) { + page_scale_ = page_scale; if (frame_info_initialized_) return; diff --git a/chromium/content/browser/accessibility/web_contents_accessibility_android.h b/chromium/content/browser/accessibility/web_contents_accessibility_android.h index 68fd068abbf..51343b13f97 100644 --- a/chromium/content/browser/accessibility/web_contents_accessibility_android.h +++ b/chromium/content/browser/accessibility/web_contents_accessibility_android.h @@ -151,12 +151,18 @@ class CONTENT_EXPORT WebContentsAccessibilityAndroid jint id, jint cursor_index); - // Set accessibility focus. This sends a message to the renderer to - // asynchronously load inline text boxes for this node only, enabling more - // accurate movement by granularities on this node. - void SetAccessibilityFocus(JNIEnv* env, - const base::android::JavaParamRef& obj, - jint id); + // Move accessibility focus. This sends a message to the renderer to + // clear accessibility focus on the previous node and set accessibility + // focus on the current node. This isn't exposed to the open web, but used + // internally. + // + // In addition, when a node gets accessibility focus we asynchronously + // load inline text boxes for this node only, enabling more accurate + // movement by granularities on this node. + void MoveAccessibilityFocus(JNIEnv* env, + const base::android::JavaParamRef& obj, + jint old_unique_id, + jint new_unique_id); // Returns true if the object is a slider. bool IsSlider(JNIEnv* env, @@ -213,7 +219,7 @@ class CONTENT_EXPORT WebContentsAccessibilityAndroid jint start, jint len); - void UpdateFrameInfo(); + void UpdateFrameInfo(float page_scale); void set_root_manager(BrowserAccessibilityManagerAndroid* manager) { root_manager_ = manager; @@ -253,6 +259,8 @@ class CONTENT_EXPORT WebContentsAccessibilityAndroid bool frame_info_initialized_; + float page_scale_ = 1.f; + BrowserAccessibilityManagerAndroid* root_manager_; // Manages the connection between web contents and the RenderFrameHost that diff --git a/chromium/content/browser/android/OWNERS b/chromium/content/browser/android/OWNERS index 6006c70ff37..13cc5f86a23 100644 --- a/chromium/content/browser/android/OWNERS +++ b/chromium/content/browser/android/OWNERS @@ -1,8 +1,6 @@ +jinsukkim@chromium.org skyostil@chromium.org tedchoc@chromium.org yfriedman@chromium.org -# ContentViewCore related -per-file content_view_*=jinsukkim@chromium.org - # COMPONENT: Content>WebApps diff --git a/chromium/content/browser/android/app_web_message_port.cc b/chromium/content/browser/android/app_web_message_port.cc index 97e66fd5146..ec7b9ba9fdf 100644 --- a/chromium/content/browser/android/app_web_message_port.cc +++ b/chromium/content/browser/android/app_web_message_port.cc @@ -9,8 +9,8 @@ #include "base/android/jni_string.h" #include "base/bind.h" #include "base/threading/thread_task_runner_handle.h" -#include "content/browser/android/string_message_codec.h" #include "jni/AppWebMessagePort_jni.h" +#include "third_party/blink/public/common/message_port/string_message_codec.h" using blink::MessagePortChannel; @@ -44,7 +44,7 @@ JNI_AppWebMessagePort_DecodeStringMessage( &encoded_message); base::string16 message; - if (!DecodeStringMessage(encoded_message, &message)) + if (!blink::DecodeStringMessage(encoded_message, &message)) return nullptr; base::android::ScopedJavaLocalRef jmessage = @@ -57,8 +57,8 @@ JNI_AppWebMessagePort_EncodeStringMessage( JNIEnv* env, const base::android::JavaParamRef& jcaller, const base::android::JavaParamRef& jmessage) { - std::vector encoded_message = - EncodeStringMessage(base::android::ConvertJavaStringToUTF16(jmessage)); + std::vector encoded_message = blink::EncodeStringMessage( + base::android::ConvertJavaStringToUTF16(jmessage)); return base::android::ToJavaByteArray(env, encoded_message); } diff --git a/chromium/content/browser/android/content_protocol_handler_impl.cc b/chromium/content/browser/android/content_protocol_handler_impl.cc index 143dc304e6e..172487374f0 100644 --- a/chromium/content/browser/android/content_protocol_handler_impl.cc +++ b/chromium/content/browser/android/content_protocol_handler_impl.cc @@ -4,7 +4,8 @@ #include "content/browser/android/content_protocol_handler_impl.h" -#include "base/memory/ptr_util.h" +#include + #include "base/task_runner.h" #include "content/browser/android/url_request_content_job.h" #include "net/base/net_errors.h" @@ -16,7 +17,7 @@ namespace content { // static std::unique_ptr ContentProtocolHandler::Create( const scoped_refptr& content_task_runner) { - return base::WrapUnique(new ContentProtocolHandlerImpl(content_task_runner)); + return std::make_unique(content_task_runner); } ContentProtocolHandlerImpl::ContentProtocolHandlerImpl( diff --git a/chromium/content/browser/android/content_ui_event_handler.cc b/chromium/content/browser/android/content_ui_event_handler.cc new file mode 100644 index 00000000000..c043c6a73c8 --- /dev/null +++ b/chromium/content/browser/android/content_ui_event_handler.cc @@ -0,0 +1,220 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/android/content_ui_event_handler.h" + +#include "base/metrics/histogram_macros.h" +#include "content/browser/renderer_host/render_widget_host_view_android.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/browser/web_contents/web_contents_view_android.h" +#include "jni/ContentUiEventHandler_jni.h" +#include "ui/android/window_android.h" +#include "ui/events/android/event_handler_android.h" +#include "ui/events/android/gesture_event_android.h" +#include "ui/events/android/gesture_event_type.h" +#include "ui/events/android/key_event_android.h" +#include "ui/events/android/motion_event_android.h" +#include "ui/events/base_event_utils.h" + +using base::android::AttachCurrentThread; +using base::android::JavaParamRef; +using base::android::JavaRef; +using base::android::ScopedJavaLocalRef; + +namespace content { + +ContentUiEventHandler::ContentUiEventHandler(JNIEnv* env, + const JavaRef& obj, + WebContentsImpl* web_contents) + : java_ref_(env, obj), web_contents_(web_contents) {} + +RenderWidgetHostViewAndroid* ContentUiEventHandler::GetRenderWidgetHostView() { + RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView(); + if (web_contents_->ShowingInterstitialPage()) { + rwhv = web_contents_->GetInterstitialPage() + ->GetMainFrame() + ->GetRenderViewHost() + ->GetWidget() + ->GetView(); + } + return static_cast(rwhv); +} + +bool ContentUiEventHandler::OnGenericMotionEvent( + const ui::MotionEventAndroid& event) { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef j_obj = java_ref_.get(env); + if (!j_obj.is_null()) { + return Java_ContentUiEventHandler_onGenericMotionEvent( + env, j_obj, event.GetJavaObject()); + } + return false; +} + +bool ContentUiEventHandler::OnKeyUp(const ui::KeyEventAndroid& event) { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef j_obj = java_ref_.get(env); + if (!j_obj.is_null()) { + return Java_ContentUiEventHandler_onKeyUp(env, j_obj, event.key_code(), + event.GetJavaObject()); + } + return false; +} + +bool ContentUiEventHandler::DispatchKeyEvent(const ui::KeyEventAndroid& event) { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef j_obj = java_ref_.get(env); + if (!j_obj.is_null()) { + return Java_ContentUiEventHandler_dispatchKeyEvent(env, j_obj, + event.GetJavaObject()); + } + return false; +} + +bool ContentUiEventHandler::ScrollBy(float delta_x, float delta_y) { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef j_obj = java_ref_.get(env); + if (!j_obj.is_null()) { + Java_ContentUiEventHandler_scrollBy(env, j_obj, delta_x, delta_y); + } + return false; +} + +bool ContentUiEventHandler::ScrollTo(float x, float y) { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef j_obj = java_ref_.get(env); + if (!j_obj.is_null()) { + Java_ContentUiEventHandler_scrollTo(env, j_obj, x, y); + } + return false; +} + +void ContentUiEventHandler::SendMouseWheelEvent( + JNIEnv* env, + const JavaParamRef& obj, + jlong time_ms, + jfloat x, + jfloat y, + jfloat ticks_x, + jfloat ticks_y) { + auto* event_handler = GetRenderWidgetHostView(); + if (!event_handler) + return; + + // Compute Event.Latency.OS.MOUSE_WHEEL histogram. + base::TimeTicks current_time = ui::EventTimeForNow(); + base::TimeTicks event_time = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(time_ms); + base::TimeDelta delta = current_time - event_time; + UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.MOUSE_WHEEL", + delta.InMicroseconds(), 1, 1000000, 50); + ui::MotionEventAndroid::Pointer pointer( + 0, x, y, 0.0f /* touch_major */, 0.0f /* touch_minor */, 0.0f, 0.0f, 0); + + auto* view = web_contents_->GetNativeView(); + auto* window = view->GetWindowAndroid(); + float pixels_per_tick = + window ? window->mouse_wheel_scroll_factor() + : ui::kDefaultMouseWheelTickMultiplier * view->GetDipScale(); + ui::MotionEventAndroid event( + env, nullptr, 1.f / view->GetDipScale(), ticks_x, ticks_y, + pixels_per_tick, time_ms, 0 /* action */, 1 /* pointer_count */, + 0 /* history_size */, 0 /* action_index */, 0, 0, 0, + 0 /* raw_offset_x_pixels */, 0 /* raw_offset_y_pixels */, + false /* for_touch_handle */, &pointer, nullptr); + event_handler->OnMouseWheelEvent(event); +} + +void ContentUiEventHandler::SendMouseEvent(JNIEnv* env, + const JavaParamRef& obj, + jlong time_ms, + jint android_action, + jfloat x, + jfloat y, + jint pointer_id, + jfloat orientation, + jfloat pressure, + jfloat tilt, + jint android_action_button, + jint android_button_state, + jint android_meta_state, + jint android_tool_type) { + auto* event_handler = GetRenderWidgetHostView(); + if (!event_handler) + return; + + // Construct a motion_event object minimally, only to convert the raw + // parameters to ui::MotionEvent values. Since we used only the cached values + // at index=0, it is okay to even pass a null event to the constructor. + ui::MotionEventAndroid::Pointer pointer( + pointer_id, x, y, 0.0f /* touch_major */, 0.0f /* touch_minor */, + orientation, tilt, android_tool_type); + ui::MotionEventAndroid event( + env, nullptr /* event */, + 1.f / web_contents_->GetNativeView()->GetDipScale(), 0.f, 0.f, 0.f, + time_ms, android_action, 1 /* pointer_count */, 0 /* history_size */, + 0 /* action_index */, android_action_button, android_button_state, + android_meta_state, 0 /* raw_offset_x_pixels */, + 0 /* raw_offset_y_pixels */, false /* for_touch_handle */, &pointer, + nullptr); + event_handler->OnMouseEvent(event); +} + +void ContentUiEventHandler::SendScrollEvent(JNIEnv* env, + const JavaParamRef& jobj, + jlong time_ms, + jfloat delta_x, + jfloat delta_y) { + auto* event_handler = GetRenderWidgetHostView(); + if (!event_handler) + return; + float dip_scale = web_contents_->GetNativeView()->GetDipScale(); + float delta_xdip = delta_x / dip_scale; + float delta_ydip = delta_y / dip_scale; + constexpr bool target_viewport = true; + constexpr bool synthetic_scroll = false; + constexpr bool prevent_boosting = false; + event_handler->OnGestureEvent(ui::GestureEventAndroid( + ui::GESTURE_EVENT_TYPE_SCROLL_START, gfx::PointF(), gfx::PointF(), + time_ms, 0, -delta_xdip, -delta_ydip, 0, 0, target_viewport, + synthetic_scroll, prevent_boosting)); + event_handler->OnGestureEvent(ui::GestureEventAndroid( + ui::GESTURE_EVENT_TYPE_SCROLL_BY, gfx::PointF(), gfx::PointF(), time_ms, + 0, -delta_xdip, -delta_ydip, 0, 0, target_viewport, synthetic_scroll, + prevent_boosting)); + event_handler->OnGestureEvent(ui::GestureEventAndroid( + ui::GESTURE_EVENT_TYPE_SCROLL_END, gfx::PointF(), gfx::PointF(), time_ms, + 0, -delta_xdip, -delta_ydip, 0, 0, target_viewport, synthetic_scroll, + prevent_boosting)); +} + +void ContentUiEventHandler::CancelFling(JNIEnv* env, + const JavaParamRef& jobj, + jlong time_ms) { + auto* event_handler = GetRenderWidgetHostView(); + if (!event_handler) + return; + event_handler->OnGestureEvent(ui::GestureEventAndroid( + ui::GESTURE_EVENT_TYPE_FLING_CANCEL, gfx::PointF(), gfx::PointF(), + time_ms, 0, 0, 0, 0, 0, /*target_viewport*/ false, + /*synthetic_scroll*/ false, true)); +} + +jlong JNI_ContentUiEventHandler_Init( + JNIEnv* env, + const JavaParamRef& obj, + const JavaParamRef& jweb_contents) { + WebContentsImpl* web_contents = static_cast( + WebContents::FromJavaWebContents(jweb_contents)); + CHECK(web_contents) + << "A ContentUiEventHandler should be created with a valid WebContents."; + auto handler = + std::make_unique(env, obj, web_contents); + auto* handler_ptr = handler.get(); + static_cast(web_contents->GetView()) + ->SetContentUiEventHandler(std::move(handler)); + return reinterpret_cast(handler_ptr); +} + +} // namespace content diff --git a/chromium/content/browser/android/content_ui_event_handler.h b/chromium/content/browser/android/content_ui_event_handler.h new file mode 100644 index 00000000000..0ff884dc16f --- /dev/null +++ b/chromium/content/browser/android/content_ui_event_handler.h @@ -0,0 +1,82 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_ANDROID_CONTENT_UI_EVENT_HANDLER_H_ +#define CONTENT_BROWSER_ANDROID_CONTENT_UI_EVENT_HANDLER_H_ + +#include "base/android/jni_android.h" +#include "base/android/jni_weak_ref.h" +#include "base/android/scoped_java_ref.h" +#include "base/macros.h" + +namespace ui { +class KeyEventAndroid; +class MotionEventAndroid; +} // namespace ui + +namespace content { + +class RenderWidgetHostViewAndroid; +class WebContentsImpl; + +// Handles UI events that need Java layer access. +// Owned by |WebContentsViewAndroid|. +class ContentUiEventHandler { + public: + ContentUiEventHandler(JNIEnv* env, + const base::android::JavaRef& obj, + WebContentsImpl* web_contents); + + base::android::ScopedJavaLocalRef GetJavaObject(); + + bool OnGenericMotionEvent(const ui::MotionEventAndroid& event); + bool OnKeyUp(const ui::KeyEventAndroid& event); + bool DispatchKeyEvent(const ui::KeyEventAndroid& event); + bool ScrollBy(float delta_x, float delta_y); + bool ScrollTo(float x, float y); + + void SendMouseWheelEvent(JNIEnv* env, + const base::android::JavaParamRef& obj, + jlong time_ms, + jfloat x, + jfloat y, + jfloat ticks_x, + jfloat ticks_y); + void SendMouseEvent(JNIEnv* env, + const base::android::JavaParamRef& obj, + jlong time_ms, + jint android_action, + jfloat x, + jfloat y, + jint pointer_id, + jfloat orientation, + jfloat pressure, + jfloat tilt, + jint android_action_button, + jint android_button_state, + jint android_meta_state, + jint android_tool_type); + void SendScrollEvent(JNIEnv* env, + const base::android::JavaParamRef& jobj, + jlong time_ms, + jfloat delta_x, + jfloat delta_y); + void CancelFling(JNIEnv* env, + const base::android::JavaParamRef& jobj, + jlong time_ms); + + private: + RenderWidgetHostViewAndroid* GetRenderWidgetHostView(); + + // A weak reference to the Java ContentUiEventHandler object. + JavaObjectWeakGlobalRef java_ref_; + + WebContentsImpl* const web_contents_; + + DISALLOW_COPY_AND_ASSIGN(ContentUiEventHandler); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_CONTENT_UI_EVENT_HANDLER_H_ diff --git a/chromium/content/browser/android/content_video_view.cc b/chromium/content/browser/android/content_video_view.cc index f057fe9e734..15011da9bd3 100644 --- a/chromium/content/browser/android/content_video_view.cc +++ b/chromium/content/browser/android/content_video_view.cc @@ -6,7 +6,7 @@ #include "base/metrics/histogram_macros.h" #include "content/public/browser/web_contents.h" -#include "jni/ContentVideoView_jni.h" +#include "jni/ContentVideoViewImpl_jni.h" using base::android::AttachCurrentThread; using base::android::JavaParamRef; @@ -24,7 +24,7 @@ ContentVideoView* g_content_video_view = NULL; } // namespace static ScopedJavaLocalRef -JNI_ContentVideoView_GetSingletonJavaContentVideoView( +JNI_ContentVideoViewImpl_GetSingletonJavaContentVideoView( JNIEnv* env, const JavaParamRef&) { if (g_content_video_view) @@ -53,8 +53,8 @@ ContentVideoView::~ContentVideoView() { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef content_video_view = GetJavaObject(env); if (!content_video_view.is_null()) { - Java_ContentVideoView_destroyContentVideoView(env, content_video_view, - true); + Java_ContentVideoViewImpl_destroyContentVideoView(env, content_video_view, + true); j_content_video_view_.reset(); } g_content_video_view = NULL; @@ -64,7 +64,7 @@ void ContentVideoView::OpenVideo() { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef content_video_view = GetJavaObject(env); if (!content_video_view.is_null()) { - Java_ContentVideoView_openVideo(env, content_video_view); + Java_ContentVideoViewImpl_openVideo(env, content_video_view); } } @@ -72,8 +72,8 @@ void ContentVideoView::OnMediaPlayerError(int error_type) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef content_video_view = GetJavaObject(env); if (!content_video_view.is_null()) { - Java_ContentVideoView_onMediaPlayerError(env, content_video_view, - error_type); + Java_ContentVideoViewImpl_onMediaPlayerError(env, content_video_view, + error_type); } } @@ -81,8 +81,8 @@ void ContentVideoView::OnVideoSizeChanged(int width, int height) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef content_video_view = GetJavaObject(env); if (!content_video_view.is_null()) { - Java_ContentVideoView_onVideoSizeChanged(env, content_video_view, width, - height); + Java_ContentVideoViewImpl_onVideoSizeChanged(env, content_video_view, width, + height); } } @@ -91,8 +91,8 @@ void ContentVideoView::ExitFullscreen() { ScopedJavaLocalRef content_video_view = GetJavaObject(env); bool release_media_player = false; if (!content_video_view.is_null()) - Java_ContentVideoView_exitFullscreen(env, content_video_view, - release_media_player); + Java_ContentVideoViewImpl_exitFullscreen(env, content_video_view, + release_media_player); } ScopedJavaLocalRef ContentVideoView::GetJavaObject(JNIEnv* env) { @@ -163,7 +163,7 @@ JavaObjectWeakGlobalRef ContentVideoView::CreateJavaObject( return JavaObjectWeakGlobalRef(env, nullptr); return JavaObjectWeakGlobalRef( - env, Java_ContentVideoView_createContentVideoView( + env, Java_ContentVideoViewImpl_createContentVideoView( env, j_web_contents, j_content_video_view_embedder, reinterpret_cast(this), video_natural_size.width(), video_natural_size.height()) diff --git a/chromium/content/browser/android/content_view_core.cc b/chromium/content/browser/android/content_view_core.cc index 37e331fec3a..abcb9272529 100644 --- a/chromium/content/browser/android/content_view_core.cc +++ b/chromium/content/browser/android/content_view_core.cc @@ -4,10 +4,6 @@ #include "content/browser/android/content_view_core.h" -#include "base/android/jni_android.h" -#include "base/macros.h" -#include "base/metrics/user_metrics.h" -#include "cc/layers/layer.h" #include "content/browser/frame_host/interstitial_page_impl.h" #include "content/browser/renderer_host/render_widget_host_view_android.h" #include "content/browser/web_contents/web_contents_impl.h" @@ -37,14 +33,10 @@ RenderWidgetHostViewAndroid* GetRenderWidgetHostViewFromHost( ContentViewCore::ContentViewCore(JNIEnv* env, const JavaRef& obj, - WebContents* web_contents, - float dpi_scale) + WebContents* web_contents) : WebContentsObserver(web_contents), java_ref_(env, obj), - web_contents_(static_cast(web_contents)), - dpi_scale_(dpi_scale), - device_orientation_(0) { - + web_contents_(static_cast(web_contents)) { // Currently, the only use case we have for overriding a user agent involves // spoofing a desktop Linux user agent for "Request desktop site". // Automatically set it for all WebContents so that it is available when a @@ -66,23 +58,6 @@ ContentViewCore::~ContentViewCore() { } } -void ContentViewCore::UpdateWindowAndroid( - JNIEnv* env, - const base::android::JavaParamRef& obj, - const JavaParamRef& jwindow_android) { - ui::WindowAndroid* window = - ui::WindowAndroid::FromJavaWindowAndroid(jwindow_android); - auto* old_window = GetWindowAndroid(); - if (window == old_window) - return; - - auto* view = GetViewAndroid(); - if (old_window) - view->RemoveFromParent(); - if (window) - window->AddChild(view); -} - void ContentViewCore::OnJavaContentViewCoreDestroyed( JNIEnv* env, const JavaParamRef& obj) { @@ -94,8 +69,15 @@ void ContentViewCore::OnJavaContentViewCoreDestroyed( } void ContentViewCore::RenderViewReady() { - if (device_orientation_ != 0) - SendOrientationChangeEventInternal(); + WebContentsViewAndroid* view = + static_cast(web_contents_->GetView()); + if (view->device_orientation() == 0) + return; + RenderWidgetHostViewAndroid* rwhva = GetRenderWidgetHostViewAndroid(); + if (rwhva) + rwhva->UpdateScreenInfo(GetViewAndroid()); + + web_contents_->OnScreenOrientationChange(); } void ContentViewCore::WebContentsDestroyed() { @@ -132,19 +114,6 @@ RenderWidgetHostViewAndroid* ContentViewCore::GetRenderWidgetHostViewAndroid() return static_cast(rwhv); } -void ContentViewCore::SendScreenRectsAndResizeWidget() { - RenderWidgetHostViewAndroid* view = GetRenderWidgetHostViewAndroid(); - if (view) { - // |SendScreenRects()| indirectly calls GetViewSize() that asks Java layer. - web_contents_->SendScreenRects(); - view->WasResized(); - } -} - -ui::WindowAndroid* ContentViewCore::GetWindowAndroid() const { - return GetViewAndroid()->GetWindowAndroid(); -} - ui::ViewAndroid* ContentViewCore::GetViewAndroid() const { return web_contents_->GetView()->GetNativeView(); } @@ -159,16 +128,6 @@ void ContentViewCore::SetFocus(JNIEnv* env, SetFocusInternal(focused); } -void ContentViewCore::SetDIPScale(JNIEnv* env, - const JavaParamRef& obj, - jfloat dpi_scale) { - if (dpi_scale_ == dpi_scale) - return; - - dpi_scale_ = dpi_scale; - SendScreenRectsAndResizeWidget(); -} - void ContentViewCore::SetFocusInternal(bool focused) { if (!GetRenderWidgetHostViewAndroid()) return; @@ -179,82 +138,16 @@ void ContentViewCore::SetFocusInternal(bool focused) { GetRenderWidgetHostViewAndroid()->LostFocus(); } -int ContentViewCore::GetTopControlsShrinkBlinkHeightPixForTesting( - JNIEnv* env, - const JavaParamRef& obj) { - RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); - return !rwhv || !rwhv->DoBrowserControlsShrinkBlinkSize() - ? 0 - : rwhv->GetTopControlsHeight() * dpi_scale_; -} - -void ContentViewCore::SendOrientationChangeEvent( - JNIEnv* env, - const JavaParamRef& obj, - jint orientation) { - if (device_orientation_ != orientation) { - base::RecordAction(base::UserMetricsAction("ScreenOrientationChange")); - device_orientation_ = orientation; - SendOrientationChangeEventInternal(); - } -} - -void ContentViewCore::ResetGestureDetection(JNIEnv* env, - const JavaParamRef& obj) { - RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); - if (rwhv) - rwhv->ResetGestureDetection(); -} - -void ContentViewCore::SetDoubleTapSupportEnabled( - JNIEnv* env, - const JavaParamRef& obj, - jboolean enabled) { - RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); - if (rwhv) - rwhv->SetDoubleTapSupportEnabled(enabled); -} - -void ContentViewCore::SetMultiTouchZoomSupportEnabled( - JNIEnv* env, - const JavaParamRef& obj, - jboolean enabled) { - RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); - if (rwhv) - rwhv->SetMultiTouchZoomSupportEnabled(enabled); -} - -void ContentViewCore::SendOrientationChangeEventInternal() { - RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid(); - if (rwhv) - rwhv->UpdateScreenInfo(GetViewAndroid()); - - static_cast(web_contents())->OnScreenOrientationChange(); -} - // This is called for each ContentView. -jlong JNI_ContentViewCoreImpl_Init( - JNIEnv* env, - const JavaParamRef& obj, - const JavaParamRef& jweb_contents, - const JavaParamRef& jview_android_delegate, - const JavaParamRef& jwindow_android, - jfloat dip_scale) { +jlong JNI_ContentViewCoreImpl_Init(JNIEnv* env, + const JavaParamRef& obj, + const JavaParamRef& jweb_contents) { WebContentsImpl* web_contents = static_cast( WebContents::FromJavaWebContents(jweb_contents)); CHECK(web_contents) << "A ContentViewCore should be created with a valid WebContents."; - ui::ViewAndroid* view_android = web_contents->GetView()->GetNativeView(); - view_android->SetDelegate(jview_android_delegate); - - ui::WindowAndroid* window_android = - ui::WindowAndroid::FromJavaWindowAndroid(jwindow_android); - DCHECK(window_android); - window_android->AddChild(view_android); - - ContentViewCore* view = - new ContentViewCore(env, obj, web_contents, dip_scale); - return reinterpret_cast(view); + return reinterpret_cast( + new ContentViewCore(env, obj, web_contents)); } } // namespace content diff --git a/chromium/content/browser/android/content_view_core.h b/chromium/content/browser/android/content_view_core.h index 36373ad9fa9..f41a9a56497 100644 --- a/chromium/content/browser/android/content_view_core.h +++ b/chromium/content/browser/android/content_view_core.h @@ -13,7 +13,6 @@ namespace ui { class ViewAndroid; -class WindowAndroid; } namespace content { @@ -25,8 +24,7 @@ class ContentViewCore : public WebContentsObserver { public: ContentViewCore(JNIEnv* env, const base::android::JavaRef& obj, - WebContents* web_contents, - float dpi_scale); + WebContents* web_contents); ~ContentViewCore() override; @@ -34,44 +32,14 @@ class ContentViewCore : public WebContentsObserver { // Methods called from Java via JNI // -------------------------------------------------------------------------- - void UpdateWindowAndroid( - JNIEnv* env, - const base::android::JavaParamRef& obj, - const base::android::JavaParamRef& jwindow_android); void OnJavaContentViewCoreDestroyed( JNIEnv* env, const base::android::JavaParamRef& obj); - // Returns the amount of the top controls height if controls are in the state - // of shrinking Blink's view size, otherwise 0. - int GetTopControlsShrinkBlinkHeightPixForTesting( - JNIEnv* env, - const base::android::JavaParamRef& obj); - - void SendOrientationChangeEvent( - JNIEnv* env, - const base::android::JavaParamRef& obj, - jint orientation); - - void ResetGestureDetection(JNIEnv* env, - const base::android::JavaParamRef& obj); - void SetDoubleTapSupportEnabled( - JNIEnv* env, - const base::android::JavaParamRef& obj, - jboolean enabled); - void SetMultiTouchZoomSupportEnabled( - JNIEnv* env, - const base::android::JavaParamRef& obj, - jboolean enabled); - void SetFocus(JNIEnv* env, const base::android::JavaParamRef& obj, jboolean focused); - void SetDIPScale(JNIEnv* env, - const base::android::JavaParamRef& obj, - jfloat dipScale); - private: // WebContentsObserver implementation. @@ -84,19 +52,13 @@ class ContentViewCore : public WebContentsObserver { // Other private methods and data // -------------------------------------------------------------------------- - void SendScreenRectsAndResizeWidget(); - ui::ViewAndroid* GetViewAndroid() const; - ui::WindowAndroid* GetWindowAndroid() const; RenderWidgetHostViewAndroid* GetRenderWidgetHostViewAndroid() const; // Update focus state of the RenderWidgetHostView. void SetFocusInternal(bool focused); - // Send device_orientation_ to renderer. - void SendOrientationChangeEventInternal(); - // A weak reference to the Java ContentViewCore object. JavaObjectWeakGlobalRef java_ref_; @@ -104,13 +66,6 @@ class ContentViewCore : public WebContentsObserver { // display in the ContentViewCore. WebContentsImpl* web_contents_; - // Device scale factor. - float dpi_scale_; - - // The cache of device's current orientation set from Java side, this value - // will be sent to Renderer once it is ready. - int device_orientation_; - DISALLOW_COPY_AND_ASSIGN(ContentViewCore); }; diff --git a/chromium/content/browser/android/content_view_render_view.cc b/chromium/content/browser/android/content_view_render_view.cc index be2b36162b9..afaefd7c88b 100644 --- a/chromium/content/browser/android/content_view_render_view.cc +++ b/chromium/content/browser/android/content_view_render_view.cc @@ -14,7 +14,6 @@ #include "base/android/scoped_java_ref.h" #include "base/bind.h" #include "base/lazy_instance.h" -#include "base/message_loop/message_loop.h" #include "cc/layers/layer.h" #include "content/public/browser/android/compositor.h" #include "content/public/browser/android/content_view_layer_renderer.h" @@ -115,6 +114,12 @@ void ContentViewRenderView::SetOverlayVideoMode( compositor_->SetNeedsComposite(); } +void ContentViewRenderView::SetNeedsComposite( + JNIEnv* env, + const base::android::JavaParamRef& obj) { + compositor_->SetNeedsComposite(); +} + void ContentViewRenderView::UpdateLayerTreeHost() { // TODO(wkorman): Rename Layout to UpdateLayerTreeHost in all Android // Compositor related classes. diff --git a/chromium/content/browser/android/content_view_render_view.h b/chromium/content/browser/android/content_view_render_view.h index 3c1194d048c..97916d3a300 100644 --- a/chromium/content/browser/android/content_view_render_view.h +++ b/chromium/content/browser/android/content_view_render_view.h @@ -48,6 +48,8 @@ class ContentViewRenderView : public CompositorClient { void SetOverlayVideoMode(JNIEnv* env, const base::android::JavaParamRef& obj, bool enabled); + void SetNeedsComposite(JNIEnv* env, + const base::android::JavaParamRef& obj); // CompositorClient implementation void UpdateLayerTreeHost() override; diff --git a/chromium/content/browser/android/content_view_statics.cc b/chromium/content/browser/android/content_view_statics.cc index abeb57cd933..6275395ace8 100644 --- a/chromium/content/browser/android/content_view_statics.cc +++ b/chromium/content/browser/android/content_view_statics.cc @@ -14,7 +14,7 @@ #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" +#include "jni/ContentViewStaticsImpl_jni.h" using base::android::ConvertJavaStringToUTF16; using base::android::ConvertUTF16ToJavaString; @@ -40,9 +40,9 @@ class SuspendedProcessWatcher : public content::RenderProcessHostObserver { // If the process crashes, stop watching the corresponding RenderProcessHost // and ensure it doesn't get over-resumed. - void RenderProcessExited(content::RenderProcessHost* host, - base::TerminationStatus status, - int exit_code) override { + void RenderProcessExited( + content::RenderProcessHost* host, + const content::ChildProcessTerminationInfo& info) override { StopWatching(host); } @@ -90,7 +90,7 @@ base::LazyInstance::DestructorAtExit } // namespace -static void JNI_ContentViewStatics_SetWebKitSharedTimersSuspended( +static void JNI_ContentViewStaticsImpl_SetWebKitSharedTimersSuspended( JNIEnv* env, const JavaParamRef& obj, jboolean suspend) { diff --git a/chromium/content/browser/android/gesture_listener_manager.cc b/chromium/content/browser/android/gesture_listener_manager.cc index 1eeb5d41b40..2cee9bf9e34 100644 --- a/chromium/content/browser/android/gesture_listener_manager.cc +++ b/chromium/content/browser/android/gesture_listener_manager.cc @@ -119,6 +119,29 @@ void GestureListenerManager::Reset(JNIEnv* env, java_ref_.reset(); } +void GestureListenerManager::ResetGestureDetection( + JNIEnv* env, + const JavaParamRef& obj) { + if (rwhva_) + rwhva_->ResetGestureDetection(); +} + +void GestureListenerManager::SetDoubleTapSupportEnabled( + JNIEnv* env, + const JavaParamRef& obj, + jboolean enabled) { + if (rwhva_) + rwhva_->SetDoubleTapSupportEnabled(enabled); +} + +void GestureListenerManager::SetMultiTouchZoomSupportEnabled( + JNIEnv* env, + const JavaParamRef& obj, + jboolean enabled) { + if (rwhva_) + rwhva_->SetMultiTouchZoomSupportEnabled(enabled); +} + void GestureListenerManager::GestureEventAck( const blink::WebGestureEvent& event, InputEventAckState ack_result) { @@ -244,6 +267,7 @@ void GestureListenerManager::UpdateRenderProcessConnection( if (new_rwhva) { new_rwhva->set_gesture_listener_manager(this); } + rwhva_ = new_rwhva; } void GestureListenerManager::OnNavigationFinished( diff --git a/chromium/content/browser/android/gesture_listener_manager.h b/chromium/content/browser/android/gesture_listener_manager.h index 693a9b7beed..59121618e4c 100644 --- a/chromium/content/browser/android/gesture_listener_manager.h +++ b/chromium/content/browser/android/gesture_listener_manager.h @@ -34,6 +34,16 @@ class GestureListenerManager : public RenderWidgetHostConnector { ~GestureListenerManager() override; void Reset(JNIEnv* env, const base::android::JavaParamRef& obj); + void ResetGestureDetection(JNIEnv* env, + const base::android::JavaParamRef& obj); + void SetDoubleTapSupportEnabled( + JNIEnv* env, + const base::android::JavaParamRef& obj, + jboolean enabled); + void SetMultiTouchZoomSupportEnabled( + JNIEnv* env, + const base::android::JavaParamRef& obj, + jboolean enabled); void GestureEventAck(const blink::WebGestureEvent& event, InputEventAckState ack_result); void DidStopFlinging(); @@ -67,6 +77,7 @@ class GestureListenerManager : public RenderWidgetHostConnector { std::unique_ptr reset_scroll_observer_; WebContentsImpl* web_contents_; + RenderWidgetHostViewAndroid* rwhva_ = nullptr; // A weak reference to the Java GestureListenerManager object. JavaObjectWeakGlobalRef java_ref_; diff --git a/chromium/content/browser/android/ime_adapter_android.cc b/chromium/content/browser/android/ime_adapter_android.cc index ed271d17508..6bb9735fc5b 100644 --- a/chromium/content/browser/android/ime_adapter_android.cc +++ b/chromium/content/browser/android/ime_adapter_android.cc @@ -19,7 +19,6 @@ #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/input_messages.h" #include "content/common/view_messages.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/native_web_keyboard_event.h" @@ -54,10 +53,10 @@ NativeWebKeyboardEvent NativeWebKeyboardEventFromKeyEvent( int scan_code, bool is_system_key, int unicode_char) { - return NativeWebKeyboardEvent(env, java_key_event, - static_cast(type), - modifiers, time_ms / 1000.0, key_code, - scan_code, unicode_char, is_system_key); + return NativeWebKeyboardEvent( + env, java_key_event, static_cast(type), + modifiers, base::TimeTicks() + base::TimeDelta::FromMilliseconds(time_ms), + key_code, scan_code, unicode_char, is_system_key); } } // anonymous namespace diff --git a/chromium/content/browser/android/interstitial_page_delegate_android.cc b/chromium/content/browser/android/interstitial_page_delegate_android.cc deleted file mode 100644 index 81b57c0f1ce..00000000000 --- a/chromium/content/browser/android/interstitial_page_delegate_android.cc +++ /dev/null @@ -1,95 +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/interstitial_page_delegate_android.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/android/scoped_java_ref.h" -#include "content/public/browser/interstitial_page.h" -#include "jni/InterstitialPageDelegateAndroid_jni.h" - -using base::android::AttachCurrentThread; -using base::android::JavaParamRef; -using base::android::ScopedJavaLocalRef; - -namespace content { - -InterstitialPageDelegateAndroid::InterstitialPageDelegateAndroid( - JNIEnv* env, - jobject obj, - const std::string& html_content) - : weak_java_obj_(env, obj), - html_content_(html_content), - page_(NULL) { -} - -InterstitialPageDelegateAndroid::~InterstitialPageDelegateAndroid() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = weak_java_obj_.get(env); - if (obj.obj()) - Java_InterstitialPageDelegateAndroid_onNativeDestroyed(env, obj); -} - -void InterstitialPageDelegateAndroid::Proceed( - JNIEnv* env, - const JavaParamRef& obj) { - if (page_) - page_->Proceed(); -} - -void InterstitialPageDelegateAndroid::DontProceed( - JNIEnv* env, - const JavaParamRef& obj) { - if (page_) - page_->DontProceed(); -} - -std::string InterstitialPageDelegateAndroid::GetHTMLContents() { - return html_content_; -} - -void InterstitialPageDelegateAndroid::OnProceed() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = weak_java_obj_.get(env); - if (obj.obj()) - Java_InterstitialPageDelegateAndroid_onProceed(env, obj); -} - -void InterstitialPageDelegateAndroid::OnDontProceed() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = weak_java_obj_.get(env); - if (obj.obj()) - Java_InterstitialPageDelegateAndroid_onDontProceed(env, obj); -} - -void InterstitialPageDelegateAndroid::CommandReceived( - const std::string& command) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = weak_java_obj_.get(env); - if (obj.obj()) { - std::string sanitized_command(command); - // The JSONified response has quotes, remove them. - if (sanitized_command.length() > 1 && sanitized_command[0] == '"') { - sanitized_command = sanitized_command.substr( - 1, sanitized_command.length() - 2); - } - - Java_InterstitialPageDelegateAndroid_commandReceived( - env, obj, - base::android::ConvertUTF8ToJavaString(env, sanitized_command)); - } -} - -static jlong JNI_InterstitialPageDelegateAndroid_Init( - JNIEnv* env, - const JavaParamRef& obj, - const JavaParamRef& html_content) { - InterstitialPageDelegateAndroid* delegate = - new InterstitialPageDelegateAndroid( - env, obj, base::android::ConvertJavaStringToUTF8(env, html_content)); - return reinterpret_cast(delegate); -} - -} // namespace content diff --git a/chromium/content/browser/android/interstitial_page_delegate_android.h b/chromium/content/browser/android/interstitial_page_delegate_android.h deleted file mode 100644 index b4900e2c461..00000000000 --- a/chromium/content/browser/android/interstitial_page_delegate_android.h +++ /dev/null @@ -1,54 +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_INTERSTITIAL_PAGE_DELEGATE_ANDROID_H_ -#define CONTENT_BROWSER_ANDROID_INTERSTITIAL_PAGE_DELEGATE_ANDROID_H_ - -#include -#include - -#include "base/android/jni_weak_ref.h" -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "content/common/content_export.h" -#include "content/public/browser/interstitial_page_delegate.h" - -namespace content { - -class InterstitialPage; - -// Native counterpart that allows interstitial pages to be constructed and -// managed from Java. -class InterstitialPageDelegateAndroid : public InterstitialPageDelegate { - public: - InterstitialPageDelegateAndroid(JNIEnv* env, - jobject obj, - const std::string& html_content); - ~InterstitialPageDelegateAndroid() override; - - void set_interstitial_page(InterstitialPage* page) { page_ = page; } - - // Methods called from Java. - void Proceed(JNIEnv* env, const base::android::JavaParamRef& obj); - void DontProceed(JNIEnv* env, - const base::android::JavaParamRef& obj); - - // Implementation of InterstitialPageDelegate - std::string GetHTMLContents() override; - void OnProceed() override; - void OnDontProceed() override; - void CommandReceived(const std::string& command) override; - - private: - JavaObjectWeakGlobalRef weak_java_obj_; - - std::string html_content_; - InterstitialPage* page_; // Owns this. - - DISALLOW_COPY_AND_ASSIGN(InterstitialPageDelegateAndroid); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_ANDROID_INTERSTITIAL_PAGE_DELEGATE_ANDROID_H_ diff --git a/chromium/content/browser/android/java/java_bridge_thread.cc b/chromium/content/browser/android/java/java_bridge_thread.cc index b0b39d31204..d0d03812cbb 100644 --- a/chromium/content/browser/android/java/java_bridge_thread.cc +++ b/chromium/content/browser/android/java/java_bridge_thread.cc @@ -5,7 +5,6 @@ #include "content/browser/android/java/java_bridge_thread.h" #include "base/lazy_instance.h" -#include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" #include "base/task_runner_util.h" #include "build/build_config.h" diff --git a/chromium/content/browser/android/overscroll_controller_android_unittest.cc b/chromium/content/browser/android/overscroll_controller_android_unittest.cc index 2745dd1243e..29fb140cdb0 100644 --- a/chromium/content/browser/android/overscroll_controller_android_unittest.cc +++ b/chromium/content/browser/android/overscroll_controller_android_unittest.cc @@ -217,10 +217,9 @@ TEST_F(OverscrollControllerAndroidUnitTest, controller_->OnOverscrolled(params); // Generate a consumed scroll update. - blink::WebGestureEvent event( - blink::WebInputEvent::kGestureScrollUpdate, - blink::WebInputEvent::kNoModifiers, - ui::EventTimeStampToSeconds(ui::EventTimeForNow())); + blink::WebGestureEvent event(blink::WebInputEvent::kGestureScrollUpdate, + blink::WebInputEvent::kNoModifiers, + ui::EventTimeForNow()); controller_->OnGestureEventAck(event, INPUT_EVENT_ACK_STATE_CONSUMED); testing::Mock::VerifyAndClearExpectations(&refresh_); diff --git a/chromium/content/browser/android/string_message_codec.cc b/chromium/content/browser/android/string_message_codec.cc deleted file mode 100644 index 5303ac43b73..00000000000 --- a/chromium/content/browser/android/string_message_codec.cc +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/android/string_message_codec.h" - -#include - -#include "base/logging.h" - -namespace content { -namespace { - -const uint32_t kVarIntShift = 7; -const uint32_t kVarIntMask = (1 << kVarIntShift) - 1; - -const uint8_t kVersionTag = 0xFF; -const uint8_t kPaddingTag = '\0'; -const uint8_t kOneByteStringTag = '"'; -const uint8_t kTwoByteStringTag = 'c'; - -const uint32_t kVersion = 10; - -static size_t BytesNeededForUint32(uint32_t value) { - size_t result = 0; - do { - result++; - value >>= kVarIntShift; - } while (value); - return result; -} - -void WriteUint8(uint8_t value, std::vector* buffer) { - buffer->push_back(value); -} - -void WriteUint32(uint32_t value, std::vector* buffer) { - for (;;) { - uint8_t b = (value & kVarIntMask); - value >>= kVarIntShift; - if (!value) { - WriteUint8(b, buffer); - break; - } - WriteUint8(b | (1 << kVarIntShift), buffer); - } -} - -void WriteBytes(const char* bytes, size_t num_bytes, - std::vector* buffer) { - buffer->insert(buffer->end(), bytes, bytes + num_bytes); -} - -bool ReadUint8(const uint8_t** ptr, const uint8_t* end, uint8_t* value) { - if (*ptr >= end) - return false; - *value = *(*ptr)++; - return true; -} - -bool ReadUint32(const uint8_t** ptr, const uint8_t* end, uint32_t* value) { - *value = 0; - uint8_t current_byte; - int shift = 0; - do { - if (*ptr >= end) - return false; - current_byte = *(*ptr)++; - *value |= (static_cast(current_byte & kVarIntMask) << shift); - shift += kVarIntShift; - } while (current_byte & (1 << kVarIntShift)); - return true; -} - -bool ContainsOnlyLatin1(const base::string16& data) { - base::char16 x = 0; - for (base::char16 c : data) - x |= c; - return !(x & 0xFF00); -} - -} // namespace - -std::vector EncodeStringMessage(const base::string16& data) { - std::vector buffer; - WriteUint8(kVersionTag, &buffer); - WriteUint32(kVersion, &buffer); - - if (ContainsOnlyLatin1(data)) { - std::string data_latin1(data.begin(), data.end()); - WriteUint8(kOneByteStringTag, &buffer); - WriteUint32(data_latin1.size(), &buffer); - WriteBytes(data_latin1.c_str(), data_latin1.size(), &buffer); - } else { - size_t num_bytes = data.size() * sizeof(base::char16); - if ((buffer.size() + 1 + BytesNeededForUint32(num_bytes)) & 1) - WriteUint8(kPaddingTag, &buffer); - WriteUint8(kTwoByteStringTag, &buffer); - WriteUint32(num_bytes, &buffer); - WriteBytes(reinterpret_cast(data.data()), num_bytes, &buffer); - } - - return buffer; -} - -bool DecodeStringMessage(const std::vector& encoded_data, - base::string16* result) { - const uint8_t* ptr = encoded_data.data(); - const uint8_t* end = ptr + encoded_data.size(); - uint8_t tag; - - // Discard any leading version and padding tags. - // There may be more than one version, due to Blink and V8 having separate - // version tags. - do { - if (!ReadUint8(&ptr, end, &tag)) - return false; - uint32_t version; - if (tag == kVersionTag && !ReadUint32(&ptr, end, &version)) - return false; - } while (tag == kVersionTag || tag == kPaddingTag); - - switch (tag) { - case kOneByteStringTag: { - uint32_t num_bytes; - if (!ReadUint32(&ptr, end, &num_bytes)) - return false; - result->assign(reinterpret_cast(ptr), - reinterpret_cast(ptr) + num_bytes); - return true; - } - case kTwoByteStringTag: { - uint32_t num_bytes; - if (!ReadUint32(&ptr, end, &num_bytes)) - return false; - result->assign(reinterpret_cast(ptr), num_bytes / 2); - return true; - } - } - - DLOG(WARNING) << "Unexpected tag: " << tag; - return false; -} - -} // namespace content diff --git a/chromium/content/browser/android/string_message_codec.h b/chromium/content/browser/android/string_message_codec.h deleted file mode 100644 index a613f057609..00000000000 --- a/chromium/content/browser/android/string_message_codec.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_ANDROID_STRING_MESSAGE_CODEC_H_ -#define CONTENT_BROWSER_ANDROID_STRING_MESSAGE_CODEC_H_ - -#include -#include "base/strings/string16.h" -#include "content/common/content_export.h" - -namespace content { - -// To support exposing HTML message ports to Java, it is necessary to be able -// to encode and decode message data using the same serialization format as V8. -// That format is an implementation detail of V8, but we cannot invoke V8 in -// the browser process. Rather than IPC over to the renderer process to execute -// the V8 serialization code, we duplicate some of the serialization logic -// (just for simple string messages) here. This is a trade-off between overall -// complexity / performance and code duplication. Fortunately, we only need to -// handle string messages and this serialization format is static, as it is a -// format we currently persist to disk via IndexedDB. - -CONTENT_EXPORT std::vector EncodeStringMessage( - const base::string16& data); - -CONTENT_EXPORT bool DecodeStringMessage( - const std::vector& encoded_data, - base::string16* result); - -} // namespace content - -#endif // CONTENT_BROWSER_ANDROID_STRING_MESSAGE_CODEC_H_ diff --git a/chromium/content/browser/android/string_message_codec_unittest.cc b/chromium/content/browser/android/string_message_codec_unittest.cc deleted file mode 100644 index d32233e30ab..00000000000 --- a/chromium/content/browser/android/string_message_codec_unittest.cc +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/android/string_message_codec.h" - -#include "base/strings/utf_string_conversions.h" -#include "base/test/scoped_task_environment.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "v8/include/v8.h" - -namespace content { -namespace { - -base::string16 DecodeWithV8(const std::vector& encoded) { - base::test::ScopedTaskEnvironment scoped_task_environment; - base::string16 result; - - v8::Isolate::CreateParams params; - params.array_buffer_allocator = - v8::ArrayBuffer::Allocator::NewDefaultAllocator(); - v8::Isolate* isolate = v8::Isolate::New(params); - { - v8::HandleScope scope(isolate); - v8::TryCatch try_catch(isolate); - - v8::Local context = v8::Context::New(isolate); - - v8::ValueDeserializer deserializer(isolate, encoded.data(), encoded.size()); - deserializer.SetSupportsLegacyWireFormat(true); - - EXPECT_TRUE(deserializer.ReadHeader(context).ToChecked()); - - v8::Local value = - deserializer.ReadValue(context).ToLocalChecked(); - v8::Local str = value->ToString(context).ToLocalChecked(); - - result.resize(str->Length()); - str->Write(&result[0], 0, result.size()); - } - isolate->Dispose(); - delete params.array_buffer_allocator; - - return result; -} - -std::vector EncodeWithV8(const base::string16& message) { - base::test::ScopedTaskEnvironment scoped_task_environment; - std::vector result; - - v8::Isolate::CreateParams params; - params.array_buffer_allocator = - v8::ArrayBuffer::Allocator::NewDefaultAllocator(); - v8::Isolate* isolate = v8::Isolate::New(params); - { - v8::HandleScope scope(isolate); - v8::TryCatch try_catch(isolate); - - v8::Local context = v8::Context::New(isolate); - - v8::Local message_as_value = - v8::String::NewFromTwoByte(isolate, - message.data(), - v8::NewStringType::kNormal, - message.size()).ToLocalChecked(); - - v8::ValueSerializer serializer(isolate); - serializer.WriteHeader(); - EXPECT_TRUE(serializer.WriteValue(context, message_as_value).ToChecked()); - - std::pair buffer = serializer.Release(); - result = std::vector(buffer.first, buffer.first + buffer.second); - free(buffer.first); - } - isolate->Dispose(); - delete params.array_buffer_allocator; - - return result; -} - -TEST(StringMessageCodecTest, SelfTest_ASCII) { - base::string16 message = base::ASCIIToUTF16("hello"); - base::string16 decoded; - EXPECT_TRUE(DecodeStringMessage(EncodeStringMessage(message), &decoded)); - EXPECT_EQ(message, decoded); -} - -TEST(StringMessageCodecTest, SelfTest_NonASCII) { - base::string16 message = base::WideToUTF16(L"hello \u263A"); - base::string16 decoded; - EXPECT_TRUE(DecodeStringMessage(EncodeStringMessage(message), &decoded)); - EXPECT_EQ(message, decoded); -} - -TEST(StringMessageCodecTest, SelfTest_NonASCIILongEnoughToForcePadding) { - base::string16 message(200, 0x263A); - base::string16 decoded; - EXPECT_TRUE(DecodeStringMessage(EncodeStringMessage(message), &decoded)); - EXPECT_EQ(message, decoded); -} - -TEST(StringMessageCodecTest, SelfToV8Test_ASCII) { - base::string16 message = base::ASCIIToUTF16("hello"); - EXPECT_EQ(message, DecodeWithV8(EncodeStringMessage(message))); -} - -TEST(StringMessageCodecTest, SelfToV8Test_NonASCII) { - base::string16 message = base::WideToUTF16(L"hello \u263A"); - EXPECT_EQ(message, DecodeWithV8(EncodeStringMessage(message))); -} - -TEST(StringMessageCodecTest, SelfToV8Test_NonASCIILongEnoughToForcePadding) { - base::string16 message(200, 0x263A); - EXPECT_EQ(message, DecodeWithV8(EncodeStringMessage(message))); -} - -TEST(StringMessageCodecTest, V8ToSelfTest_ASCII) { - base::string16 message = base::ASCIIToUTF16("hello"); - base::string16 decoded; - EXPECT_TRUE(DecodeStringMessage(EncodeWithV8(message), &decoded)); - EXPECT_EQ(message, decoded); -} - -TEST(StringMessageCodecTest, V8ToSelfTest_NonASCII) { - base::string16 message = base::WideToUTF16(L"hello \u263A"); - base::string16 decoded; - EXPECT_TRUE(DecodeStringMessage(EncodeWithV8(message), &decoded)); - EXPECT_EQ(message, decoded); -} - -TEST(StringMessageCodecTest, V8ToSelfTest_NonASCIILongEnoughToForcePadding) { - base::string16 message(200, 0x263A); - base::string16 decoded; - EXPECT_TRUE(DecodeStringMessage(EncodeWithV8(message), &decoded)); - EXPECT_EQ(message, decoded); -} - -} // namespace -} // namespace content diff --git a/chromium/content/browser/android/synchronous_compositor_browser_filter.cc b/chromium/content/browser/android/synchronous_compositor_browser_filter.cc deleted file mode 100644 index af6f7774137..00000000000 --- a/chromium/content/browser/android/synchronous_compositor_browser_filter.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/android/synchronous_compositor_browser_filter.h" - -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/lazy_instance.h" -#include "base/optional.h" -#include "base/stl_util.h" -#include "content/browser/android/synchronous_compositor_sync_call_bridge.h" -#include "content/browser/bad_message.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_process_host.h" - -namespace content { - -SynchronousCompositorBrowserFilter::SynchronousCompositorBrowserFilter( - int process_id) - : BrowserMessageFilter(SyncCompositorMsgStart), - render_process_host_(RenderProcessHost::FromID(process_id)) { - DCHECK(render_process_host_); -} - -SynchronousCompositorBrowserFilter::~SynchronousCompositorBrowserFilter() { -} - -bool SynchronousCompositorBrowserFilter::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(SynchronousCompositorBrowserFilter, message) - IPC_MESSAGE_HANDLER_GENERIC(SyncCompositorHostMsg_ReturnFrame, - ReceiveFrame(message)) - IPC_MESSAGE_HANDLER_GENERIC(SyncCompositorHostMsg_BeginFrameResponse, - BeginFrameResponse(message)) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - -bool SynchronousCompositorBrowserFilter::ReceiveFrame( - const IPC::Message& message) { - SyncCompositorHostMsg_ReturnFrame::Param param; - if (!SyncCompositorHostMsg_ReturnFrame::Read(&message, ¶m)) - return false; - - int routing_id = message.routing_id(); - auto itr = bridges_.find(routing_id); - if (itr == bridges_.end()) { - bad_message::ReceivedBadMessage(this, bad_message::SCO_INVALID_ARGUMENT); - return true; - } - - int frame_sink_id = std::get<0>(param); - uint32_t metadata_version = std::get<1>(param); - base::Optional& compositor_frame = std::get<2>(param); - - if (!itr->second->ReceiveFrameOnIOThread(frame_sink_id, metadata_version, - std::move(compositor_frame))) { - bad_message::ReceivedBadMessage(this, bad_message::SCO_INVALID_ARGUMENT); - } - return true; -} - -bool SynchronousCompositorBrowserFilter::BeginFrameResponse( - const IPC::Message& message) { - SyncCompositorHostMsg_BeginFrameResponse::Param param; - if (!SyncCompositorHostMsg_BeginFrameResponse::Read(&message, ¶m)) - return false; - - int routing_id = message.routing_id(); - auto itr = bridges_.find(routing_id); - if (itr == bridges_.end()) { - bad_message::ReceivedBadMessage(this, bad_message::SCO_INVALID_ARGUMENT); - return true; - } - const SyncCompositorCommonRendererParams& render_params = std::get<0>(param); - if (!itr->second->BeginFrameResponseOnIOThread(render_params)) - bad_message::ReceivedBadMessage(this, bad_message::SCO_INVALID_ARGUMENT); - return true; -} - -void SynchronousCompositorBrowserFilter::OnFilterAdded(IPC::Channel* channel) { - filter_ready_ = true; - for (auto& bridge : bridges_) - bridge.second->RemoteReady(); -} - -void SynchronousCompositorBrowserFilter::OnFilterRemoved() { - SignalFilterClosed(); -} - -void SynchronousCompositorBrowserFilter::OnChannelClosing() { - SignalFilterClosed(); -} - -void SynchronousCompositorBrowserFilter::OnChannelError() { - SignalFilterClosed(); -} - -void SynchronousCompositorBrowserFilter::SignalFilterClosed() { - for (auto& bridge : bridges_) - bridge.second->RemoteClosedOnIOThread(); -} - -void SynchronousCompositorBrowserFilter::RegisterSyncCallBridge( - int routing_id, - scoped_refptr host_bridge) { - DCHECK(bridges_.find(routing_id) == bridges_.end()); - bridges_[routing_id] = host_bridge; - if (filter_ready_) - host_bridge->RemoteReady(); -} - -void SynchronousCompositorBrowserFilter::UnregisterSyncCallBridge( - int routing_id) { - DCHECK(bridges_.find(routing_id) != bridges_.end()); - bridges_.erase(routing_id); -} - -} // namespace content diff --git a/chromium/content/browser/android/synchronous_compositor_browser_filter.h b/chromium/content/browser/android/synchronous_compositor_browser_filter.h deleted file mode 100644 index 111e26a77fc..00000000000 --- a/chromium/content/browser/android/synchronous_compositor_browser_filter.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_ANDROID_SYNCHRONOUS_COMPOSITOR_BROWSER_FILTER_H_ -#define CONTENT_BROWSER_ANDROID_SYNCHRONOUS_COMPOSITOR_BROWSER_FILTER_H_ - -#include -#include - -#include "base/macros.h" -#include "content/public/browser/browser_message_filter.h" - -namespace content { - -class RenderProcessHost; -class SynchronousCompositorSyncCallBridge; - -class SynchronousCompositorBrowserFilter : public BrowserMessageFilter { - public: - explicit SynchronousCompositorBrowserFilter(int process_id); - - // BrowserMessageFilter overrides. - bool OnMessageReceived(const IPC::Message& message) override; - void OnFilterAdded(IPC::Channel* channel) override; - void OnFilterRemoved() override; - void OnChannelClosing() override; - void OnChannelError() override; - - void RegisterSyncCallBridge( - int routing_id, - scoped_refptr host_bridge); - void UnregisterSyncCallBridge(int routing_id); - - private: - ~SynchronousCompositorBrowserFilter() override; - - bool ReceiveFrame(const IPC::Message& message); - bool BeginFrameResponse(const IPC::Message& message); - void SignalFilterClosed(); - - RenderProcessHost* const render_process_host_; - - std::map> bridges_; - - bool filter_ready_ = false; - - DISALLOW_COPY_AND_ASSIGN(SynchronousCompositorBrowserFilter); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_ANDROID_SYNCHRONOUS_COMPOSITOR_BROWSER_FILTER_H_ diff --git a/chromium/content/browser/android/synchronous_compositor_host.cc b/chromium/content/browser/android/synchronous_compositor_host.cc index 7a2ce5797c4..39672258292 100644 --- a/chromium/content/browser/android/synchronous_compositor_host.cc +++ b/chromium/content/browser/android/synchronous_compositor_host.cc @@ -13,7 +13,6 @@ #include "base/memory/shared_memory.h" #include "base/threading/thread_restrictions.h" #include "base/trace_event/trace_event_argument.h" -#include "content/browser/android/synchronous_compositor_browser_filter.h" #include "content/browser/android/synchronous_compositor_sync_call_bridge.h" #include "content/browser/bad_message.h" #include "content/browser/renderer_host/render_widget_host_view_android.h" @@ -36,116 +35,6 @@ namespace content { -class SynchronousCompositorLegacyChromeIPC - : public mojom::SynchronousCompositor { - public: - SynchronousCompositorLegacyChromeIPC(IPC::Sender* sender, int routing_id) - : sender_(sender), routing_id_(routing_id) {} - - void ComputeScroll(base::TimeTicks animation_time) override { - sender_->Send( - new SyncCompositorMsg_ComputeScroll(routing_id_, animation_time)); - } - - void DemandDrawHwAsync( - const SyncCompositorDemandDrawHwParams& draw_params) override { - sender_->Send( - new SyncCompositorMsg_DemandDrawHwAsync(routing_id_, draw_params)); - } - - bool DemandDrawHw( - const content::SyncCompositorDemandDrawHwParams& draw_params, - content::SyncCompositorCommonRendererParams* out_result, - uint32_t* out_layer_tree_frame_sink_id, - uint32_t* out_metadata_version, - base::Optional* out_frame) override { - return sender_->Send(new SyncCompositorMsg_DemandDrawHw( - routing_id_, draw_params, out_result, out_layer_tree_frame_sink_id, - out_metadata_version, out_frame)); - } - - void DemandDrawHw(const SyncCompositorDemandDrawHwParams& params, - DemandDrawHwCallback callback) override { - NOTREACHED(); - } - - bool SetSharedMemory( - const content::SyncCompositorSetSharedMemoryParams& params, - bool* out_success, - content::SyncCompositorCommonRendererParams* out_result) override { - return sender_->Send(new SyncCompositorMsg_SetSharedMemory( - routing_id_, params, out_success, out_result)); - } - - void SetSharedMemory(const SyncCompositorSetSharedMemoryParams& params, - SetSharedMemoryCallback callback) override { - NOTREACHED(); - } - - bool DemandDrawSw( - const content::SyncCompositorDemandDrawSwParams& draw_params, - content::SyncCompositorCommonRendererParams* out_result, - uint32_t* out_metadata_version, - base::Optional* out_meta_data) override { - return sender_->Send(new SyncCompositorMsg_DemandDrawSw( - routing_id_, draw_params, out_result, out_metadata_version, - out_meta_data)); - } - - void DemandDrawSw(const SyncCompositorDemandDrawSwParams& params, - DemandDrawSwCallback callback) override { - NOTREACHED(); - } - - void ZeroSharedMemory() override { - sender_->Send(new SyncCompositorMsg_ZeroSharedMemory(routing_id_)); - } - - bool ZoomBy( - float delta, - const gfx::Point& anchor, - content::SyncCompositorCommonRendererParams* out_result) override { - return sender_->Send( - new SyncCompositorMsg_ZoomBy(routing_id_, delta, anchor, out_result)); - } - - void ZoomBy(float zoom_delta, - const gfx::Point& anchor, - ZoomByCallback) override { - NOTREACHED(); - } - - void SetMemoryPolicy(uint32_t bytes_limit) override { - sender_->Send( - new SyncCompositorMsg_SetMemoryPolicy(routing_id_, bytes_limit)); - } - - void ReclaimResources( - uint32_t layer_tree_frame_sink_id, - const std::vector& resources) override { - sender_->Send(new SyncCompositorMsg_ReclaimResources( - routing_id_, layer_tree_frame_sink_id, resources)); - } - - void SetScroll(const gfx::ScrollOffset& total_scroll_offset) override { - sender_->Send( - new SyncCompositorMsg_SetScroll(routing_id_, total_scroll_offset)); - } - - void BeginFrame(const viz::BeginFrameArgs& args) override { - sender_->Send(new SyncCompositorMsg_BeginFrame(routing_id_, args)); - } - - void SetBeginFrameSourcePaused(bool paused) override { - sender_->Send( - new SyncCompositorMsg_SetBeginFramePaused(routing_id_, paused)); - } - - private: - IPC::Sender* const sender_; - int routing_id_; -}; - // This class runs on the IO thread and is destroyed when the renderer // side closes the mojo channel. class SynchronousCompositorControlHost @@ -224,7 +113,6 @@ SynchronousCompositorHost::SynchronousCompositorHost( client_(rwhva->synchronous_compositor_client()), process_id_(rwhva_->GetRenderWidgetHost()->GetProcess()->GetID()), routing_id_(rwhva_->GetRenderWidgetHost()->GetRoutingID()), - use_mojo_(base::FeatureList::IsEnabled(features::kMojoInputMessages)), use_in_process_zero_copy_software_draw_(use_in_proc_software_draw), host_binding_(this), bytes_limit_(0u), @@ -234,12 +122,6 @@ SynchronousCompositorHost::SynchronousCompositorHost( did_activate_pending_tree_count_(0u) { client_->DidInitializeCompositor(this, process_id_, routing_id_); bridge_ = new SynchronousCompositorSyncCallBridge(this); - - if (!use_mojo_) { - bridge_->BindFilterOnUIThread(); - legacy_compositor_ = std::make_unique( - rwhva_->GetRenderWidgetHost(), routing_id_); - } } SynchronousCompositorHost::~SynchronousCompositorHost() { @@ -248,9 +130,6 @@ SynchronousCompositorHost::~SynchronousCompositorHost() { } void SynchronousCompositorHost::InitMojo() { - if (!use_mojo_) - return; - mojom::SynchronousCompositorControlHostPtr host_control; mojom::SynchronousCompositorControlHostRequest host_request = mojo::MakeRequest(&host_control); @@ -274,19 +153,6 @@ bool SynchronousCompositorHost::IsReadyForSynchronousCall() { return res; } -bool SynchronousCompositorHost::OnMessageReceived(const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(SynchronousCompositorHost, message) - IPC_MESSAGE_HANDLER(SyncCompositorHostMsg_LayerTreeFrameSinkCreated, - LayerTreeFrameSinkCreated) - IPC_MESSAGE_HANDLER(SyncCompositorHostMsg_SetNeedsBeginFrames, - SetNeedsBeginFrames) - IPC_MESSAGE_HANDLER(SyncCompositorHostMsg_UpdateState, UpdateState) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; -} - scoped_refptr SynchronousCompositorHost::DemandDrawHwAsync( const gfx::Size& viewport_size, @@ -613,8 +479,7 @@ void SynchronousCompositorHost::SetNeedsBeginFrames(bool needs_begin_frames) { } void SynchronousCompositorHost::LayerTreeFrameSinkCreated() { - if (use_mojo_) - bridge_->RemoteReady(); + bridge_->RemoteReady(); // New LayerTreeFrameSink is not aware of state from Browser side. So need to // re-send all browser side state here. @@ -660,20 +525,12 @@ void SynchronousCompositorHost::UpdateState( } } -SynchronousCompositorBrowserFilter* SynchronousCompositorHost::GetFilter() { - return static_cast( - rwhva_->GetRenderWidgetHost()->GetProcess()) - ->synchronous_compositor_filter(); -} - RenderProcessHost* SynchronousCompositorHost::GetRenderProcessHost() { return rwhva_->GetRenderWidgetHost()->GetProcess(); } mojom::SynchronousCompositor* SynchronousCompositorHost::GetSynchronousCompositor() { - if (legacy_compositor_) - return legacy_compositor_.get(); return sync_compositor_.get(); } diff --git a/chromium/content/browser/android/synchronous_compositor_host.h b/chromium/content/browser/android/synchronous_compositor_host.h index 74e01ac917d..7333ee6a263 100644 --- a/chromium/content/browser/android/synchronous_compositor_host.h +++ b/chromium/content/browser/android/synchronous_compositor_host.h @@ -23,10 +23,6 @@ #include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/geometry/size_f.h" -namespace IPC { -class Message; -} - namespace ui { class WindowAndroid; struct DidOverscrollParams; @@ -37,8 +33,6 @@ namespace content { class RenderProcessHost; class RenderWidgetHostViewAndroid; class SynchronousCompositorClient; -class SynchronousCompositorBrowserFilter; -class SynchronousCompositorLegacyChromeIPC; class SynchronousCompositorSyncCallBridge; struct SyncCompositorCommonRendererParams; @@ -69,7 +63,6 @@ class SynchronousCompositorHost : public SynchronousCompositor, void BeginFrame(ui::WindowAndroid* window_android, const viz::BeginFrameArgs& args); void SetBeginFramePaused(bool paused); - bool OnMessageReceived(const IPC::Message& message); // Called by SynchronousCompositorSyncCallBridge. int routing_id() const { return routing_id_; } @@ -81,7 +74,6 @@ class SynchronousCompositorHost : public SynchronousCompositor, SynchronousCompositorClient* client() { return client_; } - SynchronousCompositorBrowserFilter* GetFilter(); RenderProcessHost* GetRenderProcessHost(); // mojom::SynchronousCompositorHost overrides. @@ -115,9 +107,7 @@ class SynchronousCompositorHost : public SynchronousCompositor, SynchronousCompositorClient* const client_; const int process_id_; const int routing_id_; - const bool use_mojo_; const bool use_in_process_zero_copy_software_draw_; - std::unique_ptr legacy_compositor_; mojom::SynchronousCompositorAssociatedPtr sync_compositor_; mojo::AssociatedBinding host_binding_; diff --git a/chromium/content/browser/android/synchronous_compositor_sync_call_bridge.cc b/chromium/content/browser/android/synchronous_compositor_sync_call_bridge.cc index bbd19c4f3da..1f560661134 100644 --- a/chromium/content/browser/android/synchronous_compositor_sync_call_bridge.cc +++ b/chromium/content/browser/android/synchronous_compositor_sync_call_bridge.cc @@ -4,7 +4,6 @@ #include "content/browser/android/synchronous_compositor_sync_call_bridge.h" -#include "content/browser/android/synchronous_compositor_browser_filter.h" #include "content/browser/android/synchronous_compositor_host.h" #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/public/browser/browser_thread.h" @@ -17,7 +16,6 @@ SynchronousCompositorSyncCallBridge::SynchronousCompositorSyncCallBridge( SynchronousCompositorHost* host) : routing_id_(host->routing_id()), host_(host), - mojo_enabled_(base::FeatureList::IsEnabled(features::kMojoInputMessages)), begin_frame_condition_(&lock_) { DCHECK(host); } @@ -27,23 +25,6 @@ SynchronousCompositorSyncCallBridge::~SynchronousCompositorSyncCallBridge() { DCHECK(!window_android_in_vsync_); } -void SynchronousCompositorSyncCallBridge::BindFilterOnUIThread() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(host_); - if (mojo_enabled_ || bound_to_filter_) - return; - scoped_refptr filter = host_->GetFilter(); - if (!filter) - return; - bound_to_filter_ = true; - scoped_refptr thiz = this; - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce( - &SynchronousCompositorBrowserFilter::RegisterSyncCallBridge, - std::move(filter), routing_id_, thiz)); -} - void SynchronousCompositorSyncCallBridge::RemoteReady() { base::AutoLock lock(lock_); if (remote_state_ != RemoteState::INIT) @@ -55,7 +36,6 @@ void SynchronousCompositorSyncCallBridge::RemoteClosedOnIOThread() { DCHECK_CURRENTLY_ON(BrowserThread::IO); base::AutoLock lock(lock_); SignalRemoteClosedToAllWaitersOnIOThread(); - UnregisterSyncCallBridgeIfNecessary(); } bool SynchronousCompositorSyncCallBridge::ReceiveFrameOnIOThread( @@ -84,8 +64,6 @@ bool SynchronousCompositorSyncCallBridge::ReceiveFrameOnIOThread( *frame_ptr->frame = std::move(*compositor_frame); } future->SetFrame(std::move(frame_ptr)); - - UnregisterSyncCallBridgeIfNecessary(); return true; } @@ -105,12 +83,9 @@ bool SynchronousCompositorSyncCallBridge::WaitAfterVSyncOnUIThread( ui::WindowAndroid* window_android) { DCHECK_CURRENTLY_ON(BrowserThread::UI); base::AutoLock lock(lock_); - if (!mojo_enabled_ && !bound_to_filter_) - BindFilterOnUIThread(); if (remote_state_ != RemoteState::READY) return false; DCHECK(!begin_frame_response_valid_); - DCHECK(mojo_enabled_ || bound_to_filter_); if (window_android_in_vsync_) { DCHECK_EQ(window_android_in_vsync_, window_android); return true; @@ -129,8 +104,6 @@ bool SynchronousCompositorSyncCallBridge::SetFrameFutureOnUIThread( if (remote_state_ != RemoteState::READY) return false; - BindFilterOnUIThread(); - DCHECK(mojo_enabled_ || bound_to_filter_); // Allowing arbitrary number of pending futures can lead to increase in frame // latency. Due to this, Android platform already ensures that here that there // can be at most 2 pending frames. Here, we rely on Android to do the @@ -144,20 +117,7 @@ bool SynchronousCompositorSyncCallBridge::SetFrameFutureOnUIThread( void SynchronousCompositorSyncCallBridge::HostDestroyedOnUIThread() { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(host_); - scoped_refptr filter = host_->GetFilter(); - - base::AutoLock lock(lock_); - bound_to_filter_ = false; host_ = nullptr; - - if (filter) { - unregister_callback_ = - base::BindOnce(&SynchronousCompositorSyncCallBridge:: - UnregisterSyncCallBridgeOnIOThread, - this, filter); - - UnregisterSyncCallBridgeIfNecessary(); - } } bool SynchronousCompositorSyncCallBridge::IsRemoteReadyOnUIThread() { @@ -203,19 +163,6 @@ void SynchronousCompositorSyncCallBridge::ProcessFrameMetadataOnUIThread( host_->UpdateFrameMetaData(metadata_version, std::move(metadata)); } -void SynchronousCompositorSyncCallBridge::UnregisterSyncCallBridgeOnIOThread( - scoped_refptr filter) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - { - base::AutoLock lock(lock_); - if (remote_state_ == RemoteState::READY) - SignalRemoteClosedToAllWaitersOnIOThread(); - } - if (filter) - filter->UnregisterSyncCallBridge(routing_id_); -} - void SynchronousCompositorSyncCallBridge:: SignalRemoteClosedToAllWaitersOnIOThread() { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -228,17 +175,4 @@ void SynchronousCompositorSyncCallBridge:: begin_frame_condition_.Signal(); } -void SynchronousCompositorSyncCallBridge:: - UnregisterSyncCallBridgeIfNecessary() { - // Can be called from either thread. - lock_.AssertAcquired(); - - // If no more frames left deregister if necessary. Post a task as - // unregistering will destroy this object. - if (frame_futures_.empty() && unregister_callback_) { - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - std::move(unregister_callback_)); - } -} - } // namespace content diff --git a/chromium/content/browser/android/synchronous_compositor_sync_call_bridge.h b/chromium/content/browser/android/synchronous_compositor_sync_call_bridge.h index 4a40e8bcfaa..36dc19df4ad 100644 --- a/chromium/content/browser/android/synchronous_compositor_sync_call_bridge.h +++ b/chromium/content/browser/android/synchronous_compositor_sync_call_bridge.h @@ -14,7 +14,6 @@ namespace content { -class SynchronousCompositorBrowserFilter; class SynchronousCompositorHost; // For the synchronous compositor feature of webview it is necessary @@ -71,9 +70,6 @@ class SynchronousCompositorSyncCallBridge public: explicit SynchronousCompositorSyncCallBridge(SynchronousCompositorHost* host); - // Attach a filter. - void BindFilterOnUIThread(); - // Indicatation that the remote is now ready to process requests. Called // on either UI or IO thread. void RemoteReady(); @@ -117,16 +113,9 @@ class SynchronousCompositorSyncCallBridge void ProcessFrameMetadataOnUIThread(uint32_t metadata_version, viz::CompositorFrameMetadata metadata); - void UnregisterSyncCallBridgeOnIOThread( - scoped_refptr filter); - // Signal all waiters for closure. Callee must host a lock to |lock_|. void SignalRemoteClosedToAllWaitersOnIOThread(); - // Post a task to unregister the bridge with the filter if necessary. Can - // be called on either thread but must hold a lock to |lock_|. - void UnregisterSyncCallBridgeIfNecessary(); - using FrameFutureQueue = base::circular_deque>; @@ -137,8 +126,6 @@ class SynchronousCompositorSyncCallBridge // UI thread only. ui::WindowAndroid* window_android_in_vsync_ = nullptr; SynchronousCompositorHost* host_; - bool bound_to_filter_ = false; - bool mojo_enabled_; // Shared variables between the IO thread and UI thread. base::Lock lock_; @@ -148,11 +135,6 @@ class SynchronousCompositorSyncCallBridge base::ConditionVariable begin_frame_condition_; RemoteState remote_state_ = RemoteState::INIT; - // IO thread based callback that will unbind this object from - // the SynchronousCompositorBrowserFilter. Only called once - // all pending frames are acknowledged. - base::OnceClosure unregister_callback_; - DISALLOW_COPY_AND_ASSIGN(SynchronousCompositorSyncCallBridge); }; diff --git a/chromium/content/browser/android/url_request_content_job.cc b/chromium/content/browser/android/url_request_content_job.cc index fb7f3c479a8..7ea404b10e2 100644 --- a/chromium/content/browser/android/url_request_content_job.cc +++ b/chromium/content/browser/android/url_request_content_job.cc @@ -7,7 +7,6 @@ #include "base/android/content_uri_utils.h" #include "base/bind.h" #include "base/files/file_util.h" -#include "base/message_loop/message_loop.h" #include "base/task_runner.h" #include "content/public/browser/resource_request_info.h" #include "content/public/common/resource_type.h" diff --git a/chromium/content/browser/android/url_request_content_job_unittest.cc b/chromium/content/browser/android/url_request_content_job_unittest.cc index 8815dc8f323..f15fb087e1b 100644 --- a/chromium/content/browser/android/url_request_content_job_unittest.cc +++ b/chromium/content/browser/android/url_request_content_job_unittest.cc @@ -129,7 +129,7 @@ URLRequestContentJobTest::URLRequestContentJobTest() {} void URLRequestContentJobTest::RunRequest(const Range* range, const char* intent_type) { base::FilePath test_dir; - PathService::Get(base::DIR_SOURCE_ROOT, &test_dir); + base::PathService::Get(base::DIR_SOURCE_ROOT, &test_dir); test_dir = test_dir.AppendASCII("content"); test_dir = test_dir.AppendASCII("test"); test_dir = test_dir.AppendASCII("data"); diff --git a/chromium/content/browser/appcache/OWNERS b/chromium/content/browser/appcache/OWNERS index 21ddbb8551d..db1043cbffe 100644 --- a/chromium/content/browser/appcache/OWNERS +++ b/chromium/content/browser/appcache/OWNERS @@ -1,8 +1,5 @@ pwnall@chromium.org jsbell@chromium.org -# OOO until this comment is removed. -michaeln@chromium.org - # TEAM: storage-dev@chromium.org # COMPONENT: Blink>Storage>AppCache diff --git a/chromium/content/browser/appcache/appcache_disk_cache_unittest.cc b/chromium/content/browser/appcache/appcache_disk_cache_unittest.cc index e6e94b70099..321ad52d4f1 100644 --- a/chromium/content/browser/appcache/appcache_disk_cache_unittest.cc +++ b/chromium/content/browser/appcache/appcache_disk_cache_unittest.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/test/scoped_task_environment.h" diff --git a/chromium/content/browser/appcache/appcache_interceptor.cc b/chromium/content/browser/appcache/appcache_interceptor.cc index 249851898ec..6ff24bf35ac 100644 --- a/chromium/content/browser/appcache/appcache_interceptor.cc +++ b/chromium/content/browser/appcache/appcache_interceptor.cc @@ -81,45 +81,6 @@ void AppCacheInterceptor::GetExtraResponseInfo(net::URLRequest* request, 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, - ResourceRequesterInfo* requester_info) { - // AppCache is supported only for renderer initiated requests. - DCHECK(requester_info->IsRenderer()); - AppCacheRequestHandler* handler = GetHandler(request); - if (!handler) - return; - if (!handler->SanityCheckIsSameService(requester_info->appcache_service())) { - // This can happen when V2 apps and web pages end up in the same storage - // partition. - bad_message::ReceivedBadMessage(requester_info->filter(), - bad_message::ACI_WRONG_STORAGE_PARTITION); - return; - } - DCHECK_NE(kAppCacheNoHostId, new_host_id); - handler->CompleteCrossSiteTransfer(new_process_id, new_host_id); -} - -void AppCacheInterceptor::MaybeCompleteCrossSiteTransferInOldProcess( - net::URLRequest* request, - int process_id) { - AppCacheRequestHandler* handler = GetHandler(request); - if (!handler) - return; - handler->MaybeCompleteCrossSiteTransferInOldProcess(process_id); -} - AppCacheInterceptor::AppCacheInterceptor() { } diff --git a/chromium/content/browser/appcache/appcache_interceptor.h b/chromium/content/browser/appcache/appcache_interceptor.h index 7f802d175ac..6658220ddd4 100644 --- a/chromium/content/browser/appcache/appcache_interceptor.h +++ b/chromium/content/browser/appcache/appcache_interceptor.h @@ -24,7 +24,6 @@ namespace content { class AppCacheHost; class AppCacheRequestHandler; class AppCacheServiceImpl; -class ResourceRequesterInfo; // An interceptor to hijack requests and potentially service them out of // the appcache. @@ -51,17 +50,6 @@ class CONTENT_EXPORT AppCacheInterceptor : public net::URLRequestInterceptor { int64_t* 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, - ResourceRequesterInfo* requester_info); - static void MaybeCompleteCrossSiteTransferInOldProcess( - net::URLRequest* request, - int old_process_id); - AppCacheInterceptor(); ~AppCacheInterceptor() override; diff --git a/chromium/content/browser/appcache/appcache_request_handler.cc b/chromium/content/browser/appcache/appcache_request_handler.cc index f7d16136ca7..07d2b0573e4 100644 --- a/chromium/content/browser/appcache/appcache_request_handler.cc +++ b/chromium/content/browser/appcache/appcache_request_handler.cc @@ -47,8 +47,6 @@ AppCacheRequestHandler::AppCacheRequestHandler( cache_entry_not_found_(false), is_delivering_network_response_(false), maybe_load_resource_executed_(false), - old_process_id_(0), - old_host_id_(kAppCacheNoHostId), cache_id_(kAppCacheNoCacheId), service_(host_->service()), request_(std::move(request)), @@ -222,37 +220,6 @@ void AppCacheRequestHandler::GetExtraResponseInfo(int64_t* cache_id, *manifest_url = manifest_url_; } -void AppCacheRequestHandler::PrepareForCrossSiteTransfer(int old_process_id) { - if (!host_) - return; - AppCacheBackendImpl* backend = host_->service()->GetBackend(old_process_id); - DCHECK(backend) << "appcache detected likely storage partition mismatch"; - old_process_id_ = old_process_id; - old_host_id_ = host_->host_id(); - host_for_cross_site_transfer_ = backend->TransferHostOut(host_->host_id()); - DCHECK_EQ(host_, host_for_cross_site_transfer_.get()); -} - -void AppCacheRequestHandler::CompleteCrossSiteTransfer( - int new_process_id, int new_host_id) { - if (!host_for_cross_site_transfer_.get()) - return; - DCHECK_EQ(host_, host_for_cross_site_transfer_.get()); - AppCacheBackendImpl* backend = host_->service()->GetBackend(new_process_id); - DCHECK(backend) << "appcache detected likely storage partition mismatch"; - backend->TransferHostIn(new_host_id, - std::move(host_for_cross_site_transfer_)); -} - -void AppCacheRequestHandler::MaybeCompleteCrossSiteTransferInOldProcess( - int old_process_id) { - if (!host_ || !host_for_cross_site_transfer_.get() || - old_process_id != old_process_id_) { - return; - } - CompleteCrossSiteTransfer(old_process_id_, old_host_id_); -} - // static std::unique_ptr AppCacheRequestHandler::InitializeForNavigationNetworkService( @@ -286,13 +253,11 @@ void AppCacheRequestHandler::OnServiceDestructionImminent( AppCacheServiceImpl* service) { service_ = nullptr; if (!host_) { - DCHECK(!host_for_cross_site_transfer_); DCHECK(!job_); return; } host_->RemoveObserver(this); OnDestructionImminent(host_); - host_for_cross_site_transfer_.reset(); } void AppCacheRequestHandler::DeliverAppCachedResponse( diff --git a/chromium/content/browser/appcache/appcache_request_handler.h b/chromium/content/browser/appcache/appcache_request_handler.h index 9f79c30586b..cea61697ba8 100644 --- a/chromium/content/browser/appcache/appcache_request_handler.h +++ b/chromium/content/browser/appcache/appcache_request_handler.h @@ -67,21 +67,10 @@ class CONTENT_EXPORT AppCacheRequestHandler void GetExtraResponseInfo(int64_t* cache_id, GURL* manifest_url); - // Methods to support cross site navigations. - void PrepareForCrossSiteTransfer(int old_process_id); - void CompleteCrossSiteTransfer(int new_process_id, int new_host_id); - void MaybeCompleteCrossSiteTransferInOldProcess(int old_process_id); - - // Useful for detecting storage partition mismatches in the context - // of cross site transfer navigations. - bool SanityCheckIsSameService(AppCacheService* service) { - return !host_ || (host_->service() == service); - } - // NetworkService loading // NavigationLoaderInterceptor overrides - main resource loading. - // These methods are used by the NavigationURLLoaderNetworkService. + // These methods are used by the NavigationURLLoaderImpl. // Internally they use same methods used by the network library based impl, // MaybeLoadResource and MaybeLoadFallbackForResponse. // Eventually one of the Deliver*Response() methods is called and the @@ -249,12 +238,6 @@ class CONTENT_EXPORT AppCacheRequestHandler // 3) Request has been cancelled, and the job killed. base::WeakPtr job_; - // During a cross site navigation, we transfer ownership the AppcacheHost - // from the old processes structures over to the new structures. - std::unique_ptr host_for_cross_site_transfer_; - int old_process_id_; - int old_host_id_; - // Cached information about the response being currently served by the // AppCache, if there is one. int cache_id_; diff --git a/chromium/content/browser/appcache/appcache_request_handler_unittest.cc b/chromium/content/browser/appcache/appcache_request_handler_unittest.cc index f2391591aaa..3e742cdca67 100644 --- a/chromium/content/browser/appcache/appcache_request_handler_unittest.cc +++ b/chromium/content/browser/appcache/appcache_request_handler_unittest.cc @@ -818,28 +818,6 @@ class AppCacheRequestHandlerTest TestFinished(); } - void DestroyedServiceWithCrossSiteNav() { - EXPECT_TRUE(CreateRequestAndHandler(GURL("http://blah/"), host_, - RESOURCE_TYPE_MAIN_FRAME)); - EXPECT_TRUE(handler_.get()); - handler_->PrepareForCrossSiteTransfer(backend_impl_->process_id()); - EXPECT_TRUE(handler_->host_for_cross_site_transfer_.get()); - - backend_impl_.reset(); - mock_frontend_.reset(); - mock_service_.reset(); - mock_policy_.reset(); - host_ = nullptr; - - EXPECT_FALSE(handler_->host_for_cross_site_transfer_.get()); - EXPECT_FALSE(handler_->MaybeLoadResource(nullptr)); - EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect( - nullptr, GURL("http://blah/redirect"))); - EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(nullptr)); - - TestFinished(); - } - // UnsupportedScheme ----------------------------- void UnsupportedScheme() { @@ -1065,11 +1043,6 @@ TEST_P(AppCacheRequestHandlerTest, DestroyedService) { RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedService); } -TEST_P(AppCacheRequestHandlerTest, DestroyedServiceWithCrossSiteNav) { - RunTestOnIOThread( - &AppCacheRequestHandlerTest::DestroyedServiceWithCrossSiteNav); -} - TEST_P(AppCacheRequestHandlerTest, UnsupportedScheme) { RunTestOnIOThread(&AppCacheRequestHandlerTest::UnsupportedScheme); } diff --git a/chromium/content/browser/appcache/appcache_service_impl.h b/chromium/content/browser/appcache/appcache_service_impl.h index a2a41ed9e4f..ff3bcd5153d 100644 --- a/chromium/content/browser/appcache/appcache_service_impl.h +++ b/chromium/content/browser/appcache/appcache_service_impl.h @@ -14,6 +14,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/observer_list.h" +#include "base/sequenced_task_runner.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "content/browser/url_loader_factory_getter.h" diff --git a/chromium/content/browser/appcache/appcache_storage_impl_unittest.cc b/chromium/content/browser/appcache/appcache_storage_impl_unittest.cc index d8988c284b7..43400a1f726 100644 --- a/chromium/content/browser/appcache/appcache_storage_impl_unittest.cc +++ b/chromium/content/browser/appcache/appcache_storage_impl_unittest.cc @@ -18,6 +18,7 @@ #include "base/location.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" #include "base/test/scoped_task_environment.h" diff --git a/chromium/content/browser/appcache/appcache_subresource_url_factory.cc b/chromium/content/browser/appcache/appcache_subresource_url_factory.cc index a59273f1659..72bb381ee93 100644 --- a/chromium/content/browser/appcache/appcache_subresource_url_factory.cc +++ b/chromium/content/browser/appcache/appcache_subresource_url_factory.cc @@ -127,9 +127,13 @@ class SubresourceLoader : public network::mojom::URLLoader, // network::mojom::URLLoader implementation // Called by the remote client in the renderer. - void FollowRedirect() override { + void FollowRedirect(const base::Optional& + modified_request_headers) override { + DCHECK(!modified_request_headers.has_value()) + << "Redirect with modified headers was not supported yet. " + "crbug.com/845683"; if (!handler_) { - network_loader_->FollowRedirect(); + network_loader_->FollowRedirect(base::nullopt); return; } DCHECK(network_loader_); @@ -148,7 +152,7 @@ class SubresourceLoader : public network::mojom::URLLoader, if (handler) CreateAndStartAppCacheLoader(std::move(handler)); else - network_loader_->FollowRedirect(); + network_loader_->FollowRedirect(base::nullopt); } void SetPriority(net::RequestPriority priority, diff --git a/chromium/content/browser/appcache/appcache_url_loader_job.cc b/chromium/content/browser/appcache/appcache_url_loader_job.cc index 520195a6ca8..b3969898ae2 100644 --- a/chromium/content/browser/appcache/appcache_url_loader_job.cc +++ b/chromium/content/browser/appcache/appcache_url_loader_job.cc @@ -105,7 +105,8 @@ base::WeakPtr AppCacheURLLoaderJob::GetDerivedWeakPtr() { return weak_factory_.GetWeakPtr(); } -void AppCacheURLLoaderJob::FollowRedirect() { +void AppCacheURLLoaderJob::FollowRedirect( + const base::Optional& modified_request_headers) { NOTREACHED() << "appcache never produces redirects"; } diff --git a/chromium/content/browser/appcache/appcache_url_loader_job.h b/chromium/content/browser/appcache/appcache_url_loader_job.h index f64748dc4a4..d116c91d890 100644 --- a/chromium/content/browser/appcache/appcache_url_loader_job.h +++ b/chromium/content/browser/appcache/appcache_url_loader_job.h @@ -55,7 +55,8 @@ class CONTENT_EXPORT AppCacheURLLoaderJob : public AppCacheJob, base::WeakPtr GetDerivedWeakPtr(); // network::mojom::URLLoader implementation: - void FollowRedirect() override; + void FollowRedirect(const base::Optional& + modified_request_headers) override; void ProceedWithResponse() override; void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override; diff --git a/chromium/content/browser/background_fetch/background_fetch.proto b/chromium/content/browser/background_fetch/background_fetch.proto index b33f8003e4b..ec387edd7b8 100644 --- a/chromium/content/browser/background_fetch/background_fetch.proto +++ b/chromium/content/browser/background_fetch/background_fetch.proto @@ -61,7 +61,7 @@ message BackgroundFetchOptions { // in memory. This information should be everything needed to reconstruct // the state of an interrupted background fetch. // -// Next Tag: 6 +// Next Tag: 7 message BackgroundFetchMetadata { optional int64 creation_microseconds_since_unix_epoch = 1; optional string origin = 2; @@ -69,8 +69,38 @@ message BackgroundFetchMetadata { optional BackgroundFetchRegistration registration = 3; optional BackgroundFetchOptions options = 4; - // Defaults to BackgroundFetchOptions.title. - // Can be updated by calling `BackgroundFetchUpdateEvent.updateUI`. - // https://wicg.github.io/background-fetch/#backgroundfetchupdateevent. - optional string ui_title = 5; + // Number of fetches initiated by the developer. + optional int32 num_fetches = 5; +} + +// A background fetch request that is still in a pending state. +// +// Next Tag: 4 +message BackgroundFetchPendingRequest { + optional string unique_id = 1; + optional int32 request_index = 2; + optional string serialized_request = 3; } + +// A background fetch request in the active state. This means that +// the DownloadManager started downloading the file. +// +// Next Tag: 5 +message BackgroundFetchActiveRequest { + optional string unique_id = 1; + optional int32 request_index = 2; + optional string serialized_request = 3; + optional string download_guid = 4; +} + +// A background fetch request in the completed state. This means that +// the DownloadManager returned the download. +// +// Next Tag: 6 +message BackgroundFetchCompletedRequest { + optional string unique_id = 1; + optional int32 request_index = 2; + optional string serialized_request = 3; + optional string download_guid = 4; + optional bool succeeded = 5; +} \ No newline at end of file diff --git a/chromium/content/browser/background_fetch/background_fetch_context.cc b/chromium/content/browser/background_fetch/background_fetch_context.cc index 572933b7540..0ddea261409 100644 --- a/chromium/content/browser/background_fetch/background_fetch_context.cc +++ b/chromium/content/browser/background_fetch/background_fetch_context.cc @@ -8,6 +8,7 @@ #include "base/bind_helpers.h" #include "content/browser/background_fetch/background_fetch_job_controller.h" +#include "content/browser/background_fetch/background_fetch_metrics.h" #include "content/browser/background_fetch/background_fetch_registration_id.h" #include "content/browser/background_fetch/background_fetch_registration_notifier.h" #include "content/browser/background_fetch/background_fetch_scheduler.h" @@ -19,26 +20,6 @@ namespace content { -namespace { - -void IgnoreError(blink::mojom::BackgroundFetchError) { - // TODO(johnme): Log errors to UMA. -} - -// Records the |error| status issued by the DataManager after it was requested -// to create and store a new Background Fetch registration. -void RecordRegistrationCreatedError(blink::mojom::BackgroundFetchError error) { - // TODO(peter): Add UMA. -} - -// Records the |error| status issued by the DataManager after the storage -// associated with a registration has been completely deleted. -void RecordRegistrationDeletedError(blink::mojom::BackgroundFetchError error) { - // TODO(peter): Add UMA. -} - -} // namespace - BackgroundFetchContext::BackgroundFetchContext( BrowserContext* browser_context, const scoped_refptr& service_worker_context) @@ -111,7 +92,7 @@ void BackgroundFetchContext::StartFetch( registration_id, requests, options, icon, base::BindOnce(&BackgroundFetchContext::DidCreateRegistration, weak_factory_.GetWeakPtr(), registration_id, options, icon, - std::move(callback))); + requests.size(), std::move(callback))); } void BackgroundFetchContext::GetIconDisplaySize( @@ -125,21 +106,34 @@ void BackgroundFetchContext::DidCreateRegistration( const BackgroundFetchRegistrationId& registration_id, const BackgroundFetchOptions& options, const SkBitmap& icon, + size_t num_requests, blink::mojom::BackgroundFetchService::FetchCallback callback, blink::mojom::BackgroundFetchError error, std::unique_ptr registration) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - RecordRegistrationCreatedError(error); + background_fetch::RecordRegistrationCreatedError(error); if (error != blink::mojom::BackgroundFetchError::NONE) { std::move(callback).Run(error, base::nullopt); return; } DCHECK(registration); + + BackgroundFetchRegistration* registration_ptr = registration.get(); + // The closure takes ownership of |registration|, and it's guaranteed to + // outlive CreateController, which uses the underlying pointer. + base::OnceClosure done_closure = base::BindOnce( + [](blink::mojom::BackgroundFetchService::FetchCallback callback, + blink::mojom::BackgroundFetchError error, + std::unique_ptr registration) { + std::move(callback).Run(error, *registration); + }, + std::move(callback), error, std::move(registration)); + // Create the BackgroundFetchJobController to do the actual fetching. - CreateController(registration_id, options, icon, *registration.get()); - std::move(callback).Run(error, *registration.get()); + CreateController(registration_id, options, icon, num_requests, + *registration_ptr, std::move(done_closure)); } void BackgroundFetchContext::AddRegistrationObserver( @@ -190,7 +184,9 @@ void BackgroundFetchContext::CreateController( const BackgroundFetchRegistrationId& registration_id, const BackgroundFetchOptions& options, const SkBitmap& icon, - const BackgroundFetchRegistration& registration) { + size_t num_requests, + const BackgroundFetchRegistration& registration, + base::OnceClosure done_closure) { DCHECK_CURRENTLY_ON(BrowserThread::IO); auto controller = std::make_unique( @@ -199,21 +195,31 @@ void BackgroundFetchContext::CreateController( // Safe because JobControllers are destroyed before RegistrationNotifier. base::BindRepeating(&BackgroundFetchRegistrationNotifier::Notify, base::Unretained(registration_notifier_.get())), - base::BindOnce(&BackgroundFetchContext::DidFinishJob, - weak_factory_.GetWeakPtr(), base::Bind(&IgnoreError))); + base::BindOnce( + &BackgroundFetchContext::DidFinishJob, weak_factory_.GetWeakPtr(), + base::Bind(&background_fetch::RecordSchedulerFinishedError))); + + data_manager_.GetNumCompletedRequests( + registration_id, + base::BindOnce(&BackgroundFetchContext::InitializeController, + weak_factory_.GetWeakPtr(), registration_id.unique_id(), + std::move(controller), std::move(done_closure), + num_requests)); +} - // TODO(delphick): This assumes that fetches are always started afresh in - // each browser session. We need to initialize the number of downloads using - // information loaded from the database. - controller->InitializeRequestStatus( - 0, /* completed_downloads*/ - data_manager_.GetTotalNumberOfRequests(registration_id), - std::vector() /* outstanding download GUIDs */); +void BackgroundFetchContext::InitializeController( + const std::string& unique_id, + std::unique_ptr controller, + base::OnceClosure done_closure, + size_t total_downloads, + size_t completed_downloads) { + controller->InitializeRequestStatus(completed_downloads, total_downloads, + {} /* outstanding download GUIDs */); scheduler_->AddJobController(controller.get()); - job_controllers_.insert( - std::make_pair(registration_id.unique_id(), std::move(controller))); + job_controllers_.insert({unique_id, std::move(controller)}); + std::move(done_closure).Run(); } void BackgroundFetchContext::Abort( @@ -221,13 +227,14 @@ void BackgroundFetchContext::Abort( blink::mojom::BackgroundFetchService::AbortCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DidFinishJob(std::move(callback), registration_id, true /* aborted */); + DidFinishJob(std::move(callback), registration_id, + BackgroundFetchReasonToAbort::ABORTED_BY_DEVELOPER); } void BackgroundFetchContext::DidFinishJob( base::OnceCallback callback, const BackgroundFetchRegistrationId& registration_id, - bool aborted) { + BackgroundFetchReasonToAbort reason_to_abort) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // If |aborted| is true, this will also propagate the event to any active @@ -235,13 +242,13 @@ void BackgroundFetchContext::DidFinishJob( data_manager_.MarkRegistrationForDeletion( registration_id, base::BindOnce(&BackgroundFetchContext::DidMarkForDeletion, - weak_factory_.GetWeakPtr(), registration_id, aborted, - std::move(callback))); + weak_factory_.GetWeakPtr(), registration_id, + reason_to_abort, std::move(callback))); } void BackgroundFetchContext::DidMarkForDeletion( const BackgroundFetchRegistrationId& registration_id, - bool aborted, + BackgroundFetchReasonToAbort reason_to_abort, base::OnceCallback callback, blink::mojom::BackgroundFetchError error) { std::move(callback).Run(error); @@ -253,19 +260,27 @@ void BackgroundFetchContext::DidMarkForDeletion( if (error != blink::mojom::BackgroundFetchError::NONE) return; - if (aborted) { + if (reason_to_abort == BackgroundFetchReasonToAbort::ABORTED_BY_DEVELOPER) { DCHECK(job_controllers_.count(registration_id.unique_id())); - job_controllers_[registration_id.unique_id()]->Abort(); - - CleanupRegistration(registration_id, {}); + job_controllers_[registration_id.unique_id()]->Abort(reason_to_abort); + } - event_dispatcher_.DispatchBackgroundFetchAbortEvent(registration_id, - base::DoNothing()); - } else { - data_manager_.GetSettledFetchesForRegistration( - registration_id, - base::BindOnce(&BackgroundFetchContext::DidGetSettledFetches, - weak_factory_.GetWeakPtr(), registration_id)); + switch (reason_to_abort) { + case BackgroundFetchReasonToAbort::ABORTED_BY_DEVELOPER: + case BackgroundFetchReasonToAbort::CANCELLED_FROM_UI: + CleanupRegistration(registration_id, {}); + // TODO(rayankans): Send fetches to the event dispatcher. + event_dispatcher_.DispatchBackgroundFetchAbortEvent( + registration_id, {} /* settled_fetches */, base::DoNothing()); + return; + case BackgroundFetchReasonToAbort::TOTAL_DOWNLOAD_SIZE_EXCEEDED: + case BackgroundFetchReasonToAbort::NONE: + // This will send a BackgroundFetchFetched or BackgroundFetchFail event. + data_manager_.GetSettledFetchesForRegistration( + registration_id, + base::BindOnce(&BackgroundFetchContext::DidGetSettledFetches, + weak_factory_.GetWeakPtr(), registration_id)); + return; } } @@ -278,7 +293,7 @@ void BackgroundFetchContext::DidGetSettledFetches( DCHECK_CURRENTLY_ON(BrowserThread::IO); if (error != blink::mojom::BackgroundFetchError::NONE) { - CleanupRegistration(registration_id, {}); + CleanupRegistration(registration_id, {} /* fetches */); return; } @@ -338,7 +353,8 @@ void BackgroundFetchContext::LastObserverGarbageCollected( DCHECK_CURRENTLY_ON(BrowserThread::IO); data_manager_.DeleteRegistration( - registration_id, base::BindOnce(&RecordRegistrationDeletedError)); + registration_id, + base::BindOnce(&background_fetch::RecordRegistrationDeletedError)); } } // namespace content diff --git a/chromium/content/browser/background_fetch/background_fetch_context.h b/chromium/content/browser/background_fetch/background_fetch_context.h index d2a8b980fb0..fe0892dd021 100644 --- a/chromium/content/browser/background_fetch/background_fetch_context.h +++ b/chromium/content/browser/background_fetch/background_fetch_context.h @@ -113,7 +113,17 @@ class CONTENT_EXPORT BackgroundFetchContext void CreateController(const BackgroundFetchRegistrationId& registration_id, const BackgroundFetchOptions& options, const SkBitmap& icon, - const BackgroundFetchRegistration& registration); + size_t num_requests, + const BackgroundFetchRegistration& registration, + base::OnceClosure done_closure); + + // Initializes the new Job Controller. + void InitializeController( + const std::string& unique_id, + std::unique_ptr controller, + base::OnceClosure done_closure, + size_t total_downloads, + size_t completed_downloads); // Called when an existing registration has been retrieved from the data // manager. If the registration does not exist then |registration| is nullptr. @@ -127,6 +137,7 @@ class CONTENT_EXPORT BackgroundFetchContext const BackgroundFetchRegistrationId& registration_id, const BackgroundFetchOptions& options, const SkBitmap& icon, + size_t num_requests, blink::mojom::BackgroundFetchService::FetchCallback callback, blink::mojom::BackgroundFetchError error, std::unique_ptr registration); @@ -143,12 +154,12 @@ class CONTENT_EXPORT BackgroundFetchContext void DidFinishJob( base::OnceCallback callback, const BackgroundFetchRegistrationId& registration_id, - bool aborted); + BackgroundFetchReasonToAbort reason_to_abort); // Called when the data manager finishes marking a registration as deleted. void DidMarkForDeletion( const BackgroundFetchRegistrationId& registration_id, - bool aborted, + BackgroundFetchReasonToAbort reason_to_abort, base::OnceCallback callback, blink::mojom::BackgroundFetchError error); diff --git a/chromium/content/browser/background_fetch/background_fetch_data_manager.cc b/chromium/content/browser/background_fetch/background_fetch_data_manager.cc index 500993d7b94..a8cb4f6d5fc 100644 --- a/chromium/content/browser/background_fetch/background_fetch_data_manager.cc +++ b/chromium/content/browser/background_fetch/background_fetch_data_manager.cc @@ -19,7 +19,11 @@ #include "content/browser/background_fetch/storage/delete_registration_task.h" #include "content/browser/background_fetch/storage/get_developer_ids_task.h" #include "content/browser/background_fetch/storage/get_metadata_task.h" +#include "content/browser/background_fetch/storage/get_num_requests_task.h" +#include "content/browser/background_fetch/storage/get_settled_fetches_task.h" #include "content/browser/background_fetch/storage/mark_registration_for_deletion_task.h" +#include "content/browser/background_fetch/storage/mark_request_complete_task.h" +#include "content/browser/background_fetch/storage/start_next_pending_request_task.h" #include "content/browser/background_fetch/storage/update_registration_ui_task.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" @@ -142,10 +146,7 @@ class BackgroundFetchDataManager::RegistrationData { uint64_t GetDownloaded() const { return complete_requests_downloaded_bytes_; } - int GetTotalNumberOfRequests() const { - return pending_requests_.size() + active_requests_.size() + - completed_requests_.size(); - } + size_t GetNumCompletedRequests() const { return completed_requests_.size(); } private: BackgroundFetchRegistrationId registration_id_; @@ -343,8 +344,24 @@ void BackgroundFetchDataManager::PopNextRequest( NextRequestCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBackgroundFetchPersistence)) { + auto start_next_request = base::BindOnce( + &BackgroundFetchDataManager::AddStartNextPendingRequestTask, + weak_ptr_factory_.GetWeakPtr(), + registration_id.service_worker_registration_id(), std::move(callback)); + + // Get the associated metadata, and add a StartNextPendingRequestTask. + GetMetadata(registration_id.service_worker_registration_id(), + registration_id.origin(), registration_id.developer_id(), + std::move(start_next_request)); + + return; + } + if (!IsActive(registration_id)) { - // Stop giving out requests as registration aborted (or otherwise finished). + // Stop giving out requests as registration was aborted (or otherwise + // finished). std::move(callback).Run(nullptr /* request */); return; } @@ -361,12 +378,37 @@ void BackgroundFetchDataManager::PopNextRequest( std::move(callback).Run(std::move(next_request)); } +void BackgroundFetchDataManager::AddStartNextPendingRequestTask( + int64_t service_worker_registration_id, + NextRequestCallback callback, + blink::mojom::BackgroundFetchError error, + std::unique_ptr metadata) { + if (!metadata) { + // Stop giving out requests as registration aborted (or otherwise finished). + std::move(callback).Run(nullptr /* request */); + return; + } + DCHECK_EQ(error, blink::mojom::BackgroundFetchError::NONE); + + AddDatabaseTask( + std::make_unique( + this, service_worker_registration_id, std::move(metadata), + std::move(callback))); +} + void BackgroundFetchDataManager::MarkRequestAsComplete( const BackgroundFetchRegistrationId& registration_id, BackgroundFetchRequestInfo* request, BackgroundFetchScheduler::MarkedCompleteCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBackgroundFetchPersistence)) { + AddDatabaseTask(std::make_unique( + this, registration_id, request, std::move(callback))); + return; + } + auto iter = registrations_.find(registration_id.unique_id()); DCHECK(iter != registrations_.end()); @@ -381,6 +423,13 @@ void BackgroundFetchDataManager::GetSettledFetchesForRegistration( SettledFetchesCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBackgroundFetchPersistence)) { + AddDatabaseTask(std::make_unique( + this, registration_id, std::move(callback))); + return; + } + auto iter = registrations_.find(registration_id.unique_id()); DCHECK(iter != registrations_.end()); @@ -404,58 +453,10 @@ void BackgroundFetchDataManager::GetSettledFetchesForRegistration( // The |filter| decides which values can be passed on to the Service Worker. BackgroundFetchCrossOriginFilter filter(registration_id.origin(), *request); - settled_fetch.response.url_list = request->GetURLChain(); - settled_fetch.response.response_type = - network::mojom::FetchResponseType::kDefault; - - // Include the status code, status text and the response's body as a blob - // when this is allowed by the CORS protocol. - if (filter.CanPopulateBody()) { - settled_fetch.response.status_code = request->GetResponseCode(); - settled_fetch.response.status_text = request->GetResponseText(); - settled_fetch.response.headers.insert( - request->GetResponseHeaders().begin(), - request->GetResponseHeaders().end()); - - if (request->GetFileSize() > 0) { - DCHECK(!request->GetFilePath().empty()); - DCHECK(blob_storage_context_); - - auto blob_builder = - std::make_unique(base::GenerateGUID()); - blob_builder->AppendFile(request->GetFilePath(), 0 /* offset */, - request->GetFileSize(), - base::Time() /* expected_modification_time */); - - auto blob_data_handle = - GetBlobStorageContext(blob_storage_context_.get()) - ->AddFinishedBlob(std::move(blob_builder)); - - // TODO(peter): Appropriately handle !blob_data_handle - if (blob_data_handle) { - settled_fetch.response.blob_uuid = blob_data_handle->uuid(); - settled_fetch.response.blob_size = blob_data_handle->size(); - blink::mojom::BlobPtr blob_ptr; - storage::BlobImpl::Create( - std::make_unique(*blob_data_handle), - MakeRequest(&blob_ptr)); - - settled_fetch.response.blob = - base::MakeRefCounted(std::move(blob_ptr)); - blob_data_handles.push_back(std::move(blob_data_handle)); - } - } - } else { - // TODO(crbug.com/711354): Consider Background Fetches as failed when the - // response cannot be relayed to the developer. - background_fetch_succeeded = false; - } - - // TODO(delphick): settled_fetch.response.error - settled_fetch.response.response_time = request->GetResponseTime(); - // TODO(delphick): settled_fetch.response.cors_exposed_header_names - - background_fetch_succeeded = background_fetch_succeeded && IsOK(*request); + background_fetch_succeeded = + FillServiceWorkerResponse(*request, registration_id.origin(), + &settled_fetch.response) && + background_fetch_succeeded; settled_fetches.push_back(settled_fetch); } @@ -465,6 +466,62 @@ void BackgroundFetchDataManager::GetSettledFetchesForRegistration( std::move(settled_fetches), std::move(blob_data_handles)); } +bool BackgroundFetchDataManager::FillServiceWorkerResponse( + const BackgroundFetchRequestInfo& request, + const url::Origin& origin, + ServiceWorkerResponse* response) { + DCHECK(response); + + response->url_list = request.GetURLChain(); + response->response_type = network::mojom::FetchResponseType::kDefault; + // TODO(crbug.com/838837): settled_fetch.response.error + response->response_time = request.GetResponseTime(); + // TODO(crbug.com/838837): settled_fetch.response.cors_exposed_header_names + + BackgroundFetchCrossOriginFilter filter(origin, request); + if (!filter.CanPopulateBody()) { + // TODO(crbug.com/711354): Consider Background Fetches as failed when the + // response cannot be relayed to the developer. + return false; + } + + // Include the status code, status text and the response's body as a blob + // when this is allowed by the CORS protocol. + response->status_code = request.GetResponseCode(); + response->status_text = request.GetResponseText(); + response->headers.insert(request.GetResponseHeaders().begin(), + request.GetResponseHeaders().end()); + + if (request.GetFileSize() > 0) { + DCHECK(!request.GetFilePath().empty()); + DCHECK(blob_storage_context_); + + auto blob_builder = + std::make_unique(base::GenerateGUID()); + blob_builder->AppendFile(request.GetFilePath(), 0 /* offset */, + request.GetFileSize(), + base::Time() /* expected_modification_time */); + + auto blob_data_handle = GetBlobStorageContext(blob_storage_context_.get()) + ->AddFinishedBlob(std::move(blob_builder)); + + // TODO(peter): Appropriately handle !blob_data_handle + if (blob_data_handle) { + response->blob_uuid = blob_data_handle->uuid(); + response->blob_size = blob_data_handle->size(); + blink::mojom::BlobPtr blob_ptr; + storage::BlobImpl::Create( + std::make_unique(*blob_data_handle), + MakeRequest(&blob_ptr)); + + response->blob = + base::MakeRefCounted(std::move(blob_ptr)); + } + } + + return IsOK(request); +} + void BackgroundFetchDataManager::MarkRegistrationForDeletion( const BackgroundFetchRegistrationId& registration_id, HandleBackgroundFetchErrorCallback callback) { @@ -545,10 +602,21 @@ void BackgroundFetchDataManager::GetDeveloperIdsForServiceWorker( developer_ids); } -int BackgroundFetchDataManager::GetTotalNumberOfRequests( - const BackgroundFetchRegistrationId& registration_id) const { - return registrations_.find(registration_id.unique_id()) - ->second->GetTotalNumberOfRequests(); +void BackgroundFetchDataManager::GetNumCompletedRequests( + const BackgroundFetchRegistrationId& registration_id, + NumRequestsCallback callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBackgroundFetchPersistence)) { + AddDatabaseTask(std::make_unique( + this, registration_id, background_fetch::RequestType::kCompleted, + std::move(callback))); + return; + } + + std::move(callback).Run(registrations_.find(registration_id.unique_id()) + ->second->GetNumCompletedRequests()); } bool BackgroundFetchDataManager::IsActive( diff --git a/chromium/content/browser/background_fetch/background_fetch_data_manager.h b/chromium/content/browser/background_fetch/background_fetch_data_manager.h index 3d61469caad..6b70bbf5715 100644 --- a/chromium/content/browser/background_fetch/background_fetch_data_manager.h +++ b/chromium/content/browser/background_fetch/background_fetch_data_manager.h @@ -63,6 +63,7 @@ class CONTENT_EXPORT BackgroundFetchDataManager std::unique_ptr)>; using NextRequestCallback = base::OnceCallback)>; + using NumRequestsCallback = base::OnceCallback; BackgroundFetchDataManager( BrowserContext* browser_context, @@ -133,17 +134,27 @@ class CONTENT_EXPORT BackgroundFetchDataManager const url::Origin& origin, blink::mojom::BackgroundFetchService::GetDeveloperIdsCallback callback); - int GetTotalNumberOfRequests( - const BackgroundFetchRegistrationId& registration_id) const; + // Gets the number of fetch requests that have been completed for a given + // registration. + void GetNumCompletedRequests( + const BackgroundFetchRegistrationId& registration_id, + NumRequestsCallback callback); // BackgroundFetchScheduler::RequestProvider implementation: void PopNextRequest(const BackgroundFetchRegistrationId& registration_id, NextRequestCallback callback) override; + void MarkRequestAsComplete( const BackgroundFetchRegistrationId& registration_id, BackgroundFetchRequestInfo* request, BackgroundFetchScheduler::MarkedCompleteCallback callback) override; + // TODO(rayankans): Move this function to MarkRequestCompleteTask after + // non-persistent background fetch support is removed. + bool FillServiceWorkerResponse(const BackgroundFetchRequestInfo& request, + const url::Origin& origin, + ServiceWorkerResponse* response); + private: FRIEND_TEST_ALL_PREFIXES(BackgroundFetchDataManagerTest, Cleanup); friend class BackgroundFetchDataManagerTest; @@ -151,6 +162,12 @@ class CONTENT_EXPORT BackgroundFetchDataManager class RegistrationData; + void AddStartNextPendingRequestTask( + int64_t service_worker_registration_id, + NextRequestCallback callback, + blink::mojom::BackgroundFetchError error, + std::unique_ptr metadata); + void AddDatabaseTask(std::unique_ptr task); void OnDatabaseTaskFinished(background_fetch::DatabaseTask* task); diff --git a/chromium/content/browser/background_fetch/background_fetch_data_manager_unittest.cc b/chromium/content/browser/background_fetch/background_fetch_data_manager_unittest.cc index 509c5bb5de3..8a1cd9a985e 100644 --- a/chromium/content/browser/background_fetch/background_fetch_data_manager_unittest.cc +++ b/chromium/content/browser/background_fetch/background_fetch_data_manager_unittest.cc @@ -18,9 +18,12 @@ #include "content/browser/background_fetch/background_fetch_request_info.h" #include "content/browser/background_fetch/background_fetch_test_base.h" #include "content/browser/background_fetch/storage/database_helpers.h" +#include "content/browser/background_fetch/storage/get_num_requests_task.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" +#include "content/public/browser/background_fetch_response.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_switches.h" +#include "storage/browser/blob/blob_data_handle.h" #include "testing/gmock/include/gmock/gmock.h" namespace content { @@ -41,13 +44,6 @@ const char kAlternativeUniqueId[] = "bb48a9fb-c21f-4c2d-a9ae-58bd48a9fb53"; const char kInitialTitle[] = "Initial Title"; const char kUpdatedTitle[] = "Updated Title"; -// See schema documentation in background_fetch_data_manager.cc. -// A "bgfetch_registration_" per registration (not including keys for requests). -constexpr size_t kUserDataKeysPerInactiveRegistration = 1u; - -// A "bgfetch_request_" per request. -constexpr size_t kUserDataKeysPerInactiveRequest = 1u; - void DidCreateRegistration( base::Closure quit_closure, blink::mojom::BackgroundFetchError* out_error, @@ -74,6 +70,37 @@ void DidGetRegistrationUserDataByKeyPrefix(base::Closure quit_closure, std::move(quit_closure).Run(); } +void AnnotateRequestInfoWithFakeDownloadManagerData( + BackgroundFetchRequestInfo* request_info) { + // Fill |request_info| with a failed result. + request_info->SetResult(std::make_unique( + base::Time::Now(), BackgroundFetchResult::FailureReason::UNKNOWN)); + request_info->PopulateWithResponse( + std::make_unique(std::vector(1), nullptr)); +} + +void GetNumUserData(base::Closure quit_closure, + int* out_size, + const std::vector& data, + ServiceWorkerStatusCode status) { + DCHECK(out_size); + DCHECK_EQ(SERVICE_WORKER_OK, status); + *out_size = data.size(); + std::move(quit_closure).Run(); +} + +struct ResponseStateStats { + int pending_requests = 0; + int active_requests = 0; + int completed_requests = 0; +}; + +bool operator==(const ResponseStateStats& s1, const ResponseStateStats& s2) { + return s1.pending_requests == s2.pending_requests && + s1.active_requests == s2.active_requests && + s1.completed_requests == s2.completed_requests; +} + } // namespace class BackgroundFetchDataManagerTest @@ -195,6 +222,22 @@ class BackgroundFetchDataManagerTest return ids; } + // Synchronous version of + // BackgroundFetchDataManager::PopNextRequest(). + void PopNextRequest( + const BackgroundFetchRegistrationId& registration_id, + scoped_refptr* out_request_info) { + DCHECK(out_request_info); + + base::RunLoop run_loop; + background_fetch_data_manager_->PopNextRequest( + registration_id, + base::BindOnce(&BackgroundFetchDataManagerTest::DidPopNextRequest, + base::Unretained(this), run_loop.QuitClosure(), + out_request_info)); + run_loop.Run(); + } + // Synchronous version of // BackgroundFetchDataManager::MarkRegistrationForDeletion(). void MarkRegistrationForDeletion( @@ -221,6 +264,72 @@ class BackgroundFetchDataManagerTest run_loop.Run(); } + // Synchronous version of BackgroundFetchDataManager::MarkRequestAsComplete(). + void MarkRequestAsComplete( + const BackgroundFetchRegistrationId& registration_id, + BackgroundFetchRequestInfo* request_info) { + base::RunLoop run_loop; + background_fetch_data_manager_->MarkRequestAsComplete( + registration_id, request_info, + base::BindOnce( + &BackgroundFetchDataManagerTest::DidMarkRequestAsComplete, + base::Unretained(this), run_loop.QuitClosure())); + run_loop.Run(); + } + + // Synchronous version of + // BackgroundFetchDataManager::GetSettledFetchesForRegistration(). + void GetSettledFetchesForRegistration( + const BackgroundFetchRegistrationId& registration_id, + blink::mojom::BackgroundFetchError* out_error, + bool* out_succeeded, + std::vector* out_settled_fetches) { + DCHECK(out_error); + DCHECK(out_succeeded); + DCHECK(out_settled_fetches); + + base::RunLoop run_loop; + background_fetch_data_manager_->GetSettledFetchesForRegistration( + registration_id, + base::BindOnce(&BackgroundFetchDataManagerTest:: + DidGetSettledFetchesForRegistration, + base::Unretained(this), run_loop.QuitClosure(), + out_error, out_succeeded, out_settled_fetches)); + run_loop.Run(); + } + + // Synchronous version of + // BackgroundFetchDataManager::GetNumCompletedRequests(). + void GetNumCompletedRequests( + const BackgroundFetchRegistrationId& registration_id, + size_t* out_size) { + DCHECK(out_size); + + base::RunLoop run_loop; + background_fetch_data_manager_->GetNumCompletedRequests( + registration_id, + base::BindOnce(&BackgroundFetchDataManagerTest::DidGetNumRequests, + base::Unretained(this), run_loop.QuitClosure(), + out_size)); + run_loop.Run(); + } + + // Synchronous version of GetNumRequestsTask::Start(). + void GetNumRequestsTask(const BackgroundFetchRegistrationId& registration_id, + background_fetch::RequestType type, + size_t* out_size) { + DCHECK(out_size); + + base::RunLoop run_loop; + background_fetch_data_manager_->AddDatabaseTask( + std::make_unique( + background_fetch_data_manager_.get(), registration_id, type, + base::BindOnce(&BackgroundFetchDataManagerTest::DidGetNumRequests, + base::Unretained(this), run_loop.QuitClosure(), + out_size))); + run_loop.Run(); + } + // Synchronous version of // ServiceWorkerContextWrapper::GetRegistrationUserDataByKeyPrefix. std::vector GetRegistrationUserDataByKeyPrefix( @@ -240,6 +349,45 @@ class BackgroundFetchDataManagerTest return data; } + // Gets information about the number of background fetch requests by state. + ResponseStateStats GetRequestStats(int64_t service_worker_registration_id) { + ResponseStateStats stats; + { + base::RunLoop run_loop; + embedded_worker_test_helper() + ->context_wrapper() + ->GetRegistrationUserDataByKeyPrefix( + service_worker_registration_id, + background_fetch::kPendingRequestKeyPrefix, + base::BindOnce(&GetNumUserData, run_loop.QuitClosure(), + &stats.pending_requests)); + run_loop.Run(); + } + { + base::RunLoop run_loop; + embedded_worker_test_helper() + ->context_wrapper() + ->GetRegistrationUserDataByKeyPrefix( + service_worker_registration_id, + background_fetch::kActiveRequestKeyPrefix, + base::BindOnce(&GetNumUserData, run_loop.QuitClosure(), + &stats.active_requests)); + run_loop.Run(); + } + { + base::RunLoop run_loop; + embedded_worker_test_helper() + ->context_wrapper() + ->GetRegistrationUserDataByKeyPrefix( + service_worker_registration_id, + background_fetch::kCompletedRequestKeyPrefix, + base::BindOnce(&GetNumUserData, run_loop.QuitClosure(), + &stats.completed_requests)); + run_loop.Run(); + } + return stats; + } + protected: void DidGetRegistration( base::Closure quit_closure, @@ -289,6 +437,41 @@ class BackgroundFetchDataManagerTest std::move(quit_closure).Run(); } + void DidPopNextRequest( + base::OnceClosure quit_closure, + scoped_refptr* out_request_info, + scoped_refptr request_info) { + *out_request_info = request_info; + std::move(quit_closure).Run(); + } + + void DidMarkRequestAsComplete(base::OnceClosure quit_closure) { + std::move(quit_closure).Run(); + } + + void DidGetSettledFetchesForRegistration( + base::OnceClosure quit_closure, + blink::mojom::BackgroundFetchError* out_error, + bool* out_succeeded, + std::vector* out_settled_fetches, + blink::mojom::BackgroundFetchError error, + bool succeeded, + std::vector settled_fetches, + std::vector>) { + *out_error = error; + *out_succeeded = succeeded; + *out_settled_fetches = std::move(settled_fetches); + + std::move(quit_closure).Run(); + } + + void DidGetNumRequests(base::OnceClosure quit_closure, + size_t* out_size, + size_t size) { + *out_size = size; + std::move(quit_closure).Run(); + } + BackgroundFetchRegistrationStorage registration_storage_; std::unique_ptr background_fetch_data_manager_; }; @@ -471,6 +654,7 @@ TEST_P(BackgroundFetchDataManagerTest, GetMetadata) { ASSERT_TRUE(metadata); EXPECT_EQ(metadata->origin(), origin().Serialize()); EXPECT_NE(metadata->creation_microseconds_since_unix_epoch(), 0); + EXPECT_EQ(metadata->num_fetches(), static_cast(requests.size())); // Verify that retrieving using the wrong developer id doesn't work. metadata = GetMetadata(sw_id, origin(), kAlternativeDeveloperId, &error); @@ -485,6 +669,7 @@ TEST_P(BackgroundFetchDataManagerTest, GetMetadata) { ASSERT_TRUE(metadata); EXPECT_EQ(metadata->origin(), origin().Serialize()); EXPECT_NE(metadata->creation_microseconds_since_unix_epoch(), 0); + EXPECT_EQ(metadata->num_fetches(), static_cast(requests.size())); } TEST_P(BackgroundFetchDataManagerTest, UpdateRegistrationUI) { @@ -504,15 +689,20 @@ TEST_P(BackgroundFetchDataManagerTest, UpdateRegistrationUI) { options.title = kInitialTitle; blink::mojom::BackgroundFetchError error; + // There should be no title before the registration. + std::vector title = GetRegistrationUserDataByKeyPrefix( + sw_id, background_fetch::kTitleKeyPrefix); + EXPECT_TRUE(title.empty()); + // Create a single registration. CreateRegistration(registration_id, requests, options, &error); EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE); // Verify that the title can be retrieved. - auto metadata = GetMetadata(sw_id, origin(), kExampleDeveloperId, &error); - ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE); - ASSERT_TRUE(metadata); - EXPECT_EQ(metadata->ui_title(), kInitialTitle); + title = GetRegistrationUserDataByKeyPrefix(sw_id, + background_fetch::kTitleKeyPrefix); + EXPECT_EQ(title.size(), 1u); + ASSERT_EQ(title.front(), kInitialTitle); // Update the title. UpdateRegistrationUI(registration_id, kUpdatedTitle, &error); @@ -520,10 +710,10 @@ TEST_P(BackgroundFetchDataManagerTest, UpdateRegistrationUI) { RestartDataManagerFromPersistentStorage(); // After a restart, GetMetadata should find the new title. - metadata = GetMetadata(sw_id, origin(), kExampleDeveloperId, &error); - ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE); - ASSERT_TRUE(metadata); - EXPECT_EQ(metadata->ui_title(), kUpdatedTitle); + title = GetRegistrationUserDataByKeyPrefix(sw_id, + background_fetch::kTitleKeyPrefix); + EXPECT_EQ(title.size(), 1u); + ASSERT_EQ(title.front(), kUpdatedTitle); } TEST_P(BackgroundFetchDataManagerTest, CreateAndDeleteRegistration) { @@ -592,6 +782,254 @@ TEST_P(BackgroundFetchDataManagerTest, CreateAndDeleteRegistration) { EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE); } +TEST_P(BackgroundFetchDataManagerTest, PopNextRequestAndMarkAsComplete) { + // This test only applies to persistent storage. + if (registration_storage_ == + BackgroundFetchRegistrationStorage::kNonPersistent) + return; + + int64_t sw_id = RegisterServiceWorker(); + ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id); + + scoped_refptr request_info; + + BackgroundFetchRegistrationId registration_id( + sw_id, origin(), kExampleDeveloperId, kExampleUniqueId); + + // There registration hasn't been created yet, so there are no pending + // requests. + PopNextRequest(registration_id, &request_info); + EXPECT_FALSE(request_info); + EXPECT_EQ( + GetRequestStats(sw_id), + (ResponseStateStats{0 /* pending_requests */, 0 /* active_requests */, + 0 /* completed_requests */})); + + std::vector requests(2u); + BackgroundFetchOptions options; + blink::mojom::BackgroundFetchError error; + + CreateRegistration(registration_id, requests, options, &error); + EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE); + EXPECT_EQ( + GetRequestStats(sw_id), + (ResponseStateStats{2 /* pending_requests */, 0 /* active_requests */, + 0 /* completed_requests */})); + + // Popping should work now. + PopNextRequest(registration_id, &request_info); + EXPECT_TRUE(request_info); + EXPECT_EQ(request_info->request_index(), 0); + EXPECT_FALSE(request_info->download_guid().empty()); + EXPECT_EQ( + GetRequestStats(sw_id), + (ResponseStateStats{1 /* pending_requests */, 1 /* active_requests */, + 0 /* completed_requests */})); + + // Mark as complete. + AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get()); + MarkRequestAsComplete(registration_id, request_info.get()); + ASSERT_EQ( + GetRequestStats(sw_id), + (ResponseStateStats{1 /* pending_requests */, 0 /* active_requests */, + 1 /* completed_requests */})); + + RestartDataManagerFromPersistentStorage(); + + PopNextRequest(registration_id, &request_info); + EXPECT_TRUE(request_info); + EXPECT_EQ(request_info->request_index(), 1); + EXPECT_FALSE(request_info->download_guid().empty()); + EXPECT_EQ( + GetRequestStats(sw_id), + (ResponseStateStats{0 /* pending_requests */, 1 /* active_requests */, + 1 /* completed_requests */})); + + // Mark as complete. + AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get()); + MarkRequestAsComplete(registration_id, request_info.get()); + ASSERT_EQ( + GetRequestStats(sw_id), + (ResponseStateStats{0 /* pending_requests */, 0 /* active_requests */, + 2 /* completed_requests */})); + + // We are out of pending requests. + PopNextRequest(registration_id, &request_info); + EXPECT_FALSE(request_info); + EXPECT_EQ( + GetRequestStats(sw_id), + (ResponseStateStats{0 /* pending_requests */, 0 /* active_requests */, + 2 /* completed_requests */})); +} + +TEST_P(BackgroundFetchDataManagerTest, GetSettledFetchesForRegistration) { + // This test only applies to persistent storage. + if (registration_storage_ == + BackgroundFetchRegistrationStorage::kNonPersistent) + return; + + int64_t sw_id = RegisterServiceWorker(); + ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id); + + std::vector requests(2u); + BackgroundFetchOptions options; + blink::mojom::BackgroundFetchError error; + BackgroundFetchRegistrationId registration_id( + sw_id, origin(), kExampleDeveloperId, kExampleUniqueId); + + CreateRegistration(registration_id, requests, options, &error); + ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE); + EXPECT_EQ( + GetRequestStats(sw_id), + (ResponseStateStats{2 /* pending_requests */, 0 /* active_requests */, + 0 /* completed_requests */})); + + // Nothing is downloaded yet. + bool succeeded = false; + std::vector settled_fetches; + GetSettledFetchesForRegistration(registration_id, &error, &succeeded, + &settled_fetches); + EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE); + EXPECT_TRUE(succeeded); + EXPECT_EQ(settled_fetches.size(), 0u); + + for (size_t i = 0; i < requests.size(); i++) { + scoped_refptr request_info; + PopNextRequest(registration_id, &request_info); + ASSERT_TRUE(request_info); + AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get()); + MarkRequestAsComplete(registration_id, request_info.get()); + } + + RestartDataManagerFromPersistentStorage(); + + EXPECT_EQ( + GetRequestStats(sw_id), + (ResponseStateStats{0 /* pending_requests */, 0 /* active_requests */, + requests.size() /* completed_requests */})); + + GetSettledFetchesForRegistration(registration_id, &error, &succeeded, + &settled_fetches); + + EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE); + // We are marking the responses as failed in Download Manager. + EXPECT_FALSE(succeeded); + EXPECT_EQ(settled_fetches.size(), requests.size()); +} + +TEST_P(BackgroundFetchDataManagerTest, GetNumCompletedRequests) { + int64_t sw_id = RegisterServiceWorker(); + ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id); + + BackgroundFetchRegistrationId registration_id( + sw_id, origin(), kExampleDeveloperId, kExampleUniqueId); + + // The requests are default-initialized, but valid. + std::vector requests(2u); + BackgroundFetchOptions options; + blink::mojom::BackgroundFetchError error; + + CreateRegistration(registration_id, requests, options, &error); + + size_t num_completed = 0u; + + GetNumCompletedRequests(registration_id, &num_completed); + EXPECT_EQ(num_completed, 0u); + + scoped_refptr request_info; + // Download and store first request. + PopNextRequest(registration_id, &request_info); + ASSERT_TRUE(request_info); + AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get()); + MarkRequestAsComplete(registration_id, request_info.get()); + + GetNumCompletedRequests(registration_id, &num_completed); + EXPECT_EQ(num_completed, 1u); + + RestartDataManagerFromPersistentStorage(); + + GetNumCompletedRequests(registration_id, &num_completed); + EXPECT_EQ(num_completed, 1u); + + // Download and store second request. + PopNextRequest(registration_id, &request_info); + ASSERT_TRUE(request_info); + AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get()); + MarkRequestAsComplete(registration_id, request_info.get()); + + GetNumCompletedRequests(registration_id, &num_completed); + EXPECT_EQ(num_completed, 2u); +} + +TEST_P(BackgroundFetchDataManagerTest, GetNumRequestsTask) { + // This test only applies to persistent storage. + if (registration_storage_ == + BackgroundFetchRegistrationStorage::kNonPersistent) + return; + + int64_t sw_id = RegisterServiceWorker(); + ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id); + + BackgroundFetchRegistrationId registration_id( + sw_id, origin(), kExampleDeveloperId, kExampleUniqueId); + BackgroundFetchOptions options; + blink::mojom::BackgroundFetchError error; + + CreateRegistration(registration_id, {ServiceWorkerFetchRequest()}, options, + &error); + + size_t size = 0u; + GetNumRequestsTask(registration_id, background_fetch::RequestType::kAny, + &size); + EXPECT_EQ(size, 1u); // Total requests is 1. + GetNumRequestsTask(registration_id, background_fetch::RequestType::kPending, + &size); + EXPECT_EQ(size, 1u); // Total pending requests is 1. + GetNumRequestsTask(registration_id, background_fetch::RequestType::kActive, + &size); + EXPECT_EQ(size, 0u); // No active requests. + GetNumRequestsTask(registration_id, background_fetch::RequestType::kCompleted, + &size); + EXPECT_EQ(size, 0u); // No complete requests. + + scoped_refptr request_info; + // Download and store first request. + PopNextRequest(registration_id, &request_info); + ASSERT_TRUE(request_info); + + GetNumRequestsTask(registration_id, background_fetch::RequestType::kAny, + &size); + EXPECT_EQ(size, 1u); // Total requests is 1. + GetNumRequestsTask(registration_id, background_fetch::RequestType::kPending, + &size); + EXPECT_EQ(size, 0u); // Pending requests moved to active. + GetNumRequestsTask(registration_id, background_fetch::RequestType::kActive, + &size); + EXPECT_EQ(size, 1u); // Request is active. + GetNumRequestsTask(registration_id, background_fetch::RequestType::kCompleted, + &size); + EXPECT_EQ(size, 0u); // No complete requests. + + AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get()); + MarkRequestAsComplete(registration_id, request_info.get()); + + GetNumRequestsTask(registration_id, background_fetch::RequestType::kActive, + &size); + EXPECT_EQ(size, 0u); // No active requests. + GetNumRequestsTask(registration_id, background_fetch::RequestType::kCompleted, + &size); + EXPECT_EQ(size, 1u); // Request is complete. + + RestartDataManagerFromPersistentStorage(); + + GetNumRequestsTask(registration_id, background_fetch::RequestType::kCompleted, + &size); + EXPECT_EQ(size, 1u); + GetNumRequestsTask(registration_id, background_fetch::RequestType::kAny, + &size); + EXPECT_EQ(size, 1u); // Total requests is still 1. +} + TEST_P(BackgroundFetchDataManagerTest, Cleanup) { // Tests that the BackgroundFetchDataManager cleans up registrations // marked for deletion. @@ -605,14 +1043,11 @@ TEST_P(BackgroundFetchDataManagerTest, Cleanup) { BackgroundFetchRegistrationId registration_id( sw_id, origin(), kExampleDeveloperId, kExampleUniqueId); + // The requests are default-initialized, but valid. std::vector requests(2u); BackgroundFetchOptions options; blink::mojom::BackgroundFetchError error; - size_t expected_inactive_data_count = - kUserDataKeysPerInactiveRegistration + - requests.size() * kUserDataKeysPerInactiveRequest; - if (registration_storage_ == BackgroundFetchRegistrationStorage::kPersistent) { EXPECT_EQ( @@ -623,6 +1058,15 @@ TEST_P(BackgroundFetchDataManagerTest, Cleanup) { CreateRegistration(registration_id, requests, options, &error); ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE); + if (registration_storage_ == + BackgroundFetchRegistrationStorage::kPersistent) { + // We expect as many pending entries as there are requests. + EXPECT_EQ(requests.size(), + GetRegistrationUserDataByKeyPrefix( + sw_id, background_fetch::kPendingRequestKeyPrefix) + .size()); + } + // And deactivate it. MarkRegistrationForDeletion(registration_id, &error); ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE); @@ -631,8 +1075,13 @@ TEST_P(BackgroundFetchDataManagerTest, Cleanup) { if (registration_storage_ == BackgroundFetchRegistrationStorage::kPersistent) { + // Pending Requests should be deleted after marking a registration for + // deletion. + EXPECT_EQ(0u, GetRegistrationUserDataByKeyPrefix( + sw_id, background_fetch::kPendingRequestKeyPrefix) + .size()); EXPECT_EQ( - expected_inactive_data_count, + 2u, // Metadata proto + title. GetRegistrationUserDataByKeyPrefix(sw_id, kUserDataPrefix).size()); } diff --git a/chromium/content/browser/background_fetch/background_fetch_delegate_proxy.cc b/chromium/content/browser/background_fetch/background_fetch_delegate_proxy.cc index cec58a9ba29..99679a6d0f1 100644 --- a/chromium/content/browser/background_fetch/background_fetch_delegate_proxy.cc +++ b/chromium/content/browser/background_fetch/background_fetch_delegate_proxy.cc @@ -9,6 +9,7 @@ #include "components/download/public/common/download_item.h" #include "components/download/public/common/download_url_parameters.h" #include "content/browser/background_fetch/background_fetch_job_controller.h" +#include "content/public/browser/background_fetch_description.h" #include "content/public/browser/background_fetch_response.h" #include "content/public/browser/download_manager.h" #include "ui/gfx/geometry/size.h" @@ -63,19 +64,12 @@ class BackgroundFetchDelegateProxy::Core } } - void CreateDownloadJob(const std::string& job_unique_id, - const std::string& title, - const url::Origin& origin, - const SkBitmap& icon, - int completed_parts, - int total_parts, - const std::vector& current_guids) { + void CreateDownloadJob( + std::unique_ptr fetch_description) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (delegate_) { - delegate_->CreateDownloadJob(job_unique_id, title, origin, icon, - completed_parts, total_parts, current_guids); - } + if (delegate_) + delegate_->CreateDownloadJob(std::move(fetch_description)); } void StartRequest(const std::string& job_unique_id, @@ -150,7 +144,8 @@ class BackgroundFetchDelegateProxy::Core } // BackgroundFetchDelegate::Client implementation: - void OnJobCancelled(const std::string& job_unique_id) override; + void OnJobCancelled(const std::string& job_unique_id, + BackgroundFetchReasonToAbort reason_to_abort) override; void OnDownloadUpdated(const std::string& job_unique_id, const std::string& guid, uint64_t bytes_downloaded) override; @@ -178,12 +173,13 @@ class BackgroundFetchDelegateProxy::Core }; void BackgroundFetchDelegateProxy::Core::OnJobCancelled( - const std::string& job_unique_id) { + const std::string& job_unique_id, + BackgroundFetchReasonToAbort reason_to_abort) { DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce(&BackgroundFetchDelegateProxy::OnJobCancelled, io_parent_, - job_unique_id)); + job_unique_id, reason_to_abort)); } void BackgroundFetchDelegateProxy::Core::OnDownloadUpdated( @@ -261,24 +257,17 @@ void BackgroundFetchDelegateProxy::GetIconDisplaySize( } void BackgroundFetchDelegateProxy::CreateDownloadJob( - const std::string& job_unique_id, - const std::string& title, - const url::Origin& origin, - const SkBitmap& icon, base::WeakPtr controller, - int completed_parts, - int total_parts, - const std::vector& current_guids) { + std::unique_ptr fetch_description) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(!job_details_map_.count(job_unique_id)); - job_details_map_.emplace(job_unique_id, JobDetails(controller)); + DCHECK(!job_details_map_.count(fetch_description->job_unique_id)); + job_details_map_.emplace(fetch_description->job_unique_id, + JobDetails(controller)); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::BindOnce(&Core::CreateDownloadJob, ui_core_ptr_, job_unique_id, - title, origin, icon, completed_parts, total_parts, - current_guids)); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::BindOnce(&Core::CreateDownloadJob, ui_core_ptr_, + std::move(fetch_description))); } void BackgroundFetchDelegateProxy::StartRequest( @@ -319,8 +308,12 @@ void BackgroundFetchDelegateProxy::Abort(const std::string& job_unique_id) { } void BackgroundFetchDelegateProxy::OnJobCancelled( - const std::string& job_unique_id) { + const std::string& job_unique_id, + BackgroundFetchReasonToAbort reason_to_abort) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(reason_to_abort == BackgroundFetchReasonToAbort::CANCELLED_FROM_UI || + reason_to_abort == + BackgroundFetchReasonToAbort::TOTAL_DOWNLOAD_SIZE_EXCEEDED); // TODO(delphick): The controller may not exist as persistence is not yet // implemented. @@ -330,7 +323,7 @@ void BackgroundFetchDelegateProxy::OnJobCancelled( JobDetails& job_details = job_details_iter->second; if (job_details.controller) - job_details.controller->AbortFromUser(); + job_details.controller->Abort(reason_to_abort); } void BackgroundFetchDelegateProxy::DidStartRequest( diff --git a/chromium/content/browser/background_fetch/background_fetch_delegate_proxy.h b/chromium/content/browser/background_fetch/background_fetch_delegate_proxy.h index 3b905d3631d..744278a5d18 100644 --- a/chromium/content/browser/background_fetch/background_fetch_delegate_proxy.h +++ b/chromium/content/browser/background_fetch/background_fetch_delegate_proxy.h @@ -16,11 +16,10 @@ #include "base/memory/weak_ptr.h" #include "content/browser/background_fetch/background_fetch_request_info.h" #include "content/public/browser/background_fetch_delegate.h" +#include "content/public/browser/background_fetch_description.h" #include "content/public/browser/background_fetch_response.h" #include "content/public/browser/browser_thread.h" -class SkBitmap; - namespace content { // Proxy class for passing messages between BackgroundFetchJobControllers on the @@ -45,8 +44,8 @@ class CONTENT_EXPORT BackgroundFetchDelegateProxy { virtual void DidCompleteRequest( const scoped_refptr& request) = 0; - // Called when the user aborts a Background Fetch registration. - virtual void AbortFromUser() = 0; + // Called when the delegate aborts a Background Fetch registration. + virtual void Abort(BackgroundFetchReasonToAbort) = 0; virtual ~Controller() {} }; @@ -59,22 +58,19 @@ class CONTENT_EXPORT BackgroundFetchDelegateProxy { void GetIconDisplaySize( BackgroundFetchDelegate::GetIconDisplaySizeCallback callback); - // Creates a new download grouping identified by |job_unique_id|. Further - // downloads started by StartRequest will also use this |job_unique_id| so - // that a notification can be updated with the current status. If the download - // was already started in a previous browser session, then |current_guids| - // should contain the GUIDs of in progress downloads, while completed - // downloads are recorded in |completed_parts|. - // Should only be called from the Controller (on the IO - // thread). - void CreateDownloadJob(const std::string& job_unique_id, - const std::string& title, - const url::Origin& origin, - const SkBitmap& icon, - base::WeakPtr controller, - int completed_parts, - int total_parts, - const std::vector& current_guids); + // Creates a new download grouping described by |fetch_description|. Further + // downloads started by StartRequest will also use + // |fetch_description.job_unique_id| so that a notification can be updated + // with the current status. If the download was already started in a previous + // browser session, then |fetch_description.current_guids| should contain the + // GUIDs of in progress downloads, while completed downloads are recorded in + // |fetch_description.completed_parts|. The size of the completed parts is + // recorded in |fetch_description.completed_parts_size| and total download + // size is stored in |fetch_description.total_parts_size|. Should only be + // called from the Controller (on the IO thread). + void CreateDownloadJob( + base::WeakPtr controller, + std::unique_ptr fetch_description); // Requests that the download manager start fetching |request|. // Should only be called from the Controller (on the IO @@ -88,8 +84,8 @@ class CONTENT_EXPORT BackgroundFetchDelegateProxy { void UpdateUI(const std::string& job_unique_id, const std::string& title); // Aborts in progress downloads for the given registration. Called from the - // Controller (on the IO thread) after it is aborted, either by the user or - // website. May occur even if all requests already called OnDownloadComplete. + // Controller (on the IO thread) after it is aborted. May occur even if all + // requests already called OnDownloadComplete. void Abort(const std::string& job_unique_id); private: @@ -97,7 +93,8 @@ class CONTENT_EXPORT BackgroundFetchDelegateProxy { // Called when the job identified by |job_unique|id| was cancelled by the // delegate. Should only be called on the IO thread. - void OnJobCancelled(const std::string& job_unique_id); + void OnJobCancelled(const std::string& job_unique_id, + BackgroundFetchReasonToAbort reason_to_abort); // Called when the download identified by |guid| has succeeded/failed/aborted. // Should only be called on the IO thread. diff --git a/chromium/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc b/chromium/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc index 9091e65d5c5..788e87a46a8 100644 --- a/chromium/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc +++ b/chromium/content/browser/background_fetch/background_fetch_delegate_proxy_unittest.cc @@ -11,6 +11,7 @@ #include "base/run_loop.h" #include "content/browser/background_fetch/background_fetch_test_base.h" #include "content/public/browser/background_fetch_delegate.h" +#include "content/public/browser/background_fetch_description.h" #include "content/public/browser/background_fetch_response.h" #include "content/public/browser/browser_thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -33,13 +34,7 @@ class FakeBackgroundFetchDelegate : public BackgroundFetchDelegate { std::move(callback).Run(gfx::Size(kIconDisplaySize, kIconDisplaySize)); } void CreateDownloadJob( - const std::string& job_unique_id, - const std::string& title, - const url::Origin& origin, - const SkBitmap& icon, - int completed_parts, - int total_parts, - const std::vector& current_guids) override {} + std::unique_ptr fetch_description) override {} void DownloadUrl(const std::string& job_unique_id, const std::string& guid, const std::string& method, @@ -108,7 +103,7 @@ class FakeController : public BackgroundFetchDelegateProxy::Controller { request_completed_ = true; } - void AbortFromUser() override {} + void Abort(BackgroundFetchReasonToAbort reason_to_abort) override {} bool request_started_ = false; bool request_completed_ = false; @@ -154,9 +149,13 @@ TEST_F(BackgroundFetchDelegateProxyTest, StartRequest) { EXPECT_FALSE(controller.request_started_); EXPECT_FALSE(controller.request_completed_); - delegate_proxy_.CreateDownloadJob( + auto fetch_description = std::make_unique( kExampleUniqueId, "Job 1", url::Origin(), SkBitmap(), - controller.weak_ptr_factory_.GetWeakPtr(), 0, 1, {}); + 0 /* completed_parts */, 1 /* total_parts */, + 0 /* completed_parts_size */, 0 /* total_parts_size */, + std::vector()); + delegate_proxy_.CreateDownloadJob(controller.weak_ptr_factory_.GetWeakPtr(), + std::move(fetch_description)); delegate_proxy_.StartRequest(kExampleUniqueId, url::Origin(), request); base::RunLoop().RunUntilIdle(); @@ -174,9 +173,13 @@ TEST_F(BackgroundFetchDelegateProxyTest, StartRequest_NotCompleted) { EXPECT_FALSE(controller.request_completed_); delegate_.set_complete_downloads(false); - delegate_proxy_.CreateDownloadJob( + auto fetch_description = std::make_unique( kExampleUniqueId, "Job 1", url::Origin(), SkBitmap(), - controller.weak_ptr_factory_.GetWeakPtr(), 0, 1, {}); + 0 /* completed_parts */, 1 /* total_parts */, + 0 /* completed_parts_size */, 0 /* total_parts_size */, + std::vector()); + delegate_proxy_.CreateDownloadJob(controller.weak_ptr_factory_.GetWeakPtr(), + std::move(fetch_description)); delegate_proxy_.StartRequest(kExampleUniqueId, url::Origin(), request); base::RunLoop().RunUntilIdle(); @@ -198,13 +201,21 @@ TEST_F(BackgroundFetchDelegateProxyTest, Abort) { EXPECT_FALSE(controller2.request_started_); EXPECT_FALSE(controller2.request_completed_); - delegate_proxy_.CreateDownloadJob( + auto fetch_description1 = std::make_unique( kExampleUniqueId, "Job 1", url::Origin(), SkBitmap(), - controller.weak_ptr_factory_.GetWeakPtr(), 0, 1, {}); + 0 /* completed_parts */, 1 /* total_parts */, + 0 /* completed_parts_size */, 0 /* total_parts_size */, + std::vector()); + delegate_proxy_.CreateDownloadJob(controller.weak_ptr_factory_.GetWeakPtr(), + std::move(fetch_description1)); - delegate_proxy_.CreateDownloadJob( + auto fetch_description2 = std::make_unique( kExampleUniqueId2, "Job 2", url::Origin(), SkBitmap(), - controller2.weak_ptr_factory_.GetWeakPtr(), 0, 1, {}); + 0 /* completed_parts */, 1 /* total_parts */, + 0 /* completed_parts_size */, 0 /* total_parts_size */, + std::vector()); + delegate_proxy_.CreateDownloadJob(controller2.weak_ptr_factory_.GetWeakPtr(), + std::move(fetch_description2)); delegate_proxy_.StartRequest(kExampleUniqueId, url::Origin(), request); delegate_proxy_.StartRequest(kExampleUniqueId2, url::Origin(), request2); diff --git a/chromium/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.cc b/chromium/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.cc index dc606892ca4..4f7182d5511 100644 --- a/chromium/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.cc +++ b/chromium/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.cc @@ -21,9 +21,13 @@ BackgroundFetchEmbeddedWorkerTestHelper:: void BackgroundFetchEmbeddedWorkerTestHelper::OnBackgroundFetchAbortEvent( const std::string& developer_id, + const std::string& unique_id, + const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchAbortEventCallback callback) { last_developer_id_ = developer_id; + last_unique_id_ = unique_id; + last_fetches_ = fetches; if (fail_abort_event_) { std::move(callback).Run(blink::mojom::ServiceWorkerEventStatus::REJECTED, @@ -59,10 +63,12 @@ void BackgroundFetchEmbeddedWorkerTestHelper::OnBackgroundFetchClickEvent( void BackgroundFetchEmbeddedWorkerTestHelper::OnBackgroundFetchFailEvent( const std::string& developer_id, + const std::string& unique_id, const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchFailEventCallback callback) { last_developer_id_ = developer_id; + last_unique_id_ = unique_id; last_fetches_ = fetches; if (fail_fetch_fail_event_) { diff --git a/chromium/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.h b/chromium/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.h index 7e12d00538f..248b9113586 100644 --- a/chromium/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.h +++ b/chromium/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.h @@ -68,6 +68,8 @@ class BackgroundFetchEmbeddedWorkerTestHelper // EmbeddedWorkerTestHelper overrides: void OnBackgroundFetchAbortEvent( const std::string& developer_id, + const std::string& unique_id, + const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchAbortEventCallback callback) override; void OnBackgroundFetchClickEvent( @@ -77,6 +79,7 @@ class BackgroundFetchEmbeddedWorkerTestHelper DispatchBackgroundFetchClickEventCallback callback) override; void OnBackgroundFetchFailEvent( const std::string& developer_id, + const std::string& unique_id, const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchFailEventCallback callback) override; diff --git a/chromium/content/browser/background_fetch/background_fetch_event_dispatcher.cc b/chromium/content/browser/background_fetch/background_fetch_event_dispatcher.cc index 7a52ccc795b..452c9524c84 100644 --- a/chromium/content/browser/background_fetch/background_fetch_event_dispatcher.cc +++ b/chromium/content/browser/background_fetch/background_fetch_event_dispatcher.cc @@ -76,6 +76,7 @@ BackgroundFetchEventDispatcher::~BackgroundFetchEventDispatcher() { void BackgroundFetchEventDispatcher::DispatchBackgroundFetchAbortEvent( const BackgroundFetchRegistrationId& registration_id, + const std::vector& fetches, base::OnceClosure finished_closure) { DCHECK_CURRENTLY_ON(BrowserThread::IO); LoadServiceWorkerRegistrationForDispatch( @@ -83,16 +84,19 @@ void BackgroundFetchEventDispatcher::DispatchBackgroundFetchAbortEvent( std::move(finished_closure), base::Bind( &BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchAbortEvent, - registration_id.developer_id())); + registration_id.developer_id(), registration_id.unique_id(), + fetches)); } void BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchAbortEvent( const std::string& developer_id, + const std::string& unique_id, + const std::vector& fetches, scoped_refptr service_worker_version, int request_id) { DCHECK(service_worker_version); service_worker_version->event_dispatcher()->DispatchBackgroundFetchAbortEvent( - developer_id, + developer_id, unique_id, fetches, service_worker_version->CreateSimpleEventCallback(request_id)); } @@ -130,17 +134,19 @@ void BackgroundFetchEventDispatcher::DispatchBackgroundFetchFailEvent( std::move(finished_closure), base::Bind( &BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchFailEvent, - registration_id.developer_id(), fetches)); + registration_id.developer_id(), registration_id.unique_id(), + fetches)); } void BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchFailEvent( const std::string& developer_id, + const std::string& unique_id, const std::vector& fetches, scoped_refptr service_worker_version, int request_id) { DCHECK(service_worker_version); service_worker_version->event_dispatcher()->DispatchBackgroundFetchFailEvent( - developer_id, fetches, + developer_id, unique_id, fetches, service_worker_version->CreateSimpleEventCallback(request_id)); } diff --git a/chromium/content/browser/background_fetch/background_fetch_event_dispatcher.h b/chromium/content/browser/background_fetch/background_fetch_event_dispatcher.h index 73bbd100818..7194e67e967 100644 --- a/chromium/content/browser/background_fetch/background_fetch_event_dispatcher.h +++ b/chromium/content/browser/background_fetch/background_fetch_event_dispatcher.h @@ -45,6 +45,7 @@ class CONTENT_EXPORT BackgroundFetchEventDispatcher { // background fetch was aborted by the user or another external event. void DispatchBackgroundFetchAbortEvent( const BackgroundFetchRegistrationId& registration_id, + const std::vector& fetches, base::OnceClosure finished_closure); // Dispatches the `backgroundfetchclick` event, which indicates that the user @@ -113,6 +114,8 @@ class CONTENT_EXPORT BackgroundFetchEventDispatcher { // Methods that actually invoke the event on an activated Service Worker. static void DoDispatchBackgroundFetchAbortEvent( const std::string& developer_id, + const std::string& unique_id, + const std::vector& fetches, scoped_refptr service_worker_version, int request_id); static void DoDispatchBackgroundFetchClickEvent( @@ -122,6 +125,7 @@ class CONTENT_EXPORT BackgroundFetchEventDispatcher { int request_id); static void DoDispatchBackgroundFetchFailEvent( const std::string& developer_id, + const std::string& unique_id, const std::vector& fetches, scoped_refptr service_worker_version, int request_id); diff --git a/chromium/content/browser/background_fetch/background_fetch_event_dispatcher_unittest.cc b/chromium/content/browser/background_fetch/background_fetch_event_dispatcher_unittest.cc index da84755ce0c..aac9e2f1f68 100644 --- a/chromium/content/browser/background_fetch/background_fetch_event_dispatcher_unittest.cc +++ b/chromium/content/browser/background_fetch/background_fetch_event_dispatcher_unittest.cc @@ -44,8 +44,8 @@ TEST_F(BackgroundFetchEventDispatcherTest, DispatchInvalidRegistration) { kExampleUniqueId); base::RunLoop run_loop; - event_dispatcher_.DispatchBackgroundFetchAbortEvent(invalid_registration_id, - run_loop.QuitClosure()); + event_dispatcher_.DispatchBackgroundFetchAbortEvent( + invalid_registration_id, {}, run_loop.QuitClosure()); run_loop.Run(); @@ -62,14 +62,17 @@ TEST_F(BackgroundFetchEventDispatcherTest, DispatchAbortEvent) { ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, service_worker_registration_id); + std::vector fetches; + fetches.push_back(BackgroundFetchSettledFetch()); + BackgroundFetchRegistrationId registration_id(service_worker_registration_id, origin(), kExampleDeveloperId, kExampleUniqueId); { base::RunLoop run_loop; - event_dispatcher_.DispatchBackgroundFetchAbortEvent(registration_id, - run_loop.QuitClosure()); + event_dispatcher_.DispatchBackgroundFetchAbortEvent( + registration_id, fetches, run_loop.QuitClosure()); run_loop.Run(); } @@ -77,6 +80,10 @@ TEST_F(BackgroundFetchEventDispatcherTest, DispatchAbortEvent) { ASSERT_TRUE(embedded_worker_test_helper()->last_developer_id().has_value()); EXPECT_EQ(kExampleDeveloperId, embedded_worker_test_helper()->last_developer_id().value()); + ASSERT_TRUE(embedded_worker_test_helper()->last_unique_id().has_value()); + EXPECT_EQ(kExampleUniqueId, + embedded_worker_test_helper()->last_unique_id().value()); + ASSERT_TRUE(embedded_worker_test_helper()->last_fetches().has_value()); histogram_tester_.ExpectUniqueSample( "BackgroundFetch.EventDispatchResult.AbortEvent", @@ -90,8 +97,8 @@ TEST_F(BackgroundFetchEventDispatcherTest, DispatchAbortEvent) { { base::RunLoop run_loop; - event_dispatcher_.DispatchBackgroundFetchAbortEvent(second_registration_id, - run_loop.QuitClosure()); + event_dispatcher_.DispatchBackgroundFetchAbortEvent( + second_registration_id, fetches, run_loop.QuitClosure()); run_loop.Run(); } @@ -99,6 +106,10 @@ TEST_F(BackgroundFetchEventDispatcherTest, DispatchAbortEvent) { ASSERT_TRUE(embedded_worker_test_helper()->last_developer_id().has_value()); EXPECT_EQ(kExampleDeveloperId2, embedded_worker_test_helper()->last_developer_id().value()); + ASSERT_TRUE(embedded_worker_test_helper()->last_unique_id().has_value()); + EXPECT_EQ(kExampleUniqueId2, + embedded_worker_test_helper()->last_unique_id().value()); + ASSERT_TRUE(embedded_worker_test_helper()->last_fetches().has_value()); histogram_tester_.ExpectBucketCount( "BackgroundFetch.EventDispatchResult.AbortEvent", diff --git a/chromium/content/browser/background_fetch/background_fetch_job_controller.cc b/chromium/content/browser/background_fetch/background_fetch_job_controller.cc index 83f1fa69711..aac1856ce92 100644 --- a/chromium/content/browser/background_fetch/background_fetch_job_controller.cc +++ b/chromium/content/browser/background_fetch/background_fetch_job_controller.cc @@ -45,10 +45,17 @@ void BackgroundFetchJobController::InitializeRequestStatus( completed_downloads_ = completed_downloads; total_downloads_ = total_downloads; - delegate_proxy_->CreateDownloadJob(registration_id().unique_id(), - options_.title, registration_id().origin(), - icon_, GetWeakPtr(), completed_downloads, - total_downloads, outstanding_guids); + // TODO(nator): Update this when we support uploads. + int total_downloads_size = options_.download_total; + + auto fetch_description = std::make_unique( + registration_id().unique_id(), options_.title, registration_id().origin(), + icon_, completed_downloads, total_downloads, + complete_requests_downloaded_bytes_cache_, total_downloads_size, + outstanding_guids); + + delegate_proxy_->CreateDownloadJob(GetWeakPtr(), + std::move(fetch_description)); } BackgroundFetchJobController::~BackgroundFetchJobController() { @@ -107,12 +114,6 @@ void BackgroundFetchJobController::DidCompleteRequest( request_manager_->MarkRequestAsComplete(registration_id(), request.get()); } -void BackgroundFetchJobController::AbortFromUser() { - // Aborts from user come via the BackgroundFetchDelegate, which will have - // already cancelled the download. - Abort(false /* cancel_download */); -} - void BackgroundFetchJobController::UpdateUI(const std::string& title) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -126,21 +127,23 @@ uint64_t BackgroundFetchJobController::GetInProgressDownloadedBytes() { return sum; } -void BackgroundFetchJobController::Abort() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - Abort(true /* cancel_download */); -} - -void BackgroundFetchJobController::Abort(bool cancel_download) { - if (cancel_download) - delegate_proxy_->Abort(registration_id().unique_id()); +void BackgroundFetchJobController::Abort( + BackgroundFetchReasonToAbort reason_to_abort) { + delegate_proxy_->Abort(registration_id().unique_id()); std::vector aborted_guids; for (const auto& pair : active_request_download_bytes_) aborted_guids.push_back(pair.first); request_manager_->OnJobAborted(registration_id(), std::move(aborted_guids)); - Finish(true /* aborted */); + if (reason_to_abort != BackgroundFetchReasonToAbort::ABORTED_BY_DEVELOPER) { + // Don't call Finish() here, so that we don't mark data for deletion while + // there are active fetches. + // Once the controller finishes processing, this function will be called + // again. (BackgroundFetchScheduler's finished_callback_ will call + // BackgroundFetchJobController::Abort() with |cancel_download| set to + // true.) + Finish(reason_to_abort); + } } } // namespace content diff --git a/chromium/content/browser/background_fetch/background_fetch_job_controller.h b/chromium/content/browser/background_fetch/background_fetch_job_controller.h index 02d5378318a..bc7eb93c844 100644 --- a/chromium/content/browser/background_fetch/background_fetch_job_controller.h +++ b/chromium/content/browser/background_fetch/background_fetch_job_controller.h @@ -41,7 +41,7 @@ class CONTENT_EXPORT BackgroundFetchJobController final public: using FinishedCallback = base::OnceCallback; + BackgroundFetchReasonToAbort)>; using ProgressCallback = base::RepeatingCallback& request) override; - void AbortFromUser() override; // BackgroundFetchScheduler::Controller implementation: bool HasMoreRequests() override; void StartRequest(scoped_refptr request) override; + void Abort(BackgroundFetchReasonToAbort reason_to_abort) override; private: - // Aborts a job updating the registration with the new state. If - // |cancel_download| is true, the ongoing download is also cancelled - // (otherwise it assumes that has already happened). - void Abort(bool cancel_download); // Options for the represented background fetch registration. BackgroundFetchOptions options_; diff --git a/chromium/content/browser/background_fetch/background_fetch_job_controller_unittest.cc b/chromium/content/browser/background_fetch/background_fetch_job_controller_unittest.cc index f628088aebe..f504d14b912 100644 --- a/chromium/content/browser/background_fetch/background_fetch_job_controller_unittest.cc +++ b/chromium/content/browser/background_fetch/background_fetch_job_controller_unittest.cc @@ -190,7 +190,7 @@ class BackgroundFetchJobControllerTest : public BackgroundFetchTestBase { } static void OnJobFinished(const BackgroundFetchRegistrationId&, - bool aborted) {} + BackgroundFetchReasonToAbort reason_to_abort) {} DISALLOW_COPY_AND_ASSIGN(BackgroundFetchJobControllerTest); }; @@ -268,7 +268,7 @@ TEST_F(BackgroundFetchJobControllerTest, Abort) { CreateJobController(registration_id, requests.size()); controller->StartRequest(requests[0]); - controller->AbortFromUser(); + controller->Abort(BackgroundFetchReasonToAbort::CANCELLED_FROM_UI); // Tell the delegate to abort the job as well so it doesn't send completed // messages to the JobController. delegate_->Abort(registration_id.unique_id()); diff --git a/chromium/content/browser/background_fetch/background_fetch_metrics.cc b/chromium/content/browser/background_fetch/background_fetch_metrics.cc new file mode 100644 index 00000000000..ec5979b21d1 --- /dev/null +++ b/chromium/content/browser/background_fetch/background_fetch_metrics.cc @@ -0,0 +1,27 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/background_fetch/background_fetch_metrics.h" + +#include "base/metrics/histogram_macros.h" + +namespace content { + +namespace background_fetch { + +void RecordSchedulerFinishedError(blink::mojom::BackgroundFetchError error) { + UMA_HISTOGRAM_ENUMERATION("BackgroundFetch.SchedulerFinishedError", error); +} + +void RecordRegistrationCreatedError(blink::mojom::BackgroundFetchError error) { + UMA_HISTOGRAM_ENUMERATION("BackgroundFetch.RegistrationCreatedError", error); +} + +void RecordRegistrationDeletedError(blink::mojom::BackgroundFetchError error) { + UMA_HISTOGRAM_ENUMERATION("BackgroundFetch.RegistrationDeletedError", error); +} + +} // namespace background_fetch + +} // namespace content diff --git a/chromium/content/browser/background_fetch/background_fetch_metrics.h b/chromium/content/browser/background_fetch/background_fetch_metrics.h new file mode 100644 index 00000000000..977b33bc687 --- /dev/null +++ b/chromium/content/browser/background_fetch/background_fetch_metrics.h @@ -0,0 +1,31 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_METRICS_H_ +#define CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_METRICS_H_ + +#include "third_party/blink/public/platform/modules/background_fetch/background_fetch.mojom.h" + +namespace content { + +namespace background_fetch { + +// Records the |error| status issued by the DataManager after it was requested +// to mark a Background Fetch registration for deletion. The marking is invoked +// by the scheduler controller after it is finished. +void RecordSchedulerFinishedError(blink::mojom::BackgroundFetchError error); + +// Records the |error| status issued by the DataManager after it was requested +// to create and store a new Background Fetch registration. +void RecordRegistrationCreatedError(blink::mojom::BackgroundFetchError error); + +// Records the |error| status issued by the DataManager after the storage +// associated with a registration has been completely deleted. +void RecordRegistrationDeletedError(blink::mojom::BackgroundFetchError error); + +} // namespace background_fetch + +} // namespace content + +#endif // CONTENT_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_METRICS_H_ diff --git a/chromium/content/browser/background_fetch/background_fetch_scheduler.cc b/chromium/content/browser/background_fetch/background_fetch_scheduler.cc index 1b96430ec00..62309d66bba 100644 --- a/chromium/content/browser/background_fetch/background_fetch_scheduler.cc +++ b/chromium/content/browser/background_fetch/background_fetch_scheduler.cc @@ -19,9 +19,12 @@ BackgroundFetchScheduler::Controller::Controller( BackgroundFetchScheduler::Controller::~Controller() = default; -void BackgroundFetchScheduler::Controller::Finish(bool abort) { - DCHECK(abort || !HasMoreRequests()); - std::move(finished_callback_).Run(registration_id_, abort); +void BackgroundFetchScheduler::Controller::Finish( + BackgroundFetchReasonToAbort reason_to_abort) { + DCHECK(reason_to_abort != BackgroundFetchReasonToAbort::NONE || + !HasMoreRequests()); + + std::move(finished_callback_).Run(registration_id_, reason_to_abort); } BackgroundFetchScheduler::BackgroundFetchScheduler( @@ -80,7 +83,7 @@ void BackgroundFetchScheduler::DidMarkRequestAsComplete( if (controller->HasMoreRequests()) controller_queue_.push_back(controller); else - controller->Finish(false); + controller->Finish(BackgroundFetchReasonToAbort::NONE); ScheduleDownload(); } diff --git a/chromium/content/browser/background_fetch/background_fetch_scheduler.h b/chromium/content/browser/background_fetch/background_fetch_scheduler.h index fb72f3144f9..8527582c520 100644 --- a/chromium/content/browser/background_fetch/background_fetch_scheduler.h +++ b/chromium/content/browser/background_fetch/background_fetch_scheduler.h @@ -21,6 +21,7 @@ namespace content { class BackgroundFetchRegistrationId; class BackgroundFetchRequestInfo; +enum class BackgroundFetchReasonToAbort; // Maintains a list of Controllers and chooses which ones should launch new // downloads. @@ -29,7 +30,7 @@ class CONTENT_EXPORT BackgroundFetchScheduler public: using FinishedCallback = base::OnceCallback; + BackgroundFetchReasonToAbort)>; using MarkedCompleteCallback = base::OnceCallback; // Interface for download job controllers. @@ -44,7 +45,7 @@ class CONTENT_EXPORT BackgroundFetchScheduler virtual void StartRequest( scoped_refptr request) = 0; - void Finish(bool abort); + void Finish(BackgroundFetchReasonToAbort reason_to_abort); const BackgroundFetchRegistrationId& registration_id() const { return registration_id_; diff --git a/chromium/content/browser/background_fetch/background_fetch_scheduler_unittest.cc b/chromium/content/browser/background_fetch/background_fetch_scheduler_unittest.cc index 8e917b0345e..fdee316f59d 100644 --- a/chromium/content/browser/background_fetch/background_fetch_scheduler_unittest.cc +++ b/chromium/content/browser/background_fetch/background_fetch_scheduler_unittest.cc @@ -61,7 +61,7 @@ class FakeController : public BackgroundFetchScheduler::Controller { } static void OnJobFinished(const BackgroundFetchRegistrationId&, - bool aborted) {} + BackgroundFetchReasonToAbort reason_to_abort) {} int jobs_started_ = 0; BackgroundFetchScheduler* scheduler_; diff --git a/chromium/content/browser/background_fetch/background_fetch_service_impl.cc b/chromium/content/browser/background_fetch/background_fetch_service_impl.cc index c481fb6c041..596cfaa0109 100644 --- a/chromium/content/browser/background_fetch/background_fetch_service_impl.cc +++ b/chromium/content/browser/background_fetch/background_fetch_service_impl.cc @@ -8,6 +8,7 @@ #include "base/guid.h" #include "content/browser/background_fetch/background_fetch_context.h" +#include "content/browser/background_fetch/background_fetch_metrics.h" #include "content/browser/background_fetch/background_fetch_registration_id.h" #include "content/browser/bad_message.h" #include "content/browser/storage_partition_impl.h" @@ -76,17 +77,12 @@ void BackgroundFetchServiceImpl::Fetch( const SkBitmap& icon, FetchCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!ValidateDeveloperId(developer_id)) { - std::move(callback).Run( - blink::mojom::BackgroundFetchError::INVALID_ARGUMENT, - base::nullopt /* registration */); - return; - } - - if (!ValidateRequests(requests)) { + if (!ValidateDeveloperId(developer_id) || !ValidateRequests(requests)) { std::move(callback).Run( blink::mojom::BackgroundFetchError::INVALID_ARGUMENT, base::nullopt /* registration */); + background_fetch::RecordRegistrationCreatedError( + blink::mojom::BackgroundFetchError::INVALID_ARGUMENT); return; } diff --git a/chromium/content/browser/background_fetch/background_fetch_service_unittest.cc b/chromium/content/browser/background_fetch/background_fetch_service_unittest.cc index 6e703f027a3..839e8eee9a7 100644 --- a/chromium/content/browser/background_fetch/background_fetch_service_unittest.cc +++ b/chromium/content/browser/background_fetch/background_fetch_service_unittest.cc @@ -9,6 +9,7 @@ #include "base/auto_reset.h" #include "base/macros.h" #include "base/run_loop.h" +#include "base/test/histogram_tester.h" #include "base/time/time.h" #include "content/browser/background_fetch/background_fetch_context.h" #include "content/browser/background_fetch/background_fetch_embedded_worker_test_helper.h" @@ -98,6 +99,7 @@ class BackgroundFetchServiceTest : public BackgroundFetchTestBase { DCHECK(out_error); DCHECK(out_registration); + base::HistogramTester histogram_tester; base::RunLoop run_loop; service_->Fetch( service_worker_registration_id, developer_id, requests, options, icon, @@ -107,6 +109,10 @@ class BackgroundFetchServiceTest : public BackgroundFetchTestBase { run_loop.Run(); + histogram_tester.ExpectBucketCount( + "BackgroundFetch.RegistrationCreatedError", + static_cast(*out_error), 1); + if (*out_error != blink::mojom::BackgroundFetchError::NONE) return BackgroundFetchRegistrationId(); @@ -139,6 +145,7 @@ class BackgroundFetchServiceTest : public BackgroundFetchTestBase { blink::mojom::BackgroundFetchError* out_error) { DCHECK(out_error); + base::HistogramTester histogram_tester; base::RunLoop run_loop; service_->Abort(service_worker_registration_id, developer_id, unique_id, base::BindOnce(&BackgroundFetchServiceTest::DidGetError, @@ -146,6 +153,17 @@ class BackgroundFetchServiceTest : public BackgroundFetchTestBase { run_loop.QuitClosure(), out_error)); run_loop.Run(); + + // We only delete the registration if we successfully abort. + if (*out_error == blink::mojom::BackgroundFetchError::NONE) { + // The error passed to the histogram counter is not related to this + // |*out_error|, but the result of + // BackgroundFetchDataManager::DeleteRegistration. For the purposes these + // tests, the deletion is always successful. + histogram_tester.ExpectBucketCount( + "BackgroundFetch.RegistrationDeletedError", + 0 /* blink::mojom::BackgroundFetchError::NONE */, 1); + } } // Synchronous wrapper for BackgroundFetchServiceImpl::GetRegistration(). diff --git a/chromium/content/browser/background_fetch/mock_background_fetch_delegate.cc b/chromium/content/browser/background_fetch/mock_background_fetch_delegate.cc index ffe514bd9c5..753d955a839 100644 --- a/chromium/content/browser/background_fetch/mock_background_fetch_delegate.cc +++ b/chromium/content/browser/background_fetch/mock_background_fetch_delegate.cc @@ -8,6 +8,7 @@ #include "base/files/file_util.h" #include "base/threading/thread_task_runner_handle.h" #include "content/browser/background_fetch/mock_background_fetch_delegate.h" +#include "content/public/browser/background_fetch_description.h" #include "content/public/browser/background_fetch_response.h" #include "content/public/browser/browser_thread.h" #include "net/http/http_response_headers.h" @@ -59,13 +60,7 @@ void MockBackgroundFetchDelegate::GetIconDisplaySize( GetIconDisplaySizeCallback callback) {} void MockBackgroundFetchDelegate::CreateDownloadJob( - const std::string& job_unique_id, - const std::string& title, - const url::Origin& origin, - const SkBitmap& icon, - int completed_parts, - int total_parts, - const std::vector& current_guids) {} + std::unique_ptr fetch_description) {} void MockBackgroundFetchDelegate::DownloadUrl( const std::string& job_unique_id, diff --git a/chromium/content/browser/background_fetch/mock_background_fetch_delegate.h b/chromium/content/browser/background_fetch/mock_background_fetch_delegate.h index fe63a87b786..bc685f96a56 100644 --- a/chromium/content/browser/background_fetch/mock_background_fetch_delegate.h +++ b/chromium/content/browser/background_fetch/mock_background_fetch_delegate.h @@ -66,13 +66,7 @@ class MockBackgroundFetchDelegate : public BackgroundFetchDelegate { void GetIconDisplaySize( BackgroundFetchDelegate::GetIconDisplaySizeCallback callback) override; void CreateDownloadJob( - const std::string& job_unique_id, - const std::string& title, - const url::Origin& origin, - const SkBitmap& icon, - int completed_parts, - int total_parts, - const std::vector& current_guids) override; + std::unique_ptr fetch_description) override; void DownloadUrl(const std::string& job_unique_id, const std::string& guid, const std::string& method, diff --git a/chromium/content/browser/background_fetch/storage/README.md b/chromium/content/browser/background_fetch/storage/README.md index 8292ab494e2..e21e9c327e9 100644 --- a/chromium/content/browser/background_fetch/storage/README.md +++ b/chromium/content/browser/background_fetch/storage/README.md @@ -19,17 +19,30 @@ running background fetches. key: "bgfetch_active_registration_unique_id_" value: "" ``` + ``` key: "bgfetch_registration_" value: "" ``` + +``` +key: "bgfetch_title_" +value: "" +``` + ``` -key: "bgfetch_request__" -value: "" +key: "bgfetch_pending_request__" +value: "" ``` + +``` +key: "bgfetch_active_request__" +value: "" +``` + ``` -key: "bgfetch_pending_request___" -value: "" +key: "bgfetch_completed_request__" +value: "" ``` ### Expansions @@ -42,9 +55,6 @@ introduce ambiguity. * `` is an `int` containing the index of a request within a multi-part fetch. These must be padded with zeros to ensure that the ordering is maintain when reading back from the database, e.g. `0000000000`. -* `` is the registration creation time expressed as the number -of microseconds since the unix epoch (internally stored as an `int64_t`). -Without padding with zeros, this may introduce an ordering inversion in -November 2286 and again in the year 5138, but the impact would only be on the -relative ordering with which two different fetches were scheduled. +* `` is the notification title provided by the developer. It can also +be updated by calling `BackgroundFetchUpdateEvent.updateUI`. diff --git a/chromium/content/browser/background_fetch/storage/create_metadata_task.cc b/chromium/content/browser/background_fetch/storage/create_metadata_task.cc index 18d45c6ec2a..c070e9f817b 100644 --- a/chromium/content/browser/background_fetch/storage/create_metadata_task.cc +++ b/chromium/content/browser/background_fetch/storage/create_metadata_task.cc @@ -15,11 +15,6 @@ namespace content { namespace background_fetch { -std::string RequestKey(const std::string& unique_id, int request_index) { - // Allows looking up a request by registration id and index within that. - return RequestKeyPrefix(unique_id) + base::IntToString(request_index); -} - CreateMetadataTask::CreateMetadataTask( BackgroundFetchDataManager* data_manager, const BackgroundFetchRegistrationId& registration_id, @@ -90,7 +85,7 @@ void CreateMetadataTask::InitializeMetadataProto() { metadata_proto_->set_origin(registration_id_.origin().Serialize()); metadata_proto_->set_creation_microseconds_since_unix_epoch( (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds()); - metadata_proto_->set_ui_title(options_.title); + metadata_proto_->set_num_fetches(requests_.size()); } void CreateMetadataTask::StoreMetadata() { @@ -113,17 +108,16 @@ void CreateMetadataTask::StoreMetadata() { registration_id_.unique_id()); entries.emplace_back(RegistrationKey(registration_id_.unique_id()), std::move(serialized_metadata_proto)); + entries.emplace_back(TitleKey(registration_id_.unique_id()), options_.title); // Signed integers are used for request indexes to avoid unsigned gotchas. for (int i = 0; i < base::checked_cast(requests_.size()); i++) { - // TODO(crbug.com/757760): Serialize actual values for these entries. - entries.emplace_back(RequestKey(registration_id_.unique_id(), i), - "TODO: Serialize FetchAPIRequest as value"); - entries.emplace_back( - PendingRequestKey( - metadata_proto_->creation_microseconds_since_unix_epoch(), - registration_id_.unique_id(), i), - std::string()); + proto::BackgroundFetchPendingRequest pending_request_proto; + pending_request_proto.set_unique_id(registration_id_.unique_id()); + pending_request_proto.set_request_index(i); + pending_request_proto.set_serialized_request(requests_[i].Serialize()); + entries.emplace_back(PendingRequestKey(registration_id_.unique_id(), i), + pending_request_proto.SerializeAsString()); } service_worker_context()->StoreRegistrationUserData( diff --git a/chromium/content/browser/background_fetch/storage/database_helpers.cc b/chromium/content/browser/background_fetch/storage/database_helpers.cc index be25d6d5837..cb8348e731c 100644 --- a/chromium/content/browser/background_fetch/storage/database_helpers.cc +++ b/chromium/content/browser/background_fetch/storage/database_helpers.cc @@ -23,41 +23,33 @@ std::string RegistrationKey(const std::string& unique_id) { return kRegistrationKeyPrefix + unique_id; } -std::string RequestKeyPrefix(const std::string& unique_id) { - // Allows looking up all requests within a registration. - return kRequestKeyPrefix + unique_id + kSeparator; +std::string TitleKey(const std::string& unique_id) { + return kTitleKeyPrefix + unique_id; } -std::string PendingRequestKeyPrefix( - int64_t registration_creation_microseconds_since_unix_epoch, - const std::string& unique_id) { - // These keys are ordered by the registration's creation time rather than by - // its |unique_id|, so that the highest priority pending requests in FIFO - // order can be looked up by fetching the lexicographically smallest keys. - // https://crbug.com/741609 may introduce more advanced prioritisation. - // - // Since the ordering must survive restarts, wall clock time is used, but that - // is not monotonically increasing, so the ordering is not exact, and the - // |unique_id| is appended to break ties in case the wall clock returns the - // same values more than once. - // - // On Nov 20 2286 17:46:39 the microseconds will transition from 9999999999999 - // to 10000000000000 and pending requests will briefly sort incorrectly. - return kPendingRequestKeyPrefix + - base::Int64ToString( - registration_creation_microseconds_since_unix_epoch) + - kSeparator + unique_id + kSeparator; +std::string PendingRequestKeyPrefix(const std::string& unique_id) { + return kPendingRequestKeyPrefix + unique_id + kSeparator; } -std::string PendingRequestKey( - int64_t registration_creation_microseconds_since_unix_epoch, - const std::string& unique_id, - int request_index) { - // In addition to the ordering from PendingRequestKeyPrefix, the requests - // within each registration should be prioritized according to their index. - return PendingRequestKeyPrefix( - registration_creation_microseconds_since_unix_epoch, unique_id) + - base::IntToString(request_index); +std::string PendingRequestKey(const std::string& unique_id, int request_index) { + return PendingRequestKeyPrefix(unique_id) + std::to_string(request_index); +} + +std::string ActiveRequestKeyPrefix(const std::string& unique_id) { + return kActiveRequestKeyPrefix + unique_id + kSeparator; +} + +std::string ActiveRequestKey(const std::string& unique_id, int request_index) { + return ActiveRequestKeyPrefix(unique_id) + std::to_string(request_index); +} + +std::string CompletedRequestKeyPrefix(const std::string& unique_id) { + return kCompletedRequestKeyPrefix + unique_id + kSeparator; +} + +std::string CompletedRequestKey(const std::string& unique_id, + int request_index) { + return CompletedRequestKeyPrefix(unique_id) + std::to_string(request_index); } DatabaseStatus ToDatabaseStatus(ServiceWorkerStatusCode status) { diff --git a/chromium/content/browser/background_fetch/storage/database_helpers.h b/chromium/content/browser/background_fetch/storage/database_helpers.h index 188937b5af6..c81874378ce 100644 --- a/chromium/content/browser/background_fetch/storage/database_helpers.h +++ b/chromium/content/browser/background_fetch/storage/database_helpers.h @@ -8,6 +8,7 @@ #include #include "content/common/service_worker/service_worker_status_code.h" +#include "content/common/service_worker/service_worker_types.h" namespace content { @@ -20,26 +21,32 @@ namespace background_fetch { // Warning: registration |developer_id|s may contain kSeparator characters. const char kSeparator[] = "_"; -const char kRequestKeyPrefix[] = "bgfetch_request_"; -const char kRegistrationKeyPrefix[] = "bgfetch_registration_"; -const char kPendingRequestKeyPrefix[] = "bgfetch_pending_request_"; const char kActiveRegistrationUniqueIdKeyPrefix[] = "bgfetch_active_registration_unique_id_"; +const char kRegistrationKeyPrefix[] = "bgfetch_registration_"; +const char kTitleKeyPrefix[] = "bgfetch_title_"; +const char kPendingRequestKeyPrefix[] = "bgfetch_pending_request_"; +const char kActiveRequestKeyPrefix[] = "bgfetch_active_request_"; +const char kCompletedRequestKeyPrefix[] = "bgfetch_completed_request_"; std::string ActiveRegistrationUniqueIdKey(const std::string& developer_id); std::string RegistrationKey(const std::string& unique_id); -std::string RequestKeyPrefix(const std::string& unique_id); +std::string TitleKey(const std::string& unique_id); + +std::string PendingRequestKeyPrefix(const std::string& unique_id); + +std::string PendingRequestKey(const std::string& unique_id, int request_index); + +std::string ActiveRequestKeyPrefix(const std::string& unique_id); + +std::string ActiveRequestKey(const std::string& unique_id, int request_index); -std::string PendingRequestKeyPrefix( - int64_t registration_creation_microseconds_since_unix_epoch, - const std::string& unique_id); +std::string CompletedRequestKeyPrefix(const std::string& unique_id); -std::string PendingRequestKey( - int64_t registration_creation_microseconds_since_unix_epoch, - const std::string& unique_id, - int request_index); +std::string CompletedRequestKey(const std::string& unique_id, + int request_index); enum class DatabaseStatus { kOk, kFailed, kNotFound }; diff --git a/chromium/content/browser/background_fetch/storage/delete_registration_task.cc b/chromium/content/browser/background_fetch/storage/delete_registration_task.cc index 92cb468eac8..e5c2d759758 100644 --- a/chromium/content/browser/background_fetch/storage/delete_registration_task.cc +++ b/chromium/content/browser/background_fetch/storage/delete_registration_task.cc @@ -87,7 +87,7 @@ void DeleteRegistrationTask::DidGetRegistration( service_worker_context()->ClearRegistrationUserDataByKeyPrefixes( service_worker_registration_id_, - {RegistrationKey(unique_id_), RequestKeyPrefix(unique_id_)}, + {RegistrationKey(unique_id_), TitleKey(unique_id_)}, base::BindOnce(&DeleteRegistrationTask::DidDeleteRegistration, weak_factory_.GetWeakPtr())); } diff --git a/chromium/content/browser/background_fetch/storage/get_num_requests_task.cc b/chromium/content/browser/background_fetch/storage/get_num_requests_task.cc new file mode 100644 index 00000000000..6e4fbf2f566 --- /dev/null +++ b/chromium/content/browser/background_fetch/storage/get_num_requests_task.cc @@ -0,0 +1,88 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/background_fetch/storage/get_num_requests_task.h" + +#include "content/browser/background_fetch/background_fetch.pb.h" +#include "content/browser/background_fetch/storage/database_helpers.h" +#include "content/browser/background_fetch/storage/get_metadata_task.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" + +namespace content { + +namespace background_fetch { + +namespace { + +void HandleGetMetadataCallback( + GetNumRequestsTask::NumRequestsCallback callback, + blink::mojom::BackgroundFetchError error, + std::unique_ptr metadata) { + if (error != blink::mojom::BackgroundFetchError::NONE) { + std::move(callback).Run(0u); + return; + } + + DCHECK(metadata); + std::move(callback).Run(metadata->num_fetches()); +} + +} // namespace + +GetNumRequestsTask::GetNumRequestsTask( + BackgroundFetchDataManager* data_manager, + const BackgroundFetchRegistrationId& registration_id, + RequestType type, + NumRequestsCallback callback) + : DatabaseTask(data_manager), + registration_id_(registration_id), + type_(type), + callback_(std::move(callback)), + weak_factory_(this) {} + +GetNumRequestsTask::~GetNumRequestsTask() = default; + +void GetNumRequestsTask::Start() { + switch (type_) { + case RequestType::kAny: + GetMetadata(); + return; + case RequestType::kPending: + GetRequests(PendingRequestKeyPrefix(registration_id_.unique_id())); + return; + case RequestType::kActive: + GetRequests(ActiveRequestKeyPrefix(registration_id_.unique_id())); + return; + case RequestType::kCompleted: + GetRequests(CompletedRequestKeyPrefix(registration_id_.unique_id())); + return; + } + NOTREACHED(); +} + +void GetNumRequestsTask::GetMetadata() { + AddDatabaseTask(std::make_unique( + data_manager(), registration_id_.service_worker_registration_id(), + registration_id_.origin(), registration_id_.developer_id(), + base::BindOnce(&HandleGetMetadataCallback, std::move(callback_)))); + Finished(); // Destroys |this|. +} + +void GetNumRequestsTask::GetRequests(const std::string& key_prefix) { + service_worker_context()->GetRegistrationUserDataByKeyPrefix( + registration_id_.service_worker_registration_id(), key_prefix, + base::BindOnce(&GetNumRequestsTask::DidGetRequests, + weak_factory_.GetWeakPtr())); +} + +void GetNumRequestsTask::DidGetRequests(const std::vector& data, + ServiceWorkerStatusCode status) { + DCHECK_EQ(ToDatabaseStatus(status), DatabaseStatus::kOk); + std::move(callback_).Run(data.size()); + Finished(); // Destroys |this|. +} + +} // namespace background_fetch + +} // namespace content diff --git a/chromium/content/browser/background_fetch/storage/get_num_requests_task.h b/chromium/content/browser/background_fetch/storage/get_num_requests_task.h new file mode 100644 index 00000000000..fe4857b4b91 --- /dev/null +++ b/chromium/content/browser/background_fetch/storage/get_num_requests_task.h @@ -0,0 +1,53 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_GET_NUM_REQUESTS_TASK_H_ +#define CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_GET_NUM_REQUESTS_TASK_H_ + +#include "base/callback_forward.h" +#include "content/browser/background_fetch/storage/database_task.h" +#include "content/common/content_export.h" +#include "content/common/service_worker/service_worker_status_code.h" + +namespace content { + +namespace background_fetch { + +enum class RequestType { kAny, kPending, kActive, kCompleted }; + +// Gets the number of requests per registration of the given type. +class CONTENT_EXPORT GetNumRequestsTask : public DatabaseTask { + public: + using NumRequestsCallback = base::OnceCallback; + + GetNumRequestsTask(BackgroundFetchDataManager* data_manager, + const BackgroundFetchRegistrationId& registration_id, + RequestType type, + NumRequestsCallback callback); + + ~GetNumRequestsTask() override; + + // DatabaseTask implementation: + void Start() override; + + private: + void GetMetadata(); + void GetRequests(const std::string& key_prefix); + void DidGetRequests(const std::vector& data, + ServiceWorkerStatusCode status); + + BackgroundFetchRegistrationId registration_id_; + RequestType type_; + NumRequestsCallback callback_; + + base::WeakPtrFactory weak_factory_; // Keep as last. + + DISALLOW_COPY_AND_ASSIGN(GetNumRequestsTask); +}; + +} // namespace background_fetch + +} // namespace content + +#endif // CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_GET_NUM_REQUESTS_TASK_H_ diff --git a/chromium/content/browser/background_fetch/storage/get_settled_fetches_task.cc b/chromium/content/browser/background_fetch/storage/get_settled_fetches_task.cc new file mode 100644 index 00000000000..9ea3c390098 --- /dev/null +++ b/chromium/content/browser/background_fetch/storage/get_settled_fetches_task.cc @@ -0,0 +1,111 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/background_fetch/storage/get_settled_fetches_task.h" + +#include "base/barrier_closure.h" +#include "content/browser/background_fetch/background_fetch.pb.h" +#include "content/browser/background_fetch/storage/database_helpers.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" + +namespace content { + +namespace background_fetch { + +GetSettledFetchesTask::GetSettledFetchesTask( + BackgroundFetchDataManager* data_manager, + BackgroundFetchRegistrationId registration_id, + SettledFetchesCallback callback) + : DatabaseTask(data_manager), + registration_id_(registration_id), + settled_fetches_callback_(std::move(callback)), + weak_factory_(this) {} + +GetSettledFetchesTask::~GetSettledFetchesTask() = default; + +void GetSettledFetchesTask::Start() { + service_worker_context()->GetRegistrationUserDataByKeyPrefix( + registration_id_.service_worker_registration_id(), + {CompletedRequestKeyPrefix(registration_id_.unique_id())}, + base::BindOnce(&GetSettledFetchesTask::DidGetCompletedRequests, + weak_factory_.GetWeakPtr())); +} + +void GetSettledFetchesTask::DidGetCompletedRequests( + const std::vector& data, + ServiceWorkerStatusCode status) { + switch (ToDatabaseStatus(status)) { + case DatabaseStatus::kOk: + break; + // TODO(crbug.com/780025): Log failures to UMA. + case DatabaseStatus::kFailed: + FinishTaskWithErrorCode( + blink::mojom::BackgroundFetchError::STORAGE_ERROR); + return; + case DatabaseStatus::kNotFound: + background_fetch_succeeded_ = false; + FinishTaskWithErrorCode(blink::mojom::BackgroundFetchError::INVALID_ID); + return; + } + + // Nothing failed yet so the default state is success. + if (data.empty()) { + FinishTaskWithErrorCode(blink::mojom::BackgroundFetchError::NONE); + return; + } + + base::RepeatingClosure barrier_closure = base::BarrierClosure( + data.size(), + base::BindOnce(&GetSettledFetchesTask::FinishTaskWithErrorCode, + weak_factory_.GetWeakPtr(), + blink::mojom::BackgroundFetchError::NONE)); + + settled_fetches_.reserve(data.size()); + for (const std::string& serialized_completed_request : data) { + proto::BackgroundFetchCompletedRequest completed_request; + if (!completed_request.ParseFromString(serialized_completed_request)) { + NOTREACHED() + << "Database is corrupt"; // TODO(crbug.com/780027): Nuke it. + } + + settled_fetches_.emplace_back(BackgroundFetchSettledFetch()); + settled_fetches_.back().request = + std::move(ServiceWorkerFetchRequest::ParseFromString( + completed_request.serialized_request())); + if (!completed_request.succeeded()) { + FillFailedResponse(&settled_fetches_.back().response, barrier_closure); + continue; + } + FillSuccessfulResponse(&settled_fetches_.back().response, barrier_closure); + } +} + +void GetSettledFetchesTask::FillFailedResponse( + ServiceWorkerResponse* response, + const base::RepeatingClosure& callback) { + DCHECK(response); + background_fetch_succeeded_ = false; + // TODO(rayankans): Fill failed response with error reports. + std::move(callback).Run(); +} + +void GetSettledFetchesTask::FillSuccessfulResponse( + ServiceWorkerResponse* response, + const base::RepeatingClosure& callback) { + DCHECK(response); + // TODO(rayankans): Get the response stored in Cache Storage. + std::move(callback).Run(); +} + +void GetSettledFetchesTask::FinishTaskWithErrorCode( + blink::mojom::BackgroundFetchError error) { + std::move(settled_fetches_callback_) + .Run(error, background_fetch_succeeded_, std::move(settled_fetches_), + {} /* blob_data_handles */); + Finished(); // Destroys |this|. +} + +} // namespace background_fetch + +} // namespace content diff --git a/chromium/content/browser/background_fetch/storage/get_settled_fetches_task.h b/chromium/content/browser/background_fetch/storage/get_settled_fetches_task.h new file mode 100644 index 00000000000..7fba53179fc --- /dev/null +++ b/chromium/content/browser/background_fetch/storage/get_settled_fetches_task.h @@ -0,0 +1,61 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_GET_SETTLED_FETCHES_TASK_H_ +#define CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_GET_SETTLED_FETCHES_TASK_H_ + +#include "base/callback_forward.h" +#include "content/browser/background_fetch/storage/database_task.h" +#include "content/common/service_worker/service_worker_status_code.h" +#include "storage/browser/blob/blob_data_handle.h" + +namespace content { + +namespace background_fetch { + +class GetSettledFetchesTask : public DatabaseTask { + public: + using SettledFetchesCallback = base::OnceCallback, + std::vector>)>; + + GetSettledFetchesTask(BackgroundFetchDataManager* data_manager, + BackgroundFetchRegistrationId registration_id, + SettledFetchesCallback callback); + + ~GetSettledFetchesTask() override; + + // DatabaseTask implementation: + void Start() override; + + private: + void DidGetCompletedRequests(const std::vector& data, + ServiceWorkerStatusCode status); + + void FillFailedResponse(ServiceWorkerResponse* response, + const base::RepeatingClosure& callback); + + void FillSuccessfulResponse(ServiceWorkerResponse* response, + const base::RepeatingClosure& callback); + + void FinishTaskWithErrorCode(blink::mojom::BackgroundFetchError error); + + BackgroundFetchRegistrationId registration_id_; + SettledFetchesCallback settled_fetches_callback_; + + std::vector settled_fetches_; + bool background_fetch_succeeded_ = true; + + base::WeakPtrFactory weak_factory_; // Keep as last. + + DISALLOW_COPY_AND_ASSIGN(GetSettledFetchesTask); +}; + +} // namespace background_fetch + +} // namespace content + +#endif // CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_GET_SETTLED_FETCHES_TASK_H_ diff --git a/chromium/content/browser/background_fetch/storage/mark_registration_for_deletion_task.cc b/chromium/content/browser/background_fetch/storage/mark_registration_for_deletion_task.cc index 365e6bf7115..576d8fb64ab 100644 --- a/chromium/content/browser/background_fetch/storage/mark_registration_for_deletion_task.cc +++ b/chromium/content/browser/background_fetch/storage/mark_registration_for_deletion_task.cc @@ -65,8 +65,7 @@ void MarkRegistrationForDeletionTask::DidGetActiveUniqueId( } proto::BackgroundFetchMetadata metadata_proto; - if (metadata_proto.ParseFromString(data[1]) && - metadata_proto.has_creation_microseconds_since_unix_epoch()) { + if (metadata_proto.ParseFromString(data[1])) { // Mark registration as no longer active. Also deletes pending request // keys, since those are globally sorted and requests within deactivated // registrations are no longer eligible to be started. Pending request @@ -74,9 +73,7 @@ void MarkRegistrationForDeletionTask::DidGetActiveUniqueId( service_worker_context()->ClearRegistrationUserDataByKeyPrefixes( registration_id_.service_worker_registration_id(), {ActiveRegistrationUniqueIdKey(registration_id_.developer_id()), - PendingRequestKeyPrefix( - metadata_proto.creation_microseconds_since_unix_epoch(), - registration_id_.unique_id())}, + PendingRequestKeyPrefix(registration_id_.unique_id())}, base::BindOnce(&MarkRegistrationForDeletionTask::DidDeactivate, weak_factory_.GetWeakPtr())); } else { diff --git a/chromium/content/browser/background_fetch/storage/mark_request_complete_task.cc b/chromium/content/browser/background_fetch/storage/mark_request_complete_task.cc new file mode 100644 index 00000000000..63884ce938d --- /dev/null +++ b/chromium/content/browser/background_fetch/storage/mark_request_complete_task.cc @@ -0,0 +1,106 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/background_fetch/storage/mark_request_complete_task.h" + +#include "content/browser/background_fetch/background_fetch_data_manager.h" +#include "content/browser/background_fetch/storage/database_helpers.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" +#include "third_party/blink/public/mojom/blob/blob.mojom.h" + +namespace content { + +namespace background_fetch { + +MarkRequestCompleteTask::MarkRequestCompleteTask( + BackgroundFetchDataManager* data_manager, + BackgroundFetchRegistrationId registration_id, + scoped_refptr request_info, + MarkedCompleteCallback callback) + : DatabaseTask(data_manager), + registration_id_(registration_id), + request_info_(std::move(request_info)), + callback_(std::move(callback)), + weak_factory_(this) {} + +MarkRequestCompleteTask::~MarkRequestCompleteTask() = default; + +void MarkRequestCompleteTask::Start() { + StoreResponse(); +} + +void MarkRequestCompleteTask::StoreResponse() { + ServiceWorkerResponse response; + bool is_response_valid = data_manager()->FillServiceWorkerResponse( + *request_info_, registration_id_.origin(), &response); + + if (!is_response_valid) { + // No point in caching the response, just do the request state transition. + CreateAndStoreCompletedRequest(false); + return; + } + // TODO(rayankans): Store the request/response pair in cache storage, and call + // `CreateAndStoreActiveRequest` via a callback. + CreateAndStoreCompletedRequest(true); +} + +void MarkRequestCompleteTask::CreateAndStoreCompletedRequest(bool succeeded) { + completed_request_.set_unique_id(registration_id_.unique_id()); + completed_request_.set_request_index(request_info_->request_index()); + completed_request_.set_serialized_request( + request_info_->fetch_request().Serialize()); + completed_request_.set_download_guid(request_info_->download_guid()); + completed_request_.set_succeeded(succeeded); + + service_worker_context()->StoreRegistrationUserData( + registration_id_.service_worker_registration_id(), + registration_id_.origin().GetURL(), + {{CompletedRequestKey(completed_request_.unique_id(), + completed_request_.request_index()), + completed_request_.SerializeAsString()}}, + base::BindRepeating(&MarkRequestCompleteTask::DidStoreCompletedRequest, + weak_factory_.GetWeakPtr())); +} + +void MarkRequestCompleteTask::DidStoreCompletedRequest( + ServiceWorkerStatusCode status) { + switch (ToDatabaseStatus(status)) { + case DatabaseStatus::kOk: + break; + case DatabaseStatus::kFailed: + case DatabaseStatus::kNotFound: + // TODO(crbug.com/780025): Log failures to UMA. + Finished(); // Destroys |this|. + return; + } + + // Delete the active request. + service_worker_context()->ClearRegistrationUserData( + registration_id_.service_worker_registration_id(), + {ActiveRequestKey(completed_request_.unique_id(), + completed_request_.request_index())}, + base::BindOnce(&MarkRequestCompleteTask::DidDeleteActiveRequest, + weak_factory_.GetWeakPtr())); +} + +void MarkRequestCompleteTask::DidDeleteActiveRequest( + ServiceWorkerStatusCode status) { + switch (ToDatabaseStatus(status)) { + case DatabaseStatus::kNotFound: + case DatabaseStatus::kFailed: + // TODO(crbug.com/780025): Log failures to UMA. + break; + case DatabaseStatus::kOk: + std::move(callback_).Run(); + break; + } + + Finished(); // Destroys |this|. + + // TODO(rayankans): Update download stats. +} + +} // namespace background_fetch + +} // namespace content \ No newline at end of file diff --git a/chromium/content/browser/background_fetch/storage/mark_request_complete_task.h b/chromium/content/browser/background_fetch/storage/mark_request_complete_task.h new file mode 100644 index 00000000000..b36c836ba04 --- /dev/null +++ b/chromium/content/browser/background_fetch/storage/mark_request_complete_task.h @@ -0,0 +1,60 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_MARK_REQUEST_COMPLETE_TASK_H_ +#define CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_MARK_REQUEST_COMPLETE_TASK_H_ + +#include "base/callback_forward.h" +#include "base/memory/scoped_refptr.h" +#include "content/browser/background_fetch/background_fetch.pb.h" +#include "content/browser/background_fetch/background_fetch_request_info.h" +#include "content/browser/background_fetch/storage/database_task.h" +#include "content/common/service_worker/service_worker_status_code.h" + +namespace content { + +namespace background_fetch { + +// Moves the request from an active state to a complete state. Stores the +// download response in cache storage. +class MarkRequestCompleteTask : public DatabaseTask { + public: + using MarkedCompleteCallback = base::OnceCallback; + + MarkRequestCompleteTask( + BackgroundFetchDataManager* data_manager, + BackgroundFetchRegistrationId registration_id, + scoped_refptr request_info, + MarkedCompleteCallback callback); + + ~MarkRequestCompleteTask() override; + + // DatabaseTask implementation: + void Start() override; + + private: + void StoreResponse(); + + void CreateAndStoreCompletedRequest(bool succeeded); + + void DidStoreCompletedRequest(ServiceWorkerStatusCode status); + + void DidDeleteActiveRequest(ServiceWorkerStatusCode status); + + BackgroundFetchRegistrationId registration_id_; + scoped_refptr request_info_; + MarkedCompleteCallback callback_; + + proto::BackgroundFetchCompletedRequest completed_request_; + + base::WeakPtrFactory weak_factory_; // Keep as last. + + DISALLOW_COPY_AND_ASSIGN(MarkRequestCompleteTask); +}; + +} // namespace background_fetch + +} // namespace content + +#endif // CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_MARK_REQUEST_COMPLETE_TASK_H_ \ No newline at end of file diff --git a/chromium/content/browser/background_fetch/storage/start_next_pending_request_task.cc b/chromium/content/browser/background_fetch/storage/start_next_pending_request_task.cc new file mode 100644 index 00000000000..107bb160409 --- /dev/null +++ b/chromium/content/browser/background_fetch/storage/start_next_pending_request_task.cc @@ -0,0 +1,161 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/background_fetch/storage/start_next_pending_request_task.h" + +#include "base/guid.h" +#include "content/browser/background_fetch/storage/database_helpers.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" + +namespace content { + +namespace background_fetch { + +StartNextPendingRequestTask::StartNextPendingRequestTask( + BackgroundFetchDataManager* data_manager, + int64_t service_worker_registration_id, + std::unique_ptr metadata, + NextRequestCallback callback) + : DatabaseTask(data_manager), + service_worker_registration_id_(service_worker_registration_id), + metadata_(std::move(metadata)), + callback_(std::move(callback)), + weak_factory_(this) { + DCHECK(metadata_); +} + +StartNextPendingRequestTask::~StartNextPendingRequestTask() = default; + +void StartNextPendingRequestTask::Start() { + GetPendingRequests(); +} + +void StartNextPendingRequestTask::GetPendingRequests() { + service_worker_context()->GetRegistrationUserDataByKeyPrefix( + service_worker_registration_id_, + PendingRequestKeyPrefix(metadata_->registration().unique_id()), + base::BindOnce(&StartNextPendingRequestTask::DidGetPendingRequests, + weak_factory_.GetWeakPtr())); +} + +void StartNextPendingRequestTask::DidGetPendingRequests( + const std::vector& data, + ServiceWorkerStatusCode status) { + switch (ToDatabaseStatus(status)) { + case DatabaseStatus::kNotFound: + case DatabaseStatus::kFailed: + // TODO(crbug.com/780025): Log failures to UMA. + std::move(callback_).Run(nullptr /* request */); + Finished(); // Destroys |this|. + return; + case DatabaseStatus::kOk: + if (data.empty()) { + // There are no pending requests. + std::move(callback_).Run(nullptr /* request */); + Finished(); // Destroys |this|. + return; + } + } + + if (!pending_request_.ParseFromString(data.front())) { + NOTREACHED() << "Database is corrupt"; // TODO(crbug.com/780027): Nuke it. + } + + // Make sure there isn't already an Active Request. + // This might happen if the browser is killed in-between writes. + service_worker_context()->GetRegistrationUserData( + service_worker_registration_id_, + {ActiveRequestKey(pending_request_.unique_id(), + pending_request_.request_index())}, + base::BindOnce(&StartNextPendingRequestTask::DidFindActiveRequest, + weak_factory_.GetWeakPtr())); +} + +void StartNextPendingRequestTask::DidFindActiveRequest( + const std::vector& data, + ServiceWorkerStatusCode status) { + switch (ToDatabaseStatus(status)) { + case DatabaseStatus::kFailed: + // TODO(crbug.com/780025): Log failures to UMA. + std::move(callback_).Run(nullptr /* request */); + Finished(); // Destroys |this|. + return; + case DatabaseStatus::kNotFound: + CreateAndStoreActiveRequest(); + return; + case DatabaseStatus::kOk: + // We already stored the active request. + if (!active_request_.ParseFromString(data.front())) { + NOTREACHED() + << "Database is corrupt"; // TODO(crbug.com/780027): Nuke it. + } + StartDownload(); + return; + } + NOTREACHED(); +} + +void StartNextPendingRequestTask::CreateAndStoreActiveRequest() { + proto::BackgroundFetchActiveRequest active_request; + + active_request_.set_download_guid(base::GenerateGUID()); + active_request_.set_unique_id(pending_request_.unique_id()); + active_request_.set_request_index(pending_request_.request_index()); + // Transfer ownership of the request to avoid a potentially expensive copy. + active_request_.set_allocated_serialized_request( + pending_request_.release_serialized_request()); + + service_worker_context()->StoreRegistrationUserData( + service_worker_registration_id_, GURL(metadata_->origin()), + {{ActiveRequestKey(active_request_.unique_id(), + active_request_.request_index()), + active_request_.SerializeAsString()}}, + base::BindRepeating(&StartNextPendingRequestTask::DidStoreActiveRequest, + weak_factory_.GetWeakPtr())); +} + +void StartNextPendingRequestTask::DidStoreActiveRequest( + ServiceWorkerStatusCode status) { + switch (ToDatabaseStatus(status)) { + case DatabaseStatus::kOk: + break; + case DatabaseStatus::kFailed: + case DatabaseStatus::kNotFound: + // TODO(crbug.com/780025): Log failures to UMA. + std::move(callback_).Run(nullptr /* request */); + Finished(); // Destroys |this|. + return; + } + StartDownload(); +} + +void StartNextPendingRequestTask::StartDownload() { + DCHECK(!active_request_.download_guid().empty()); + + auto next_request = base::MakeRefCounted( + active_request_.request_index(), + ServiceWorkerFetchRequest::ParseFromString( + active_request_.serialized_request())); + next_request->SetDownloadGuid(active_request_.download_guid()); + + std::move(callback_).Run(next_request); + + // Delete the pending request. + service_worker_context()->ClearRegistrationUserData( + service_worker_registration_id_, + {PendingRequestKey(pending_request_.unique_id(), + pending_request_.request_index())}, + base::BindOnce(&StartNextPendingRequestTask::DidDeletePendingRequest, + weak_factory_.GetWeakPtr())); +} + +void StartNextPendingRequestTask::DidDeletePendingRequest( + ServiceWorkerStatusCode status) { + // TODO(crbug.com/780025): Log failures to UMA. + Finished(); // Destroys |this|. +} + +} // namespace background_fetch + +} // namespace content diff --git a/chromium/content/browser/background_fetch/storage/start_next_pending_request_task.h b/chromium/content/browser/background_fetch/storage/start_next_pending_request_task.h new file mode 100644 index 00000000000..476db6aa670 --- /dev/null +++ b/chromium/content/browser/background_fetch/storage/start_next_pending_request_task.h @@ -0,0 +1,72 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_START_NEXT_PENDING_REQUEST_TASK_H_ +#define CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_START_NEXT_PENDING_REQUEST_TASK_H_ + +#include "base/callback_forward.h" +#include "content/browser/background_fetch/background_fetch.pb.h" +#include "content/browser/background_fetch/background_fetch_request_info.h" +#include "content/browser/background_fetch/storage/database_task.h" +#include "content/common/service_worker/service_worker_status_code.h" + +namespace content { + +namespace background_fetch { + +// Gets a pending request for a given registration, and moves it +// to an active state. +class StartNextPendingRequestTask : public DatabaseTask { + public: + using NextRequestCallback = + base::OnceCallback)>; + + StartNextPendingRequestTask( + BackgroundFetchDataManager* data_manager, + int64_t service_worker_registration_id, + std::unique_ptr metadata, + NextRequestCallback callback); + + ~StartNextPendingRequestTask() override; + + // DatabaseTask implementation: + void Start() override; + + private: + void GetPendingRequests(); + + void DidGetPendingRequests(const std::vector& data, + ServiceWorkerStatusCode status); + + void DidFindActiveRequest(const std::vector& data, + ServiceWorkerStatusCode status); + + void CreateAndStoreActiveRequest(); + + void DidStoreActiveRequest(ServiceWorkerStatusCode status); + + void StartDownload(); + + void DidDeletePendingRequest(ServiceWorkerStatusCode status); + + int64_t service_worker_registration_id_; + std::unique_ptr metadata_; + NextRequestCallback callback_; + + // protos don't support move semantics, so these class members will be used + // to avoid unnecessary copying within callbacks. + proto::BackgroundFetchPendingRequest pending_request_; + proto::BackgroundFetchActiveRequest active_request_; + + base::WeakPtrFactory + weak_factory_; // Keep as last. + + DISALLOW_COPY_AND_ASSIGN(StartNextPendingRequestTask); +}; + +} // namespace background_fetch + +} // namespace content + +#endif // CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_START_NEXT_PENDING_REQUEST_TASK_H_ diff --git a/chromium/content/browser/background_fetch/storage/update_registration_ui_task.cc b/chromium/content/browser/background_fetch/storage/update_registration_ui_task.cc index 0c2bafb7825..5927855db09 100644 --- a/chromium/content/browser/background_fetch/storage/update_registration_ui_task.cc +++ b/chromium/content/browser/background_fetch/storage/update_registration_ui_task.cc @@ -26,61 +26,21 @@ UpdateRegistrationUITask::UpdateRegistrationUITask( UpdateRegistrationUITask::~UpdateRegistrationUITask() = default; void UpdateRegistrationUITask::Start() { - service_worker_context()->GetRegistrationUserData( - registration_id_.service_worker_registration_id(), - {RegistrationKey(registration_id_.unique_id())}, - base::BindOnce(&UpdateRegistrationUITask::DidGetMetadata, - weak_factory_.GetWeakPtr())); -} - -void UpdateRegistrationUITask::DidGetMetadata( - const std::vector& data, - ServiceWorkerStatusCode status) { - switch (ToDatabaseStatus(status)) { - case DatabaseStatus::kNotFound: - case DatabaseStatus::kFailed: - std::move(callback_).Run( - blink::mojom::BackgroundFetchError::STORAGE_ERROR); - Finished(); // Destroys |this|. - return; - case DatabaseStatus::kOk: - if (data.size() != 1u) { - std::move(callback_).Run( - blink::mojom::BackgroundFetchError::STORAGE_ERROR); - Finished(); // Destroys |this|. - return; - } - UpdateUI(data[0]); - return; - } -} - -void UpdateRegistrationUITask::UpdateUI( - const std::string& serialized_metadata_proto) { - proto::BackgroundFetchMetadata metadata_proto; - if (!metadata_proto.ParseFromString(serialized_metadata_proto)) { - std::move(callback_).Run(blink::mojom::BackgroundFetchError::STORAGE_ERROR); - Finished(); // Destroys |this|. - return; - } - - metadata_proto.set_ui_title(updated_title_); - service_worker_context()->StoreRegistrationUserData( registration_id_.service_worker_registration_id(), registration_id_.origin().GetURL(), - {{RegistrationKey(registration_id_.unique_id()), - metadata_proto.SerializeAsString()}}, - base::BindOnce(&UpdateRegistrationUITask::DidUpdateUI, + {{TitleKey(registration_id_.unique_id()), updated_title_}}, + base::BindOnce(&UpdateRegistrationUITask::DidUpdateTitle, weak_factory_.GetWeakPtr())); } -void UpdateRegistrationUITask::DidUpdateUI(ServiceWorkerStatusCode status) { +void UpdateRegistrationUITask::DidUpdateTitle(ServiceWorkerStatusCode status) { switch (ToDatabaseStatus(status)) { case DatabaseStatus::kOk: break; case DatabaseStatus::kFailed: case DatabaseStatus::kNotFound: + // TODO(crbug.com/780025): Log failures to UMA. std::move(callback_).Run( blink::mojom::BackgroundFetchError::STORAGE_ERROR); Finished(); // Destroys |this|. diff --git a/chromium/content/browser/background_fetch/storage/update_registration_ui_task.h b/chromium/content/browser/background_fetch/storage/update_registration_ui_task.h index 54aebf6ed8d..08167977bd2 100644 --- a/chromium/content/browser/background_fetch/storage/update_registration_ui_task.h +++ b/chromium/content/browser/background_fetch/storage/update_registration_ui_task.h @@ -33,12 +33,7 @@ class UpdateRegistrationUITask : public DatabaseTask { void Start() override; private: - void DidGetMetadata(const std::vector& data, - ServiceWorkerStatusCode status); - - void UpdateUI(const std::string& serialized_metadata_proto); - - void DidUpdateUI(ServiceWorkerStatusCode status); + void DidUpdateTitle(ServiceWorkerStatusCode status); BackgroundFetchRegistrationId registration_id_; std::string updated_title_; diff --git a/chromium/content/browser/background_sync/background_sync_context.cc b/chromium/content/browser/background_sync/background_sync_context.cc index 625ae345c6a..d460b065966 100644 --- a/chromium/content/browser/background_sync/background_sync_context.cc +++ b/chromium/content/browser/background_sync/background_sync_context.cc @@ -7,7 +7,6 @@ #include #include "base/bind.h" -#include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "content/browser/background_sync/background_sync_manager.h" #include "content/browser/background_sync/background_sync_service_impl.h" @@ -16,11 +15,15 @@ namespace content { -BackgroundSyncContext::BackgroundSyncContext() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); -} +BackgroundSyncContext::BackgroundSyncContext() + : base::RefCountedDeleteOnSequence( + BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)) {} BackgroundSyncContext::~BackgroundSyncContext() { + // The destructor must run on the IO thread because it implicitly accesses + // background_sync_manager_ and services_, when it runs their destructors. + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!background_sync_manager_); DCHECK(services_.empty()); } @@ -37,7 +40,6 @@ void BackgroundSyncContext::Init( void BackgroundSyncContext::Shutdown() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce(&BackgroundSyncContext::ShutdownOnIO, this)); @@ -46,7 +48,6 @@ void BackgroundSyncContext::Shutdown() { void BackgroundSyncContext::CreateService( blink::mojom::BackgroundSyncServiceRequest request) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce(&BackgroundSyncContext::CreateServiceOnIOThread, this, @@ -86,9 +87,9 @@ void BackgroundSyncContext::CreateServiceOnIOThread( mojo::InterfaceRequest request) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(background_sync_manager_); - BackgroundSyncServiceImpl* service = - new BackgroundSyncServiceImpl(this, std::move(request)); - services_[service] = base::WrapUnique(service); + auto service = + std::make_unique(this, std::move(request)); + services_[service.get()] = std::move(service); } void BackgroundSyncContext::ShutdownOnIO() { diff --git a/chromium/content/browser/background_sync/background_sync_context.h b/chromium/content/browser/background_sync/background_sync_context.h index 38d317146c7..f0e1f89260a 100644 --- a/chromium/content/browser/background_sync/background_sync_context.h +++ b/chromium/content/browser/background_sync/background_sync_context.h @@ -9,7 +9,7 @@ #include #include "base/macros.h" -#include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_delete_on_sequence.h" #include "content/common/content_export.h" #include "third_party/blink/public/platform/modules/background_sync/background_sync.mojom.h" @@ -23,8 +23,10 @@ class ServiceWorkerContextWrapper; // processes/origins. Most logic is delegated to the owned BackgroundSyncManager // instance, which is only accessed on the IO thread. class CONTENT_EXPORT BackgroundSyncContext - : public base::RefCountedThreadSafe { + : public base::RefCountedDeleteOnSequence { public: + REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); + BackgroundSyncContext(); // Init and Shutdown are for use on the UI thread when the @@ -46,7 +48,8 @@ class CONTENT_EXPORT BackgroundSyncContext BackgroundSyncManager* background_sync_manager() const; protected: - friend class base::RefCountedThreadSafe; + friend class base::RefCountedDeleteOnSequence; + friend class base::DeleteHelper; virtual ~BackgroundSyncContext(); void set_background_sync_manager_for_testing( diff --git a/chromium/content/browser/background_sync/background_sync_manager.cc b/chromium/content/browser/background_sync/background_sync_manager.cc index 0c6fe683b64..62d6f5a2d7d 100644 --- a/chromium/content/browser/background_sync/background_sync_manager.cc +++ b/chromium/content/browser/background_sync/background_sync_manager.cc @@ -279,10 +279,30 @@ void BackgroundSyncManager::EmulateDispatchSyncEvent( bool last_chance, ServiceWorkerVersion::StatusCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + ServiceWorkerStatusCode code = CanEmulateSyncEvent(active_version); + if (code != SERVICE_WORKER_OK) { + std::move(callback).Run(code); + return; + } DispatchSyncEvent(tag, std::move(active_version), last_chance, std::move(callback)); } +void BackgroundSyncManager::EmulateServiceWorkerOffline( + int64_t service_worker_id, + bool is_offline) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + // Multiple DevTools sessions may want to set the same SW offline, which + // is supposed to disable the background sync. For consistency with the + // network stack, SW remains offline until all DevTools sessions disable + // the offline mode. + emulated_offline_sw_[service_worker_id] += is_offline ? 1 : -1; + if (emulated_offline_sw_[service_worker_id] > 0) + return; + emulated_offline_sw_.erase(service_worker_id); + FireReadyEvents(); +} + BackgroundSyncManager::BackgroundSyncManager( scoped_refptr service_worker_context) : op_scheduler_(CacheStorageSchedulerClient::CLIENT_BACKGROUND_SYNC), @@ -845,7 +865,8 @@ bool BackgroundSyncManager::AreOptionConditionsMet( } bool BackgroundSyncManager::IsRegistrationReadyToFire( - const BackgroundSyncRegistration& registration) { + const BackgroundSyncRegistration& registration, + int64_t service_worker_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (registration.sync_state() != blink::mojom::BackgroundSyncState::PENDING) @@ -854,6 +875,9 @@ bool BackgroundSyncManager::IsRegistrationReadyToFire( if (clock_->Now() < registration.delay_until()) return false; + if (base::ContainsKey(emulated_offline_sw_, service_worker_id)) + return false; + return AreOptionConditionsMet(*registration.options()); } @@ -933,7 +957,8 @@ void BackgroundSyncManager::FireReadyEventsImpl(base::OnceClosure callback) { for (auto& key_and_registration : sw_id_and_registrations.second.registration_map) { BackgroundSyncRegistration* registration = &key_and_registration.second; - if (IsRegistrationReadyToFire(*registration)) { + + if (IsRegistrationReadyToFire(*registration, service_worker_id)) { sw_id_and_tags_to_fire.push_back( std::make_pair(service_worker_id, key_and_registration.first)); // The state change is not saved to persistent storage because @@ -1202,4 +1227,16 @@ base::OnceClosure BackgroundSyncManager::MakeEmptyCompletion() { return op_scheduler_.WrapCallbackToRunNext(base::DoNothing::Once()); } +ServiceWorkerStatusCode BackgroundSyncManager::CanEmulateSyncEvent( + scoped_refptr active_version) { + if (!active_version) + return SERVICE_WORKER_ERROR_FAILED; + if (!network_observer_->NetworkSufficient(NETWORK_STATE_ONLINE)) + return SERVICE_WORKER_ERROR_NETWORK; + int64_t registration_id = active_version->registration_id(); + if (base::ContainsKey(emulated_offline_sw_, registration_id)) + return SERVICE_WORKER_ERROR_NETWORK; + return SERVICE_WORKER_OK; +} + } // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_manager.h b/chromium/content/browser/background_sync/background_sync_manager.h index 797e632efd5..38091eaa269 100644 --- a/chromium/content/browser/background_sync/background_sync_manager.h +++ b/chromium/content/browser/background_sync/background_sync_manager.h @@ -107,6 +107,9 @@ class CONTENT_EXPORT BackgroundSyncManager bool last_chance, ServiceWorkerVersion::StatusCallback callback); + // Called from DevTools to toggle service worker "offline" status + void EmulateServiceWorkerOffline(int64_t service_worker_id, bool is_offline); + protected: explicit BackgroundSyncManager( scoped_refptr context); @@ -223,8 +226,8 @@ class CONTENT_EXPORT BackgroundSyncManager StatusAndRegistrationsCallback callback); bool AreOptionConditionsMet(const BackgroundSyncRegistrationOptions& options); - bool IsRegistrationReadyToFire( - const BackgroundSyncRegistration& registration); + bool IsRegistrationReadyToFire(const BackgroundSyncRegistration& registration, + int64_t service_worker_id); // Determines if the browser needs to be able to run in the background (e.g., // to run a pending registration or verify that a firing registration @@ -282,6 +285,9 @@ class CONTENT_EXPORT BackgroundSyncManager base::OnceClosure MakeEmptyCompletion(); + ServiceWorkerStatusCode CanEmulateSyncEvent( + scoped_refptr active_version); + SWIdToRegistrationsMap active_registrations_; CacheStorageScheduler op_scheduler_; scoped_refptr service_worker_context_; @@ -300,6 +306,8 @@ class CONTENT_EXPORT BackgroundSyncManager base::Clock* clock_; + std::map emulated_offline_sw_; + base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(BackgroundSyncManager); diff --git a/chromium/content/browser/background_sync/background_sync_manager_unittest.cc b/chromium/content/browser/background_sync/background_sync_manager_unittest.cc index 554c5ee9798..5fc543bee78 100644 --- a/chromium/content/browser/background_sync/background_sync_manager_unittest.cc +++ b/chromium/content/browser/background_sync/background_sync_manager_unittest.cc @@ -392,7 +392,7 @@ class BackgroundSyncManagerTest : public testing::Test { EXPECT_TRUE(Register(sync_options)); EXPECT_EQ(sync_events_called + 1, sync_events_called_); - EXPECT_TRUE(GetRegistration(sync_options_1_)); + EXPECT_TRUE(GetRegistration(sync_options)); EXPECT_TRUE(sync_fired_callback_); } @@ -1348,4 +1348,114 @@ TEST_F(BackgroundSyncManagerTest, LastChance) { EXPECT_TRUE(test_background_sync_manager_->last_chance()); } +TEST_F(BackgroundSyncManagerTest, EmulateOfflineSingleClient) { + InitSyncEventTest(); + + background_sync_manager_->EmulateServiceWorkerOffline(sw_registration_id_1_, + true); + EXPECT_TRUE(Register(sync_options_1_)); + EXPECT_EQ(0, sync_events_called_); + EXPECT_TRUE(GetRegistration(sync_options_1_)); + + background_sync_manager_->EmulateServiceWorkerOffline(sw_registration_id_1_, + false); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, sync_events_called_); + EXPECT_FALSE(GetRegistration(sync_options_1_)); + + EXPECT_TRUE(Register(sync_options_2_)); + EXPECT_EQ(2, sync_events_called_); + EXPECT_FALSE(GetRegistration(sync_options_2_)); +} + +TEST_F(BackgroundSyncManagerTest, EmulateOfflineMultipleClients) { + InitSyncEventTest(); + + background_sync_manager_->EmulateServiceWorkerOffline(sw_registration_id_1_, + true); + EXPECT_TRUE(Register(sync_options_1_)); + EXPECT_EQ(0, sync_events_called_); + EXPECT_TRUE(GetRegistration(sync_options_1_)); + + background_sync_manager_->EmulateServiceWorkerOffline(sw_registration_id_1_, + true); + + EXPECT_TRUE(Register(sync_options_2_)); + EXPECT_EQ(0, sync_events_called_); + EXPECT_TRUE(GetRegistration(sync_options_2_)); + + background_sync_manager_->EmulateServiceWorkerOffline(sw_registration_id_1_, + false); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, sync_events_called_); + EXPECT_TRUE(GetRegistration(sync_options_1_)); + EXPECT_TRUE(GetRegistration(sync_options_2_)); + + background_sync_manager_->EmulateServiceWorkerOffline(sw_registration_id_1_, + false); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(2, sync_events_called_); + EXPECT_FALSE(GetRegistration(sync_options_1_)); + EXPECT_FALSE(GetRegistration(sync_options_2_)); +} + +static void EmulateDispatchSyncEventCallback( + bool* was_called, + ServiceWorkerStatusCode* code, + ServiceWorkerStatusCode status_code) { + *was_called = true; + *code = status_code; +} + +TEST_F(BackgroundSyncManagerTest, EmulateDispatchSyncEvent) { + InitSyncEventTest(); + bool was_called = false; + ServiceWorkerStatusCode code = SERVICE_WORKER_ERROR_MAX_VALUE; + background_sync_manager_->EmulateDispatchSyncEvent( + "emulated_tag", sw_registration_1_->active_version(), false, + base::BindOnce(EmulateDispatchSyncEventCallback, &was_called, &code)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(was_called); + EXPECT_EQ(SERVICE_WORKER_OK, code); + + EXPECT_EQ(1, sync_events_called_); + + background_sync_manager_->EmulateServiceWorkerOffline(sw_registration_id_1_, + true); + + was_called = false; + code = SERVICE_WORKER_ERROR_MAX_VALUE; + background_sync_manager_->EmulateDispatchSyncEvent( + "emulated_tag", sw_registration_1_->active_version(), false, + base::BindOnce(EmulateDispatchSyncEventCallback, &was_called, &code)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(was_called); + EXPECT_EQ(SERVICE_WORKER_ERROR_NETWORK, code); + + background_sync_manager_->EmulateServiceWorkerOffline(sw_registration_id_1_, + false); + + SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE); + was_called = false; + code = SERVICE_WORKER_ERROR_MAX_VALUE; + background_sync_manager_->EmulateDispatchSyncEvent( + "emulated_tag", sw_registration_1_->active_version(), false, + base::BindOnce(EmulateDispatchSyncEventCallback, &was_called, &code)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(was_called); + EXPECT_EQ(SERVICE_WORKER_ERROR_NETWORK, code); + + SetNetwork(net::NetworkChangeNotifier::CONNECTION_WIFI); + was_called = false; + code = SERVICE_WORKER_ERROR_MAX_VALUE; + background_sync_manager_->EmulateDispatchSyncEvent( + "emulated_tag", sw_registration_1_->active_version(), false, + base::BindOnce(EmulateDispatchSyncEventCallback, &was_called, &code)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(was_called); + EXPECT_EQ(SERVICE_WORKER_OK, code); + + EXPECT_EQ(2, sync_events_called_); +} } // namespace content diff --git a/chromium/content/browser/background_sync/background_sync_service_impl_unittest.cc b/chromium/content/browser/background_sync/background_sync_service_impl_unittest.cc index 6cbdfccaf3b..aecf773c011 100644 --- a/chromium/content/browser/background_sync/background_sync_service_impl_unittest.cc +++ b/chromium/content/browser/background_sync/background_sync_service_impl_unittest.cc @@ -142,7 +142,8 @@ class BackgroundSyncServiceImplTest : public testing::Test { void CreateBackgroundSyncContext() { // Registering for background sync includes a check for having a same-origin // main frame. Use a test context that allows control over that check. - background_sync_context_ = new TestBackgroundSyncContext(); + background_sync_context_ = + base::MakeRefCounted(); background_sync_context_->Init(embedded_worker_helper_->context_wrapper()); // Tests do not expect the sync event to fire immediately after diff --git a/chromium/content/browser/bad_message.h b/chromium/content/browser/bad_message.h index 80ab47d8248..20115161d55 100644 --- a/chromium/content/browser/bad_message.h +++ b/chromium/content/browser/bad_message.h @@ -52,7 +52,7 @@ enum BadMessageReason { OBSOLETE_DBMF_DB_NOT_OPEN_ON_CLOSE = 25, OBSOLETE_DBMF_INVALID_ORIGIN_ON_SQLITE_ERROR = 26, RDH_INVALID_PRIORITY = 27, - RDH_REQUEST_NOT_TRANSFERRING = 28, + OBSOLETE_RDH_REQUEST_NOT_TRANSFERRING = 28, RDH_BAD_DOWNLOAD = 29, OBSOLETE_NMF_NO_PERMISSION_SHOW = 30, OBSOLETE_NMF_NO_PERMISSION_CLOSE = 31, @@ -130,8 +130,8 @@ enum BadMessageReason { DWNLD_INVALID_SAVABLE_RESOURCE_LINKS_RESPONSE = 103, DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE = 104, BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN = 105, - ACI_WRONG_STORAGE_PARTITION = 106, - RDHI_WRONG_STORAGE_PARTITION = 107, + OBSOLETE_ACI_WRONG_STORAGE_PARTITION = 106, + OBSOLETE_RDHI_WRONG_STORAGE_PARTITION = 107, RDH_INVALID_REQUEST_ID = 108, BDH_SERVICE_NOT_ALLOWED_FOR_ORIGIN = 109, WSI_UNEXPECTED_ADD_CHANNEL_REQUEST = 110, @@ -151,7 +151,7 @@ enum BadMessageReason { OBSOLETE_DBMF_INVALID_ORIGIN_ON_GET_SPACE = 124, OBSOLETE_DBMF_INVALID_ORIGIN_ON_MODIFIED = 125, OBSOLETE_DBMF_INVALID_ORIGIN_ON_CLOSED = 126, - WSI_INVALID_HEADER_VALUE = 127, + OBSOLETE_WSI_INVALID_HEADER_VALUE = 127, OBSOLETE_SWDH_SET_HOSTED_VERSION_INVALID_HOST = 128, OBSOLETE_SWDH_SET_HOSTED_VERSION_PROCESS_MISMATCH = 129, OBSOLETE_MSDH_INVALID_FRAME_ID = 130, @@ -170,8 +170,9 @@ enum BadMessageReason { OBSOLETE_SWDH_ENABLE_NAVIGATION_PRELOAD_NO_HOST = 143, OBSOLETE_SWDH_ENABLE_NAVIGATION_PRELOAD_INVALID_ORIGIN = 144, OBSOLETE_SWDH_ENABLE_NAVIGATION_PRELOAD_BAD_REGISTRATION_ID = 145, - RDH_TRANSFERRING_REQUEST_NOT_FOUND = 146, // Disabled - crbug.com/659613. - RDH_TRANSFERRING_NONNAVIGATIONAL_REQUEST = 147, + OBSOLETE_RDH_TRANSFERRING_REQUEST_NOT_FOUND = + 146, // Disabled - crbug.com/659613. + OBSOLETE_RDH_TRANSFERRING_NONNAVIGATIONAL_REQUEST = 147, OBSOLETE_SWDH_GET_NAVIGATION_PRELOAD_STATE_NO_HOST = 148, OBSOLETE_SWDH_GET_NAVIGATION_PRELOAD_STATE_INVALID_ORIGIN = 149, OBSOLETE_SWDH_GET_NAVIGATION_PRELOAD_STATE_BAD_REGISTRATION_ID = 150, @@ -225,6 +226,10 @@ enum BadMessageReason { SYNC_COMPOSITOR_NO_FUTURE_FRAME = 198, SYNC_COMPOSITOR_NO_BEGIN_FRAME = 199, WEBUI_BAD_HOST_ACCESS = 200, + RFMF_BLOB_URL_TOKEN_FOR_NON_BLOB_URL = 201, + PERMISSION_SERVICE_BAD_PERMISSION_DESCRIPTOR = 202, + RFH_BLOB_URL_TOKEN_FOR_NON_BLOB_URL = 203, + RFPH_BLOB_URL_TOKEN_FOR_NON_BLOB_URL = 204, // Please add new elements here. The naming convention is abbreviated class // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the diff --git a/chromium/content/browser/blob_storage/blob_url_unittest.cc b/chromium/content/browser/blob_storage/blob_url_unittest.cc index 82e79a3125b..e7d27de9650 100644 --- a/chromium/content/browser/blob_storage/blob_url_unittest.cc +++ b/chromium/content/browser/blob_storage/blob_url_unittest.cc @@ -13,7 +13,6 @@ #include "base/files/scoped_temp_dir.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" #include "base/numerics/safe_conversions.h" #include "base/run_loop.h" #include "base/threading/thread_task_runner_handle.h" diff --git a/chromium/content/browser/blob_storage/chrome_blob_storage_context.cc b/chromium/content/browser/blob_storage/chrome_blob_storage_context.cc index 0b5780dfe11..17347dbfd10 100644 --- a/chromium/content/browser/blob_storage/chrome_blob_storage_context.cc +++ b/chromium/content/browser/blob_storage/chrome_blob_storage_context.cc @@ -19,12 +19,12 @@ #include "base/task_runner.h" #include "base/task_scheduler/post_task.h" #include "content/browser/resource_context_impl.h" -#include "content/common/wrapper_shared_url_loader_factory.h" #include "content/public/browser/blob_handle.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_features.h" #include "services/network/public/cpp/resource_request_body.h" +#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" #include "storage/browser/blob/blob_data_builder.h" #include "storage/browser/blob/blob_impl.h" #include "storage/browser/blob/blob_memory_controller.h" @@ -174,27 +174,6 @@ std::unique_ptr ChromeBlobStorageContext::CreateMemoryBackedBlob( return blob_handle; } -std::unique_ptr ChromeBlobStorageContext::CreateFileBackedBlob( - const FilePath& path, - int64_t offset, - int64_t size, - const base::Time& expected_modification_time) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - std::string uuid(base::GenerateGUID()); - auto blob_data_builder = std::make_unique(uuid); - blob_data_builder->AppendFile(path, offset, size, expected_modification_time); - - std::unique_ptr blob_data_handle = - context_->AddFinishedBlob(std::move(blob_data_builder)); - if (!blob_data_handle) - return std::unique_ptr(); - - std::unique_ptr blob_handle( - new BlobHandleImpl(std::move(blob_data_handle))); - return blob_handle; -} - // static scoped_refptr ChromeBlobStorageContext::URLLoaderFactoryForToken( @@ -214,7 +193,7 @@ ChromeBlobStorageContext::URLLoaderFactoryForToken( }, base::WrapRefCounted(GetFor(browser_context)), MakeRequest(&blob_url_loader_factory_ptr), token.PassInterface())); - return base::MakeRefCounted( + return base::MakeRefCounted( std::move(blob_url_loader_factory_ptr)); } diff --git a/chromium/content/browser/blob_storage/chrome_blob_storage_context.h b/chromium/content/browser/blob_storage/chrome_blob_storage_context.h index 043d968ca6a..812a6a5cebe 100644 --- a/chromium/content/browser/blob_storage/chrome_blob_storage_context.h +++ b/chromium/content/browser/blob_storage/chrome_blob_storage_context.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_FILEAPI_CHROME_BLOB_STORAGE_CONTEXT_H_ -#define CONTENT_BROWSER_FILEAPI_CHROME_BLOB_STORAGE_CONTEXT_H_ +#ifndef CONTENT_BROWSER_BLOB_STORAGE_CHROME_BLOB_STORAGE_CONTEXT_H_ +#define CONTENT_BROWSER_BLOB_STORAGE_CHROME_BLOB_STORAGE_CONTEXT_H_ #include #include @@ -20,9 +20,7 @@ #include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h" namespace base { -class FilePath; class TaskRunner; -class Time; } namespace network { @@ -67,13 +65,6 @@ class CONTENT_EXPORT ChromeBlobStorageContext size_t length, const std::string& content_type); - // Returns a NULL scoped_ptr on failure. - std::unique_ptr CreateFileBackedBlob( - const base::FilePath& path, - int64_t offset, - int64_t size, - const base::Time& expected_modification_time); - // Must be called on the UI thread. static scoped_refptr URLLoaderFactoryForToken(BrowserContext* browser_context, @@ -117,4 +108,4 @@ extern const char kBlobStorageContextKeyName[]; } // namespace content -#endif // CONTENT_BROWSER_FILEAPI_CHROME_BLOB_STORAGE_CONTEXT_H_ +#endif // CONTENT_BROWSER_BLOB_STORAGE_CHROME_BLOB_STORAGE_CONTEXT_H_ diff --git a/chromium/content/browser/browser_associated_interface_unittest.cc b/chromium/content/browser/browser_associated_interface_unittest.cc index 0de9b41eb07..8e3a8041c0c 100644 --- a/chromium/content/browser/browser_associated_interface_unittest.cc +++ b/chromium/content/browser/browser_associated_interface_unittest.cc @@ -149,7 +149,7 @@ class TestClientRunner { proxy.channel()->Send(message.release()); } - driver->RequestQuit(base::MessageLoop::QuitWhenIdleClosure()); + driver->RequestQuit(base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).Run(); diff --git a/chromium/content/browser/browser_child_process_host_impl.cc b/chromium/content/browser/browser_child_process_host_impl.cc index dcf54720afd..1686c7a9f2b 100644 --- a/chromium/content/browser/browser_child_process_host_impl.cc +++ b/chromium/content/browser/browser_child_process_host_impl.cc @@ -25,7 +25,7 @@ #include "base/synchronization/waitable_event.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" -#include "components/tracing/common/trace_config_file.h" +#include "components/tracing/common/trace_startup_config.h" #include "components/tracing/common/tracing_switches.h" #include "content/browser/bad_message.h" #include "content/browser/browser_main_loop.h" @@ -43,7 +43,6 @@ #include "content/public/common/connection_filter.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" -#include "content/public/common/mojo_channel_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" @@ -82,14 +81,18 @@ void NotifyProcessHostDisconnected(const ChildProcessData& data) { observer.BrowserChildProcessHostDisconnected(data); } -void NotifyProcessCrashed(const ChildProcessData& data, int exit_code) { +#if !defined(OS_ANDROID) +void NotifyProcessCrashed(const ChildProcessData& data, + const ChildProcessTerminationInfo& info) { for (auto& observer : g_browser_child_process_observers.Get()) - observer.BrowserChildProcessCrashed(data, exit_code); + observer.BrowserChildProcessCrashed(data, info); } +#endif -void NotifyProcessKilled(const ChildProcessData& data, int exit_code) { +void NotifyProcessKilled(const ChildProcessData& data, + const ChildProcessTerminationInfo& info) { for (auto& observer : g_browser_child_process_observers.Get()) - observer.BrowserChildProcessKilled(data, exit_code); + observer.BrowserChildProcessKilled(data, info); } } // namespace @@ -213,24 +216,9 @@ void BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags( // static void BrowserChildProcessHostImpl::CopyTraceStartupFlags( base::CommandLine* cmd_line) { - const base::CommandLine& browser_cmd_line = - *base::CommandLine::ForCurrentProcess(); - - if (browser_cmd_line.HasSwitch(switches::kTraceStartup) && - BrowserMainLoop::GetInstance()->is_tracing_startup_for_duration()) { - // Pass kTraceStartup switch to renderer only if startup tracing has not - // finished. - cmd_line->AppendSwitchASCII( - switches::kTraceStartup, - browser_cmd_line.GetSwitchValueASCII(switches::kTraceStartup)); - if (browser_cmd_line.HasSwitch(switches::kTraceStartupRecordMode)) { - cmd_line->AppendSwitchASCII(switches::kTraceStartupRecordMode, - browser_cmd_line.GetSwitchValueASCII( - switches::kTraceStartupRecordMode)); - } - } else if (tracing::TraceConfigFile::GetInstance()->IsEnabled()) { + if (tracing::TraceStartupConfig::GetInstance()->IsEnabled()) { const auto trace_config = - tracing::TraceConfigFile::GetInstance()->GetTraceConfig(); + tracing::TraceStartupConfig::GetInstance()->GetTraceConfig(); if (!trace_config.IsArgumentFilterEnabled()) { // The only trace option that we can pass through switches is the record // mode. Other trace options should have the default value. @@ -274,8 +262,9 @@ void BrowserChildProcessHostImpl::Launch( arraysize(kForwardSwitches)); if (child_connection_) { - cmd_line->AppendSwitchASCII(switches::kServiceRequestChannelToken, - child_connection_->service_token()); + cmd_line->AppendSwitchASCII( + service_manager::switches::kServiceRequestChannelToken, + child_connection_->service_token()); } DCHECK(broker_client_invitation_); @@ -358,13 +347,16 @@ void BrowserChildProcessHostImpl::HistogramBadMessageTerminated( PROCESS_TYPE_MAX); } -base::TerminationStatus BrowserChildProcessHostImpl::GetTerminationStatus( - bool known_dead, int* exit_code) { +ChildProcessTerminationInfo BrowserChildProcessHostImpl::GetTerminationInfo( + bool known_dead) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!child_process_) // If the delegate doesn't use Launch() helper. - return base::GetTerminationStatus(data_.handle, exit_code); - return child_process_->GetChildTerminationStatus(known_dead, - exit_code); + if (!child_process_) { + // If the delegate doesn't use Launch() helper. + ChildProcessTerminationInfo info; + info.status = base::GetTerminationStatus(data_.handle, &info.exit_code); + return info; + } + return child_process_->GetChildTerminationInfo(known_dead); } bool BrowserChildProcessHostImpl::OnMessageReceived( @@ -424,7 +416,7 @@ void BrowserChildProcessHostImpl::TerminateOnBadMessageReceived( // what the bad message was. base::debug::DumpWithoutCrashing(); - child_process_->GetProcess().Terminate(RESULT_CODE_KILLED_BAD_MESSAGE, false); + child_process_->Terminate(RESULT_CODE_KILLED_BAD_MESSAGE); } void BrowserChildProcessHostImpl::OnChannelInitialized(IPC::Channel* channel) { @@ -439,32 +431,33 @@ void BrowserChildProcessHostImpl::OnChildDisconnected() { early_exit_watcher_.StopWatching(); #endif if (child_process_.get() || data_.handle) { - int exit_code; - base::TerminationStatus status = GetTerminationStatus( - true /* known_dead */, &exit_code); - switch (status) { + ChildProcessTerminationInfo info = + GetTerminationInfo(true /* known_dead */); +#if defined(OS_ANDROID) + delegate_->OnProcessCrashed(info.exit_code); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::BindOnce(&NotifyProcessKilled, data_, info)); +#else // OS_ANDROID + switch (info.status) { case base::TERMINATION_STATUS_PROCESS_CRASHED: case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: { - delegate_->OnProcessCrashed(exit_code); + delegate_->OnProcessCrashed(info.exit_code); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::BindOnce(&NotifyProcessCrashed, data_, exit_code)); + base::BindOnce(&NotifyProcessCrashed, data_, info)); UMA_HISTOGRAM_ENUMERATION("ChildProcess.Crashed2", static_cast(data_.process_type), PROCESS_TYPE_MAX); break; } -#if defined(OS_ANDROID) - case base::TERMINATION_STATUS_OOM_PROTECTED: -#endif #if defined(OS_CHROMEOS) case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM: #endif case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: { - delegate_->OnProcessCrashed(exit_code); + delegate_->OnProcessCrashed(info.exit_code); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::BindOnce(&NotifyProcessKilled, data_, exit_code)); + base::BindOnce(&NotifyProcessKilled, data_, info)); // Report that this child process was killed. UMA_HISTOGRAM_ENUMERATION("ChildProcess.Killed2", static_cast(data_.process_type), @@ -480,11 +473,12 @@ void BrowserChildProcessHostImpl::OnChildDisconnected() { default: break; } +#endif // OS_ANDROID UMA_HISTOGRAM_ENUMERATION("ChildProcess.Disconnected2", static_cast(data_.process_type), PROCESS_TYPE_MAX); #if defined(OS_CHROMEOS) - if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM) { + if (info.status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM) { UMA_HISTOGRAM_ENUMERATION("ChildProcess.Killed2.OOM", static_cast(data_.process_type), PROCESS_TYPE_MAX); @@ -646,8 +640,7 @@ void BrowserChildProcessHostImpl::OnMojoError( base::debug::ScopedCrashKeyString scoped_error_key( bad_message::GetMojoErrorCrashKey(), error); base::debug::DumpWithoutCrashing(); - process->child_process_->GetProcess().Terminate( - RESULT_CODE_KILLED_BAD_MESSAGE, false); + process->child_process_->Terminate(RESULT_CODE_KILLED_BAD_MESSAGE); } #if defined(OS_WIN) diff --git a/chromium/content/browser/browser_child_process_host_impl.h b/chromium/content/browser/browser_child_process_host_impl.h index 5dc64aa2224..ee21de31936 100644 --- a/chromium/content/browser/browser_child_process_host_impl.h +++ b/chromium/content/browser/browser_child_process_host_impl.h @@ -75,8 +75,7 @@ class CONTENT_EXPORT BrowserChildProcessHostImpl bool terminate_on_shutdown) override; const ChildProcessData& GetData() const override; ChildProcessHost* GetHost() const override; - base::TerminationStatus GetTerminationStatus(bool known_dead, - int* exit_code) override; + ChildProcessTerminationInfo GetTerminationInfo(bool known_dead) override; std::unique_ptr TakeMetricsAllocator() override; void SetName(const base::string16& name) override; diff --git a/chromium/content/browser/browser_context.cc b/chromium/content/browser/browser_context.cc index 15cb62d76bb..69271e39f27 100644 --- a/chromium/content/browser/browser_context.cc +++ b/chromium/content/browser/browser_context.cc @@ -24,6 +24,7 @@ #include "base/rand_util.h" #include "base/task_scheduler/post_task.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/unguessable_token.h" #include "build/build_config.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/browsing_data/browsing_data_remover_impl.h" @@ -337,26 +338,6 @@ void BrowserContext::CreateMemoryBackedBlob(BrowserContext* browser_context, std::move(callback)); } -// static -void BrowserContext::CreateFileBackedBlob( - BrowserContext* browser_context, - const base::FilePath& path, - int64_t offset, - int64_t size, - const base::Time& expected_modification_time, - BlobCallback callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - ChromeBlobStorageContext* blob_context = - ChromeBlobStorageContext::GetFor(browser_context); - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&ChromeBlobStorageContext::CreateFileBackedBlob, - base::WrapRefCounted(blob_context), path, offset, size, - expected_modification_time), - std::move(callback)); -} - // static BrowserContext::BlobContextGetter BrowserContext::GetBlobStorageContext( BrowserContext* browser_context) { @@ -535,14 +516,12 @@ void BrowserContext::Initialize( connection->Start(); } -#if BUILDFLAG(ENABLE_WEBRTC) if (!browser_context->IsOffTheRecord()) { WebRtcEventLogger* const logger = WebRtcEventLogger::Get(); if (logger) { logger->EnableForBrowserContext(browser_context); } } -#endif } // static @@ -583,7 +562,7 @@ ServiceManagerConnection* BrowserContext::GetServiceManagerConnectionFor( } BrowserContext::BrowserContext() - : media_device_id_salt_(CreateRandomMediaDeviceIDSalt()) {} + : unique_id_(base::UnguessableToken::Create().ToString()) {} BrowserContext::~BrowserContext() { CHECK(GetUserData(kMojoWasInitialized)) @@ -595,12 +574,10 @@ BrowserContext::~BrowserContext() { DCHECK(was_notify_will_be_destroyed_called_); -#if BUILDFLAG(ENABLE_WEBRTC) WebRtcEventLogger* const logger = WebRtcEventLogger::Get(); if (logger) { logger->DisableForBrowserContext(this); } -#endif RemoveBrowserContextFromUserIdMap(this); @@ -614,15 +591,16 @@ void BrowserContext::ShutdownStoragePartitions() { } std::string BrowserContext::GetMediaDeviceIDSalt() { - return media_device_id_salt_; + return unique_id_; } // static std::string BrowserContext::CreateRandomMediaDeviceIDSalt() { - std::string salt; - base::Base64Encode(base::RandBytesAsString(16), &salt); - DCHECK(!salt.empty()); - return salt; + return base::UnguessableToken::Create().ToString(); +} + +const std::string& BrowserContext::UniqueId() const { + return unique_id_; } media::VideoDecodePerfHistory* BrowserContext::GetVideoDecodePerfHistory() { diff --git a/chromium/content/browser/browser_main.cc b/chromium/content/browser/browser_main.cc index d706269f9db..f06e59b43ff 100644 --- a/chromium/content/browser/browser_main.cc +++ b/chromium/content/browser/browser_main.cc @@ -7,8 +7,9 @@ #include #include "base/trace_event/trace_event.h" +#include "content/browser/browser_main_runner_impl.h" +#include "content/browser/browser_process_sub_thread.h" #include "content/common/content_constants_internal.h" -#include "content/public/browser/browser_main_runner.h" namespace content { @@ -30,16 +31,20 @@ class ScopedBrowserMainEvent { } // namespace // Main routine for running as the Browser process. -int BrowserMain(const MainFunctionParams& parameters) { +int BrowserMain( + const MainFunctionParams& parameters, + std::unique_ptr service_manager_thread) { ScopedBrowserMainEvent scoped_browser_main_event; base::trace_event::TraceLog::GetInstance()->set_process_name("Browser"); base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex( kTraceEventBrowserProcessSortIndex); - std::unique_ptr main_runner(BrowserMainRunner::Create()); + std::unique_ptr main_runner( + BrowserMainRunnerImpl::Create()); - int exit_code = main_runner->Initialize(parameters); + int exit_code = + main_runner->Initialize(parameters, std::move(service_manager_thread)); if (exit_code >= 0) return exit_code; diff --git a/chromium/content/browser/browser_main.h b/chromium/content/browser/browser_main.h index 8fab146352a..02f3af601fe 100644 --- a/chromium/content/browser/browser_main.h +++ b/chromium/content/browser/browser_main.h @@ -5,13 +5,18 @@ #ifndef CONTENT_BROWSER_BROWSER_MAIN_H_ #define CONTENT_BROWSER_BROWSER_MAIN_H_ +#include + #include "content/common/content_export.h" namespace content { +class BrowserProcessSubThread; struct MainFunctionParams; -CONTENT_EXPORT int BrowserMain(const content::MainFunctionParams& parameters); +CONTENT_EXPORT int BrowserMain( + const content::MainFunctionParams& parameters, + std::unique_ptr service_manager_thread); } // namespace content diff --git a/chromium/content/browser/browser_main_loop.cc b/chromium/content/browser/browser_main_loop.cc index 81c93e51b65..10de251accf 100644 --- a/chromium/content/browser/browser_main_loop.cc +++ b/chromium/content/browser/browser_main_loop.cc @@ -16,6 +16,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/debug/alias.h" +#include "base/debug/stack_trace.h" #include "base/deferred_sequenced_task_runner.h" #include "base/feature_list.h" #include "base/location.h" @@ -23,6 +24,7 @@ #include "base/memory/memory_coordinator_proxy.h" #include "base/memory/memory_pressure_monitor.h" #include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_current.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/user_metrics.h" @@ -46,7 +48,7 @@ #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "components/discardable_memory/service/discardable_shared_memory_manager.h" -#include "components/tracing/common/trace_config_file.h" +#include "components/tracing/common/trace_startup_config.h" #include "components/tracing/common/trace_to_console.h" #include "components/tracing/common/tracing_switches.h" #include "components/viz/common/features.h" @@ -55,7 +57,6 @@ #include "components/viz/service/display_embedder/compositing_mode_reporter_impl.h" #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" -#include "content/browser/browser_process_sub_thread.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/compositor/gpu_process_transport_factory.h" @@ -87,6 +88,7 @@ #include "content/browser/tracing/background_tracing_manager_impl.h" #include "content/browser/tracing/tracing_controller_impl.h" #include "content/browser/utility_process_host.h" +#include "content/browser/webrtc/webrtc_internals.h" #include "content/browser/webui/content_web_ui_controller_factory.h" #include "content/browser/webui/url_data_manager.h" #include "content/common/content_switches_internal.h" @@ -105,9 +107,7 @@ #include "content/public/common/main_function_params.h" #include "content/public/common/result_codes.h" #include "content/public/common/service_names.mojom.h" -#include "content/public/common/zygote_buildflags.h" #include "device/gamepad/gamepad_service.h" -#include "gpu/vulkan/buildflags.h" #include "media/audio/audio_manager.h" #include "media/audio/audio_system.h" #include "media/audio/audio_thread_impl.h" @@ -118,15 +118,18 @@ #include "media/mojo/buildflags.h" #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/embedder/scoped_ipc_support.h" +#include "mojo/public/cpp/bindings/sync_call_restrictions.h" #include "net/base/network_change_notifier.h" #include "net/socket/client_socket_factory.h" #include "net/ssl/ssl_config_service.h" #include "ppapi/buildflags/buildflags.h" #include "services/audio/public/cpp/audio_system_factory.h" +#include "services/audio/public/mojom/constants.mojom.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.h" #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h" #include "services/resource_coordinator/public/mojom/service_constants.mojom.h" #include "services/service_manager/runner/common/client_util.h" +#include "services/service_manager/zygote/common/zygote_buildflags.h" #include "skia/ext/event_tracer_impl.h" #include "skia/ext/skia_memory_dump_provider.h" #include "sql/sql_memory_dump_provider.h" @@ -160,7 +163,6 @@ #endif #if defined(OS_MACOSX) -#include "base/allocator/allocator_interception_mac.h" #include "base/memory/memory_pressure_monitor_mac.h" #include "content/browser/cocoa/system_hotkey_helper_mac.h" #include "content/browser/mach_broker_mac.h" @@ -217,10 +219,6 @@ #include "content/browser/media/cdm_registry_impl.h" #endif -#if BUILDFLAG(ENABLE_WEBRTC) -#include "content/browser/webrtc/webrtc_internals.h" -#endif - #if defined(USE_X11) #include "gpu/config/gpu_driver_bug_workaround_type.h" #include "ui/base/x/x11_util_internal.h" // nogncheck @@ -232,10 +230,6 @@ #include "crypto/nss_util.h" #endif -#if BUILDFLAG(ENABLE_VULKAN) -#include "gpu/vulkan/vulkan_implementation.h" -#endif - #if BUILDFLAG(ENABLE_MUS) #include "services/ui/common/image_cursors_set.h" #endif @@ -267,6 +261,12 @@ static void GLibLogHandler(const gchar* log_domain, LOG(DFATAL) << log_domain << ": " << message; } else if (log_level & (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL)) { LOG(ERROR) << log_domain << ": " << message; +#if defined(THREAD_SANITIZER) + // TODO(thomasanderson): This is temporary debugging for + // https://crbug.com/821704. Revert this CL once we have the stack trace: + // https://chromium-review.googlesource.com/#/c/chromium/src/+/1069247 + base::debug::StackTrace().Print(); +#endif } else if (log_level & (G_LOG_LEVEL_WARNING)) { LOG(WARNING) << log_domain << ": " << message; } else if (log_level & @@ -456,13 +456,13 @@ class HDRProxy { public: static void Initialize() { display::win::ScreenWin::SetRequestHDRStatusCallback( - base::Bind(&HDRProxy::RequestHDRStatus)); + base::BindRepeating(&HDRProxy::RequestHDRStatus)); } static void RequestHDRStatus() { // The request must be sent to the GPU process from the IO thread. BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&HDRProxy::RequestOnIOThread)); + base::BindOnce(&HDRProxy::RequestOnIOThread)); } private: @@ -471,7 +471,7 @@ class HDRProxy { GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, false); if (gpu_process_host) { gpu_process_host->RequestHDRStatus( - base::Bind(&HDRProxy::GotResultOnIOThread)); + base::BindRepeating(&HDRProxy::GotResultOnIOThread)); } else { bool hdr_enabled = false; GotResultOnIOThread(hdr_enabled); @@ -479,7 +479,7 @@ class HDRProxy { } static void GotResultOnIOThread(bool hdr_enabled) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&HDRProxy::GotResult, hdr_enabled)); + base::BindOnce(&HDRProxy::GotResult, hdr_enabled)); } static void GotResult(bool hdr_enabled) { display::win::ScreenWin::SetHDREnabled(hdr_enabled); @@ -506,14 +506,8 @@ BrowserMainLoop* BrowserMainLoop::GetInstance() { BrowserMainLoop::BrowserMainLoop(const MainFunctionParams& parameters) : parameters_(parameters), parsed_command_line_(parameters.command_line), - result_code_(RESULT_CODE_NORMAL_EXIT), - created_threads_(false), - // ContentMainRunner should have enabled tracing of the browser process - // when kTraceStartup or kTraceConfigFile is in the command line. - is_tracing_startup_for_duration_( - parameters.command_line.HasSwitch(switches::kTraceStartup) || - (tracing::TraceConfigFile::GetInstance()->IsEnabled() && - tracing::TraceConfigFile::GetInstance()->GetStartupDuration() > 0)) { + result_code_(service_manager::RESULT_CODE_NORMAL_EXIT), + created_threads_(false) { DCHECK(!g_current_browser_main_loop); g_current_browser_main_loop = this; @@ -530,9 +524,13 @@ BrowserMainLoop::~BrowserMainLoop() { g_current_browser_main_loop = nullptr; } -void BrowserMainLoop::Init() { +void BrowserMainLoop::Init( + std::unique_ptr service_manager_thread) { TRACE_EVENT0("startup", "BrowserMainLoop::Init"); + // This is always invoked before |io_thread_| is initialized (i.e. never + // resets it). + io_thread_ = std::move(service_manager_thread); parts_.reset( GetContentClient()->browser()->CreateBrowserMainParts(parameters_)); } @@ -585,7 +583,7 @@ int BrowserMainLoop::EarlyInitialization() { } #endif const int pre_early_init_error_code = parts_->PreEarlyInitialization(); - if (pre_early_init_error_code != content::RESULT_CODE_NORMAL_EXIT) + if (pre_early_init_error_code != service_manager::RESULT_CODE_NORMAL_EXIT) return pre_early_init_error_code; } @@ -599,7 +597,8 @@ int BrowserMainLoop::EarlyInitialization() { command_line->GetSwitchValueASCII(switches::kDisableFeatures)); } -#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_CHROMEOS) +#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \ + defined(OS_ANDROID) // We use quite a few file descriptors for our IPC as well as disk the disk // cache,and the default limit on the Mac is low (256), so bump it up. @@ -607,8 +606,9 @@ int BrowserMainLoop::EarlyInitialization() { // Low soft limits combined with liberal use of file descriptors means power // users can easily hit this limit with many open tabs. Bump up the limit to // an arbitrarily high number. See https://crbug.com/539567 - base::SetFdLimit(8192); -#endif // defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_CHROMEOS) + base::IncreaseFdLimitTo(8192); +#endif // defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_CHROMEOS) || + // defined(OS_ANDROID) #if defined(OS_WIN) net::EnsureWinsockInit(); @@ -635,7 +635,7 @@ int BrowserMainLoop::EarlyInitialization() { if (parts_) parts_->PostEarlyInitialization(); - return content::RESULT_CODE_NORMAL_EXIT; + return service_manager::RESULT_CODE_NORMAL_EXIT; } void BrowserMainLoop::PreMainMessageLoopStart() { @@ -653,18 +653,13 @@ void BrowserMainLoop::MainMessageLoopStart() { TRACE_EVENT0("startup", "BrowserMainLoop::MainMessageLoopStart"); // Create a MessageLoop if one does not already exist for the current thread. - if (!base::MessageLoop::current()) + if (!base::MessageLoopCurrent::Get()) main_message_loop_.reset(new base::MessageLoopForUI); InitializeMainThread(); } void BrowserMainLoop::PostMainMessageLoopStart() { - { - TRACE_EVENT0("startup", - "BrowserMainLoop::Subsystem:CreateBrowserThread::IO"); - InitializeIOThread(); - } { TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:SystemMonitor"); system_monitor_.reset(new base::SystemMonitor); @@ -773,14 +768,6 @@ int BrowserMainLoop::PreCreateThreads() { InitializeMemoryManagementComponent(); -#if defined(OS_MACOSX) - if (base::CommandLine::InitializedForCurrentProcess() && - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableHeapProfiling)) { - base::allocator::PeriodicallyShimNewMallocZones(); - } -#endif - #if BUILDFLAG(ENABLE_PLUGINS) // Prior to any processing happening on the IO thread, we create the // plugin service as it is predominantly used from the IO thread, @@ -938,12 +925,14 @@ int BrowserMainLoop::CreateThreads() { *task_scheduler_init_params.get()); } - // The thread used for BrowserThread::IO is created in - // |PostMainMessageLoopStart()|, but it's only tagged as BrowserThread::IO - // here in order to prevent any code from statically posting to it before + // The |io_thread| can have optionally been injected into Init(), but if not, + // create it here. Thre thread is only tagged as BrowserThread::IO here in + // order to prevent any code from statically posting to it before // CreateThreads() (as such maintaining the invariant that PreCreateThreads() // et al. "happen-before" BrowserThread::IO is "brought up"). - DCHECK(io_thread_); + if (!io_thread_) { + io_thread_ = BrowserProcessSubThread::CreateIOThread(); + } io_thread_->RegisterAsBrowserThread(); created_threads_ = true; @@ -1143,12 +1132,17 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { } } -base::SequencedTaskRunner* BrowserMainLoop::audio_service_runner() { - return audio_service_runner_.get(); +media::AudioManager* BrowserMainLoop::audio_manager() const { + DCHECK(audio_manager_) << "AudioManager is not instantiated - running the " + "audio service out of process?"; + return audio_manager_.get(); } -void BrowserMainLoop::InitializeIOThreadForTesting() { - InitializeIOThread(); +base::SequencedTaskRunner* BrowserMainLoop::audio_service_runner() { + DCHECK(audio_service_runner_) << "The audio service task runner is not " + "instantiated - running the audio service " + "out of process?"; + return audio_service_runner_.get(); } #if !defined(OS_ANDROID) @@ -1164,8 +1158,8 @@ void BrowserMainLoop::GetCompositingModeReporter( // CompositingModeReporter. return; #else - if (features::IsMusEnabled()) { - // Mus == ChromeOS, which doesn't support software compositing, so no need + if (features::IsMashEnabled()) { + // Mash == ChromeOS, which doesn't support software compositing, so no need // to report compositing mode. return; } @@ -1184,7 +1178,7 @@ void BrowserMainLoop::InitializeMainThread() { // Register the main thread. The main thread's task runner should already have // been initialized in MainMessageLoopStart() (or before if - // MessageLoop::current() was externally provided). + // MessageLoopCurrent::Get() was externally provided). DCHECK(base::ThreadTaskRunnerHandle::IsSet()); main_thread_.reset(new BrowserThreadImpl( BrowserThread::UI, base::ThreadTaskRunnerHandle::Get())); @@ -1202,7 +1196,7 @@ int BrowserMainLoop::BrowserThreadsStarted() { InitializeMojo(); #if BUILDFLAG(ENABLE_MUS) - if (features::IsMusEnabled()) { + if (features::IsMashEnabled()) { base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableSurfaceSynchronization); } @@ -1214,11 +1208,6 @@ int BrowserMainLoop::BrowserThreadsStarted() { base::PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::DISPLAY); #endif -#if BUILDFLAG(ENABLE_VULKAN) - if (parsed_command_line_.HasSwitch(switches::kEnableVulkan)) - gpu::InitializeVulkan(); -#endif - // Initialize the GPU shader cache. This needs to be initialized before // BrowserGpuChannelHostFactory below, since that depends on an initialized // ShaderCacheFactory. @@ -1287,7 +1276,7 @@ int BrowserMainLoop::BrowserThreadsStarted() { { TRACE_EVENT0("startup", "BrowserThreadsStarted::Subsystem:AudioMan"); - CreateAudioManager(); + InitializeAudio(); } { @@ -1303,18 +1292,20 @@ int BrowserMainLoop::BrowserThreadsStarted() { device_monitor_linux_.reset( new media::DeviceMonitorLinux(io_thread_->task_runner())); #elif defined(OS_MACOSX) + // On Mac, the audio task runner must belong to the main thread. + // See audio_thread_impl.cc and https://crbug.com/158170. + DCHECK(!audio_manager_ || + audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); device_monitor_mac_.reset( - new media::DeviceMonitorMac(audio_manager_->GetTaskRunner())); + new media::DeviceMonitorMac(base::ThreadTaskRunnerHandle::Get())); #endif -#if BUILDFLAG(ENABLE_WEBRTC) // Instantiated once using CreateSingletonInstance(), and accessed only using // GetInstance(), which is not allowed to create the object. This allows us // to ensure that it cannot be used before objects it relies on have been // created; namely, WebRtcEventLogManager. // Allowed to leak when the browser exits. WebRTCInternals::CreateSingletonInstance(); -#endif // RDH needs the IO thread to be created { @@ -1342,16 +1333,29 @@ int BrowserMainLoop::BrowserThreadsStarted() { { TRACE_EVENT0("startup", "BrowserMainLoop::BrowserThreadsStarted:InitMediaStreamManager"); - media_stream_manager_.reset(new MediaStreamManager( - audio_system_.get(), audio_manager_->GetTaskRunner())); + + scoped_refptr audio_task_runner = + audio_manager_ ? audio_manager_->GetTaskRunner() : nullptr; + +#if defined(OS_MACOSX) + // On Mac, the audio task runner must belong to the main thread. + // See audio_thread_impl.cc and https://crbug.com/158170. + if (audio_task_runner) { + DCHECK(audio_task_runner->BelongsToCurrentThread()); + } else { + audio_task_runner = base::ThreadTaskRunnerHandle::Get(); + } +#endif + + media_stream_manager_ = std::make_unique( + audio_system_.get(), std::move(audio_task_runner)); } { TRACE_EVENT0("startup", "BrowserMainLoop::BrowserThreadsStarted:InitSpeechRecognition"); speech_recognition_manager_.reset(new SpeechRecognitionManagerImpl( - audio_system_.get(), audio_manager_.get(), - media_stream_manager_.get())); + audio_system_.get(), media_stream_manager_.get())); } { @@ -1473,12 +1477,13 @@ bool BrowserMainLoop::InitializeToolkit() { // Env creates the compositor. Aura widgets need the compositor to be created // before they can be initialized by the browser. - env_ = aura::Env::CreateInstance( - features::IsMusEnabled() ? aura::Env::Mode::MUS : aura::Env::Mode::LOCAL); + env_ = aura::Env::CreateInstance(features::IsMashEnabled() + ? aura::Env::Mode::MUS + : aura::Env::Mode::LOCAL); #endif // defined(USE_AURA) #if BUILDFLAG(ENABLE_MUS) - if (features::IsMusEnabled()) + if (features::IsMashEnabled()) image_cursors_set_ = std::make_unique(); #endif @@ -1504,30 +1509,12 @@ void BrowserMainLoop::MainMessageLoopRun() { #endif } -void BrowserMainLoop::InitializeIOThread() { - base::Thread::Options options; - options.message_loop_type = base::MessageLoop::TYPE_IO; -#if defined(OS_ANDROID) || defined(OS_CHROMEOS) - // Up the priority of the |io_thread_| as some of its IPCs relate to - // display tasks. - options.priority = base::ThreadPriority::DISPLAY; -#endif - - io_thread_ = std::make_unique(BrowserThread::IO); - - if (!io_thread_->StartWithOptions(options)) - LOG(FATAL) << "Failed to start BrowserThread::IO"; -} - void BrowserMainLoop::InitializeMojo() { if (!parsed_command_line_.HasSwitch(switches::kSingleProcess)) { // Disallow mojo sync calls in the browser process. Note that we allow sync // calls in single-process mode since renderer IPCs are made from a browser // thread. - bool sync_call_allowed = false; - MojoResult result = mojo::edk::SetProperty( - MOJO_PROPERTY_TYPE_SYNC_CALL_ALLOWED, &sync_call_allowed); - DCHECK_EQ(MOJO_RESULT_OK, result); + mojo::SyncCallRestrictions::DisallowSyncCall(); } mojo_ipc_support_.reset(new mojo::edk::ScopedIPCSupport( @@ -1557,30 +1544,24 @@ void BrowserMainLoop::InitializeMojo() { // Start startup tracing through TracingController's interface. TraceLog has // been enabled in content_main_runner where threads are not available. Now We // need to start tracing for all other tracing agents, which require threads. - if (parsed_command_line_.HasSwitch(switches::kTraceStartup)) { - base::trace_event::TraceConfig trace_config( - parsed_command_line_.GetSwitchValueASCII(switches::kTraceStartup), - parsed_command_line_.GetSwitchValueASCII( - switches::kTraceStartupRecordMode)); + auto* trace_startup_config = tracing::TraceStartupConfig::GetInstance(); + if (trace_startup_config->IsEnabled()) { + // This checks kTraceConfigFile switch. TracingController::GetInstance()->StartTracing( - trace_config, TracingController::StartTracingDoneCallback()); + trace_startup_config->GetTraceConfig(), + TracingController::StartTracingDoneCallback()); } else if (parsed_command_line_.HasSwitch(switches::kTraceToConsole)) { TracingController::GetInstance()->StartTracing( tracing::GetConfigForTraceToConsole(), TracingController::StartTracingDoneCallback()); - } else if (tracing::TraceConfigFile::GetInstance()->IsEnabled()) { - // This checks kTraceConfigFile switch. - TracingController::GetInstance()->StartTracing( - tracing::TraceConfigFile::GetInstance()->GetTraceConfig(), - TracingController::StartTracingDoneCallback()); } // Start tracing to a file for certain duration if needed. Only do this after // starting the main message loop to avoid calling // MessagePumpForUI::ScheduleWork() before MessagePumpForUI::Start() as it // will crash the browser. - if (is_tracing_startup_for_duration_) { + if (trace_startup_config->IsTracingStartupForDuration()) { TRACE_EVENT0("startup", "BrowserMainLoop::InitStartupTracingForDuration"); - InitStartupTracingForDuration(parsed_command_line_); + InitStartupTracingForDuration(); } if (parts_) { @@ -1589,93 +1570,82 @@ void BrowserMainLoop::InitializeMojo() { } } -base::FilePath BrowserMainLoop::GetStartupTraceFileName( - const base::CommandLine& command_line) const { +base::FilePath BrowserMainLoop::GetStartupTraceFileName() const { base::FilePath trace_file; - if (command_line.HasSwitch(switches::kTraceStartup)) { - trace_file = command_line.GetSwitchValuePath( - switches::kTraceStartupFile); - // trace_file = "none" means that startup events will show up for the next - // begin/end tracing (via about:tracing or AutomationProxy::BeginTracing/ - // EndTracing, for example). - if (trace_file == base::FilePath().AppendASCII("none")) - return trace_file; - - 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 - } - } else { + #if defined(OS_ANDROID) - TracingControllerAndroid::GenerateTracingFilePath(&trace_file); + TracingControllerAndroid::GenerateTracingFilePath(&trace_file); #else - trace_file = tracing::TraceConfigFile::GetInstance()->GetResultFile(); -#endif + trace_file = tracing::TraceStartupConfig::GetInstance()->GetResultFile(); + if (trace_file.empty()) { + // Default to saving the startup trace into the current dir. + trace_file = base::FilePath().AppendASCII("chrometrace.log"); } +#endif return trace_file; } -void BrowserMainLoop::InitStartupTracingForDuration( - const base::CommandLine& command_line) { - DCHECK(is_tracing_startup_for_duration_); - - startup_trace_file_ = GetStartupTraceFileName(parsed_command_line_); +void BrowserMainLoop::InitStartupTracingForDuration() { + DCHECK(tracing::TraceStartupConfig::GetInstance() + ->IsTracingStartupForDuration()); - int delay_secs = 5; - if (command_line.HasSwitch(switches::kTraceStartup)) { - std::string delay_str = command_line.GetSwitchValueASCII( - switches::kTraceStartupDuration); - if (!delay_str.empty() && !base::StringToInt(delay_str, &delay_secs)) { - DLOG(WARNING) << "Could not parse --" << switches::kTraceStartupDuration - << "=" << delay_str << " defaulting to 5 (secs)"; - delay_secs = 5; - } - } else { - delay_secs = tracing::TraceConfigFile::GetInstance()->GetStartupDuration(); - } + startup_trace_file_ = GetStartupTraceFileName(); - startup_trace_timer_.Start(FROM_HERE, - base::TimeDelta::FromSeconds(delay_secs), - this, - &BrowserMainLoop::EndStartupTracing); + startup_trace_timer_.Start( + FROM_HERE, + base::TimeDelta::FromSeconds( + tracing::TraceStartupConfig::GetInstance()->GetStartupDuration()), + this, &BrowserMainLoop::EndStartupTracing); } void BrowserMainLoop::EndStartupTracing() { - DCHECK(is_tracing_startup_for_duration_); + // Do nothing if startup tracing is already stopped. + if (!tracing::TraceStartupConfig::GetInstance()->IsEnabled()) + return; - is_tracing_startup_for_duration_ = false; TracingController::GetInstance()->StopTracing( TracingController::CreateFileEndpoint( startup_trace_file_, base::Bind(OnStoppedStartupTracing, startup_trace_file_))); } -void BrowserMainLoop::CreateAudioManager() { +void BrowserMainLoop::InitializeAudio() { DCHECK(!audio_manager_); audio_manager_ = GetContentClient()->browser()->CreateAudioManager( MediaInternals::GetInstance()); - if (!audio_manager_) { + DCHECK_EQ(!!audio_manager_, + GetContentClient()->browser()->OverridesAudioManager()); + + // Do not initialize |audio_manager_| if running out of process. + if (!audio_manager_ && + !base::FeatureList::IsEnabled(features::kAudioServiceOutOfProcess)) { audio_manager_ = media::AudioManager::Create(std::make_unique(), MediaInternals::GetInstance()); + CHECK(audio_manager_); } - CHECK(audio_manager_); - AudioMirroringManager* const mirroring_manager = - AudioMirroringManager::GetInstance(); - audio_manager_->SetDiverterCallbacks( - mirroring_manager->GetAddDiverterCallback(), - mirroring_manager->GetRemoveDiverterCallback()); + // Iff |audio_manager_| is instantiated, the audio service will run + // in-process. Complete the setup for that: + if (audio_manager_) { + AudioMirroringManager* const mirroring_manager = + AudioMirroringManager::GetInstance(); + audio_manager_->SetDiverterCallbacks( + mirroring_manager->GetAddDiverterCallback(), + mirroring_manager->GetRemoveDiverterCallback()); - TRACE_EVENT_INSTANT0("startup", "Starting Audio service task runner", - TRACE_EVENT_SCOPE_THREAD); - audio_service_runner_->StartWithTaskRunner(audio_manager_->GetTaskRunner()); + TRACE_EVENT_INSTANT0("startup", "Starting Audio service task runner", + TRACE_EVENT_SCOPE_THREAD); + audio_service_runner_->StartWithTaskRunner(audio_manager_->GetTaskRunner()); + } + + if (base::FeatureList::IsEnabled(features::kAudioServiceLaunchOnStartup)) { + content::ServiceManagerConnection::GetForProcess() + ->GetConnector() + ->StartService(audio::mojom::kServiceName); + } audio_system_ = audio::CreateAudioSystem( content::ServiceManagerConnection::GetForProcess() @@ -1684,4 +1654,11 @@ void BrowserMainLoop::CreateAudioManager() { CHECK(audio_system_); } +bool BrowserMainLoop::AudioServiceOutOfProcess() const { + // Returns true iff kAudioServiceOutOfProcess feature is enabled and if the + // embedder does not provide its own in-process AudioManager. + return base::FeatureList::IsEnabled(features::kAudioServiceOutOfProcess) && + !GetContentClient()->browser()->OverridesAudioManager(); +} + } // namespace content diff --git a/chromium/content/browser/browser_main_loop.h b/chromium/content/browser/browser_main_loop.h index 20b2ac7b95a..c8618c586f5 100644 --- a/chromium/content/browser/browser_main_loop.h +++ b/chromium/content/browser/browser_main_loop.h @@ -13,12 +13,17 @@ #include "base/memory/ref_counted.h" #include "base/timer/timer.h" #include "build/build_config.h" +#include "content/browser/browser_process_sub_thread.h" #include "content/public/browser/browser_main_runner.h" #include "media/media_buildflags.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "services/viz/public/interfaces/compositing/compositing_mode_watcher.mojom.h" #include "ui/base/ui_features.h" +#if defined(OS_CHROMEOS) +#include "content/browser/media/keyboard_mic_registration.h" +#endif + #if defined(USE_AURA) namespace aura { class Env; @@ -91,7 +96,6 @@ class HostFrameSinkManager; namespace content { class BrowserMainParts; class BrowserOnlineStateObserver; -class BrowserProcessSubThread; class BrowserThreadImpl; class LoaderDelegateImpl; class MediaStreamManager; @@ -125,7 +129,10 @@ class CONTENT_EXPORT BrowserMainLoop { explicit BrowserMainLoop(const MainFunctionParams& parameters); virtual ~BrowserMainLoop(); - void Init(); + // |service_manager_thread| is optional. If set, it will be registered as + // BrowserThread::IO in CreateThreads() instead of creating a brand new + // thread. + void Init(std::unique_ptr service_manager_thread); // Return value is exit status. Anything other than RESULT_CODE_NORMAL_EXIT // is considered an error. @@ -153,12 +160,11 @@ class CONTENT_EXPORT BrowserMainLoop { // through stopping threads to PostDestroyThreads. void ShutdownThreadsAndCleanUp(); - void InitializeIOThreadForTesting(); - int GetResultCode() const { return result_code_; } - media::AudioManager* audio_manager() const { return audio_manager_.get(); } + media::AudioManager* audio_manager() const; base::SequencedTaskRunner* audio_service_runner(); + bool AudioServiceOutOfProcess() const; media::AudioSystem* audio_system() const { return audio_system_.get(); } MediaStreamManager* media_stream_manager() const { return media_stream_manager_.get(); @@ -166,15 +172,20 @@ class CONTENT_EXPORT BrowserMainLoop { media::UserInputMonitor* user_input_monitor() const { return user_input_monitor_.get(); } + +#if defined(OS_CHROMEOS) + KeyboardMicRegistration* keyboard_mic_registration() { + return &keyboard_mic_registration_; + } +#endif + discardable_memory::DiscardableSharedMemoryManager* discardable_shared_memory_manager() const { return discardable_shared_memory_manager_.get(); } midi::MidiService* midi_service() const { return midi_service_.get(); } - bool is_tracing_startup_for_duration() const { - return is_tracing_startup_for_duration_; - } + base::FilePath GetStartupTraceFileName() const; const base::FilePath& startup_trace_file() const { return startup_trace_file_; @@ -243,17 +254,11 @@ class CONTENT_EXPORT BrowserMainLoop { void MainMessageLoopRun(); - // Initializes |io_thread_|. It will not be promoted to BrowserThread::IO - // until CreateThreads(). - void InitializeIOThread(); - void InitializeMojo(); - base::FilePath GetStartupTraceFileName( - const base::CommandLine& command_line) const; - void InitStartupTracingForDuration(const base::CommandLine& command_line); + void InitStartupTracingForDuration(); void EndStartupTracing(); - void CreateAudioManager(); + void InitializeAudio(); bool UsingInProcessGpu() const; @@ -282,7 +287,6 @@ class CONTENT_EXPORT BrowserMainLoop { const base::CommandLine& parsed_command_line_; int result_code_; bool created_threads_; // True if the non-UI threads were created. - bool is_tracing_startup_for_duration_; // Members initialized in |MainMessageLoopStart()| --------------------------- std::unique_ptr main_message_loop_; @@ -345,10 +349,20 @@ class CONTENT_EXPORT BrowserMainLoop { // |user_input_monitor_| has to outlive |audio_manager_|, so declared first. std::unique_ptr user_input_monitor_; + + // |audio_manager_| is not instantiated when the audio service runs out of + // process. std::unique_ptr audio_manager_; + + // Task runner for the audio service when it runs in the browser process. scoped_refptr audio_service_runner_; + std::unique_ptr audio_system_; +#if defined(OS_CHROMEOS) + KeyboardMicRegistration keyboard_mic_registration_; +#endif + std::unique_ptr midi_service_; // Must be deleted on the IO thread. diff --git a/chromium/content/browser/browser_main_loop_unittest.cc b/chromium/content/browser/browser_main_loop_unittest.cc index 728331bda09..24e38ba8a61 100644 --- a/chromium/content/browser/browser_main_loop_unittest.cc +++ b/chromium/content/browser/browser_main_loop_unittest.cc @@ -29,7 +29,7 @@ TEST(BrowserMainLoopTest, CreateThreadsInSingleProcess) { *scoped_command_line.GetProcessCommandLine()); BrowserMainLoop browser_main_loop(main_function_params); browser_main_loop.MainMessageLoopStart(); - browser_main_loop.InitializeIOThreadForTesting(); + browser_main_loop.Init(nullptr); browser_main_loop.CreateThreads(); EXPECT_GE(base::TaskScheduler::GetInstance() ->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated( diff --git a/chromium/content/browser/browser_main_runner.cc b/chromium/content/browser/browser_main_runner.cc deleted file mode 100644 index 20d32d31d87..00000000000 --- a/chromium/content/browser/browser_main_runner.cc +++ /dev/null @@ -1,269 +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/public/browser/browser_main_runner.h" - -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/debug/debugger.h" -#include "base/debug/leak_annotations.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/metrics/histogram_macros.h" -#include "base/run_loop.h" -#include "base/sampling_heap_profiler/sampling_heap_profiler.h" -#include "base/strings/string_number_conversions.h" -#include "base/synchronization/atomic_flag.h" -#include "base/time/time.h" -#include "base/trace_event/heap_profiler_allocation_context_tracker.h" -#include "base/trace_event/trace_event.h" -#include "build/build_config.h" -#include "components/tracing/common/trace_config_file.h" -#include "components/tracing/common/tracing_switches.h" -#include "content/browser/browser_main_loop.h" -#include "content/browser/browser_shutdown_profile_dumper.h" -#include "content/browser/notification_service_impl.h" -#include "content/common/content_switches_internal.h" -#include "content/public/common/content_switches.h" -#include "content/public/common/main_function_params.h" -#include "third_party/skia/include/core/SkGraphics.h" -#include "ui/base/ime/input_method_initializer.h" - -#if defined(OS_ANDROID) -#include "content/browser/android/tracing_controller_android.h" -#endif - -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#include "ui/base/win/scoped_ole_initializer.h" -#include "ui/gfx/win/direct_write.h" -#endif - -namespace content { - -namespace { - -base::LazyInstance::Leaky g_exited_main_message_loop; - -} // namespace - -class BrowserMainRunnerImpl : public BrowserMainRunner { - public: - BrowserMainRunnerImpl() - : initialization_started_(false), is_shutdown_(false) {} - - ~BrowserMainRunnerImpl() override { - if (initialization_started_ && !is_shutdown_) - Shutdown(); - } - - int Initialize(const MainFunctionParams& parameters) override { - SCOPED_UMA_HISTOGRAM_LONG_TIMER( - "Startup.BrowserMainRunnerImplInitializeLongTime"); - TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize"); - - // On Android we normally initialize the browser in a series of UI thread - // tasks. While this is happening a second request can come from the OS or - // another application to start the browser. If this happens then we must - // not run these parts of initialization twice. - if (!initialization_started_) { - initialization_started_ = true; - - const base::TimeTicks start_time_step1 = base::TimeTicks::Now(); - - base::SamplingHeapProfiler::InitTLSSlot(); - if (parameters.command_line.HasSwitch(switches::kSamplingHeapProfiler)) { - base::SamplingHeapProfiler* profiler = - base::SamplingHeapProfiler::GetInstance(); - unsigned sampling_interval = 0; - bool parsed = - base::StringToUint(parameters.command_line.GetSwitchValueASCII( - switches::kSamplingHeapProfiler), - &sampling_interval); - if (parsed && sampling_interval > 0) - profiler->SetSamplingInterval(sampling_interval * 1024); - profiler->Start(); - } - - SkGraphics::Init(); - - if (parameters.command_line.HasSwitch(switches::kWaitForDebugger)) - base::debug::WaitForDebugger(60, true); - - if (parameters.command_line.HasSwitch(switches::kBrowserStartupDialog)) - WaitForDebugger("Browser"); - - notification_service_.reset(new NotificationServiceImpl); - -#if defined(OS_WIN) - // Ole must be initialized before starting message pump, so that TSF - // (Text Services Framework) module can interact with the message pump - // on Windows 8 Metro mode. - ole_initializer_.reset(new ui::ScopedOleInitializer); - // Enable DirectWrite font rendering if needed. - gfx::win::MaybeInitializeDirectWrite(); -#endif // OS_WIN - - main_loop_.reset(new BrowserMainLoop(parameters)); - - main_loop_->Init(); - - if (parameters.created_main_parts_closure) { - parameters.created_main_parts_closure->Run(main_loop_->parts()); - delete parameters.created_main_parts_closure; - } - - const int early_init_error_code = main_loop_->EarlyInitialization(); - if (early_init_error_code > 0) - return early_init_error_code; - - // Must happen before we try to use a message loop or display any UI. - if (!main_loop_->InitializeToolkit()) - return 1; - - main_loop_->PreMainMessageLoopStart(); - main_loop_->MainMessageLoopStart(); - main_loop_->PostMainMessageLoopStart(); - -// WARNING: If we get a WM_ENDSESSION, objects created on the stack here -// are NOT deleted. If you need something to run during WM_ENDSESSION add it -// to browser_shutdown::Shutdown or BrowserProcess::EndSession. - - ui::InitializeInputMethod(); - UMA_HISTOGRAM_TIMES("Startup.BrowserMainRunnerImplInitializeStep1Time", - base::TimeTicks::Now() - start_time_step1); - } - const base::TimeTicks start_time_step2 = base::TimeTicks::Now(); - main_loop_->CreateStartupTasks(); - int result_code = main_loop_->GetResultCode(); - if (result_code > 0) - return result_code; - - UMA_HISTOGRAM_TIMES("Startup.BrowserMainRunnerImplInitializeStep2Time", - base::TimeTicks::Now() - start_time_step2); - - // Return -1 to indicate no early termination. - return -1; - } - -#if defined(OS_ANDROID) - void SynchronouslyFlushStartupTasks() override { - main_loop_->SynchronouslyFlushStartupTasks(); - } -#endif - - int Run() override { - DCHECK(initialization_started_); - DCHECK(!is_shutdown_); - main_loop_->RunMainMessageLoopParts(); - return main_loop_->GetResultCode(); - } - - void Shutdown() override { - DCHECK(initialization_started_); - DCHECK(!is_shutdown_); - -#ifdef LEAK_SANITIZER - // Invoke leak detection now, to avoid dealing with shutdown-only leaks. - // Normally this will have already happened in - // BroserProcessImpl::ReleaseModule(), so this call has no effect. This is - // only for processes which do not instantiate a BrowserProcess. - // If leaks are found, the process will exit here. - __lsan_do_leak_check(); -#endif - - main_loop_->PreShutdown(); - - // If startup tracing has not been finished yet, replace it's dumper - // with special version, which would save trace file on exit (i.e. - // startup tracing becomes a version of shutdown tracing). - // There are two cases: - // 1. Startup duration is not reached. - // 2. Or startup duration is not specified for --trace-config-file flag. - std::unique_ptr startup_profiler; - if (main_loop_->is_tracing_startup_for_duration()) { - main_loop_->StopStartupTracingTimer(); - if (main_loop_->startup_trace_file() != - base::FilePath().AppendASCII("none")) { - startup_profiler.reset( - new BrowserShutdownProfileDumper(main_loop_->startup_trace_file())); - } - } else if (tracing::TraceConfigFile::GetInstance()->IsEnabled()) { - base::FilePath result_file; -#if defined(OS_ANDROID) - TracingControllerAndroid::GenerateTracingFilePath(&result_file); -#else - result_file = tracing::TraceConfigFile::GetInstance()->GetResultFile(); -#endif - startup_profiler.reset(new BrowserShutdownProfileDumper(result_file)); - } - - // The shutdown tracing got enabled in AttemptUserExit earlier, but someone - // needs to write the result to disc. For that a dumper needs to get created - // which will dump the traces to disc when it gets destroyed. - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - std::unique_ptr shutdown_profiler; - if (command_line.HasSwitch(switches::kTraceShutdown)) { - shutdown_profiler.reset(new BrowserShutdownProfileDumper( - BrowserShutdownProfileDumper::GetShutdownProfileFileName())); - } - - { - // The trace event has to stay between profiler creation and destruction. - TRACE_EVENT0("shutdown", "BrowserMainRunner"); - g_exited_main_message_loop.Get().Set(); - - main_loop_->ShutdownThreadsAndCleanUp(); - - ui::ShutdownInputMethod(); - #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. - if (base::RunLoop::IsRunningOnCurrentThread()) - base::RunLoop::QuitCurrentDeprecated(); - #endif - main_loop_.reset(nullptr); - - notification_service_.reset(nullptr); - - is_shutdown_ = true; - } - } - - protected: - // True if we have started to initialize the runner. - bool initialization_started_; - - // True if the runner has been shut down. - bool is_shutdown_; - - std::unique_ptr notification_service_; - std::unique_ptr main_loop_; -#if defined(OS_WIN) - std::unique_ptr ole_initializer_; -#endif - - private: - DISALLOW_COPY_AND_ASSIGN(BrowserMainRunnerImpl); -}; - -// static -BrowserMainRunner* BrowserMainRunner::Create() { - return new BrowserMainRunnerImpl(); -} - -// static -bool BrowserMainRunner::ExitedMainMessageLoop() { - return g_exited_main_message_loop.IsCreated() && - g_exited_main_message_loop.Get().IsSet(); -} - -} // namespace content diff --git a/chromium/content/browser/browser_main_runner_impl.cc b/chromium/content/browser/browser_main_runner_impl.cc new file mode 100644 index 00000000000..6381234ca78 --- /dev/null +++ b/chromium/content/browser/browser_main_runner_impl.cc @@ -0,0 +1,256 @@ +// Copyright (c) 2012 The Chromium Authors. 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_main_runner_impl.h" + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/debug/debugger.h" +#include "base/debug/leak_annotations.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/metrics/histogram_macros.h" +#include "base/run_loop.h" +#include "base/sampling_heap_profiler/sampling_heap_profiler.h" +#include "base/strings/string_number_conversions.h" +#include "base/synchronization/atomic_flag.h" +#include "base/time/time.h" +#include "base/trace_event/heap_profiler_allocation_context_tracker.h" +#include "base/trace_event/trace_event.h" +#include "build/build_config.h" +#include "components/tracing/common/trace_startup_config.h" +#include "components/tracing/common/tracing_switches.h" +#include "content/browser/browser_main_loop.h" +#include "content/browser/browser_process_sub_thread.h" +#include "content/browser/browser_shutdown_profile_dumper.h" +#include "content/browser/notification_service_impl.h" +#include "content/common/content_switches_internal.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/main_function_params.h" +#include "third_party/skia/include/core/SkGraphics.h" +#include "ui/base/ime/input_method_initializer.h" + +#if defined(OS_ANDROID) +#include "content/browser/android/tracing_controller_android.h" +#endif + +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#include "ui/base/win/scoped_ole_initializer.h" +#include "ui/gfx/win/direct_write.h" +#endif + +namespace content { +namespace { + +base::LazyInstance::Leaky g_exited_main_message_loop; + +} // namespace + +// static +BrowserMainRunnerImpl* BrowserMainRunnerImpl::Create() { + return new BrowserMainRunnerImpl(); +} + +BrowserMainRunnerImpl::BrowserMainRunnerImpl() + : initialization_started_(false), is_shutdown_(false) {} + +BrowserMainRunnerImpl::~BrowserMainRunnerImpl() { + if (initialization_started_ && !is_shutdown_) + Shutdown(); +} + +int BrowserMainRunnerImpl::Initialize(const MainFunctionParams& parameters) { + return Initialize(parameters, nullptr); +} + +int BrowserMainRunnerImpl::Initialize( + const MainFunctionParams& parameters, + std::unique_ptr service_manager_thread) { + SCOPED_UMA_HISTOGRAM_LONG_TIMER( + "Startup.BrowserMainRunnerImplInitializeLongTime"); + TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize"); + + // On Android we normally initialize the browser in a series of UI thread + // tasks. While this is happening a second request can come from the OS or + // another application to start the browser. If this happens then we must + // not run these parts of initialization twice. + if (!initialization_started_) { + initialization_started_ = true; + + const base::TimeTicks start_time_step1 = base::TimeTicks::Now(); + + base::SamplingHeapProfiler::InitTLSSlot(); + if (parameters.command_line.HasSwitch(switches::kSamplingHeapProfiler)) { + base::SamplingHeapProfiler* profiler = + base::SamplingHeapProfiler::GetInstance(); + unsigned sampling_interval = 0; + bool parsed = + base::StringToUint(parameters.command_line.GetSwitchValueASCII( + switches::kSamplingHeapProfiler), + &sampling_interval); + if (parsed && sampling_interval > 0) + profiler->SetSamplingInterval(sampling_interval * 1024); + profiler->Start(); + } + + SkGraphics::Init(); + + if (parameters.command_line.HasSwitch(switches::kWaitForDebugger)) + base::debug::WaitForDebugger(60, true); + + if (parameters.command_line.HasSwitch(switches::kBrowserStartupDialog)) + WaitForDebugger("Browser"); + + notification_service_.reset(new NotificationServiceImpl); + +#if defined(OS_WIN) + // Ole must be initialized before starting message pump, so that TSF + // (Text Services Framework) module can interact with the message pump + // on Windows 8 Metro mode. + ole_initializer_.reset(new ui::ScopedOleInitializer); + // Enable DirectWrite font rendering if needed. + gfx::win::MaybeInitializeDirectWrite(); +#endif // OS_WIN + + main_loop_.reset(new BrowserMainLoop(parameters)); + + main_loop_->Init(std::move(service_manager_thread)); + + if (parameters.created_main_parts_closure) { + parameters.created_main_parts_closure->Run(main_loop_->parts()); + delete parameters.created_main_parts_closure; + } + + const int early_init_error_code = main_loop_->EarlyInitialization(); + if (early_init_error_code > 0) + return early_init_error_code; + + // Must happen before we try to use a message loop or display any UI. + if (!main_loop_->InitializeToolkit()) + return 1; + + main_loop_->PreMainMessageLoopStart(); + main_loop_->MainMessageLoopStart(); + main_loop_->PostMainMessageLoopStart(); + + // WARNING: If we get a WM_ENDSESSION, objects created on the stack here + // are NOT deleted. If you need something to run during WM_ENDSESSION add it + // to browser_shutdown::Shutdown or BrowserProcess::EndSession. + + ui::InitializeInputMethod(); + UMA_HISTOGRAM_TIMES("Startup.BrowserMainRunnerImplInitializeStep1Time", + base::TimeTicks::Now() - start_time_step1); + } + const base::TimeTicks start_time_step2 = base::TimeTicks::Now(); + main_loop_->CreateStartupTasks(); + int result_code = main_loop_->GetResultCode(); + if (result_code > 0) + return result_code; + + UMA_HISTOGRAM_TIMES("Startup.BrowserMainRunnerImplInitializeStep2Time", + base::TimeTicks::Now() - start_time_step2); + + // Return -1 to indicate no early termination. + return -1; +} + +#if defined(OS_ANDROID) +void BrowserMainRunnerImpl::SynchronouslyFlushStartupTasks() { + main_loop_->SynchronouslyFlushStartupTasks(); +} +#endif + +int BrowserMainRunnerImpl::Run() { + DCHECK(initialization_started_); + DCHECK(!is_shutdown_); + main_loop_->RunMainMessageLoopParts(); + return main_loop_->GetResultCode(); +} + +void BrowserMainRunnerImpl::Shutdown() { + DCHECK(initialization_started_); + DCHECK(!is_shutdown_); + +#ifdef LEAK_SANITIZER + // Invoke leak detection now, to avoid dealing with shutdown-only leaks. + // Normally this will have already happened in + // BroserProcessImpl::ReleaseModule(), so this call has no effect. This is + // only for processes which do not instantiate a BrowserProcess. + // If leaks are found, the process will exit here. + __lsan_do_leak_check(); +#endif + + main_loop_->PreShutdown(); + + // If startup tracing has not been finished yet, replace it's dumper + // with special version, which would save trace file on exit (i.e. + // startup tracing becomes a version of shutdown tracing). + // There are two cases: + // 1. Startup duration is not reached. + // 2. Or startup duration is not specified for --trace-config-file flag. + std::unique_ptr startup_profiler; + if (tracing::TraceStartupConfig::GetInstance() + ->IsTracingStartupForDuration()) { + main_loop_->StopStartupTracingTimer(); + if (main_loop_->startup_trace_file() != + base::FilePath().AppendASCII("none")) { + startup_profiler.reset( + new BrowserShutdownProfileDumper(main_loop_->startup_trace_file())); + } + } else if (tracing::TraceStartupConfig::GetInstance()->IsEnabled()) { + base::FilePath result_file = main_loop_->GetStartupTraceFileName(); + startup_profiler.reset(new BrowserShutdownProfileDumper(result_file)); + } + + // The shutdown tracing got enabled in AttemptUserExit earlier, but someone + // needs to write the result to disc. For that a dumper needs to get created + // which will dump the traces to disc when it gets destroyed. + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + std::unique_ptr shutdown_profiler; + if (command_line.HasSwitch(switches::kTraceShutdown)) { + shutdown_profiler.reset(new BrowserShutdownProfileDumper( + BrowserShutdownProfileDumper::GetShutdownProfileFileName())); + } + + { + // The trace event has to stay between profiler creation and destruction. + TRACE_EVENT0("shutdown", "BrowserMainRunner"); + g_exited_main_message_loop.Get().Set(); + + main_loop_->ShutdownThreadsAndCleanUp(); + + ui::ShutdownInputMethod(); +#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. + if (base::RunLoop::IsRunningOnCurrentThread()) + base::RunLoop::QuitCurrentDeprecated(); +#endif + main_loop_.reset(nullptr); + + notification_service_.reset(nullptr); + + is_shutdown_ = true; + } +} + +// static +BrowserMainRunner* BrowserMainRunner::Create() { + return BrowserMainRunnerImpl::Create(); +} + +// static +bool BrowserMainRunner::ExitedMainMessageLoop() { + return g_exited_main_message_loop.IsCreated() && + g_exited_main_message_loop.Get().IsSet(); +} + +} // namespace content diff --git a/chromium/content/browser/browser_main_runner_impl.h b/chromium/content/browser/browser_main_runner_impl.h new file mode 100644 index 00000000000..adb084fe27c --- /dev/null +++ b/chromium/content/browser/browser_main_runner_impl.h @@ -0,0 +1,65 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_BROWSER_MAIN_RUNNER_IMPL_H_ +#define CONTENT_BROWSER_BROWSER_MAIN_RUNNER_IMPL_H_ + +#include + +#include "base/macros.h" +#include "build/build_config.h" +#include "content/public/browser/browser_main_runner.h" + +#if defined(OS_WIN) +namespace ui { +class ScopedOleInitializer; +} +#endif + +namespace content { + +class BrowserProcessSubThread; +class BrowserMainLoop; +class NotificationServiceImpl; + +class BrowserMainRunnerImpl : public BrowserMainRunner { + public: + static BrowserMainRunnerImpl* Create(); + + BrowserMainRunnerImpl(); + ~BrowserMainRunnerImpl() override; + + // BrowserMainRunner: + int Initialize(const MainFunctionParams& parameters) override; +#if defined(OS_ANDROID) + void SynchronouslyFlushStartupTasks() override; +#endif + int Run() override; + void Shutdown() override; + + // Initialize all necessary browser state with a |service_manager_thread| + // on which ServiceManager is currently running. + int Initialize( + const MainFunctionParams& parameters, + std::unique_ptr service_manager_thread); + + private: + // True if we have started to initialize the runner. + bool initialization_started_; + + // True if the runner has been shut down. + bool is_shutdown_; + + std::unique_ptr notification_service_; + std::unique_ptr main_loop_; +#if defined(OS_WIN) + std::unique_ptr ole_initializer_; +#endif + + DISALLOW_COPY_AND_ASSIGN(BrowserMainRunnerImpl); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_BROWSER_MAIN_RUNNER_IMPL_H_ diff --git a/chromium/content/browser/browser_plugin/browser_plugin_guest.cc b/chromium/content/browser/browser_plugin/browser_plugin_guest.cc index 15994d5dada..a0bc9305e62 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_guest.cc +++ b/chromium/content/browser/browser_plugin/browser_plugin_guest.cc @@ -12,7 +12,6 @@ #include #include "base/macros.h" -#include "base/message_loop/message_loop.h" #include "base/metrics/user_metrics.h" #include "base/pickle.h" #include "base/strings/utf_string_conversions.h" @@ -36,9 +35,8 @@ #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/frame_resize_params.h" +#include "content/common/frame_visual_properties.h" #include "content/common/input/ime_text_span_conversions.h" -#include "content/common/input_messages.h" #include "content/common/text_input_state.h" #include "content/common/view_messages.h" #include "content/public/browser/browser_context.h" @@ -176,11 +174,11 @@ void BrowserPluginGuest::DisableAutoResize() { browser_plugin_instance_id_)); } -void BrowserPluginGuest::ResizeDueToAutoResize(const gfx::Size& new_size, - uint64_t sequence_number) { +void BrowserPluginGuest::DidUpdateVisualProperties( + const cc::RenderFrameMetadata& metadata) { SendMessageToEmbedder( - std::make_unique( - browser_plugin_instance_id_, sequence_number)); + std::make_unique( + browser_plugin_instance_id_, metadata)); } void BrowserPluginGuest::SizeContents(const gfx::Size& new_size) { @@ -288,8 +286,8 @@ bool BrowserPluginGuest::OnMessageReceivedFromEmbedder( IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetFocus, OnSetFocus) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SetVisibility, OnSetVisibility) IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UnlockMouse_ACK, OnUnlockMouseAck) - IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_UpdateResizeParams, - OnUpdateResizeParams) + IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_SynchronizeVisualProperties, + OnSynchronizeVisualProperties) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -715,8 +713,6 @@ bool BrowserPluginGuest::OnMessageReceived(const IPC::Message& message) { bool handled = true; // In --site-per-process, we do not need most of BrowserPluginGuest to drive // inner WebContents. - // Right now InputHostMsg_ImeCompositionRangeChanged hits NOTREACHED() in - // RWHVChildFrame, so we're disabling message handling entirely here. // TODO(lazyboy): Fix this as part of http://crbug.com/330264. The required // parts of code from this class should be extracted to a separate class for // --site-per-process. @@ -724,12 +720,6 @@ bool BrowserPluginGuest::OnMessageReceived(const IPC::Message& message) { return false; IPC_BEGIN_MESSAGE_MAP(BrowserPluginGuest, message) - IPC_MESSAGE_HANDLER(InputHostMsg_ImeCancelComposition, - OnImeCancelComposition) -#if defined(OS_MACOSX) || defined(USE_AURA) - IPC_MESSAGE_HANDLER(InputHostMsg_ImeCompositionRangeChanged, - OnImeCompositionRangeChanged) -#endif IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, OnHasTouchEventHandlers) IPC_MESSAGE_HANDLER(ViewHostMsg_LockMouse, OnLockMouse) @@ -1041,13 +1031,15 @@ void BrowserPluginGuest::OnUnlockMouseAck(int browser_plugin_instance_id) { mouse_locked_ = false; } -void BrowserPluginGuest::OnUpdateResizeParams( +void BrowserPluginGuest::OnSynchronizeVisualProperties( int browser_plugin_instance_id, const viz::LocalSurfaceId& local_surface_id, - const FrameResizeParams& resize_params) { + const FrameVisualProperties& visual_properties) { if (local_surface_id_ > local_surface_id || - ((frame_rect_.size() != resize_params.screen_space_rect.size() || - screen_info_ != resize_params.screen_info) && + ((frame_rect_.size() != visual_properties.screen_space_rect.size() || + screen_info_ != visual_properties.screen_info || + capture_sequence_number_ != + visual_properties.capture_sequence_number) && local_surface_id_ == local_surface_id)) { SiteInstance* owner_site_instance = delegate_->GetOwnerSiteInstance(); bad_message::ReceivedBadMessage( @@ -1056,30 +1048,35 @@ void BrowserPluginGuest::OnUpdateResizeParams( return; } - screen_info_ = resize_params.screen_info; - frame_rect_ = resize_params.screen_space_rect; + screen_info_ = visual_properties.screen_info; + frame_rect_ = visual_properties.screen_space_rect; GetWebContents()->SendScreenRects(); local_surface_id_ = local_surface_id; + bool capture_sequence_number_changed = + capture_sequence_number_ != visual_properties.capture_sequence_number; + capture_sequence_number_ = visual_properties.capture_sequence_number; RenderWidgetHostView* view = web_contents()->GetRenderWidgetHostView(); if (!view) return; + // We could add functionality to set a specific capture sequence number on the + // |view|, but knowing that it's changed is sufficient for us simply request + // that our RenderWidgetHostView synchronizes its surfaces. Note that this + // should only happen during layout tests, since that is the only call that + // should trigger the capture sequence number to change. + if (capture_sequence_number_changed) + view->EnsureSurfaceSynchronizedForLayoutTest(); + RenderWidgetHostImpl* render_widget_host = RenderWidgetHostImpl::From(view->GetRenderWidgetHost()); DCHECK(render_widget_host); - render_widget_host->SetAutoResize(resize_params.auto_resize_enabled, - resize_params.min_size_for_auto_resize, - resize_params.max_size_for_auto_resize); - - if (render_widget_host->auto_resize_enabled()) { - render_widget_host->DidAllocateLocalSurfaceIdForAutoResize( - resize_params.auto_resize_sequence_number); - return; - } + render_widget_host->SetAutoResize(visual_properties.auto_resize_enabled, + visual_properties.min_size_for_auto_resize, + visual_properties.max_size_for_auto_resize); - render_widget_host->WasResized(); + render_widget_host->SynchronizeVisualProperties(); } void BrowserPluginGuest::OnHasTouchEventHandlers(bool accept) { @@ -1133,19 +1130,4 @@ void BrowserPluginGuest::OnTextInputStateChanged(const TextInputState& params) { web_contents()->GetRenderWidgetHostView())); } -void BrowserPluginGuest::OnImeCancelComposition() { - static_cast( - web_contents()->GetRenderWidgetHostView())->ImeCancelComposition(); -} - -#if defined(OS_MACOSX) || defined(USE_AURA) -void BrowserPluginGuest::OnImeCompositionRangeChanged( - const gfx::Range& range, - const std::vector& character_bounds) { - static_cast( - web_contents()->GetRenderWidgetHostView())->ImeCompositionRangeChanged( - range, character_bounds); -} -#endif - } // 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 5c2d39745cc..964137dafc0 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_guest.h +++ b/chromium/content/browser/browser_plugin/browser_plugin_guest.h @@ -30,6 +30,7 @@ #include "base/values.h" #include "build/build_config.h" #include "components/viz/common/surfaces/local_surface_id.h" +#include "components/viz/common/surfaces/scoped_surface_id_allocator.h" #include "content/common/edit_command.h" #include "content/public/browser/browser_plugin_guest_delegate.h" #include "content/public/browser/guest_host.h" @@ -56,6 +57,10 @@ namespace gfx { class Range; } // namespace gfx +namespace cc { +class RenderFrameMetadata; +} // namespace cc + namespace viz { class LocalSurfaceId; class SurfaceInfo; @@ -71,7 +76,7 @@ class RenderWidgetHostView; class RenderWidgetHostViewBase; class SiteInstance; struct DropData; -struct FrameResizeParams; +struct FrameVisualProperties; struct ScreenInfo; struct TextInputState; @@ -181,8 +186,7 @@ class CONTENT_EXPORT BrowserPluginGuest : public GuestHost, void EnableAutoResize(const gfx::Size& min_size, const gfx::Size& max_size); void DisableAutoResize(); - void ResizeDueToAutoResize(const gfx::Size& new_size, - uint64_t sequence_number); + void DidUpdateVisualProperties(const cc::RenderFrameMetadata& metadata); // WebContentsObserver implementation. void DidFinishNavigation(NavigationHandle* navigation_handle) override; @@ -334,9 +338,10 @@ class CONTENT_EXPORT BrowserPluginGuest : public GuestHost, void OnSetVisibility(int instance_id, bool visible); void OnUnlockMouse(); void OnUnlockMouseAck(int instance_id); - void OnUpdateResizeParams(int instance_id, - const viz::LocalSurfaceId& local_surface_id, - const FrameResizeParams& resize_params); + void OnSynchronizeVisualProperties( + int instance_id, + const viz::LocalSurfaceId& local_surface_id, + const FrameVisualProperties& visual_properties); void OnTextInputStateChanged(const TextInputState& params); void OnImeSetComposition( @@ -349,12 +354,6 @@ class CONTENT_EXPORT BrowserPluginGuest : public GuestHost, int relative_cursor_pos); void OnImeFinishComposingText(int instance_id, bool keep_selection); void OnExtendSelectionAndDelete(int instance_id, int before, int after); - void OnImeCancelComposition(); -#if defined(OS_MACOSX) || defined(USE_AURA) - void OnImeCompositionRangeChanged( - const gfx::Range& range, - const std::vector& character_bounds); -#endif // Message handlers for messages from guest. void OnHandleInputEventAck( @@ -456,6 +455,7 @@ class CONTENT_EXPORT BrowserPluginGuest : public GuestHost, viz::LocalSurfaceId local_surface_id_; ScreenInfo screen_info_; + uint32_t capture_sequence_number_ = 0u; // Weak pointer used to ask GeolocationPermissionContext about geolocation // permission. 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 fec9859d9b3..93bf2a10367 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc +++ b/chromium/content/browser/browser_plugin/browser_plugin_message_filter.cc @@ -28,6 +28,11 @@ BrowserPluginMessageFilter::~BrowserPluginMessageFilter() { bool BrowserPluginMessageFilter::OnMessageReceived( const IPC::Message& message) { + if (sub_filter_for_testing_ && + sub_filter_for_testing_->OnMessageReceived(message)) { + return true; + } + // Any message requested by a BrowserPluginGuest should be routed through // a BrowserPluginGuestManager. if (BrowserPluginGuest::ShouldForwardToBrowserPluginGuest(message)) { @@ -82,4 +87,9 @@ void BrowserPluginMessageFilter::ForwardMessageToGuest( ->OnMessageReceivedFromEmbedder(message); } +void BrowserPluginMessageFilter::SetSubFilterForTesting( + scoped_refptr sub_filter) { + sub_filter_for_testing_ = sub_filter; +} + } // 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 646f424825e..7c03bd30a64 100644 --- a/chromium/content/browser/browser_plugin/browser_plugin_message_filter.h +++ b/chromium/content/browser/browser_plugin/browser_plugin_message_filter.h @@ -23,6 +23,9 @@ class BrowserPluginMessageFilter : public BrowserMessageFilter { bool OnMessageReceived(const IPC::Message& message) override; void OnDestruct() const override; + // Test-only functions: + void SetSubFilterForTesting(scoped_refptr sub_filter); + private: friend class BrowserThread; friend class base::DeleteHelper; @@ -33,6 +36,8 @@ class BrowserPluginMessageFilter : public BrowserMessageFilter { const int render_process_id_; + scoped_refptr sub_filter_for_testing_; + DISALLOW_COPY_AND_ASSIGN(BrowserPluginMessageFilter); }; diff --git a/chromium/content/browser/browser_process_sub_thread.cc b/chromium/content/browser/browser_process_sub_thread.cc index 0902fb6bfd0..6f33cee4d65 100644 --- a/chromium/content/browser/browser_process_sub_thread.cc +++ b/chromium/content/browser/browser_process_sub_thread.cc @@ -6,13 +6,18 @@ #include "base/compiler_specific.h" #include "base/debug/alias.h" +#include "base/metrics/histogram_macros.h" #include "base/threading/thread_restrictions.h" #include "base/trace_event/memory_dump_manager.h" #include "content/browser/browser_child_process_host_impl.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" #include "content/browser/notification_service_impl.h" +#include "content/browser/utility_process_host.h" +#include "content/common/child_process_host_impl.h" +#include "content/public/browser/browser_child_process_host_iterator.h" #include "content/public/browser/browser_thread_delegate.h" +#include "content/public/common/process_type.h" #include "net/url_request/url_fetcher.h" #include "net/url_request/url_request.h" @@ -70,6 +75,24 @@ void BrowserProcessSubThread::AllowBlockingForTesting() { is_blocking_allowed_for_testing_ = true; } +// static +std::unique_ptr +BrowserProcessSubThread::CreateIOThread() { + TRACE_EVENT0("startup", "BrowserProcessSubThread::CreateIOThread"); + base::Thread::Options options; + options.message_loop_type = base::MessageLoop::TYPE_IO; +#if defined(OS_ANDROID) || defined(OS_CHROMEOS) + // Up the priority of the |io_thread_| as some of its IPCs relate to + // display tasks. + options.priority = base::ThreadPriority::DISPLAY; +#endif + std::unique_ptr io_thread( + new BrowserProcessSubThread(BrowserThread::IO)); + if (!io_thread->StartWithOptions(options)) + LOG(FATAL) << "Failed to start BrowserThread:IO"; + return io_thread; +} + void BrowserProcessSubThread::Init() { DCHECK_CALLED_ON_VALID_THREAD(browser_thread_checker_); @@ -168,6 +191,40 @@ void BrowserProcessSubThread::IOThreadCleanUp() { // Destroy all URLRequests started by URLFetchers. net::URLFetcher::CancelAll(); + for (BrowserChildProcessHostIterator it(PROCESS_TYPE_UTILITY); !it.Done(); + ++it) { + UtilityProcessHost* utility_process = + static_cast(it.GetDelegate()); + if (utility_process->sandbox_type() == + service_manager::SANDBOX_TYPE_NETWORK) { + // Even though the TerminateAll call above tries to kill all child + // processes, that will fail sometimes (e.g. on Windows if there's pending + // I/O). Once the network service is sandboxed this will be taken care of, + // since the sandbox ensures child processes are terminated. Until then, + // wait on the network process for a bit. This is done so that: + // 1) when Chrome quits, we ensure that cookies & cache are flushed + // 2) tests aren't killed by swarming because of child processes that + // outlive the parent process. + // https://crbug.com/841001 + const int kMaxSecondsToWaitForNetworkProcess = 10; + ChildProcessHostImpl* child_process = + static_cast(it.GetHost()); + auto& process = child_process->peer_process(); + if (!process.IsValid()) + continue; + base::ScopedAllowBaseSyncPrimitives scoped_allow_base_sync_primitives; + const base::TimeTicks start_time = base::TimeTicks::Now(); + process.WaitForExitWithTimeout( + base::TimeDelta::FromSeconds(kMaxSecondsToWaitForNetworkProcess), + nullptr); + // Record time spent for the method call. + base::TimeDelta network_wait_time = base::TimeTicks::Now() - start_time; + UMA_HISTOGRAM_TIMES("NetworkService.ShutdownTime", network_wait_time); + LOG(ERROR) << "Waited " << network_wait_time.InMilliseconds() + << " ms for network service"; + } + } + // If any child processes are still running, terminate them and // and delete the BrowserChildProcessHost instances to release whatever // IO thread only resources they are referencing. diff --git a/chromium/content/browser/browser_process_sub_thread.h b/chromium/content/browser/browser_process_sub_thread.h index f4b87997e3f..edc223865ba 100644 --- a/chromium/content/browser/browser_process_sub_thread.h +++ b/chromium/content/browser/browser_process_sub_thread.h @@ -53,6 +53,10 @@ class CONTENT_EXPORT BrowserProcessSubThread : public base::Thread { // starting this BrowserProcessSubThread. void AllowBlockingForTesting(); + // Creates and starts the IO thread. It should not be promoted to + // BrowserThread::IO until BrowserMainLoop::CreateThreads(). + static std::unique_ptr CreateIOThread(); + protected: void Init() override; void Run(base::RunLoop* run_loop) override; diff --git a/chromium/content/browser/browser_side_navigation_browsertest.cc b/chromium/content/browser/browser_side_navigation_browsertest.cc index a2a4b80f88e..bbc595fb84f 100644 --- a/chromium/content/browser/browser_side_navigation_browsertest.cc +++ b/chromium/content/browser/browser_side_navigation_browsertest.cc @@ -18,7 +18,6 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/notification_types.h" -#include "content/public/browser/resource_dispatcher_host_delegate.h" #include "content/public/browser/web_contents.h" #include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/content_features.h" @@ -44,22 +43,6 @@ namespace content { -namespace { - -class RequestBlockingResourceDispatcherHostDelegate - : public ResourceDispatcherHostDelegate { - public: - // ResourceDispatcherHostDelegate implementation: - bool ShouldBeginRequest(const std::string& method, - const GURL& url, - ResourceType resource_type, - ResourceContext* resource_context) override { - return false; - } -}; - -} // namespace - // Test with BrowserSideNavigation enabled (aka PlzNavigate). // If you don't need a custom embedded test server, please use the next class // below (BrowserSideNavigationBrowserTest), it will automatically start the @@ -482,7 +465,9 @@ IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserDisableWebSecurityTest, base::TimeTicks::Now() /* navigation_start */, "GET", nullptr /* post_data */, base::Optional(), CSPDisposition::CHECK, false /* started_from_context_menu */, - false /* has_user_gesture */, base::nullopt /* suggested_filename */); + false /* has_user_gesture */, + std::vector() /* initiator_csp */, + CSPSource() /* initiator_self_source */); mojom::BeginNavigationParamsPtr begin_params = mojom::BeginNavigationParams::New( std::string() /* headers */, net::LOAD_NORMAL, @@ -647,206 +632,6 @@ IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBaseBrowserTest, EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); } -// TODO(arthursonzogni): Remove these tests once NavigationMojoResponse has -// launched. -class NavigationMojoResponseBrowserTest : public ContentBrowserTest { - public: - NavigationMojoResponseBrowserTest() {} - - protected: - void SetUp() override { - base::test::ScopedFeatureList().InitAndEnableFeature( - features::kNavigationMojoResponse); - ContentBrowserTest::SetUp(); - } - - void SetUpOnMainThread() override { - host_resolver()->AddRule("*", "127.0.0.1"); - ASSERT_TRUE(embedded_test_server()->Start()); - } -}; - -// Ensure that browser initiated basic navigations work with browser side -// navigation. -// TODO(arthursonzogni): Remove this test once NavigationMojoResponse has -// launched. -IN_PROC_BROWSER_TEST_F(NavigationMojoResponseBrowserTest, - BrowserInitiatedNavigations) { - // Perform a navigation with no live renderer. - { - TestNavigationObserver observer(shell()->web_contents()); - GURL url(embedded_test_server()->GetURL("/title1.html")); - NavigateToURL(shell(), url); - EXPECT_EQ(url, observer.last_navigation_url()); - EXPECT_TRUE(observer.last_navigation_succeeded()); - } - - RenderFrameHost* initial_rfh = - static_cast(shell()->web_contents()) - ->GetFrameTree() - ->root() - ->current_frame_host(); - - // Perform a same site navigation. - { - TestNavigationObserver observer(shell()->web_contents()); - GURL url(embedded_test_server()->GetURL("/title2.html")); - NavigateToURL(shell(), url); - EXPECT_EQ(url, observer.last_navigation_url()); - EXPECT_TRUE(observer.last_navigation_succeeded()); - } - - // The RenderFrameHost should not have changed. - EXPECT_EQ(initial_rfh, static_cast(shell()->web_contents()) - ->GetFrameTree() - ->root() - ->current_frame_host()); - - // Perform a cross-site navigation. - { - TestNavigationObserver observer(shell()->web_contents()); - GURL url = embedded_test_server()->GetURL("foo.com", "/title3.html"); - NavigateToURL(shell(), url); - EXPECT_EQ(url, observer.last_navigation_url()); - EXPECT_TRUE(observer.last_navigation_succeeded()); - } - - // The RenderFrameHost should have changed. - EXPECT_NE(initial_rfh, static_cast(shell()->web_contents()) - ->GetFrameTree() - ->root() - ->current_frame_host()); -} - -// Ensure that renderer initiated same-site navigations work with browser side -// navigation. -// TODO(arthursonzogni): Remove this test once NavigationMojoResponse has -// launched. -IN_PROC_BROWSER_TEST_F(NavigationMojoResponseBrowserTest, - RendererInitiatedSameSiteNavigation) { - // Perform a navigation with no live renderer. - { - TestNavigationObserver observer(shell()->web_contents()); - GURL url(embedded_test_server()->GetURL("/simple_links.html")); - NavigateToURL(shell(), url); - EXPECT_EQ(url, observer.last_navigation_url()); - EXPECT_TRUE(observer.last_navigation_succeeded()); - } - - RenderFrameHost* initial_rfh = - static_cast(shell()->web_contents()) - ->GetFrameTree() - ->root() - ->current_frame_host(); - - // Simulate clicking on a same-site link. - { - TestNavigationObserver observer(shell()->web_contents()); - GURL url(embedded_test_server()->GetURL("/title2.html")); - bool success = false; - EXPECT_TRUE(ExecuteScriptAndExtractBool( - shell(), "window.domAutomationController.send(clickSameSiteLink());", - &success)); - EXPECT_TRUE(success); - EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); - EXPECT_EQ(url, observer.last_navigation_url()); - EXPECT_TRUE(observer.last_navigation_succeeded()); - } - - // The RenderFrameHost should not have changed. - EXPECT_EQ(initial_rfh, static_cast(shell()->web_contents()) - ->GetFrameTree() - ->root() - ->current_frame_host()); -} - -// Ensure that renderer initiated cross-site navigations work with browser side -// navigation. -// TODO(arthursonzogni): Remove this test once NavigationMojoResponse has -// launched. -IN_PROC_BROWSER_TEST_F(NavigationMojoResponseBrowserTest, - RendererInitiatedCrossSiteNavigation) { - // Perform a navigation with no live renderer. - { - TestNavigationObserver observer(shell()->web_contents()); - GURL url(embedded_test_server()->GetURL("/simple_links.html")); - NavigateToURL(shell(), url); - EXPECT_EQ(url, observer.last_navigation_url()); - EXPECT_TRUE(observer.last_navigation_succeeded()); - } - - RenderFrameHost* initial_rfh = - static_cast(shell()->web_contents()) - ->GetFrameTree() - ->root() - ->current_frame_host(); - - // Simulate clicking on a cross-site link. - { - TestNavigationObserver observer(shell()->web_contents()); - const char kReplacePortNumber[] = - "window.domAutomationController.send(setPortNumber(%d));"; - uint16_t port_number = embedded_test_server()->port(); - GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html"); - bool success = false; - EXPECT_TRUE(ExecuteScriptAndExtractBool( - shell(), base::StringPrintf(kReplacePortNumber, port_number), - &success)); - success = false; - EXPECT_TRUE(ExecuteScriptAndExtractBool( - shell(), "window.domAutomationController.send(clickCrossSiteLink());", - &success)); - EXPECT_TRUE(success); - EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); - EXPECT_EQ(url, observer.last_navigation_url()); - EXPECT_TRUE(observer.last_navigation_succeeded()); - } - - // The RenderFrameHost should not have changed unless site-per-process is - // enabled. - if (AreAllSitesIsolatedForTesting()) { - EXPECT_NE(initial_rfh, - static_cast(shell()->web_contents()) - ->GetFrameTree() - ->root() - ->current_frame_host()); - } else { - EXPECT_EQ(initial_rfh, - static_cast(shell()->web_contents()) - ->GetFrameTree() - ->root() - ->current_frame_host()); - } -} - -// Ensure that browser side navigation handles navigation failures. -// TODO(arthursonzogni): Remove this test once NavigationMojoResponse has -// launched. -IN_PROC_BROWSER_TEST_F(NavigationMojoResponseBrowserTest, FailedNavigation) { - // Perform a navigation with no live renderer. - { - TestNavigationObserver observer(shell()->web_contents()); - GURL url(embedded_test_server()->GetURL("/title1.html")); - NavigateToURL(shell(), url); - EXPECT_EQ(url, observer.last_navigation_url()); - EXPECT_TRUE(observer.last_navigation_succeeded()); - } - - // Now navigate to an unreachable url. - { - TestNavigationObserver observer(shell()->web_contents()); - GURL error_url(embedded_test_server()->GetURL("/close-socket")); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&net::URLRequestFailedJob::AddUrlHandler)); - NavigateToURL(shell(), error_url); - EXPECT_EQ(error_url, observer.last_navigation_url()); - NavigationEntry* entry = - shell()->web_contents()->GetController().GetLastCommittedEntry(); - EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType()); - } -} - // Data URLs can have a reference fragment like any other URLs. This test makes // sure it is taken into account. IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest, @@ -919,41 +704,4 @@ IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest, } } -// Requests not allowed by a ResourceDispatcherHostDelegate must be aborted. -IN_PROC_BROWSER_TEST_F(BrowserSideNavigationBrowserTest, - RequestBlockedByResourceDispatcherHostDelegate) { - // The Network Service doesn't use a ResourceDispatcherHost. A request can't - // be canceled by a ResourceDispatcherHostDelegate. - if (base::FeatureList::IsEnabled(network::features::kNetworkService)) - return; - - // Add a ResourceDispatcherHost blocking every requests. - RequestBlockingResourceDispatcherHostDelegate delegate; - base::RunLoop loop; - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce( - [](base::OnceClosure resume, - ResourceDispatcherHostDelegate* delegate) { - ResourceDispatcherHost::Get()->SetDelegate(delegate); - std::move(resume).Run(); - }, - loop.QuitClosure(), &delegate)); - loop.Run(); - - // Navigate somewhere. The navigation will be aborted. - GURL simple_url(embedded_test_server()->GetURL("/simple_page.html")); - TestNavigationManager manager(shell()->web_contents(), simple_url); - NavigationHandleObserver handle_observer(shell()->web_contents(), simple_url); - shell()->LoadURL(simple_url); - - EXPECT_TRUE(manager.WaitForRequestStart()); - EXPECT_FALSE(manager.WaitForResponse()); - manager.WaitForNavigationFinished(); - - EXPECT_FALSE(handle_observer.has_committed()); - EXPECT_TRUE(handle_observer.is_error()); - EXPECT_EQ(net::ERR_ABORTED, handle_observer.net_error_code()); -} - } // namespace content diff --git a/chromium/content/browser/browser_thread_unittest.cc b/chromium/content/browser/browser_thread_unittest.cc index 703fe3448d4..cf5b1635992 100644 --- a/chromium/content/browser/browser_thread_unittest.cc +++ b/chromium/content/browser/browser_thread_unittest.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/callback.h" #include "base/location.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" @@ -23,9 +24,9 @@ namespace content { class BrowserThreadTest : public testing::Test { public: void Release() const { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - loop_.task_runner()->PostTask(FROM_HERE, - base::MessageLoop::QuitWhenIdleClosure()); + EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); + EXPECT_TRUE(on_release_); + std::move(on_release_).Run(); } void StopUIThread() { ui_thread_->Stop(); } @@ -52,42 +53,49 @@ class BrowserThreadTest : public testing::Test { BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::IO); } - static void BasicFunction(base::MessageLoop* message_loop) { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - message_loop->task_runner()->PostTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); + // Prepares this BrowserThreadTest for Release() to be invoked. |on_release| + // will be invoked when this occurs. + void ExpectRelease(base::OnceClosure on_release) { + on_release_ = std::move(on_release); + } + + static void BasicFunction(base::OnceClosure continuation) { + EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); + std::move(continuation).Run(); } class DeletedOnIO : public base::RefCountedThreadSafe { public: - explicit DeletedOnIO(base::MessageLoop* message_loop) - : message_loop_(message_loop) {} + explicit DeletedOnIO(base::OnceClosure on_deletion) + : on_deletion_(std::move(on_deletion)) {} private: friend struct BrowserThread::DeleteOnThread; friend class base::DeleteHelper; ~DeletedOnIO() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - message_loop_->task_runner()->PostTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); + EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); + std::move(on_deletion_).Run(); } - base::MessageLoop* message_loop_; + base::OnceClosure on_deletion_; }; private: std::unique_ptr ui_thread_; std::unique_ptr io_thread_; - // It's kind of ugly to make this mutable - solely so we can post the Quit - // Task from Release(). This should be fixed. - mutable base::MessageLoop loop_; + + base::MessageLoop loop_; + // Must be set before Release() to verify the deletion is intentional. Will be + // run from the next call to Release(). mutable so it can be consumed from + // Release(). + mutable base::OnceClosure on_release_; }; class UIThreadDestructionObserver - : public base::MessageLoop::DestructionObserver { + : public base::MessageLoopCurrent::DestructionObserver { public: explicit UIThreadDestructionObserver(bool* did_shutdown, const base::Closure& callback) @@ -102,10 +110,10 @@ class UIThreadDestructionObserver private: static void Watch(UIThreadDestructionObserver* observer) { - base::MessageLoop::current()->AddDestructionObserver(observer); + base::MessageLoopCurrent::Get()->AddDestructionObserver(observer); } - // base::MessageLoop::DestructionObserver: + // base::MessageLoopCurrent::DestructionObserver: void WillDestroyCurrentMessageLoop() override { // Ensure that even during MessageLoop teardown the BrowserThread ID is // correctly associated with this thread and the BrowserThreadTaskRunner @@ -113,7 +121,7 @@ class UIThreadDestructionObserver EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); EXPECT_TRUE(ui_task_runner_->BelongsToCurrentThread()); - base::MessageLoop::current()->RemoveDestructionObserver(this); + base::MessageLoopCurrent::Get()->RemoveDestructionObserver(this); *did_shutdown_ = true; callback_task_runner_->PostTask(FROM_HERE, callback_); } @@ -125,47 +133,57 @@ class UIThreadDestructionObserver }; TEST_F(BrowserThreadTest, PostTask) { + base::RunLoop run_loop; BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::BindOnce(&BasicFunction, base::MessageLoop::current())); - base::RunLoop().Run(); + base::BindOnce(&BasicFunction, run_loop.QuitWhenIdleClosure())); + run_loop.Run(); } TEST_F(BrowserThreadTest, Release) { + base::RunLoop run_loop; + ExpectRelease(run_loop.QuitWhenIdleClosure()); BrowserThread::ReleaseSoon(BrowserThread::UI, FROM_HERE, this); - base::RunLoop().Run(); + run_loop.Run(); } TEST_F(BrowserThreadTest, ReleasedOnCorrectThread) { + base::RunLoop run_loop; { scoped_refptr test( - new DeletedOnIO(base::MessageLoop::current())); + new DeletedOnIO(run_loop.QuitWhenIdleClosure())); } - base::RunLoop().Run(); + run_loop.Run(); } TEST_F(BrowserThreadTest, PostTaskViaTaskRunner) { scoped_refptr task_runner = BrowserThread::GetTaskRunnerForThread(BrowserThread::IO); + base::RunLoop run_loop; task_runner->PostTask( - FROM_HERE, base::BindOnce(&BasicFunction, base::MessageLoop::current())); - base::RunLoop().Run(); + FROM_HERE, + base::BindOnce(&BasicFunction, run_loop.QuitWhenIdleClosure())); + run_loop.Run(); } TEST_F(BrowserThreadTest, ReleaseViaTaskRunner) { scoped_refptr task_runner = BrowserThread::GetTaskRunnerForThread(BrowserThread::UI); + + base::RunLoop run_loop; + ExpectRelease(run_loop.QuitWhenIdleClosure()); task_runner->ReleaseSoon(FROM_HERE, this); - base::RunLoop().Run(); + run_loop.Run(); } TEST_F(BrowserThreadTest, PostTaskAndReply) { // Most of the heavy testing for PostTaskAndReply() is done inside the // task runner test. This just makes sure we get piped through at all. - ASSERT_TRUE(BrowserThread::PostTaskAndReply( - BrowserThread::IO, FROM_HERE, base::DoNothing(), - base::BindOnce(&base::RunLoop::QuitCurrentWhenIdleDeprecated))); - base::RunLoop().Run(); + base::RunLoop run_loop; + ASSERT_TRUE(BrowserThread::PostTaskAndReply(BrowserThread::IO, FROM_HERE, + base::DoNothing(), + run_loop.QuitWhenIdleClosure())); + run_loop.Run(); } TEST_F(BrowserThreadTest, RunsTasksInCurrentSequencedDuringShutdown) { diff --git a/chromium/content/browser/browsing_data/OWNERS b/chromium/content/browser/browsing_data/OWNERS index 8d413bc741f..4b224ec6732 100644 --- a/chromium/content/browser/browsing_data/OWNERS +++ b/chromium/content/browser/browsing_data/OWNERS @@ -1,7 +1,7 @@ bauerb@chromium.org dullweber@chromium.org markusheintz@chromium.org -michaeln@chromium.org +jsbell@chromium.org msramek@chromium.org # COMPONENT: Privacy diff --git a/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl.cc b/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl.cc index 174800f2958..cba9445e35b 100644 --- a/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl.cc +++ b/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/callback.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" -#include "net/cookies/canonical_cookie.h" #include "url/origin.h" using net::registry_controlled_domains::GetDomainAndRegistry; @@ -56,38 +55,6 @@ bool MatchesURL( (mode == BrowsingDataFilterBuilder::WHITELIST)); } -// True if no domains can see the given cookie and we're a blacklist, or any -// domains can see the cookie and we're a whitelist. -// The whitelist or blacklist is represented as |domains_and_ips| and |mode|. -bool MatchesCookieForRegisterableDomainsAndIPs( - const std::set& domains_and_ips, - BrowsingDataFilterBuilder::Mode mode, - const net::CanonicalCookie& cookie) { - if (domains_and_ips.empty()) - return mode == BrowsingDataFilterBuilder::BLACKLIST; - std::string cookie_domain = cookie.Domain(); - if (cookie.IsDomainCookie()) - cookie_domain = cookie_domain.substr(1); - std::string parsed_cookie_domain = - GetDomainAndRegistry(cookie_domain, INCLUDE_PRIVATE_REGISTRIES); - // This means we're an IP address or an internal hostname. - if (parsed_cookie_domain.empty()) - parsed_cookie_domain = cookie_domain; - return (mode == BrowsingDataFilterBuilder::WHITELIST) == - (domains_and_ips.find(parsed_cookie_domain) != domains_and_ips.end()); -} - -// True if none of the supplied domains matches this Channel ID's server ID -// and we're a blacklist, or one of them does and we're a whitelist. -// The whitelist or blacklist is represented as |domains_and_ips| and |mode|. -bool MatchesChannelIDForRegisterableDomainsAndIPs( - const std::set& domains_and_ips, - BrowsingDataFilterBuilder::Mode mode, - const std::string& channel_id_server_id) { - return ((mode == BrowsingDataFilterBuilder::WHITELIST) == - (domains_and_ips.find(channel_id_server_id) != domains_and_ips.end())); -} - // True if none of the supplied domains matches this plugin's |site| and we're // a blacklist, or one of them does and we're a whitelist. The whitelist or // blacklist is represented by |domains_and_ips| and |mode|. @@ -159,13 +126,13 @@ BrowsingDataFilterBuilderImpl::BuildGeneralFilter() const { return base::BindRepeating(&MatchesURL, origins_, domains_, mode_); } -network::mojom::ClearCacheUrlFilterPtr -BrowsingDataFilterBuilderImpl::BuildClearCacheUrlFilter() const { - network::mojom::ClearCacheUrlFilterPtr filter = - network::mojom::ClearCacheUrlFilter::New(); +network::mojom::ClearDataFilterPtr +BrowsingDataFilterBuilderImpl::BuildNetworkServiceFilter() const { + network::mojom::ClearDataFilterPtr filter = + network::mojom::ClearDataFilter::New(); filter->type = (mode_ == Mode::WHITELIST) - ? network::mojom::ClearCacheUrlFilter::Type::DELETE_MATCHES - : network::mojom::ClearCacheUrlFilter::Type::KEEP_MATCHES; + ? network::mojom::ClearDataFilter::Type::DELETE_MATCHES + : network::mojom::ClearDataFilter::Type::KEEP_MATCHES; filter->origins.insert(filter->origins.begin(), origins_.begin(), origins_.end()); filter->domains.insert(filter->domains.begin(), domains_.begin(), @@ -173,22 +140,24 @@ BrowsingDataFilterBuilderImpl::BuildClearCacheUrlFilter() const { return filter; } -base::RepeatingCallback -BrowsingDataFilterBuilderImpl::BuildCookieFilter() const { - DCHECK(origins_.empty()) << - "Origin-based deletion is not suitable for cookies. Please use " - "different scoping, such as RegistrableDomainFilterBuilder."; - return base::BindRepeating(&MatchesCookieForRegisterableDomainsAndIPs, - domains_, mode_); -} - -base::RepeatingCallback -BrowsingDataFilterBuilderImpl::BuildChannelIDFilter() const { - DCHECK(origins_.empty()) << - "Origin-based deletion is not suitable for channel IDs. Please use " - "different scoping, such as RegistrableDomainFilterBuilder."; - return base::BindRepeating(&MatchesChannelIDForRegisterableDomainsAndIPs, - domains_, mode_); +network::mojom::CookieDeletionFilterPtr +BrowsingDataFilterBuilderImpl::BuildCookieDeletionFilter() const { + DCHECK(origins_.empty()) + << "Origin-based deletion is not suitable for cookies. Please use " + "different scoping, such as RegistrableDomainFilterBuilder."; + auto deletion_filter = network::mojom::CookieDeletionFilter::New(); + + switch (mode_) { + case WHITELIST: + deletion_filter->including_domains.emplace(domains_.begin(), + domains_.end()); + break; + case BLACKLIST: + deletion_filter->excluding_domains.emplace(domains_.begin(), + domains_.end()); + break; + } + return deletion_filter; } base::RepeatingCallback diff --git a/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl.h b/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl.h index ed3fe8fcd1c..a310963340d 100644 --- a/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl.h +++ b/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl.h @@ -24,12 +24,9 @@ class CONTENT_EXPORT BrowsingDataFilterBuilderImpl bool IsEmptyBlacklist() const override; base::RepeatingCallback BuildGeneralFilter() const override; - network::mojom::ClearCacheUrlFilterPtr BuildClearCacheUrlFilter() + network::mojom::ClearDataFilterPtr BuildNetworkServiceFilter() const override; + network::mojom::CookieDeletionFilterPtr BuildCookieDeletionFilter() const override; - base::RepeatingCallback - BuildCookieFilter() const override; - base::RepeatingCallback - BuildChannelIDFilter() const override; base::RepeatingCallback BuildPluginFilter() const override; Mode GetMode() const override; diff --git a/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl_unittest.cc b/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl_unittest.cc index 81fc09dc81e..61e3563d867 100644 --- a/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl_unittest.cc +++ b/chromium/content/browser/browsing_data/browsing_data_filter_builder_impl_unittest.cc @@ -11,10 +11,15 @@ #include "base/callback.h" #include "net/cookies/canonical_cookie.h" +#include "net/cookies/cookie_deletion_info.h" +#include "services/network/cookie_manager.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/origin.h" +using CookieDeletionInfo = net::CookieDeletionInfo; + namespace content { namespace { @@ -47,10 +52,11 @@ void RunTestCase(TestCase test_case, << test_case.url; } -void RunTestCase( - TestCase test_case, - const base::Callback& filter) { +void RunTestCase(TestCase test_case, + network::mojom::CookieDeletionFilterPtr deletion_filter) { // Test with regular cookie, http only, domain, and secure. + CookieDeletionInfo delete_info = + network::DeletionFilterToInfo(std::move(deletion_filter)); std::string cookie_line = "A=2"; GURL test_url(test_case.url); EXPECT_TRUE(test_url.is_valid()) << test_case.url; @@ -59,28 +65,28 @@ void RunTestCase( EXPECT_TRUE(cookie) << cookie_line << " from " << test_case.url << " is not a valid cookie"; if (cookie) - EXPECT_EQ(test_case.should_match, filter.Run(*cookie)) + EXPECT_EQ(test_case.should_match, delete_info.Matches(*cookie)) << cookie->DebugString(); cookie_line = std::string("A=2;domain=") + test_url.host(); cookie = net::CanonicalCookie::Create( test_url, cookie_line, base::Time::Now(), net::CookieOptions()); if (cookie) - EXPECT_EQ(test_case.should_match, filter.Run(*cookie)) + EXPECT_EQ(test_case.should_match, delete_info.Matches(*cookie)) << cookie->DebugString(); cookie_line = std::string("A=2; HttpOnly;") + test_url.host(); cookie = net::CanonicalCookie::Create( test_url, cookie_line, base::Time::Now(), net::CookieOptions()); if (cookie) - EXPECT_EQ(test_case.should_match, filter.Run(*cookie)) + EXPECT_EQ(test_case.should_match, delete_info.Matches(*cookie)) << cookie->DebugString(); cookie_line = std::string("A=2; HttpOnly; Secure;") + test_url.host(); cookie = net::CanonicalCookie::Create( test_url, cookie_line, base::Time::Now(), net::CookieOptions()); if (cookie) - EXPECT_EQ(test_case.should_match, filter.Run(*cookie)) + EXPECT_EQ(test_case.should_match, delete_info.Matches(*cookie)) << cookie->DebugString(); } @@ -210,8 +216,6 @@ TEST(BrowsingDataFilterBuilderImplTest, builder.AddRegisterableDomain(std::string(kIPAddress)); builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain)); builder.AddRegisterableDomain(std::string(kInternalHostname)); - base::Callback filter = - builder.BuildCookieFilter(); TestCase test_cases[] = { // Any cookie with the same registerable domain as the origins is matched. @@ -250,7 +254,7 @@ TEST(BrowsingDataFilterBuilderImplTest, }; for (TestCase test_case : test_cases) - RunTestCase(test_case, filter); + RunTestCase(test_case, builder.BuildCookieDeletionFilter()); } TEST(BrowsingDataFilterBuilderImplTest, @@ -262,8 +266,6 @@ TEST(BrowsingDataFilterBuilderImplTest, builder.AddRegisterableDomain(std::string(kIPAddress)); builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain)); builder.AddRegisterableDomain(std::string(kInternalHostname)); - base::Callback filter = - builder.BuildCookieFilter(); TestCase test_cases[] = { // Any cookie that doesn't have the same registerable domain is matched. @@ -302,71 +304,45 @@ TEST(BrowsingDataFilterBuilderImplTest, }; for (TestCase test_case : test_cases) - RunTestCase(test_case, filter); + RunTestCase(test_case, builder.BuildCookieDeletionFilter()); } -TEST(BrowsingDataFilterBuilderImplTest, - RegistrableDomainMatchesChannelIDsWhitelist) { +TEST(BrowsingDataFilterBuilderImplTest, NetworkServiceFilterWhitelist) { BrowsingDataFilterBuilderImpl builder( BrowsingDataFilterBuilderImpl::WHITELIST); + ASSERT_EQ(BrowsingDataFilterBuilderImpl::WHITELIST, builder.GetMode()); builder.AddRegisterableDomain(std::string(kGoogleDomain)); builder.AddRegisterableDomain(std::string(kLongETLDDomain)); builder.AddRegisterableDomain(std::string(kIPAddress)); builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain)); builder.AddRegisterableDomain(std::string(kInternalHostname)); - base::Callback filter = - builder.BuildChannelIDFilter(); - - TestCase test_cases[] = { - // Channel ID server identifiers can be second level domains, ... - {"google.com", true}, - {"website.sp.nom.br", true}, - {"second-level-domain.fileserver", true}, - - // ... IP addresses, or internal hostnames. - {"192.168.1.1", true}, - {"fileserver", true}, - - // Channel IDs not in the whitelist are not matched. - {"example.com", false}, - {"192.168.1.2", false}, - {"website.fileserver", false}, - }; - - for (TestCase test_case : test_cases) - RunTestCase(test_case, filter); + network::mojom::ClearDataFilterPtr filter = + builder.BuildNetworkServiceFilter(); + + EXPECT_EQ(network::mojom::ClearDataFilter_Type::DELETE_MATCHES, filter->type); + EXPECT_THAT(filter->domains, testing::UnorderedElementsAre( + kGoogleDomain, kLongETLDDomain, kIPAddress, + kUnknownRegistryDomain, kInternalHostname)); + EXPECT_TRUE(filter->origins.empty()); } -TEST(BrowsingDataFilterBuilderImplTest, - RegistrableDomainMatchesChannelIDsBlacklist) { +TEST(BrowsingDataFilterBuilderImplTest, NetworkServiceFilterBlacklist) { BrowsingDataFilterBuilderImpl builder( BrowsingDataFilterBuilderImpl::BLACKLIST); + ASSERT_EQ(BrowsingDataFilterBuilderImpl::BLACKLIST, builder.GetMode()); builder.AddRegisterableDomain(std::string(kGoogleDomain)); builder.AddRegisterableDomain(std::string(kLongETLDDomain)); builder.AddRegisterableDomain(std::string(kIPAddress)); builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain)); builder.AddRegisterableDomain(std::string(kInternalHostname)); - base::Callback filter = - builder.BuildChannelIDFilter(); - - TestCase test_cases[] = { - // Channel ID server identifiers can be second level domains, ... - {"google.com", false}, - {"website.sp.nom.br", false}, - {"second-level-domain.fileserver", false}, - - // ...IP addresses, or internal hostnames. - {"192.168.1.1", false}, - {"fileserver", false}, - - // Channel IDs that are not blacklisted are matched. - {"example.com", true}, - {"192.168.1.2", true}, - {"website.fileserver", true}, - }; - - for (TestCase test_case : test_cases) - RunTestCase(test_case, filter); + network::mojom::ClearDataFilterPtr filter = + builder.BuildNetworkServiceFilter(); + + EXPECT_EQ(network::mojom::ClearDataFilter_Type::KEEP_MATCHES, filter->type); + EXPECT_THAT(filter->domains, testing::UnorderedElementsAre( + kGoogleDomain, kLongETLDDomain, kIPAddress, + kUnknownRegistryDomain, kInternalHostname)); + EXPECT_TRUE(filter->origins.empty()); } TEST(BrowsingDataFilterBuilderImplTest, diff --git a/chromium/content/browser/browsing_data/browsing_data_remover_impl.cc b/chromium/content/browser/browsing_data/browsing_data_remover_impl.cc index bb4e8bd443c..4c478ad0079 100644 --- a/chromium/content/browser/browsing_data/browsing_data_remover_impl.cc +++ b/chromium/content/browser/browsing_data/browsing_data_remover_impl.cc @@ -26,15 +26,6 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/storage_partition.h" #include "mojo/public/cpp/bindings/callback_helpers.h" -#include "net/base/net_errors.h" -#include "net/cookies/cookie_store.h" -#include "net/http/http_network_session.h" -#include "net/http/http_transaction_factory.h" -#include "net/http/transport_security_state.h" -#include "net/ssl/channel_id_service.h" -#include "net/ssl/channel_id_store.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_getter.h" #include "ppapi/buildflags/buildflags.h" #include "services/network/public/cpp/features.h" #include "storage/browser/quota/special_storage_policy.h" @@ -105,50 +96,6 @@ bool DoesOriginMatchMaskAndURLs( return false; } -void ClearHttpAuthCacheOnIOThread( - scoped_refptr context_getter, - base::Time delete_begin) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - net::HttpNetworkSession* http_session = context_getter->GetURLRequestContext() - ->http_transaction_factory() - ->GetSession(); - DCHECK(http_session); - http_session->http_auth_cache()->ClearEntriesAddedWithin(base::Time::Now() - - delete_begin); - http_session->CloseAllConnections(); -} - -void OnClearedChannelIDsOnIOThread(net::URLRequestContextGetter* rq_context, - base::OnceClosure callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - // Need to close open SSL connections which may be using the channel ids we - // are deleting. - // TODO(mattm): http://crbug.com/166069 Make the server bound cert - // service/store have observers that can notify relevant things directly. - rq_context->GetURLRequestContext() - ->ssl_config_service() - ->NotifySSLConfigChange(); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(callback)); -} - -void ClearChannelIDsOnIOThread( - const base::Callback& domain_predicate, - base::Time delete_begin, - base::Time delete_end, - scoped_refptr request_context, - base::OnceClosure callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - net::ChannelIDService* channel_id_service = - request_context->GetURLRequestContext()->channel_id_service(); - channel_id_service->GetChannelIDStore()->DeleteForDomainsCreatedBetween( - domain_predicate, delete_begin, delete_end, - base::Bind(&OnClearedChannelIDsOnIOThread, - base::RetainedRef(std::move(request_context)), - base::Passed(std::move(callback)))); -} - } // namespace BrowsingDataRemoverImpl::BrowsingDataRemoverImpl( @@ -353,16 +300,16 @@ void BrowsingDataRemoverImpl::RemoveImpl( !(remove_mask & DATA_TYPE_AVOID_CLOSING_CONNECTIONS) && origin_type_mask_ & ORIGIN_TYPE_UNPROTECTED_WEB) { base::RecordAction(UserMetricsAction("ClearBrowsingData_ChannelIDs")); - // Since we are running on the UI thread don't call GetURLRequestContext(). - scoped_refptr request_context = - BrowserContext::GetDefaultStoragePartition(browser_context_) - ->GetURLRequestContext(); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&ClearChannelIDsOnIOThread, - filter_builder.BuildChannelIDFilter(), delete_begin_, - delete_end_, std::move(request_context), - CreatePendingTaskCompletionClosure())); + + network::mojom::ClearDataFilterPtr service_filter = + filter_builder.BuildNetworkServiceFilter(); + DCHECK(service_filter->origins.empty()) + << "Origin-based deletion is not suitable for channel IDs."; + + BrowserContext::GetDefaultStoragePartition(browser_context_) + ->GetNetworkContext() + ->ClearChannelIds(delete_begin, delete_end, std::move(service_filter), + CreatePendingTaskCompletionClosureForMojo()); } ////////////////////////////////////////////////////////////////////////////// @@ -405,6 +352,10 @@ void BrowsingDataRemoverImpl::RemoveImpl( storage_partition_remove_mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS; } + if (remove_mask & DATA_TYPE_BACKGROUND_FETCH) { + storage_partition_remove_mask |= + StoragePartition::REMOVE_DATA_MASK_BACKGROUND_FETCH; + } // Content Decryption Modules used by Encrypted Media store licenses in a // private filesystem. These are different than content licenses used by @@ -435,12 +386,14 @@ void BrowsingDataRemoverImpl::RemoveImpl( } // If cookies are supposed to be conditionally deleted from the storage - // partition, create a cookie matcher function. - StoragePartition::CookieMatcherFunction cookie_matcher; + // partition, create the deletion info object. + network::mojom::CookieDeletionFilterPtr deletion_filter; if (!filter_builder.IsEmptyBlacklist() && (storage_partition_remove_mask & StoragePartition::REMOVE_DATA_MASK_COOKIES)) { - cookie_matcher = filter_builder.BuildCookieFilter(); + deletion_filter = filter_builder.BuildCookieDeletionFilter(); + } else { + deletion_filter = network::mojom::CookieDeletionFilter::New(); } BrowsingDataRemoverDelegate::EmbedderOriginTypeMatcher embedder_matcher; @@ -451,7 +404,7 @@ void BrowsingDataRemoverImpl::RemoveImpl( storage_partition_remove_mask, quota_storage_remove_mask, base::BindRepeating(&DoesOriginMatchMaskAndURLs, origin_type_mask_, filter, std::move(embedder_matcher)), - std::move(cookie_matcher), delete_begin_, delete_end_, + std::move(deletion_filter), delete_begin_, delete_end_, CreatePendingTaskCompletionClosure()); } @@ -469,7 +422,7 @@ void BrowsingDataRemoverImpl::RemoveImpl( // The clearing of the HTTP cache happens in the network service process // when enabled. network_context->ClearHttpCache( - delete_begin, delete_end, filter_builder.BuildClearCacheUrlFilter(), + delete_begin, delete_end, filter_builder.BuildNetworkServiceFilter(), CreatePendingTaskCompletionClosureForMojo()); } @@ -493,18 +446,30 @@ void BrowsingDataRemoverImpl::RemoveImpl( StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE; } +#if BUILDFLAG(ENABLE_REPORTING) + ////////////////////////////////////////////////////////////////////////////// + // Reporting cache. + if (remove_mask & DATA_TYPE_COOKIES) { + network::mojom::NetworkContext* network_context = + BrowserContext::GetDefaultStoragePartition(browser_context_) + ->GetNetworkContext(); + network_context->ClearReportingCacheClients( + filter_builder.BuildNetworkServiceFilter(), + CreatePendingTaskCompletionClosureForMojo()); + network_context->ClearNetworkErrorLogging( + filter_builder.BuildNetworkServiceFilter(), + CreatePendingTaskCompletionClosureForMojo()); + } +#endif // BUILDFLAG(ENABLE_REPORTING) + ////////////////////////////////////////////////////////////////////////////// // Auth cache. if ((remove_mask & DATA_TYPE_COOKIES) && !(remove_mask & DATA_TYPE_AVOID_CLOSING_CONNECTIONS)) { - scoped_refptr request_context = - BrowserContext::GetDefaultStoragePartition(browser_context_) - ->GetURLRequestContext(); - BrowserThread::PostTaskAndReply( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&ClearHttpAuthCacheOnIOThread, - std::move(request_context), delete_begin_), - CreatePendingTaskCompletionClosure()); + BrowserContext::GetDefaultStoragePartition(browser_context_) + ->GetNetworkContext() + ->ClearHttpAuthCache(delete_begin, + CreatePendingTaskCompletionClosureForMojo()); } ////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/content/browser/browsing_data/browsing_data_remover_impl_browsertest.cc b/chromium/content/browser/browsing_data/browsing_data_remover_impl_browsertest.cc index 2fcc7faa4fa..aab8824e253 100644 --- a/chromium/content/browser/browsing_data/browsing_data_remover_impl_browsertest.cc +++ b/chromium/content/browser/browsing_data/browsing_data_remover_impl_browsertest.cc @@ -8,14 +8,20 @@ #include "base/bind.h" #include "base/files/file_path.h" +#include "base/test/bind_test_util.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browsing_data_remover.h" +#include "content/public/browser/notification_service.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/content_client.h" #include "content/public/test/browsing_data_remover_test_util.h" #include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" #include "content/public/test/simple_url_loader_test_helper.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 "net/base/net_errors.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -30,6 +36,7 @@ namespace { const char kHstsPath[] = "/hsts"; +const char kHttpAuthPath[] = "/http_auth"; const char kHstsResponseBody[] = "HSTS set"; std::unique_ptr HandleHstsRequest( @@ -45,6 +52,26 @@ std::unique_ptr HandleHstsRequest( return nullptr; } +// Handles |request| to "/http_auth". If "Authorization" header is present, +// responds with a non-empty HTTP 200 page (regardless of auth credentials). +// Otherwise serves a Basic Auth challenge. +std::unique_ptr HandleHttpAuthRequest( + const net::test_server::HttpRequest& request) { + if (request.relative_url != kHttpAuthPath) + return nullptr; + + auto http_response = std::make_unique(); + if (base::ContainsKey(request.headers, "Authorization")) { + http_response->set_code(net::HTTP_OK); + http_response->set_content("Success!"); + } else { + http_response->set_code(net::HTTP_UNAUTHORIZED); + http_response->AddCustomHeader("WWW-Authenticate", + "Basic realm=\"test realm\""); + } + return http_response; +} + } // namespace namespace content { @@ -58,7 +85,9 @@ class BrowsingDataRemoverImplBrowserTest : public ContentBrowserTest { net::test_server::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); ssl_server_.AddDefaultHandlers( base::FilePath(FILE_PATH_LITERAL("content/test/data"))); - ssl_server_.RegisterRequestHandler(base::Bind(&HandleHstsRequest)); + ssl_server_.RegisterRequestHandler(base::BindRepeating(&HandleHstsRequest)); + ssl_server_.RegisterRequestHandler( + base::BindRepeating(&HandleHttpAuthRequest)); EXPECT_TRUE(ssl_server_.Start()); } @@ -130,6 +159,40 @@ class BrowsingDataRemoverImplBrowserTest : public ContentBrowserTest { return false; } + // Sets HTTP auth cache by making a request with credentials specified in the + // URL to a page with an auth challenge. + void IssueRequestThatSetsHttpAuthCache() { + GURL url = ssl_server_.GetURL(kHttpAuthPath); + GURL::Replacements replacements; + replacements.SetUsernameStr("user"); + replacements.SetPasswordStr("password"); + GURL url_with_creds = url.ReplaceComponents(replacements); + ASSERT_TRUE(NavigateToURL(shell(), url_with_creds)); + + ASSERT_TRUE(IsHttpAuthCacheSet()); + } + + // Determines if auth cache is populated by seeing if a request to a page with + // an auth challenge succeeds. + bool IsHttpAuthCacheSet() { + // Set a login request callback to be used instead of a login dialog since + // such a dialog is difficult to control programmatically and doesn't work + // on all platforms. + bool login_requested = false; + ShellContentBrowserClient::Get()->set_login_request_callback( + base::BindLambdaForTesting([&]() { login_requested = true; })); + + GURL url = ssl_server_.GetURL(kHttpAuthPath); + bool navigation_suceeded = NavigateToURL(shell(), url); + + // Because our login request callback does nothing, navigation should + // succeed iff login is not needed unless some other unexpected error + // occurs. + EXPECT_NE(navigation_suceeded, login_requested); + + return !login_requested && navigation_suceeded; + } + network::mojom::URLLoaderFactory* url_loader_factory() { return BrowserContext::GetDefaultStoragePartition( shell()->web_contents()->GetBrowserContext()) @@ -160,4 +223,29 @@ IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest, EXPECT_TRUE(IsHstsSet()); } +IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest, ClearHttpAuthCache) { + ASSERT_FALSE(IsHttpAuthCacheSet()); + IssueRequestThatSetsHttpAuthCache(); + + RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES); + EXPECT_FALSE(IsHttpAuthCacheSet()); +} + +IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest, + PreserveHttpAuthCache) { + ASSERT_FALSE(IsHttpAuthCacheSet()); + IssueRequestThatSetsHttpAuthCache(); + + RemoveAndWait(BrowsingDataRemover::DATA_TYPE_DOWNLOADS); + EXPECT_TRUE(IsHttpAuthCacheSet()); +} + +IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest, + ClearHttpAuthCacheWhenEmpty) { + ASSERT_FALSE(IsHttpAuthCacheSet()); + + RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES); + EXPECT_FALSE(IsHttpAuthCacheSet()); +} + } // namespace content diff --git a/chromium/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc b/chromium/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc index cf4705ac2e8..c7322c1f278 100644 --- a/chromium/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc +++ b/chromium/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc @@ -20,7 +20,6 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/strings/utf_string_conversions.h" @@ -43,6 +42,7 @@ #include "content/public/test/test_storage_partition.h" #include "content/public/test/test_utils.h" #include "net/cookies/canonical_cookie.h" +#include "net/cookies/cookie_deletion_info.h" #include "net/cookies/cookie_store.h" #include "net/http/http_network_session.h" #include "net/http/http_transaction_factory.h" @@ -52,24 +52,36 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "ppapi/buildflags/buildflags.h" +#include "services/network/cookie_manager.h" #include "storage/browser/test/mock_special_storage_policy.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/origin.h" -using testing::_; +#if BUILDFLAG(ENABLE_REPORTING) +#include "net/network_error_logging/network_error_logging_delegate.h" +#include "net/network_error_logging/network_error_logging_service.h" +#include "net/reporting/reporting_cache.h" +#include "net/reporting/reporting_report.h" +#include "net/reporting/reporting_service.h" +#include "net/reporting/reporting_test_util.h" +#endif // BUILDFLAG(ENABLE_REPORTING) + using testing::ByRef; using testing::Eq; using testing::Invoke; using testing::IsEmpty; -using testing::Matcher; using testing::MakeMatcher; -using testing::MatcherInterface; using testing::MatchResultListener; +using testing::Matcher; +using testing::MatcherInterface; using testing::Not; using testing::Return; using testing::SizeIs; +using testing::UnorderedElementsAre; using testing::WithArgs; +using testing::_; +using CookieDeletionFilterPtr = network::mojom::CookieDeletionFilterPtr; namespace content { @@ -94,27 +106,37 @@ const GURL kOrigin4(kTestOrigin4); const GURL kOriginExt(kTestOriginExt); const GURL kOriginDevTools(kTestOriginDevTools); -const base::FilePath::CharType kDomStorageOrigin1[] = - FILE_PATH_LITERAL("http_host1_1.localstorage"); - -const base::FilePath::CharType kDomStorageOrigin2[] = - FILE_PATH_LITERAL("http_host2_1.localstorage"); - -const base::FilePath::CharType kDomStorageOrigin3[] = - FILE_PATH_LITERAL("http_host3_1.localstorage"); - -const base::FilePath::CharType kDomStorageExt[] = FILE_PATH_LITERAL( - "chrome-extension_abcdefghijklmnopqrstuvwxyz_0.localstorage"); - struct StoragePartitionRemovalData { - uint32_t remove_mask = 0; - uint32_t quota_storage_remove_mask = 0; + StoragePartitionRemovalData() + : remove_mask(0), + quota_storage_remove_mask(0), + cookie_deletion_filter(network::mojom::CookieDeletionFilter::New()) {} + + StoragePartitionRemovalData(const StoragePartitionRemovalData& other) + : remove_mask(other.remove_mask), + quota_storage_remove_mask(other.quota_storage_remove_mask), + remove_begin(other.remove_begin), + remove_end(other.remove_end), + origin_matcher(other.origin_matcher), + cookie_deletion_filter(other.cookie_deletion_filter.Clone()) {} + + StoragePartitionRemovalData& operator=( + const StoragePartitionRemovalData& rhs) { + remove_mask = rhs.remove_mask; + quota_storage_remove_mask = rhs.quota_storage_remove_mask; + remove_begin = rhs.remove_begin; + remove_end = rhs.remove_end; + origin_matcher = rhs.origin_matcher; + cookie_deletion_filter = rhs.cookie_deletion_filter.Clone(); + return *this; + } + + uint32_t remove_mask; + uint32_t quota_storage_remove_mask; base::Time remove_begin; base::Time remove_end; StoragePartition::OriginMatcherFunction origin_matcher; - StoragePartition::CookieMatcherFunction cookie_matcher; - - StoragePartitionRemovalData() {} + CookieDeletionFilterPtr cookie_deletion_filter; }; net::CanonicalCookie CreateCookieWithHost(const GURL& source) { @@ -162,7 +184,7 @@ class StoragePartitionRemovalTestStoragePartition void ClearData(uint32_t remove_mask, uint32_t quota_storage_remove_mask, const OriginMatcherFunction& origin_matcher, - const CookieMatcherFunction& cookie_matcher, + CookieDeletionFilterPtr cookie_deletion_filter, const base::Time begin, const base::Time end, base::OnceClosure callback) override { @@ -173,7 +195,8 @@ class StoragePartitionRemovalTestStoragePartition storage_partition_removal_data_.remove_begin = begin; storage_partition_removal_data_.remove_end = end; storage_partition_removal_data_.origin_matcher = origin_matcher; - storage_partition_removal_data_.cookie_matcher = cookie_matcher; + storage_partition_removal_data_.cookie_deletion_filter = + std::move(cookie_deletion_filter); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -182,7 +205,7 @@ class StoragePartitionRemovalTestStoragePartition base::Unretained(this), std::move(callback))); } - StoragePartitionRemovalData GetStoragePartitionRemovalData() { + const StoragePartitionRemovalData& GetStoragePartitionRemovalData() const { return storage_partition_removal_data_; } @@ -254,71 +277,15 @@ base::Time AnHourAgo() { return base::Time::Now() - base::TimeDelta::FromHours(1); } +bool FilterMatchesCookie(const CookieDeletionFilterPtr& filter, + const net::CanonicalCookie& cookie) { + return network::DeletionFilterToInfo(filter.Clone()).Matches(cookie); +} + } // namespace // Testers ------------------------------------------------------------------- -class RemoveCookieTester { - public: - RemoveCookieTester() {} - - // Returns true, if the given cookie exists in the cookie store. - bool ContainsCookie() { - scoped_refptr message_loop_runner = - new MessageLoopRunner(); - quit_closure_ = message_loop_runner->QuitClosure(); - get_cookie_success_ = false; - cookie_store_->GetCookieListWithOptionsAsync( - kOrigin1, net::CookieOptions(), - base::BindOnce(&RemoveCookieTester::GetCookieListCallback, - base::Unretained(this))); - message_loop_runner->Run(); - return get_cookie_success_; - } - - void AddCookie() { - scoped_refptr message_loop_runner = - new MessageLoopRunner(); - quit_closure_ = message_loop_runner->QuitClosure(); - cookie_store_->SetCookieWithOptionsAsync( - kOrigin1, "A=1", net::CookieOptions(), - base::BindOnce(&RemoveCookieTester::SetCookieCallback, - base::Unretained(this))); - message_loop_runner->Run(); - } - - protected: - void SetCookieStore(net::CookieStore* cookie_store) { - cookie_store_ = cookie_store; - } - - private: - void GetCookieListCallback(const net::CookieList& cookie_list) { - std::string cookie_line = - net::CanonicalCookie::BuildCookieLine(cookie_list); - if (cookie_line == std::string("A=1")) { - get_cookie_success_ = true; - } else { - EXPECT_EQ("", cookie_line); - get_cookie_success_ = false; - } - quit_closure_.Run(); - } - - void SetCookieCallback(bool result) { - ASSERT_TRUE(result); - quit_closure_.Run(); - } - - bool get_cookie_success_ = false; - base::Closure quit_closure_; - - // CookieStore must out live |this|. - net::CookieStore* cookie_store_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(RemoveCookieTester); -}; - class RemoveChannelIDTester : public net::SSLConfigService::Observer { public: explicit RemoveChannelIDTester(BrowserContext* browser_context) { @@ -381,78 +348,6 @@ class RemoveChannelIDTester : public net::SSLConfigService::Observer { DISALLOW_COPY_AND_ASSIGN(RemoveChannelIDTester); }; -class RemoveLocalStorageTester { - public: - explicit RemoveLocalStorageTester(BrowserContext* browser_context) - : browser_context_(browser_context) { - dom_storage_context_ = - BrowserContext::GetDefaultStoragePartition(browser_context_) - ->GetDOMStorageContext(); - } - - // Returns true, if the given origin URL exists. - bool DOMStorageExistsForOrigin(const GURL& origin) { - scoped_refptr message_loop_runner = - new MessageLoopRunner(); - quit_closure_ = message_loop_runner->QuitClosure(); - GetLocalStorageUsage(); - message_loop_runner->Run(); - for (size_t i = 0; i < infos_.size(); ++i) { - if (origin == infos_[i].origin) - return true; - } - return false; - } - - void AddDOMStorageTestData() { - // Note: This test depends on details of how the dom_storage library - // stores data in the host file system. - base::FilePath storage_path = - browser_context_->GetPath().AppendASCII("Local Storage"); - base::CreateDirectory(storage_path); - - // Write some files. - base::WriteFile(storage_path.Append(kDomStorageOrigin1), nullptr, 0); - base::WriteFile(storage_path.Append(kDomStorageOrigin2), nullptr, 0); - base::WriteFile(storage_path.Append(kDomStorageOrigin3), nullptr, 0); - base::WriteFile(storage_path.Append(kDomStorageExt), nullptr, 0); - - // Tweak their dates. - base::Time now = base::Time::Now(); - base::TouchFile(storage_path.Append(kDomStorageOrigin1), now, now); - - base::Time one_day_ago = now - base::TimeDelta::FromDays(1); - base::TouchFile(storage_path.Append(kDomStorageOrigin2), one_day_ago, - one_day_ago); - - base::Time sixty_days_ago = now - base::TimeDelta::FromDays(60); - base::TouchFile(storage_path.Append(kDomStorageOrigin3), sixty_days_ago, - sixty_days_ago); - - base::TouchFile(storage_path.Append(kDomStorageExt), now, now); - } - - private: - void GetLocalStorageUsage() { - dom_storage_context_->GetLocalStorageUsage( - base::Bind(&RemoveLocalStorageTester::OnGotLocalStorageUsage, - base::Unretained(this))); - } - void OnGotLocalStorageUsage(const std::vector& infos) { - infos_ = infos; - quit_closure_.Run(); - } - - // We don't own these pointers. - BrowserContext* browser_context_; - DOMStorageContext* dom_storage_context_ = nullptr; - - std::vector infos_; - base::Closure quit_closure_; - - DISALLOW_COPY_AND_ASSIGN(RemoveLocalStorageTester); -}; - class RemoveDownloadsTester { public: explicit RemoveDownloadsTester(BrowserContext* browser_context) @@ -548,7 +443,7 @@ class BrowsingDataRemoverImplTest : public testing::Test { int GetOriginTypeMask() { return remover_->GetLastUsedOriginTypeMask(); } - StoragePartitionRemovalData GetStoragePartitionRemovalData() { + const StoragePartitionRemovalData& GetStoragePartitionRemovalData() const { return storage_partition_removal_data_; } @@ -645,12 +540,16 @@ TEST_F(BrowsingDataRemoverImplTest, RemoveCookiesDomainBlacklist) { // Even though it's a different origin, it's the same domain. EXPECT_FALSE(removal_data.origin_matcher.Run(kOrigin4, mock_policy())); - EXPECT_FALSE(removal_data.cookie_matcher.Run(CreateCookieWithHost(kOrigin1))); - EXPECT_TRUE(removal_data.cookie_matcher.Run(CreateCookieWithHost(kOrigin2))); - EXPECT_FALSE(removal_data.cookie_matcher.Run(CreateCookieWithHost(kOrigin3))); + EXPECT_FALSE(FilterMatchesCookie(removal_data.cookie_deletion_filter, + CreateCookieWithHost(kOrigin1))); + EXPECT_TRUE(FilterMatchesCookie(removal_data.cookie_deletion_filter, + CreateCookieWithHost(kOrigin2))); + EXPECT_FALSE(FilterMatchesCookie(removal_data.cookie_deletion_filter, + CreateCookieWithHost(kOrigin3))); // This is false, because this is the same domain as 3, just with a different // scheme. - EXPECT_FALSE(removal_data.cookie_matcher.Run(CreateCookieWithHost(kOrigin4))); + EXPECT_FALSE(FilterMatchesCookie(removal_data.cookie_deletion_filter, + CreateCookieWithHost(kOrigin4))); } // Test that removing cookies clears HTTP auth data. @@ -1491,6 +1390,169 @@ TEST_F(BrowsingDataRemoverImplTest, RemoveDownloadsByOrigin) { std::move(builder)); } +#if BUILDFLAG(ENABLE_REPORTING) +TEST_F(BrowsingDataRemoverImplTest, RemoveReportingCache) { + auto reporting_context = std::make_unique( + base::DefaultClock::GetInstance(), base::DefaultTickClock::GetInstance(), + net::ReportingPolicy()); + net::ReportingCache* reporting_cache = reporting_context->cache(); + std::unique_ptr reporting_service = + net::ReportingService::CreateForTesting(std::move(reporting_context)); + + BrowserContext::GetDefaultStoragePartition(GetBrowserContext()) + ->GetURLRequestContext() + ->GetURLRequestContext() + ->set_reporting_service(reporting_service.get()); + + GURL domain("https://google.com"); + reporting_cache->SetClient(url::Origin::Create(domain), domain, + net::ReportingClient::Subdomains::EXCLUDE, "group", + base::TimeTicks::Max(), 0, 1); + + std::vector clients; + reporting_cache->GetClients(&clients); + ASSERT_EQ(1u, clients.size()); + + BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(), + BrowsingDataRemover::DATA_TYPE_COOKIES, false); + + reporting_cache->GetClients(&clients); + EXPECT_TRUE(clients.empty()); +} + +TEST_F(BrowsingDataRemoverImplTest, RemoveReportingCache_SpecificOrigins) { + auto reporting_context = std::make_unique( + base::DefaultClock::GetInstance(), base::DefaultTickClock::GetInstance(), + net::ReportingPolicy()); + net::ReportingCache* reporting_cache = reporting_context->cache(); + std::unique_ptr reporting_service = + net::ReportingService::CreateForTesting(std::move(reporting_context)); + + BrowserContext::GetDefaultStoragePartition(GetBrowserContext()) + ->GetURLRequestContext() + ->GetURLRequestContext() + ->set_reporting_service(reporting_service.get()); + + GURL domain1("https://google.com"); + reporting_cache->SetClient(url::Origin::Create(domain1), domain1, + net::ReportingClient::Subdomains::EXCLUDE, "group", + base::TimeTicks::Max(), 0, 1); + GURL domain2("https://host2.com"); + reporting_cache->SetClient(url::Origin::Create(domain2), domain2, + net::ReportingClient::Subdomains::EXCLUDE, "group", + base::TimeTicks::Max(), 0, 1); + GURL domain3("https://host3.com"); + reporting_cache->SetClient(url::Origin::Create(domain3), domain3, + net::ReportingClient::Subdomains::EXCLUDE, "group", + base::TimeTicks::Max(), 0, 1); + GURL domain4("https://host4.com"); + reporting_cache->SetClient(url::Origin::Create(domain4), domain4, + net::ReportingClient::Subdomains::EXCLUDE, "group", + base::TimeTicks::Max(), 0, 1); + + std::vector clients; + reporting_cache->GetClients(&clients); + ASSERT_EQ(4u, clients.size()); + + std::unique_ptr filter_builder( + BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST)); + filter_builder->AddRegisterableDomain("google.com"); + filter_builder->AddRegisterableDomain("host3.com"); + BlockUntilOriginDataRemoved(base::Time(), base::Time::Max(), + BrowsingDataRemover::DATA_TYPE_COOKIES, + std::move(filter_builder)); + + reporting_cache->GetClients(&clients); + EXPECT_EQ(2u, clients.size()); + std::vector origins; + for (const net::ReportingClient* client : clients) { + origins.push_back(client->endpoint); + } + EXPECT_THAT(origins, UnorderedElementsAre(domain2, domain4)); +} + +TEST_F(BrowsingDataRemoverImplTest, RemoveReportingCache_NoService) { + ASSERT_FALSE(BrowserContext::GetDefaultStoragePartition(GetBrowserContext()) + ->GetURLRequestContext() + ->GetURLRequestContext() + ->reporting_service()); + + BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(), + BrowsingDataRemover::DATA_TYPE_COOKIES, false); +} + +TEST_F(BrowsingDataRemoverImplTest, RemoveNetworkErrorLogging) { + std::unique_ptr logging_service = + net::NetworkErrorLoggingService::Create( + net::NetworkErrorLoggingDelegate::Create()); + BrowserContext::GetDefaultStoragePartition(GetBrowserContext()) + ->GetURLRequestContext() + ->GetURLRequestContext() + ->set_network_error_logging_service(logging_service.get()); + + GURL domain("https://google.com"); + logging_service->OnHeader(url::Origin::Create(domain), + "{\"report-to\":\"group\",\"max-age\":86400}"); + + ASSERT_EQ(1u, logging_service->GetPolicyOriginsForTesting().size()); + + BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(), + BrowsingDataRemover::DATA_TYPE_COOKIES, false); + + EXPECT_TRUE(logging_service->GetPolicyOriginsForTesting().empty()); +} + +TEST_F(BrowsingDataRemoverImplTest, RemoveNetworkErrorLogging_SpecificOrigins) { + std::unique_ptr logging_service = + net::NetworkErrorLoggingService::Create( + net::NetworkErrorLoggingDelegate::Create()); + BrowserContext::GetDefaultStoragePartition(GetBrowserContext()) + ->GetURLRequestContext() + ->GetURLRequestContext() + ->set_network_error_logging_service(logging_service.get()); + + GURL domain1("https://google.com"); + logging_service->OnHeader(url::Origin::Create(domain1), + "{\"report-to\":\"group\",\"max-age\":86400}"); + GURL domain2("https://host2.com"); + logging_service->OnHeader(url::Origin::Create(domain2), + "{\"report-to\":\"group\",\"max-age\":86400}"); + GURL domain3("https://host3.com"); + logging_service->OnHeader(url::Origin::Create(domain3), + "{\"report-to\":\"group\",\"max-age\":86400}"); + GURL domain4("https://host4.com"); + logging_service->OnHeader(url::Origin::Create(domain4), + "{\"report-to\":\"group\",\"max-age\":86400}"); + + ASSERT_EQ(4u, logging_service->GetPolicyOriginsForTesting().size()); + + std::unique_ptr filter_builder( + BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST)); + filter_builder->AddRegisterableDomain("google.com"); + filter_builder->AddRegisterableDomain("host3.com"); + BlockUntilOriginDataRemoved(base::Time(), base::Time::Max(), + BrowsingDataRemover::DATA_TYPE_COOKIES, + std::move(filter_builder)); + + std::set policy_origins = + logging_service->GetPolicyOriginsForTesting(); + EXPECT_EQ(2u, policy_origins.size()); + EXPECT_THAT(policy_origins, + UnorderedElementsAre(url::Origin::Create(domain2), + url::Origin::Create(domain4))); +} + +TEST_F(BrowsingDataRemoverImplTest, RemoveNetworkErrorLogging_NoService) { + ASSERT_FALSE(BrowserContext::GetDefaultStoragePartition(GetBrowserContext()) + ->GetURLRequestContext() + ->GetURLRequestContext() + ->network_error_logging_service()); + + BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(), + BrowsingDataRemover::DATA_TYPE_COOKIES, false); +} +#endif // BUILDFLAG(ENABLE_REPORTING) + class MultipleTasksObserver { public: // A simple implementation of BrowsingDataRemover::Observer. diff --git a/chromium/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc b/chromium/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc index bd37230b055..8fba2e45093 100644 --- a/chromium/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc +++ b/chromium/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc @@ -666,7 +666,7 @@ IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, MAYBE_Credentials) { std::string credentials; bool should_run; } kTestCases[] = { - {true, "", false}, + {true, "", true}, {true, "omit", false}, {true, "same-origin", true}, {true, "include", true}, diff --git a/chromium/content/browser/browsing_data/conditional_cache_deletion_helper.h b/chromium/content/browser/browsing_data/conditional_cache_deletion_helper.h index da7b590e8f9..ba0eb7369ac 100644 --- a/chromium/content/browser/browsing_data/conditional_cache_deletion_helper.h +++ b/chromium/content/browser/browsing_data/conditional_cache_deletion_helper.h @@ -8,7 +8,7 @@ #include #include "base/callback_forward.h" -#include "base/message_loop/message_loop.h" +#include "base/sequenced_task_runner_helpers.h" #include "content/common/content_export.h" #include "net/base/completion_callback.h" #include "net/base/net_errors.h" diff --git a/chromium/content/browser/byte_stream.cc b/chromium/content/browser/byte_stream.cc index 010e8d85295..91b59676545 100644 --- a/chromium/content/browser/byte_stream.cc +++ b/chromium/content/browser/byte_stream.cc @@ -60,7 +60,7 @@ class ByteStreamWriterImpl : public ByteStreamWriter { bool Write(scoped_refptr buffer, size_t byte_count) override; void Flush() override; void Close(int status) override; - void RegisterCallback(const base::Closure& source_callback) override; + void RegisterCallback(const base::RepeatingClosure& source_callback) override; size_t GetTotalBufferedBytes() const override; // PostTask target from |ByteStreamReaderImpl::MaybeUpdateInput|. @@ -83,7 +83,7 @@ class ByteStreamWriterImpl : public ByteStreamWriter { // True while this object is alive. scoped_refptr my_lifetime_flag_; - base::Closure space_available_callback_; + base::RepeatingClosure space_available_callback_; ContentVector input_contents_; size_t input_contents_size_; @@ -118,7 +118,7 @@ class ByteStreamReaderImpl : public ByteStreamReader { // Overridden from ByteStreamReader. StreamState Read(scoped_refptr* data, size_t* length) override; int GetStatus() const override; - void RegisterCallback(const base::Closure& sink_callback) override; + void RegisterCallback(const base::RepeatingClosure& sink_callback) override; // PostTask target from |ByteStreamWriterImpl::Write| and // |ByteStreamWriterImpl::Close|. @@ -154,7 +154,7 @@ class ByteStreamReaderImpl : public ByteStreamReader { bool received_status_; int status_; - base::Closure data_available_callback_; + base::RepeatingClosure data_available_callback_; // Time of last point at which data in stream transitioned from full // to non-full. Nulled when a callback is sent. diff --git a/chromium/content/browser/byte_stream.h b/chromium/content/browser/byte_stream.h index 375eeb5c16b..784bc67cc13 100644 --- a/chromium/content/browser/byte_stream.h +++ b/chromium/content/browser/byte_stream.h @@ -155,7 +155,8 @@ class CONTENT_EXPORT ByteStreamWriter { // available (i.e. in the case of that race either of the before // or after callbacks may be called). // The callback will not be called after ByteStreamWriter destruction. - virtual void RegisterCallback(const base::Closure& source_callback) = 0; + virtual void RegisterCallback( + const base::RepeatingClosure& source_callback) = 0; // Returns the number of bytes sent to the reader but not yet reported by // the reader as read. @@ -191,7 +192,8 @@ class CONTENT_EXPORT ByteStreamReader { // with data becoming available (i.e. in the case of that race // either of the before or after callbacks may be called). // The callback will not be called after ByteStreamReader destruction. - virtual void RegisterCallback(const base::Closure& sink_callback) = 0; + virtual void RegisterCallback( + const base::RepeatingClosure& sink_callback) = 0; }; CONTENT_EXPORT void CreateByteStream( diff --git a/chromium/content/browser/byte_stream_unittest.cc b/chromium/content/browser/byte_stream_unittest.cc index e774da17afb..7335cf2f717 100644 --- a/chromium/content/browser/byte_stream_unittest.cc +++ b/chromium/content/browser/byte_stream_unittest.cc @@ -331,7 +331,7 @@ TEST_F(ByteStreamTest, ByteStream_SinkCallback) { // Setup callback int num_callbacks = 0; byte_stream_output->RegisterCallback( - base::Bind(CountCallbacks, &num_callbacks)); + base::BindRepeating(CountCallbacks, &num_callbacks)); EXPECT_TRUE(Write(byte_stream_input.get(), 4000)); base::RunLoop().RunUntilIdle(); @@ -383,7 +383,7 @@ TEST_F(ByteStreamTest, ByteStream_SourceCallback) { // Add data. int num_callbacks = 0; byte_stream_input->RegisterCallback( - base::Bind(CountCallbacks, &num_callbacks)); + base::BindRepeating(CountCallbacks, &num_callbacks)); EXPECT_TRUE(Write(byte_stream_input.get(), 2000)); EXPECT_TRUE(Write(byte_stream_input.get(), 2001)); EXPECT_FALSE(Write(byte_stream_input.get(), 6000)); @@ -437,7 +437,7 @@ TEST_F(ByteStreamTest, ByteStream_SinkInterrupt) { // Record initial state. int num_callbacks = 0; byte_stream_output->RegisterCallback( - base::Bind(CountCallbacks, &num_callbacks)); + base::BindRepeating(CountCallbacks, &num_callbacks)); // Add data, and pass it across. EXPECT_TRUE(Write(byte_stream_input.get(), 4000)); @@ -451,7 +451,7 @@ TEST_F(ByteStreamTest, ByteStream_SinkInterrupt) { // (simulates race with post task). int num_alt_callbacks = 0; byte_stream_output->RegisterCallback( - base::Bind(CountCallbacks, &num_alt_callbacks)); + base::BindRepeating(CountCallbacks, &num_alt_callbacks)); task_runner->RunUntilIdle(); EXPECT_EQ(0, num_callbacks); EXPECT_EQ(1, num_alt_callbacks); @@ -483,7 +483,7 @@ TEST_F(ByteStreamTest, ByteStream_SourceInterrupt) { // Setup state for test. int num_callbacks = 0; byte_stream_input->RegisterCallback( - base::Bind(CountCallbacks, &num_callbacks)); + base::BindRepeating(CountCallbacks, &num_callbacks)); EXPECT_TRUE(Write(byte_stream_input.get(), 2000)); EXPECT_TRUE(Write(byte_stream_input.get(), 2001)); EXPECT_FALSE(Write(byte_stream_input.get(), 6000)); @@ -503,7 +503,7 @@ TEST_F(ByteStreamTest, ByteStream_SourceInterrupt) { // Which should do the right thing when it's run. int num_alt_callbacks = 0; byte_stream_input->RegisterCallback( - base::Bind(CountCallbacks, &num_alt_callbacks)); + base::BindRepeating(CountCallbacks, &num_alt_callbacks)); task_runner->RunUntilIdle(); EXPECT_EQ(0, num_callbacks); EXPECT_EQ(1, num_alt_callbacks); @@ -532,7 +532,7 @@ TEST_F(ByteStreamTest, ByteStream_ZeroCallback) { // Record initial state. int num_callbacks = 0; byte_stream_output->RegisterCallback( - base::Bind(CountCallbacks, &num_callbacks)); + base::BindRepeating(CountCallbacks, &num_callbacks)); // Immediately close the stream. byte_stream_input->Close(0); diff --git a/chromium/content/browser/cache_storage/OWNERS b/chromium/content/browser/cache_storage/OWNERS index b78333e3092..f802dd14208 100644 --- a/chromium/content/browser/cache_storage/OWNERS +++ b/chromium/content/browser/cache_storage/OWNERS @@ -1,4 +1,3 @@ -michaeln@chromium.org nhiroki@chromium.org jkarlin@chromium.org jsbell@chromium.org diff --git a/chromium/content/browser/cache_storage/cache_storage.cc b/chromium/content/browser/cache_storage/cache_storage.cc index 241d017ab4c..d91b54a4d2a 100644 --- a/chromium/content/browser/cache_storage/cache_storage.cc +++ b/chromium/content/browser/cache_storage/cache_storage.cc @@ -32,6 +32,7 @@ #include "content/browser/cache_storage/cache_storage_cache_handle.h" #include "content/browser/cache_storage/cache_storage_index.h" #include "content/browser/cache_storage/cache_storage_manager.h" +#include "content/browser/cache_storage/cache_storage_quota_client.h" #include "content/browser/cache_storage/cache_storage_scheduler.h" #include "content/public/browser/browser_thread.h" #include "crypto/symmetric_key.h" @@ -122,13 +123,15 @@ class CacheStorage::CacheLoader { storage::QuotaManagerProxy* quota_manager_proxy, base::WeakPtr blob_context, CacheStorage* cache_storage, - const url::Origin& origin) + const url::Origin& origin, + CacheStorageOwner owner) : cache_task_runner_(cache_task_runner), request_context_getter_(request_context_getter), quota_manager_proxy_(quota_manager_proxy), blob_context_(blob_context), cache_storage_(cache_storage), - origin_(origin) { + origin_(origin), + owner_(owner) { DCHECK(!origin_.unique()); } @@ -179,6 +182,7 @@ class CacheStorage::CacheLoader { CacheStorage* cache_storage_; url::Origin origin_; + CacheStorageOwner owner_; }; // Creates memory-only ServiceWorkerCaches. Because these caches have no @@ -192,13 +196,15 @@ class CacheStorage::MemoryLoader : public CacheStorage::CacheLoader { storage::QuotaManagerProxy* quota_manager_proxy, base::WeakPtr blob_context, CacheStorage* cache_storage, - const url::Origin& origin) + const url::Origin& origin, + CacheStorageOwner owner) : CacheLoader(cache_task_runner, request_context, quota_manager_proxy, blob_context, cache_storage, - origin) {} + origin, + owner) {} std::unique_ptr CreateCache( const std::string& cache_name, @@ -206,7 +212,7 @@ class CacheStorage::MemoryLoader : public CacheStorage::CacheLoader { int64_t cache_padding, std::unique_ptr cache_padding_key) override { return CacheStorageCache::CreateMemoryCache( - origin_, cache_name, cache_storage_, request_context_getter_, + origin_, owner_, cache_name, cache_storage_, request_context_getter_, quota_manager_proxy_, blob_context_, s_padding_key.Get().CreateDuplicate()); } @@ -260,13 +266,15 @@ class CacheStorage::SimpleCacheLoader : public CacheStorage::CacheLoader { storage::QuotaManagerProxy* quota_manager_proxy, base::WeakPtr blob_context, CacheStorage* cache_storage, - const url::Origin& origin) + const url::Origin& origin, + CacheStorageOwner owner) : CacheLoader(cache_task_runner, request_context, quota_manager_proxy, blob_context, cache_storage, - origin), + origin, + owner), origin_path_(origin_path), weak_ptr_factory_(this) {} @@ -281,7 +289,7 @@ class CacheStorage::SimpleCacheLoader : public CacheStorage::CacheLoader { std::string cache_dir = cache_name_to_cache_dir_[cache_name]; base::FilePath cache_path = origin_path_.AppendASCII(cache_dir); return CacheStorageCache::CreatePersistentCache( - origin_, cache_name, cache_storage_, cache_path, + origin_, owner_, cache_name, cache_storage_, cache_path, request_context_getter_, quota_manager_proxy_, blob_context_, cache_size, cache_padding, std::move(cache_padding_key)); } @@ -577,7 +585,8 @@ CacheStorage::CacheStorage( scoped_refptr quota_manager_proxy, base::WeakPtr blob_context, CacheStorageManager* cache_storage_manager, - const url::Origin& origin) + const url::Origin& origin, + CacheStorageOwner owner) : initialized_(false), initializing_(false), memory_only_(memory_only), @@ -587,16 +596,17 @@ CacheStorage::CacheStorage( cache_task_runner_(cache_task_runner), quota_manager_proxy_(quota_manager_proxy), origin_(origin), + owner_(owner), cache_storage_manager_(cache_storage_manager), weak_factory_(this) { if (memory_only) cache_loader_.reset(new MemoryLoader( cache_task_runner_.get(), std::move(request_context), - quota_manager_proxy.get(), blob_context, this, origin)); + quota_manager_proxy.get(), blob_context, this, origin, owner)); else cache_loader_.reset(new SimpleCacheLoader( origin_path_, cache_task_runner_.get(), std::move(request_context), - quota_manager_proxy.get(), blob_context, this, origin)); + quota_manager_proxy.get(), blob_context, this, origin, owner)); } CacheStorage::~CacheStorage() { @@ -610,7 +620,7 @@ void CacheStorage::OpenCache(const std::string& cache_name, LazyInit(); quota_manager_proxy_->NotifyStorageAccessed( - storage::QuotaClient::kServiceWorkerCache, origin_, + CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_, StorageType::kTemporary); scheduler_->ScheduleOperation(base::BindOnce( @@ -626,7 +636,7 @@ void CacheStorage::HasCache(const std::string& cache_name, LazyInit(); quota_manager_proxy_->NotifyStorageAccessed( - storage::QuotaClient::kServiceWorkerCache, origin_, + CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_, StorageType::kTemporary); scheduler_->ScheduleOperation(base::BindOnce( @@ -642,7 +652,7 @@ void CacheStorage::DoomCache(const std::string& cache_name, LazyInit(); quota_manager_proxy_->NotifyStorageAccessed( - storage::QuotaClient::kServiceWorkerCache, origin_, + CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_, StorageType::kTemporary); scheduler_->ScheduleOperation(base::BindOnce( @@ -657,7 +667,7 @@ void CacheStorage::EnumerateCaches(IndexCallback callback) { LazyInit(); quota_manager_proxy_->NotifyStorageAccessed( - storage::QuotaClient::kServiceWorkerCache, origin_, + CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_, StorageType::kTemporary); scheduler_->ScheduleOperation(base::BindOnce( @@ -668,7 +678,7 @@ void CacheStorage::EnumerateCaches(IndexCallback callback) { void CacheStorage::MatchCache( const std::string& cache_name, std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -676,18 +686,18 @@ void CacheStorage::MatchCache( LazyInit(); quota_manager_proxy_->NotifyStorageAccessed( - storage::QuotaClient::kServiceWorkerCache, origin_, + CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_, StorageType::kTemporary); scheduler_->ScheduleOperation( base::BindOnce(&CacheStorage::MatchCacheImpl, weak_factory_.GetWeakPtr(), - cache_name, std::move(request), match_params, + cache_name, std::move(request), std::move(match_params), scheduler_->WrapCallbackToRunNext(std::move(callback)))); } void CacheStorage::MatchAllCaches( std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -695,12 +705,32 @@ void CacheStorage::MatchAllCaches( LazyInit(); quota_manager_proxy_->NotifyStorageAccessed( - storage::QuotaClient::kServiceWorkerCache, origin_, + CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_, StorageType::kTemporary); scheduler_->ScheduleOperation(base::BindOnce( &CacheStorage::MatchAllCachesImpl, weak_factory_.GetWeakPtr(), - std::move(request), match_params, + std::move(request), std::move(match_params), + scheduler_->WrapCallbackToRunNext(std::move(callback)))); +} + +void CacheStorage::WriteToCache( + const std::string& cache_name, + std::unique_ptr request, + std::unique_ptr response, + CacheStorage::ErrorCallback callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!initialized_) + LazyInit(); + + quota_manager_proxy_->NotifyStorageAccessed( + CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_, + StorageType::kTemporary); + + scheduler_->ScheduleOperation(base::BindOnce( + &CacheStorage::WriteToCacheImpl, weak_factory_.GetWeakPtr(), cache_name, + std::move(request), std::move(response), scheduler_->WrapCallbackToRunNext(std::move(callback)))); } @@ -877,13 +907,14 @@ void CacheStorage::CreateCacheDidCreateCache( cache_name, cache_ptr->cache_size(), cache_ptr->cache_padding(), cache_ptr->cache_padding_key()->key())); + CacheStorageCacheHandle handle = CreateCacheHandle(cache_ptr); cache_loader_->WriteIndex( *cache_index_, base::BindOnce(&CacheStorage::CreateCacheDidWriteIndex, weak_factory_.GetWeakPtr(), std::move(callback), CreateCacheHandle(cache_ptr))); - cache_loader_->NotifyCacheCreated(cache_name, CreateCacheHandle(cache_ptr)); + cache_loader_->NotifyCacheCreated(cache_name, std::move(handle)); if (cache_storage_manager_) cache_storage_manager_->NotifyCacheListChanged(origin_); } @@ -967,7 +998,7 @@ void CacheStorage::DeleteCacheFinalize(CacheStorageCache* doomed_cache) { void CacheStorage::DeleteCacheDidGetSize(CacheStorageCache* doomed_cache, int64_t cache_size) { quota_manager_proxy_->NotifyStorageModified( - storage::QuotaClient::kServiceWorkerCache, origin_, + CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_, StorageType::kTemporary, -1 * cache_size); cache_loader_->CleanUpDeletedCache(doomed_cache); @@ -983,7 +1014,7 @@ void CacheStorage::EnumerateCachesImpl(IndexCallback callback) { void CacheStorage::MatchCacheImpl( const std::string& cache_name, std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback) { CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_name); @@ -997,7 +1028,7 @@ void CacheStorage::MatchCacheImpl( // match is done. CacheStorageCache* cache_ptr = cache_handle.value(); cache_ptr->Match( - std::move(request), match_params, + std::move(request), std::move(match_params), base::BindOnce(&CacheStorage::MatchCacheDidMatch, weak_factory_.GetWeakPtr(), std::move(cache_handle), std::move(callback))); @@ -1013,7 +1044,7 @@ void CacheStorage::MatchCacheDidMatch( void CacheStorage::MatchAllCachesImpl( std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback) { std::vector* match_responses = new std::vector(cache_index_->num_entries()); @@ -1031,7 +1062,8 @@ void CacheStorage::MatchAllCachesImpl( CacheStorageCache* cache_ptr = cache_handle.value(); cache_ptr->Match( - std::make_unique(*request), match_params, + std::make_unique(*request), + match_params ? match_params->Clone() : nullptr, base::BindOnce(&CacheStorage::MatchAllCachesDidMatch, weak_factory_.GetWeakPtr(), std::move(cache_handle), &match_responses->at(idx), barrier_closure)); @@ -1064,6 +1096,24 @@ void CacheStorage::MatchAllCachesDidMatchAll( std::move(callback).Run(CacheStorageError::kErrorNotFound, nullptr); } +void CacheStorage::WriteToCacheImpl( + const std::string& cache_name, + std::unique_ptr request, + std::unique_ptr response, + CacheStorage::ErrorCallback callback) { + CacheStorageCacheHandle cache_handle = GetLoadedCache(cache_name); + + if (!cache_handle.value()) { + std::move(callback).Run(CacheStorageError::kErrorCacheNameNotFound); + return; + } + + CacheStorageCache* cache_ptr = cache_handle.value(); + DCHECK(cache_ptr); + + cache_ptr->Put(std::move(request), std::move(response), std::move(callback)); +} + void CacheStorage::AddCacheHandleRef(CacheStorageCache* cache) { DCHECK_CURRENTLY_ON(BrowserThread::IO); auto iter = cache_handle_counts_.find(cache); diff --git a/chromium/content/browser/cache_storage/cache_storage.h b/chromium/content/browser/cache_storage/cache_storage.h index acfbcd77ec0..af34ef18253 100644 --- a/chromium/content/browser/cache_storage/cache_storage.h +++ b/chromium/content/browser/cache_storage/cache_storage.h @@ -40,6 +40,7 @@ class CacheStorageCacheHandle; class CacheStorageIndex; class CacheStorageManager; class CacheStorageScheduler; +enum class CacheStorageOwner; namespace cache_storage_manager_unittest { class CacheStorageManagerTest; @@ -76,7 +77,8 @@ class CONTENT_EXPORT CacheStorage : public CacheStorageCacheObserver { scoped_refptr quota_manager_proxy, base::WeakPtr blob_context, CacheStorageManager* cache_storage_manager, - const url::Origin& origin); + const url::Origin& origin, + CacheStorageOwner owner); // Any unfinished asynchronous operations may not complete or call their // callbacks. @@ -105,7 +107,7 @@ class CONTENT_EXPORT CacheStorage : public CacheStorageCacheObserver { // Calls match on the cache with the given |cache_name|. void MatchCache(const std::string& cache_name, std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback); // Calls match on all of the caches in parallel, calling |callback| with the @@ -113,9 +115,15 @@ class CONTENT_EXPORT CacheStorage : public CacheStorageCacheObserver { // entry. If no response is found then |callback| is called with // blink::mojom::CacheStorageError::kErrorNotFound. void MatchAllCaches(std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback); + // Puts the request/response pair in the cache. + void WriteToCache(const std::string& cache_name, + std::unique_ptr request, + std::unique_ptr response, + CacheStorage::ErrorCallback callback); + // Sums the sizes of each cache and closes them. Runs |callback| with the // size. The sizes include any doomed caches and will also force close all // caches even if there are existing handles to them. @@ -201,7 +209,7 @@ class CONTENT_EXPORT CacheStorage : public CacheStorageCacheObserver { // The MatchCache callbacks are below. void MatchCacheImpl(const std::string& cache_name, std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback); void MatchCacheDidMatch(CacheStorageCacheHandle cache_handle, CacheStorageCache::ResponseCallback callback, @@ -210,7 +218,7 @@ class CONTENT_EXPORT CacheStorage : public CacheStorageCacheObserver { // The MatchAllCaches callbacks are below. void MatchAllCachesImpl(std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback); void MatchAllCachesDidMatch( CacheStorageCacheHandle cache_handle, @@ -222,6 +230,12 @@ class CONTENT_EXPORT CacheStorage : public CacheStorageCacheObserver { std::unique_ptr> match_responses, CacheStorageCache::ResponseCallback callback); + // WriteToCache callbacks. + void WriteToCacheImpl(const std::string& cache_name, + std::unique_ptr request, + std::unique_ptr response, + CacheStorage::ErrorCallback callback); + void GetSizeThenCloseAllCachesImpl(SizeCallback callback); void SizeImpl(SizeCallback callback); @@ -280,6 +294,9 @@ class CONTENT_EXPORT CacheStorage : public CacheStorageCacheObserver { // The origin that this CacheStorage is associated with. url::Origin origin_; + // The owner that this CacheStorage is associated with. + CacheStorageOwner owner_; + // The manager that owns this cache storage. Only set to null by // RemoveManager() when this cache storage is being deleted. CacheStorageManager* cache_storage_manager_; diff --git a/chromium/content/browser/cache_storage/cache_storage_cache.cc b/chromium/content/browser/cache_storage/cache_storage_cache.cc index 90956c40767..d5459e40398 100644 --- a/chromium/content/browser/cache_storage/cache_storage_cache.cc +++ b/chromium/content/browser/cache_storage/cache_storage_cache.cc @@ -27,6 +27,7 @@ #include "content/browser/cache_storage/cache_storage_blob_to_disk_cache.h" #include "content/browser/cache_storage/cache_storage_cache_handle.h" #include "content/browser/cache_storage/cache_storage_cache_observer.h" +#include "content/browser/cache_storage/cache_storage_quota_client.h" #include "content/browser/cache_storage/cache_storage_scheduler.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_features.h" @@ -379,11 +380,11 @@ struct CacheStorageCache::QueryCacheResult { struct CacheStorageCache::QueryCacheContext { QueryCacheContext(std::unique_ptr request, - const CacheStorageCacheQueryParams& options, + blink::mojom::QueryParamsPtr options, QueryCacheCallback callback, QueryTypes query_types) : request(std::move(request)), - options(options), + options(std::move(options)), callback(std::move(callback)), query_types(query_types), matches(std::make_unique()) {} @@ -400,7 +401,7 @@ struct CacheStorageCache::QueryCacheContext { // Input to QueryCache std::unique_ptr request; - CacheStorageCacheQueryParams options; + blink::mojom::QueryParamsPtr options; QueryCacheCallback callback; QueryTypes query_types = 0; size_t estimated_out_bytes = 0; @@ -419,6 +420,7 @@ struct CacheStorageCache::QueryCacheContext { // static std::unique_ptr CacheStorageCache::CreateMemoryCache( const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, CacheStorage* cache_storage, scoped_refptr request_context_getter, @@ -426,7 +428,7 @@ std::unique_ptr CacheStorageCache::CreateMemoryCache( base::WeakPtr blob_context, std::unique_ptr cache_padding_key) { CacheStorageCache* cache = new CacheStorageCache( - origin, cache_name, base::FilePath(), cache_storage, + origin, owner, cache_name, base::FilePath(), cache_storage, std::move(request_context_getter), std::move(quota_manager_proxy), blob_context, 0 /* cache_size */, 0 /* cache_padding */, std::move(cache_padding_key)); @@ -438,6 +440,7 @@ std::unique_ptr CacheStorageCache::CreateMemoryCache( // static std::unique_ptr CacheStorageCache::CreatePersistentCache( const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, CacheStorage* cache_storage, const base::FilePath& path, @@ -448,7 +451,7 @@ std::unique_ptr CacheStorageCache::CreatePersistentCache( int64_t cache_padding, std::unique_ptr cache_padding_key) { CacheStorageCache* cache = new CacheStorageCache( - origin, cache_name, path, cache_storage, + origin, owner, cache_name, path, cache_storage, std::move(request_context_getter), std::move(quota_manager_proxy), blob_context, cache_size, cache_padding, std::move(cache_padding_key)); cache->SetObserver(cache_storage); @@ -462,7 +465,7 @@ base::WeakPtr CacheStorageCache::AsWeakPtr() { void CacheStorageCache::Match( std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, ResponseCallback callback) { if (backend_state_ == BACKEND_CLOSED) { std::move(callback).Run(CacheStorageError::kErrorStorage, nullptr); @@ -471,13 +474,13 @@ void CacheStorageCache::Match( scheduler_->ScheduleOperation(base::BindOnce( &CacheStorageCache::MatchImpl, weak_ptr_factory_.GetWeakPtr(), - std::move(request), match_params, + std::move(request), std::move(match_params), scheduler_->WrapCallbackToRunNext(std::move(callback)))); } void CacheStorageCache::MatchAll( std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, ResponsesCallback callback) { if (backend_state_ == BACKEND_CLOSED) { std::move(callback).Run(CacheStorageError::kErrorStorage, @@ -487,7 +490,7 @@ void CacheStorageCache::MatchAll( scheduler_->ScheduleOperation(base::BindOnce( &CacheStorageCache::MatchAllImpl, weak_ptr_factory_.GetWeakPtr(), - std::move(request), match_params, + std::move(request), std::move(match_params), scheduler_->WrapCallbackToRunNext(std::move(callback)))); } @@ -515,7 +518,7 @@ void CacheStorageCache::WriteSideData(ErrorCallback callback, } void CacheStorageCache::BatchOperation( - const std::vector& operations, + std::vector operations, ErrorCallback callback, BadMessageCallback bad_message_callback) { if (backend_state_ == BACKEND_CLOSED) { @@ -530,9 +533,9 @@ void CacheStorageCache::BatchOperation( base::CheckedNumeric safe_space_required = 0; base::CheckedNumeric safe_side_data_size = 0; for (const auto& operation : operations) { - if (operation.operation_type == CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT) { - safe_space_required += operation.response.blob_size; - safe_side_data_size += operation.response.side_data_blob_size; + if (operation->operation_type == blink::mojom::OperationType::kPut) { + safe_space_required += operation->response->blob_size; + safe_side_data_size += operation->response->side_data_blob_size; } } if (!safe_space_required.IsValid() || !safe_side_data_size.IsValid()) { @@ -556,19 +559,21 @@ void CacheStorageCache::BatchOperation( blink::mojom::StorageType::kTemporary, base::AdaptCallbackForRepeating(base::BindOnce( &CacheStorageCache::BatchDidGetUsageAndQuota, - weak_ptr_factory_.GetWeakPtr(), operations, std::move(callback), - std::move(bad_message_callback), space_required, side_data_size))); + weak_ptr_factory_.GetWeakPtr(), std::move(operations), + std::move(callback), std::move(bad_message_callback), + space_required, side_data_size))); return; } - BatchDidGetUsageAndQuota( - operations, std::move(callback), std::move(bad_message_callback), - 0 /* space_required */, 0 /* side_data_size */, - blink::mojom::QuotaStatusCode::kOk, 0 /* usage */, 0 /* quota */); + BatchDidGetUsageAndQuota(std::move(operations), std::move(callback), + std::move(bad_message_callback), + 0 /* space_required */, 0 /* side_data_size */, + blink::mojom::QuotaStatusCode::kOk, 0 /* usage */, + 0 /* quota */); } void CacheStorageCache::BatchDidGetUsageAndQuota( - const std::vector& operations, + std::vector operations, ErrorCallback callback, BadMessageCallback bad_message_callback, uint64_t space_required, @@ -617,24 +622,23 @@ void CacheStorageCache::BatchDidGetUsageAndQuota( // will no-op automatically.) CacheStorageCacheHandle handle = CreateCacheHandle(); - for (const auto& operation : operations) { - switch (operation.operation_type) { - case CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT: + for (auto& operation : operations) { + switch (operation->operation_type) { + case blink::mojom::OperationType::kPut: if (skip_side_data) { - CacheStorageBatchOperation new_operation = operation; - new_operation.response.side_data_blob_uuid = std::string(); - new_operation.response.side_data_blob_size = 0; - new_operation.response.side_data_blob = nullptr; - Put(new_operation, completion_callback); + operation->response->side_data_blob_uuid = std::string(); + operation->response->side_data_blob_size = 0; + operation->response->side_data_blob = nullptr; + Put(std::move(operation), completion_callback); } else { - Put(operation, completion_callback); + Put(std::move(operation), completion_callback); } break; - case CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE: + case blink::mojom::OperationType::kDelete: DCHECK_EQ(1u, operations.size()); - Delete(operation, completion_callback); + Delete(std::move(operation), completion_callback); break; - case CACHE_STORAGE_CACHE_OPERATION_TYPE_UNDEFINED: + case blink::mojom::OperationType::kUndefined: NOTREACHED(); // TODO(nhiroki): This should return "TypeError". // http://crbug.com/425505 @@ -664,7 +668,7 @@ void CacheStorageCache::BatchDidAllOperations(ErrorCallback callback) { } void CacheStorageCache::Keys(std::unique_ptr request, - const CacheStorageCacheQueryParams& options, + blink::mojom::QueryParamsPtr options, RequestsCallback callback) { if (backend_state_ == BACKEND_CLOSED) { std::move(callback).Run(CacheStorageError::kErrorStorage, nullptr); @@ -673,7 +677,7 @@ void CacheStorageCache::Keys(std::unique_ptr request, scheduler_->ScheduleOperation(base::BindOnce( &CacheStorageCache::KeysImpl, weak_ptr_factory_.GetWeakPtr(), - std::move(request), options, + std::move(request), std::move(options), scheduler_->WrapCallbackToRunNext(std::move(callback)))); } @@ -724,6 +728,7 @@ CacheStorageCache::~CacheStorageCache() { CacheStorageCache::CacheStorageCache( const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, const base::FilePath& path, CacheStorage* cache_storage, @@ -734,6 +739,7 @@ CacheStorageCache::CacheStorageCache( int64_t cache_padding, std::unique_ptr cache_padding_key) : origin_(origin), + owner_(owner), cache_name_(cache_name), path_(path), cache_storage_(cache_storage), @@ -764,7 +770,7 @@ CacheStorageCache::CacheStorageCache( void CacheStorageCache::QueryCache( std::unique_ptr request, - const CacheStorageCacheQueryParams& options, + blink::mojom::QueryParamsPtr options, QueryTypes query_types, QueryCacheCallback callback) { DCHECK_NE( @@ -775,19 +781,22 @@ void CacheStorageCache::QueryCache( return; } - if (!options.ignore_method && request && !request->method.empty() && - request->method != "GET") { + if ((!options || !options->ignore_method) && request && + !request->method.empty() && request->method != "GET") { std::move(callback).Run(CacheStorageError::kSuccess, std::make_unique()); return; } ServiceWorkerFetchRequest* request_ptr = request.get(); - std::unique_ptr query_cache_context(new QueryCacheContext( - std::move(request), options, std::move(callback), query_types)); + std::unique_ptr query_cache_context( + new QueryCacheContext(std::move(request), std::move(options), + std::move(callback), query_types)); if (query_cache_context->request && - !query_cache_context->request->url.is_empty() && !options.ignore_search) { + !query_cache_context->request->url.is_empty() && + (!query_cache_context->options || + !query_cache_context->options->ignore_search)) { // There is no need to scan the entire backend, just open the exact // URL. disk_cache::Entry** entry_ptr = &query_cache_context->enumerated_entry; @@ -880,7 +889,8 @@ void CacheStorageCache::QueryCacheFilterEntry( GURL requestURL = query_cache_context->request->url; GURL cachedURL = GURL(entry->GetKey()); - if (query_cache_context->options.ignore_search) { + if (query_cache_context->options && + query_cache_context->options->ignore_search) { requestURL = RemoveQueryParam(requestURL); cachedURL = RemoveQueryParam(cachedURL); } @@ -922,7 +932,8 @@ void CacheStorageCache::QueryCacheDidReadMetadata( match->response = CreateResponse(*metadata, cache_name_); if (query_cache_context->request && - !query_cache_context->options.ignore_vary && + (!query_cache_context->options || + !query_cache_context->options->ignore_vary) && !VaryMatches(query_cache_context->request->headers, match->request->headers, match->response->headers)) { query_cache_context->matches->pop_back(); @@ -997,10 +1008,10 @@ int32_t CacheStorageCache::GetResponsePaddingVersion() { void CacheStorageCache::MatchImpl( std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, ResponseCallback callback) { MatchAllImpl( - std::move(request), match_params, + std::move(request), std::move(match_params), base::BindOnce(&CacheStorageCache::MatchDidMatchAll, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } @@ -1027,7 +1038,7 @@ void CacheStorageCache::MatchDidMatchAll( void CacheStorageCache::MatchAllImpl( std::unique_ptr request, - const CacheStorageCacheQueryParams& options, + blink::mojom::QueryParamsPtr options, ResponsesCallback callback) { DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_); if (backend_state_ != BACKEND_OPEN) { @@ -1037,7 +1048,7 @@ void CacheStorageCache::MatchAllImpl( } QueryCache( - std::move(request), options, + std::move(request), std::move(options), QUERY_CACHE_REQUESTS | QUERY_CACHE_RESPONSES_WITH_BODIES, base::BindOnce(&CacheStorageCache::MatchAllDidQueryCache, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); @@ -1197,19 +1208,28 @@ void CacheStorageCache::WriteSideDataDidWrite( base::BindOnce(std::move(callback), CacheStorageError::kSuccess)); } -void CacheStorageCache::Put(const CacheStorageBatchOperation& operation, +void CacheStorageCache::Put(blink::mojom::BatchOperationPtr operation, ErrorCallback callback) { DCHECK(BACKEND_OPEN == backend_state_ || initializing_); - DCHECK_EQ(CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT, operation.operation_type); + DCHECK_EQ(blink::mojom::OperationType::kPut, operation->operation_type); std::unique_ptr request( new ServiceWorkerFetchRequest( - operation.request.url, operation.request.method, - operation.request.headers, operation.request.referrer, - operation.request.is_reload)); + operation->request.url, operation->request.method, + operation->request.headers, operation->request.referrer, + operation->request.is_reload)); std::unique_ptr response = - std::make_unique(operation.response); + std::make_unique(*operation->response); + + Put(std::move(request), std::move(response), std::move(callback)); +} + +void CacheStorageCache::Put(std::unique_ptr request, + std::unique_ptr response, + ErrorCallback callback) { + DCHECK(BACKEND_OPEN == backend_state_ || initializing_); + blink::mojom::BlobPtr blob; blink::mojom::BlobPtr side_data_blob; @@ -1219,7 +1239,7 @@ void CacheStorageCache::Put(const CacheStorageBatchOperation& operation, side_data_blob = response->side_data_blob->Clone(); UMA_HISTOGRAM_ENUMERATION("ServiceWorkerCache.Cache.AllWritesResponseType", - operation.response.response_type); + response->response_type); auto put_context = std::make_unique( std::move(request), std::move(response), std::move(blob), @@ -1247,11 +1267,11 @@ void CacheStorageCache::PutImpl(std::unique_ptr put_context) { put_context->request->url, "", ServiceWorkerHeaderMap(), Referrer(), false); - CacheStorageCacheQueryParams query_options; - query_options.ignore_method = true; - query_options.ignore_vary = true; + blink::mojom::QueryParamsPtr query_options = blink::mojom::QueryParams::New(); + query_options->ignore_method = true; + query_options->ignore_vary = true; DeleteImpl( - std::move(delete_request), query_options, + std::move(delete_request), std::move(query_options), base::BindOnce(&CacheStorageCache::PutDidDeleteEntry, weak_ptr_factory_.GetWeakPtr(), std::move(put_context))); } @@ -1458,9 +1478,10 @@ void CacheStorageCache::CalculateCacheSizePaddingGotSize( // necessary. DCHECK_EQ(backend_state_, BACKEND_UNINITIALIZED); std::unique_ptr request; - CacheStorageCacheQueryParams options; - options.ignore_search = true; - QueryCache(std::move(request), options, QUERY_CACHE_RESPONSES_NO_BODIES, + blink::mojom::QueryParamsPtr options = blink::mojom::QueryParams::New(); + options->ignore_search = true; + QueryCache(std::move(request), std::move(options), + QUERY_CACHE_RESPONSES_NO_BODIES, base::BindOnce(&CacheStorageCache::PaddingDidQueryCache, weak_ptr_factory_.GetWeakPtr(), std::move(callback), cache_size)); @@ -1516,7 +1537,7 @@ void CacheStorageCache::UpdateCacheSizeGotSize( last_reported_size_ = PaddedCacheSize(); quota_manager_proxy_->NotifyStorageModified( - storage::QuotaClient::kServiceWorkerCache, origin_, + CacheStorageQuotaClient::GetIDFromOwner(owner_), origin_, blink::mojom::StorageType::kTemporary, size_delta); if (cache_storage_) @@ -1528,27 +1549,26 @@ void CacheStorageCache::UpdateCacheSizeGotSize( std::move(callback).Run(); } -void CacheStorageCache::Delete(const CacheStorageBatchOperation& operation, +void CacheStorageCache::Delete(blink::mojom::BatchOperationPtr operation, ErrorCallback callback) { DCHECK(BACKEND_OPEN == backend_state_ || initializing_); - DCHECK_EQ(CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE, - operation.operation_type); + DCHECK_EQ(blink::mojom::OperationType::kDelete, operation->operation_type); std::unique_ptr request( new ServiceWorkerFetchRequest( - operation.request.url, operation.request.method, - operation.request.headers, operation.request.referrer, - operation.request.is_reload)); + operation->request.url, operation->request.method, + operation->request.headers, operation->request.referrer, + operation->request.is_reload)); scheduler_->ScheduleOperation(base::BindOnce( &CacheStorageCache::DeleteImpl, weak_ptr_factory_.GetWeakPtr(), - std::move(request), operation.match_params, + std::move(request), std::move(operation->match_params), scheduler_->WrapCallbackToRunNext(std::move(callback)))); } void CacheStorageCache::DeleteImpl( std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, ErrorCallback callback) { DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_); if (backend_state_ != BACKEND_OPEN) { @@ -1557,7 +1577,7 @@ void CacheStorageCache::DeleteImpl( } QueryCache( - std::move(request), match_params, + std::move(request), std::move(match_params), QUERY_CACHE_ENTRIES | QUERY_CACHE_RESPONSES_NO_BODIES, base::BindOnce(&CacheStorageCache::DeleteDidQueryCache, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); @@ -1593,7 +1613,7 @@ void CacheStorageCache::DeleteDidQueryCache( void CacheStorageCache::KeysImpl( std::unique_ptr request, - const CacheStorageCacheQueryParams& options, + blink::mojom::QueryParamsPtr options, RequestsCallback callback) { DCHECK_NE(BACKEND_UNINITIALIZED, backend_state_); if (backend_state_ != BACKEND_OPEN) { @@ -1602,7 +1622,7 @@ void CacheStorageCache::KeysImpl( } QueryCache( - std::move(request), options, QUERY_CACHE_REQUESTS, + std::move(request), std::move(options), QUERY_CACHE_REQUESTS, base::BindOnce(&CacheStorageCache::KeysDidQueryCache, weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } diff --git a/chromium/content/browser/cache_storage/cache_storage_cache.h b/chromium/content/browser/cache_storage/cache_storage_cache.h index 75d0bc95ac9..f343ccbf97d 100644 --- a/chromium/content/browser/cache_storage/cache_storage_cache.h +++ b/chromium/content/browser/cache_storage/cache_storage_cache.h @@ -16,7 +16,6 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "content/common/cache_storage/cache_storage_types.h" #include "content/common/service_worker/service_worker_types.h" #include "net/base/io_buffer.h" #include "net/disk_cache/disk_cache.h" @@ -44,6 +43,7 @@ class CacheStorageCacheHandle; class CacheStorageCacheObserver; class CacheStorageScheduler; class TestCacheStorageCache; +enum class CacheStorageOwner; namespace proto { class CacheMetadata; @@ -81,6 +81,7 @@ class CONTENT_EXPORT CacheStorageCache { static std::unique_ptr CreateMemoryCache( const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, CacheStorage* cache_storage, scoped_refptr request_context_getter, @@ -89,6 +90,7 @@ class CONTENT_EXPORT CacheStorageCache { std::unique_ptr cache_padding_key); static std::unique_ptr CreatePersistentCache( const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, CacheStorage* cache_storage, const base::FilePath& path, @@ -106,14 +108,14 @@ class CONTENT_EXPORT CacheStorageCache { // Returns ERROR_TYPE_NOT_FOUND if not found. void Match(std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, ResponseCallback callback); // Returns blink::mojom::CacheStorageError::kSuccess and matched // responses in this cache. If there are no responses, returns // blink::mojom::CacheStorageError::kSuccess and an empty vector. void MatchAll(std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, ResponsesCallback callback); // Writes the side data (ex: V8 code cache) for the specified cache entry. @@ -141,11 +143,11 @@ class CONTENT_EXPORT CacheStorageCache { // // TODO(nhiroki): This function should run all operations atomically. // http://crbug.com/486637 - void BatchOperation(const std::vector& operations, + void BatchOperation(std::vector operations, ErrorCallback callback, BadMessageCallback bad_message_callback); void BatchDidGetUsageAndQuota( - const std::vector& operations, + std::vector operations, ErrorCallback callback, BadMessageCallback bad_message_callback, uint64_t space_required, @@ -166,7 +168,7 @@ class CONTENT_EXPORT CacheStorageCache { // Returns blink::mojom::CacheStorageError::kSuccess and a vector of // requests if there are no errors. void Keys(std::unique_ptr request, - const CacheStorageCacheQueryParams& options, + blink::mojom::QueryParamsPtr options, RequestsCallback callback); // Closes the backend. Future operations that require the backend @@ -180,6 +182,14 @@ class CONTENT_EXPORT CacheStorageCache { // the cache's size. void GetSizeThenClose(SizeCallback callback); + // Puts the request/response pair in the cache. This is a public member to + // directly bypass the batch operations and write into the cache. This is used + // by non-CacheAPI owners. The Cache Storage API uses batch operations defined + // in the dispatcher. + void Put(std::unique_ptr request, + std::unique_ptr response, + ErrorCallback callback); + // Async operations in progress will cancel and not run their callbacks. virtual ~CacheStorageCache(); @@ -243,6 +253,7 @@ class CONTENT_EXPORT CacheStorageCache { CacheStorageCache( const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, const base::FilePath& path, CacheStorage* cache_storage, @@ -260,7 +271,7 @@ class CONTENT_EXPORT CacheStorageCache { // REQUESTS_AND_RESPONSES then only out_requests, out_responses, and // out_blob_data_handles are valid. void QueryCache(std::unique_ptr request, - const CacheStorageCacheQueryParams& options, + blink::mojom::QueryParamsPtr options, QueryTypes query_types, QueryCacheCallback callback); void QueryCacheDidOpenFastPath( @@ -280,7 +291,7 @@ class CONTENT_EXPORT CacheStorageCache { // Match callbacks void MatchImpl(std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, ResponseCallback callback); void MatchDidMatchAll(ResponseCallback callback, blink::mojom::CacheStorageError match_all_error, @@ -288,7 +299,7 @@ class CONTENT_EXPORT CacheStorageCache { // MatchAll callbacks void MatchAllImpl(std::unique_ptr request, - const CacheStorageCacheQueryParams& options, + blink::mojom::QueryParamsPtr options, ResponsesCallback callback); void MatchAllDidQueryCache( ResponsesCallback callback, @@ -343,7 +354,7 @@ class CONTENT_EXPORT CacheStorageCache { // Puts the request and response object in the cache. The response body (if // present) is stored in the cache, but not the request body. Returns OK on // success. - void Put(const CacheStorageBatchOperation& operation, ErrorCallback callback); + void Put(blink::mojom::BatchOperationPtr operation, ErrorCallback callback); void PutImpl(std::unique_ptr put_context); void PutDidDeleteEntry(std::unique_ptr put_context, blink::mojom::CacheStorageError error); @@ -373,10 +384,10 @@ class CONTENT_EXPORT CacheStorageCache { int current_cache_size); // Returns ERROR_NOT_FOUND if not found. Otherwise deletes and returns OK. - void Delete(const CacheStorageBatchOperation& operation, + void Delete(blink::mojom::BatchOperationPtr operation, ErrorCallback callback); void DeleteImpl(std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, ErrorCallback callback); void DeleteDidQueryCache( ErrorCallback callback, @@ -385,7 +396,7 @@ class CONTENT_EXPORT CacheStorageCache { // Keys callbacks. void KeysImpl(std::unique_ptr request, - const CacheStorageCacheQueryParams& options, + blink::mojom::QueryParamsPtr options, RequestsCallback callback); void KeysDidQueryCache( RequestsCallback callback, @@ -441,6 +452,7 @@ class CONTENT_EXPORT CacheStorageCache { std::unique_ptr backend_; url::Origin origin_; + CacheStorageOwner owner_; const std::string cache_name_; base::FilePath path_; diff --git a/chromium/content/browser/cache_storage/cache_storage_cache_unittest.cc b/chromium/content/browser/cache_storage/cache_storage_cache_unittest.cc index 3243379ec93..92d66a32d43 100644 --- a/chromium/content/browser/cache_storage/cache_storage_cache_unittest.cc +++ b/chromium/content/browser/cache_storage/cache_storage_cache_unittest.cc @@ -23,7 +23,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/cache_storage/cache_storage_cache_handle.h" -#include "content/common/cache_storage/cache_storage_types.h" +#include "content/browser/cache_storage/cache_storage_manager.h" #include "content/common/service_worker/service_worker_types.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" @@ -283,6 +283,7 @@ class TestCacheStorageCache : public CacheStorageCache { const scoped_refptr& quota_manager_proxy, base::WeakPtr blob_context) : CacheStorageCache(origin, + CacheStorageOwner::kCacheAPI, cache_name, path, cache_storage, @@ -490,11 +491,11 @@ class CacheStorageCacheTest : public testing::Test { } CacheStorageError BatchOperation( - const std::vector& operations) { + std::vector operations) { std::unique_ptr loop(new base::RunLoop()); cache_->BatchOperation( - operations, + std::move(operations), base::BindOnce(&CacheStorageCacheTest::ErrorTypeCallback, base::Unretained(this), base::Unretained(loop.get())), base::BindOnce(&OnBadMessage, base::Unretained(&bad_message_reason_))); @@ -507,23 +508,24 @@ class CacheStorageCacheTest : public testing::Test { bool Put(const ServiceWorkerFetchRequest& request, const ServiceWorkerResponse& response) { - CacheStorageBatchOperation operation; - operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation.request = request; - operation.response = response; - - CacheStorageError error = - BatchOperation(std::vector(1, operation)); + blink::mojom::BatchOperationPtr operation = + blink::mojom::BatchOperation::New(); + operation->operation_type = blink::mojom::OperationType::kPut; + operation->request = request; + operation->response = response; + + std::vector operations; + operations.emplace_back(std::move(operation)); + CacheStorageError error = BatchOperation(std::move(operations)); return error == CacheStorageError::kSuccess; } bool Match(const ServiceWorkerFetchRequest& request, - const CacheStorageCacheQueryParams& match_params = - CacheStorageCacheQueryParams()) { + blink::mojom::QueryParamsPtr match_params = nullptr) { std::unique_ptr loop(new base::RunLoop()); cache_->Match( - CopyFetchRequest(request), match_params, + CopyFetchRequest(request), std::move(match_params), base::BindOnce(&CacheStorageCacheTest::ResponseAndErrorCallback, base::Unretained(this), base::Unretained(loop.get()))); loop->Run(); @@ -532,11 +534,11 @@ class CacheStorageCacheTest : public testing::Test { } bool MatchAll(const ServiceWorkerFetchRequest& request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, std::vector* responses) { base::RunLoop loop; cache_->MatchAll( - CopyFetchRequest(request), match_params, + CopyFetchRequest(request), std::move(match_params), base::BindOnce(&CacheStorageCacheTest::ResponsesAndErrorCallback, base::Unretained(this), loop.QuitClosure(), responses)); loop.Run(); @@ -544,31 +546,30 @@ class CacheStorageCacheTest : public testing::Test { } bool MatchAll(std::vector* responses) { - return MatchAll(ServiceWorkerFetchRequest(), CacheStorageCacheQueryParams(), - responses); + return MatchAll(ServiceWorkerFetchRequest(), nullptr, responses); } bool Delete(const ServiceWorkerFetchRequest& request, - const CacheStorageCacheQueryParams& match_params = - CacheStorageCacheQueryParams()) { - CacheStorageBatchOperation operation; - operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE; - operation.request = request; - operation.match_params = match_params; - - CacheStorageError error = - BatchOperation(std::vector(1, operation)); + blink::mojom::QueryParamsPtr match_params = nullptr) { + blink::mojom::BatchOperationPtr operation = + blink::mojom::BatchOperation::New(); + operation->operation_type = blink::mojom::OperationType::kDelete; + operation->request = request; + operation->match_params = std::move(match_params); + + std::vector operations; + operations.emplace_back(std::move(operation)); + CacheStorageError error = BatchOperation(std::move(operations)); return error == CacheStorageError::kSuccess; } bool Keys( const ServiceWorkerFetchRequest& request = ServiceWorkerFetchRequest(), - const CacheStorageCacheQueryParams& match_params = - CacheStorageCacheQueryParams()) { + blink::mojom::QueryParamsPtr match_params = nullptr) { std::unique_ptr loop(new base::RunLoop()); cache_->Keys( - CopyFetchRequest(request), match_params, + CopyFetchRequest(request), std::move(match_params), base::BindOnce(&CacheStorageCacheTest::RequestsCallback, base::Unretained(this), base::Unretained(loop.get()))); loop->Run(); @@ -757,36 +758,39 @@ TEST_P(CacheStorageCacheTestP, PutBody) { } TEST_P(CacheStorageCacheTestP, PutBody_Multiple) { - CacheStorageBatchOperation operation1; - operation1.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation1.request = body_request_; - operation1.request.url = GURL("http://example.com/1"); - operation1.response = body_response_; - operation1.response.url_list.push_back(GURL("http://example.com/1")); - - CacheStorageBatchOperation operation2; - operation2.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation2.request = body_request_; - operation2.request.url = GURL("http://example.com/2"); - operation2.response = body_response_; - operation2.response.url_list.push_back(GURL("http://example.com/2")); - - CacheStorageBatchOperation operation3; - operation3.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation3.request = body_request_; - operation3.request.url = GURL("http://example.com/3"); - operation3.response = body_response_; - operation3.response.url_list.push_back(GURL("http://example.com/3")); - - std::vector operations; - operations.push_back(operation1); - operations.push_back(operation2); - operations.push_back(operation3); - - EXPECT_EQ(CacheStorageError::kSuccess, BatchOperation(operations)); - EXPECT_TRUE(Match(operation1.request)); - EXPECT_TRUE(Match(operation2.request)); - EXPECT_TRUE(Match(operation3.request)); + blink::mojom::BatchOperationPtr operation1 = + blink::mojom::BatchOperation::New(); + operation1->operation_type = blink::mojom::OperationType::kPut; + operation1->request = body_request_; + operation1->request.url = GURL("http://example.com/1"); + operation1->response = body_response_; + operation1->response->url_list.push_back(GURL("http://example.com/1")); + + blink::mojom::BatchOperationPtr operation2 = + blink::mojom::BatchOperation::New(); + operation2->operation_type = blink::mojom::OperationType::kPut; + operation2->request = body_request_; + operation2->request.url = GURL("http://example.com/2"); + operation2->response = body_response_; + operation2->response->url_list.push_back(GURL("http://example.com/2")); + + blink::mojom::BatchOperationPtr operation3 = + blink::mojom::BatchOperation::New(); + operation3->operation_type = blink::mojom::OperationType::kPut; + operation3->request = body_request_; + operation3->request.url = GURL("http://example.com/3"); + operation3->response = body_response_; + operation3->response->url_list.push_back(GURL("http://example.com/3")); + + std::vector operations; + operations.push_back(operation1->Clone()); + operations.push_back(operation2->Clone()); + operations.push_back(operation3->Clone()); + + EXPECT_EQ(CacheStorageError::kSuccess, BatchOperation(std::move(operations))); + EXPECT_TRUE(Match(operation1->request)); + EXPECT_TRUE(Match(operation2->request)); + EXPECT_TRUE(Match(operation3->request)); } TEST_P(CacheStorageCacheTestP, MatchLimit) { @@ -814,25 +818,25 @@ TEST_P(CacheStorageCacheTestP, MatchAllLimit) { callback_response_->EstimatedStructSize(); std::vector responses; - CacheStorageCacheQueryParams match_params; + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); // There is enough room for both requests and responses SetMaxQuerySizeBytes(body_request_size + query_request_size); - EXPECT_TRUE(MatchAll(body_request_, match_params, &responses)); + EXPECT_TRUE(MatchAll(body_request_, match_params->Clone(), &responses)); EXPECT_EQ(1u, responses.size()); - match_params.ignore_search = true; - EXPECT_TRUE(MatchAll(body_request_, match_params, &responses)); + match_params->ignore_search = true; + EXPECT_TRUE(MatchAll(body_request_, match_params->Clone(), &responses)); EXPECT_EQ(2u, responses.size()); // There is not enough room for both requests and responses SetMaxQuerySizeBytes(body_request_size); - match_params.ignore_search = false; - EXPECT_TRUE(MatchAll(body_request_, match_params, &responses)); + match_params->ignore_search = false; + EXPECT_TRUE(MatchAll(body_request_, match_params->Clone(), &responses)); EXPECT_EQ(1u, responses.size()); - match_params.ignore_search = true; - EXPECT_FALSE(MatchAll(body_request_, match_params, &responses)); + match_params->ignore_search = true; + EXPECT_FALSE(MatchAll(body_request_, match_params->Clone(), &responses)); EXPECT_EQ(CacheStorageError::kErrorQueryTooLarge, callback_error_); } @@ -876,14 +880,17 @@ TEST_P(CacheStorageCacheTestP, ResponseURLEmpty) { } TEST_P(CacheStorageCacheTestP, PutBodyDropBlobRef) { - CacheStorageBatchOperation operation; - operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation.request = body_request_; - operation.response = body_response_; - + blink::mojom::BatchOperationPtr operation = + blink::mojom::BatchOperation::New(); + operation->operation_type = blink::mojom::OperationType::kPut; + operation->request = body_request_; + operation->response = body_response_; + + std::vector operations; + operations.emplace_back(std::move(operation)); std::unique_ptr loop(new base::RunLoop()); cache_->BatchOperation( - std::vector(1, operation), + std::move(operations), base::BindOnce(&CacheStorageCacheTestP::ErrorTypeCallback, base::Unretained(this), base::Unretained(loop.get())), CacheStorageCache::BadMessageCallback()); @@ -896,15 +903,18 @@ TEST_P(CacheStorageCacheTestP, PutBodyDropBlobRef) { } TEST_P(CacheStorageCacheTestP, PutBadMessage) { - CacheStorageBatchOperation operation; - operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation.request = body_request_; - operation.response = body_response_; - operation.response.blob_size = UINT64_MAX; - - std::vector operations = - std::vector(2, operation); - EXPECT_EQ(CacheStorageError::kErrorStorage, BatchOperation(operations)); + blink::mojom::BatchOperationPtr operation = + blink::mojom::BatchOperation::New(); + operation->operation_type = blink::mojom::OperationType::kPut; + operation->request = body_request_; + operation->response = body_response_; + operation->response->blob_size = UINT64_MAX; + + std::vector operations; + operations.push_back(operation->Clone()); + operations.push_back(operation->Clone()); + EXPECT_EQ(CacheStorageError::kErrorStorage, + BatchOperation(std::move(operations))); EXPECT_EQ("CSDH_UNEXPECTED_OPERATION", bad_message_reason_); EXPECT_FALSE(Match(body_request_)); @@ -925,24 +935,26 @@ TEST_P(CacheStorageCacheTestP, PutReplace) { } TEST_P(CacheStorageCacheTestP, PutReplaceInBatch) { - CacheStorageBatchOperation operation1; - operation1.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation1.request = body_request_; - operation1.response = no_body_response_; + blink::mojom::BatchOperationPtr operation1 = + blink::mojom::BatchOperation::New(); + operation1->operation_type = blink::mojom::OperationType::kPut; + operation1->request = body_request_; + operation1->response = no_body_response_; - CacheStorageBatchOperation operation2; - operation2.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation2.request = body_request_; - operation2.response = body_response_; + blink::mojom::BatchOperationPtr operation2 = + blink::mojom::BatchOperation::New(); + operation2->operation_type = blink::mojom::OperationType::kPut; + operation2->request = body_request_; + operation2->response = body_response_; - std::vector operations; - operations.push_back(operation1); - operations.push_back(operation2); + std::vector operations; + operations.push_back(operation1->Clone()); + operations.push_back(operation2->Clone()); - EXPECT_EQ(CacheStorageError::kSuccess, BatchOperation(operations)); + EXPECT_EQ(CacheStorageError::kSuccess, BatchOperation(std::move(operations))); // |operation2| should win. - EXPECT_TRUE(Match(operation2.request)); + EXPECT_TRUE(Match(operation2->request)); EXPECT_TRUE(callback_response_->blob); } @@ -1031,9 +1043,9 @@ TEST_P(CacheStorageCacheTestP, Match_IgnoreSearch) { EXPECT_TRUE(Put(body_request_with_query_, body_response_with_query_)); EXPECT_FALSE(Match(body_request_)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = true; - EXPECT_TRUE(Match(body_request_, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_search = true; + EXPECT_TRUE(Match(body_request_, std::move(match_params))); } TEST_P(CacheStorageCacheTestP, Match_IgnoreMethod) { @@ -1043,9 +1055,9 @@ TEST_P(CacheStorageCacheTestP, Match_IgnoreMethod) { post_request.method = "POST"; EXPECT_FALSE(Match(post_request)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_method = true; - EXPECT_TRUE(Match(post_request, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_method = true; + EXPECT_TRUE(Match(post_request, std::move(match_params))); } TEST_P(CacheStorageCacheTestP, Match_IgnoreVary) { @@ -1057,9 +1069,9 @@ TEST_P(CacheStorageCacheTestP, Match_IgnoreVary) { body_request_.headers["vary_foo"] = "bar"; EXPECT_FALSE(Match(body_request_)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_vary = true; - EXPECT_TRUE(Match(body_request_, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_vary = true; + EXPECT_TRUE(Match(body_request_, std::move(match_params))); } TEST_P(CacheStorageCacheTestP, Keys_IgnoreSearch) { @@ -1068,9 +1080,9 @@ TEST_P(CacheStorageCacheTestP, Keys_IgnoreSearch) { EXPECT_TRUE(Keys(body_request_)); EXPECT_EQ(0u, callback_strings_.size()); - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = true; - EXPECT_TRUE(Keys(body_request_, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_search = true; + EXPECT_TRUE(Keys(body_request_, std::move(match_params))); EXPECT_EQ(1u, callback_strings_.size()); } @@ -1082,9 +1094,9 @@ TEST_P(CacheStorageCacheTestP, Keys_IgnoreMethod) { EXPECT_TRUE(Keys(post_request)); EXPECT_EQ(0u, callback_strings_.size()); - CacheStorageCacheQueryParams match_params; - match_params.ignore_method = true; - EXPECT_TRUE(Keys(post_request, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_method = true; + EXPECT_TRUE(Keys(post_request, std::move(match_params))); EXPECT_EQ(1u, callback_strings_.size()); } @@ -1099,9 +1111,9 @@ TEST_P(CacheStorageCacheTestP, Keys_IgnoreVary) { EXPECT_TRUE(Keys(body_request_)); EXPECT_EQ(0u, callback_strings_.size()); - CacheStorageCacheQueryParams match_params; - match_params.ignore_vary = true; - EXPECT_TRUE(Keys(body_request_, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_vary = true; + EXPECT_TRUE(Keys(body_request_, std::move(match_params))); EXPECT_EQ(1u, callback_strings_.size()); } @@ -1109,9 +1121,9 @@ TEST_P(CacheStorageCacheTestP, Delete_IgnoreSearch) { EXPECT_TRUE(Put(body_request_with_query_, body_response_with_query_)); EXPECT_FALSE(Delete(body_request_)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = true; - EXPECT_TRUE(Delete(body_request_, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_search = true; + EXPECT_TRUE(Delete(body_request_, std::move(match_params))); } TEST_P(CacheStorageCacheTestP, Delete_IgnoreMethod) { @@ -1121,9 +1133,9 @@ TEST_P(CacheStorageCacheTestP, Delete_IgnoreMethod) { post_request.method = "POST"; EXPECT_FALSE(Delete(post_request)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_method = true; - EXPECT_TRUE(Delete(post_request, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_method = true; + EXPECT_TRUE(Delete(post_request, std::move(match_params))); } TEST_P(CacheStorageCacheTestP, Delete_IgnoreVary) { @@ -1134,9 +1146,9 @@ TEST_P(CacheStorageCacheTestP, Delete_IgnoreVary) { body_request_.headers["vary_foo"] = "bar"; EXPECT_FALSE(Delete(body_request_)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_vary = true; - EXPECT_TRUE(Delete(body_request_, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_vary = true; + EXPECT_TRUE(Delete(body_request_, std::move(match_params))); } TEST_P(CacheStorageCacheTestP, MatchAll_IgnoreMethod) { @@ -1145,13 +1157,13 @@ TEST_P(CacheStorageCacheTestP, MatchAll_IgnoreMethod) { ServiceWorkerFetchRequest post_request = body_request_; post_request.method = "POST"; std::vector responses; - CacheStorageCacheQueryParams match_params; - EXPECT_TRUE(MatchAll(post_request, match_params, &responses)); + EXPECT_TRUE(MatchAll(post_request, nullptr, &responses)); EXPECT_EQ(0u, responses.size()); - match_params.ignore_method = true; - EXPECT_TRUE(MatchAll(post_request, match_params, &responses)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_method = true; + EXPECT_TRUE(MatchAll(post_request, std::move(match_params), &responses)); EXPECT_EQ(1u, responses.size()); } @@ -1160,17 +1172,17 @@ TEST_P(CacheStorageCacheTestP, MatchAll_IgnoreVary) { body_response_.headers["vary"] = "vary_foo"; EXPECT_TRUE(Put(body_request_, body_response_)); std::vector responses; - CacheStorageCacheQueryParams match_params; - EXPECT_TRUE(MatchAll(body_request_, match_params, &responses)); + EXPECT_TRUE(MatchAll(body_request_, nullptr, &responses)); EXPECT_EQ(1u, responses.size()); body_request_.headers["vary_foo"] = "bar"; - EXPECT_TRUE(MatchAll(body_request_, match_params, &responses)); + EXPECT_TRUE(MatchAll(body_request_, nullptr, &responses)); EXPECT_EQ(0u, responses.size()); - match_params.ignore_vary = true; - EXPECT_TRUE(MatchAll(body_request_, match_params, &responses)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_vary = true; + EXPECT_TRUE(MatchAll(body_request_, std::move(match_params), &responses)); EXPECT_EQ(1u, responses.size()); } @@ -1180,9 +1192,9 @@ TEST_P(CacheStorageCacheTestP, MatchAll_IgnoreSearch) { EXPECT_TRUE(Put(no_body_request_, no_body_response_)); std::vector responses; - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = true; - EXPECT_TRUE(MatchAll(body_request_, match_params, &responses)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_search = true; + EXPECT_TRUE(MatchAll(body_request_, std::move(match_params), &responses)); ASSERT_EQ(2u, responses.size()); @@ -1208,13 +1220,13 @@ TEST_P(CacheStorageCacheTestP, MatchAll_Head) { EXPECT_TRUE(Put(body_request_, body_response_)); std::vector responses; - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = true; - EXPECT_TRUE(MatchAll(body_head_request_, match_params, &responses)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_search = true; + EXPECT_TRUE(MatchAll(body_head_request_, match_params->Clone(), &responses)); EXPECT_TRUE(responses.empty()); - match_params.ignore_method = true; - EXPECT_TRUE(MatchAll(body_head_request_, match_params, &responses)); + match_params->ignore_method = true; + EXPECT_TRUE(MatchAll(body_head_request_, match_params->Clone(), &responses)); ASSERT_EQ(1u, responses.size()); EXPECT_TRUE( ResponseMetadataEqual(SetCacheName(body_response_), responses[0])); @@ -1315,10 +1327,10 @@ TEST_P(CacheStorageCacheTestP, KeysWithIgnoreSearchTrue) { EXPECT_TRUE(Put(body_request_, body_response_)); EXPECT_TRUE(Put(body_request_with_query_, body_response_with_query_)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = true; + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_search = true; - EXPECT_TRUE(Keys(body_request_with_query_, match_params)); + EXPECT_TRUE(Keys(body_request_with_query_, std::move(match_params))); std::vector expected_keys = { body_request_.url.spec(), body_request_with_query_.url.spec()}; EXPECT_EQ(expected_keys, callback_strings_); @@ -1330,12 +1342,10 @@ TEST_P(CacheStorageCacheTestP, KeysWithIgnoreSearchFalse) { EXPECT_TRUE(Put(body_request_with_query_, body_response_with_query_)); // Default value of ignore_search is false. - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = false; - EXPECT_EQ(match_params.ignore_search, - CacheStorageCacheQueryParams().ignore_search); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + EXPECT_EQ(match_params->ignore_search, false); - EXPECT_TRUE(Keys(body_request_with_query_, match_params)); + EXPECT_TRUE(Keys(body_request_with_query_, std::move(match_params))); std::vector expected_keys = { body_request_with_query_.url.spec()}; EXPECT_EQ(expected_keys, callback_strings_); @@ -1376,9 +1386,9 @@ TEST_P(CacheStorageCacheTestP, DeleteWithIgnoreSearchTrue) { // The following delete operation will remove both of body_request_ and // body_request_with_query_ from cache storage. - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = true; - EXPECT_TRUE(Delete(body_request_with_query_, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_search = true; + EXPECT_TRUE(Delete(body_request_with_query_, std::move(match_params))); EXPECT_TRUE(Keys()); expected_keys.clear(); @@ -1398,12 +1408,10 @@ TEST_P(CacheStorageCacheTestP, DeleteWithIgnoreSearchFalse) { EXPECT_EQ(expected_keys, callback_strings_); // Default value of ignore_search is false. - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = false; - EXPECT_EQ(match_params.ignore_search, - CacheStorageCacheQueryParams().ignore_search); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + EXPECT_EQ(match_params->ignore_search, false); - EXPECT_TRUE(Delete(body_request_with_query_, match_params)); + EXPECT_TRUE(Delete(body_request_with_query_, std::move(match_params))); EXPECT_TRUE(Keys()); std::vector expected_keys2{no_body_request_.url.spec(), @@ -1504,15 +1512,17 @@ TEST_P(CacheStorageCacheTestP, PutWithSideData_BadMessage) { CopySideDataToResponse(side_data_blob_handle.get(), &response); - CacheStorageBatchOperation operation; - operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation.request = body_request_; - operation.response = response; - operation.response.blob_size = UINT64_MAX; - - std::vector operations = - std::vector(1, operation); - EXPECT_EQ(CacheStorageError::kErrorStorage, BatchOperation(operations)); + blink::mojom::BatchOperationPtr operation = + blink::mojom::BatchOperation::New(); + operation->operation_type = blink::mojom::OperationType::kPut; + operation->request = body_request_; + operation->response = response; + operation->response->blob_size = UINT64_MAX; + + std::vector operations; + operations.emplace_back(std::move(operation)); + EXPECT_EQ(CacheStorageError::kErrorStorage, + BatchOperation(std::move(operations))); EXPECT_EQ("CSDH_UNEXPECTED_OPERATION", bad_message_reason_); EXPECT_FALSE(Match(body_request_)); @@ -1836,14 +1846,17 @@ TEST_P(CacheStorageCacheTestP, VerifySerialScheduling) { int sequence_out = -1; - CacheStorageBatchOperation operation1; - operation1.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation1.request = body_request_; - operation1.response = body_response_; + blink::mojom::BatchOperationPtr operation1 = + blink::mojom::BatchOperation::New(); + operation1->operation_type = blink::mojom::OperationType::kPut; + operation1->request = body_request_; + operation1->response = body_response_; std::unique_ptr close_loop1(new base::RunLoop()); + std::vector operations1; + operations1.emplace_back(std::move(operation1)); cache_->BatchOperation( - std::vector(1, operation1), + std::move(operations1), base::BindOnce(&CacheStorageCacheTest::SequenceCallback, base::Unretained(this), 1, &sequence_out, close_loop1.get()), @@ -1852,15 +1865,18 @@ TEST_P(CacheStorageCacheTestP, VerifySerialScheduling) { // Blocks on creating the cache entry. base::RunLoop().RunUntilIdle(); - CacheStorageBatchOperation operation2; - operation2.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation2.request = body_request_; - operation2.response = body_response_; + blink::mojom::BatchOperationPtr operation2 = + blink::mojom::BatchOperation::New(); + operation2->operation_type = blink::mojom::OperationType::kPut; + operation2->request = body_request_; + operation2->response = body_response_; delayable_backend->set_delay_open_entry(false); std::unique_ptr close_loop2(new base::RunLoop()); + std::vector operations2; + operations2.emplace_back(std::move(operation2)); cache_->BatchOperation( - std::vector(1, operation2), + std::move(operations2), base::BindOnce(&CacheStorageCacheTest::SequenceCallback, base::Unretained(this), 2, &sequence_out, close_loop2.get()), diff --git a/chromium/content/browser/cache_storage/cache_storage_context_impl.cc b/chromium/content/browser/cache_storage/cache_storage_context_impl.cc index e7a6c1f7b3f..17157f81340 100644 --- a/chromium/content/browser/cache_storage/cache_storage_context_impl.cc +++ b/chromium/content/browser/cache_storage/cache_storage_context_impl.cc @@ -91,13 +91,14 @@ void CacheStorageContextImpl::GetAllOriginsInfo( return; } - cache_manager_->GetAllOriginsUsage(callback); + cache_manager_->GetAllOriginsUsage(CacheStorageOwner::kCacheAPI, callback); } void CacheStorageContextImpl::DeleteForOrigin(const GURL& origin) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (cache_manager_) - cache_manager_->DeleteOriginData(url::Origin::Create(origin)); + cache_manager_->DeleteOriginData(url::Origin::Create(origin), + CacheStorageOwner::kCacheAPI); } void CacheStorageContextImpl::AddObserver( diff --git a/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.cc b/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.cc index 9680429f601..60669ac676e 100644 --- a/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.cc +++ b/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/logging.h" #include "base/macros.h" +#include "base/optional.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" @@ -61,7 +62,7 @@ class CacheStorageDispatcherHost::CacheImpl // blink::mojom::CacheStorageCache implementation: void Match(const ServiceWorkerFetchRequest& request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, MatchCallback callback) override { content::CacheStorageCache* cache = cache_handle_.value(); if (!cache) { @@ -73,15 +74,15 @@ class CacheStorageDispatcherHost::CacheImpl auto scoped_request = std::make_unique( request.url, request.method, request.headers, request.referrer, request.is_reload); - cache->Match(std::move(scoped_request), match_params, - base::BindOnce(&CacheImpl::OnCacheMatchCallback, - weak_factory_.GetWeakPtr(), std::move(callback), - cache_handle_.Clone())); + + cache->Match( + std::move(scoped_request), std::move(match_params), + base::BindOnce(&CacheImpl::OnCacheMatchCallback, + weak_factory_.GetWeakPtr(), std::move(callback))); } void OnCacheMatchCallback( blink::mojom::CacheStorageCache::MatchCallback callback, - CacheStorageCacheHandle cache_handle, blink::mojom::CacheStorageError error, std::unique_ptr response) { if (error != CacheStorageError::kSuccess) { @@ -92,8 +93,8 @@ class CacheStorageDispatcherHost::CacheImpl std::move(callback).Run(blink::mojom::MatchResult::NewResponse(*response)); } - void MatchAll(const ServiceWorkerFetchRequest& request, - const CacheStorageCacheQueryParams& match_params, + void MatchAll(const base::Optional& request, + blink::mojom::QueryParamsPtr match_params, MatchAllCallback callback) override { content::CacheStorageCache* cache = cache_handle_.value(); if (!cache) { @@ -102,36 +103,22 @@ class CacheStorageDispatcherHost::CacheImpl return; } - if (request.url.is_empty()) { - cache->MatchAll( - nullptr, match_params, - base::BindOnce(&CacheImpl::OnCacheMatchAllCallback, - weak_factory_.GetWeakPtr(), std::move(callback), - cache_handle_.Clone())); - return; - } + std::unique_ptr request_ptr; - auto scoped_request = std::make_unique( - request.url, request.method, request.headers, request.referrer, - request.is_reload); - if (match_params.ignore_search) { - cache->MatchAll( - std::move(scoped_request), match_params, - base::BindOnce(&CacheImpl::OnCacheMatchAllCallback, - weak_factory_.GetWeakPtr(), std::move(callback), - cache_handle_.Clone())); - return; + if (request && !request->url.is_empty()) { + request_ptr = std::make_unique( + request->url, request->method, request->headers, request->referrer, + request->is_reload); } - cache->Match(std::move(scoped_request), match_params, - base::BindOnce(&CacheImpl::OnCacheMatchAllCallbackAdapter, - weak_factory_.GetWeakPtr(), std::move(callback), - cache_handle_.Clone())); + cache->MatchAll( + std::move(request_ptr), std::move(match_params), + base::BindOnce(&CacheImpl::OnCacheMatchAllCallback, + weak_factory_.GetWeakPtr(), std::move(callback))); } void OnCacheMatchAllCallback( blink::mojom::CacheStorageCache::MatchAllCallback callback, - CacheStorageCacheHandle cache_handle, blink::mojom::CacheStorageError error, std::vector responses) { if (error != CacheStorageError::kSuccess && @@ -144,22 +131,8 @@ class CacheStorageDispatcherHost::CacheImpl blink::mojom::MatchAllResult::NewResponses(std::move(responses))); } - void OnCacheMatchAllCallbackAdapter( - blink::mojom::CacheStorageCache::MatchAllCallback callback, - CacheStorageCacheHandle cache_handle, - blink::mojom::CacheStorageError error, - std::unique_ptr response) { - std::vector responses; - if (error == CacheStorageError::kSuccess) { - DCHECK(response); - responses.push_back(std::move(*response.release())); - } - OnCacheMatchAllCallback(std::move(callback), std::move(cache_handle), error, - std::move(responses)); - } - - void Keys(const ServiceWorkerFetchRequest& request, - const CacheStorageCacheQueryParams& match_params, + void Keys(const base::Optional& request, + blink::mojom::QueryParamsPtr match_params, KeysCallback callback) override { content::CacheStorageCache* cache = cache_handle_.value(); if (!cache) { @@ -168,18 +141,21 @@ class CacheStorageDispatcherHost::CacheImpl return; } - auto request_ptr = std::make_unique( - request.url, request.method, request.headers, request.referrer, - request.is_reload); - cache->Keys(std::move(request_ptr), match_params, - base::BindOnce(&CacheImpl::OnCacheKeysCallback, - weak_factory_.GetWeakPtr(), std::move(callback), - cache_handle_.Clone())); + std::unique_ptr request_ptr; + + if (request) { + request_ptr = std::make_unique( + request->url, request->method, request->headers, request->referrer, + request->is_reload); + } + cache->Keys( + std::move(request_ptr), std::move(match_params), + base::BindOnce(&CacheImpl::OnCacheKeysCallback, + weak_factory_.GetWeakPtr(), std::move(callback))); } void OnCacheKeysCallback( blink::mojom::CacheStorageCache::KeysCallback callback, - CacheStorageCacheHandle cache_handle, blink::mojom::CacheStorageError error, std::unique_ptr requests) { if (error != CacheStorageError::kSuccess) { @@ -190,7 +166,7 @@ class CacheStorageDispatcherHost::CacheImpl std::move(callback).Run(blink::mojom::CacheKeysResult::NewKeys(*requests)); } - void Batch(const std::vector& batch_operations, + void Batch(std::vector batch_operations, BatchCallback callback) override { content::CacheStorageCache* cache = cache_handle_.value(); if (!cache) { @@ -198,17 +174,15 @@ class CacheStorageDispatcherHost::CacheImpl return; } cache->BatchOperation( - batch_operations, + std::move(batch_operations), base::BindOnce(&CacheImpl::OnCacheBatchCallback, - weak_factory_.GetWeakPtr(), std::move(callback), - cache_handle_.Clone()), + weak_factory_.GetWeakPtr(), std::move(callback)), base::BindOnce(&CacheImpl::OnBadMessage, weak_factory_.GetWeakPtr(), mojo::GetBadMessageCallback())); } void OnCacheBatchCallback( blink::mojom::CacheStorageCache::BatchCallback callback, - CacheStorageCacheHandle cache_handle, blink::mojom::CacheStorageError error) { std::move(callback).Run(error); } @@ -257,7 +231,7 @@ void CacheStorageDispatcherHost::Has( if (!ValidState()) return; context_->cache_manager()->HasCache( - origin, base::UTF16ToUTF8(cache_name), + origin, CacheStorageOwner::kCacheAPI, base::UTF16ToUTF8(cache_name), base::BindOnce(&CacheStorageDispatcherHost::OnHasCallback, this, std::move(callback))); } @@ -275,7 +249,7 @@ void CacheStorageDispatcherHost::Open( if (!ValidState()) return; context_->cache_manager()->OpenCache( - origin, base::UTF16ToUTF8(cache_name), + origin, CacheStorageOwner::kCacheAPI, base::UTF16ToUTF8(cache_name), base::BindOnce(&CacheStorageDispatcherHost::OnOpenCallback, this, origin, std::move(callback))); } @@ -292,7 +266,9 @@ void CacheStorageDispatcherHost::Delete( } if (!ValidState()) return; - context_->cache_manager()->DeleteCache(origin, base::UTF16ToUTF8(cache_name), + context_->cache_manager()->DeleteCache(origin, CacheStorageOwner::kCacheAPI, + base::UTF16ToUTF8(cache_name), + std::move(callback)); } @@ -309,13 +285,14 @@ void CacheStorageDispatcherHost::Keys( if (!ValidState()) return; context_->cache_manager()->EnumerateCaches( - origin, base::BindOnce(&CacheStorageDispatcherHost::OnKeysCallback, this, - std::move(callback))); + origin, CacheStorageOwner::kCacheAPI, + base::BindOnce(&CacheStorageDispatcherHost::OnKeysCallback, this, + std::move(callback))); } void CacheStorageDispatcherHost::Match( const content::ServiceWorkerFetchRequest& request, - const content::CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, blink::mojom::CacheStorage::MatchCallback callback) { TRACE_EVENT0("CacheStorage", "CacheStorageDispatcherHost::OnCacheStorageMatch"); @@ -330,15 +307,17 @@ void CacheStorageDispatcherHost::Match( request.url, request.method, request.headers, request.referrer, request.is_reload); - if (match_params.cache_name.is_null()) { + if (!match_params->cache_name) { context_->cache_manager()->MatchAllCaches( - origin, std::move(scoped_request), std::move(match_params), + origin, CacheStorageOwner::kCacheAPI, std::move(scoped_request), + std::move(match_params), base::BindOnce(&CacheStorageDispatcherHost::OnMatchCallback, this, std::move(callback))); return; } + std::string cache_name = base::UTF16ToUTF8(*match_params->cache_name); context_->cache_manager()->MatchCache( - origin, base::UTF16ToUTF8(match_params.cache_name.string()), + origin, CacheStorageOwner::kCacheAPI, std::move(cache_name), std::move(scoped_request), std::move(match_params), base::BindOnce(&CacheStorageDispatcherHost::OnMatchCallback, this, std::move(callback))); diff --git a/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.h b/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.h index 3dde6c6d6a6..f4f99042e39 100644 --- a/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.h +++ b/chromium/content/browser/cache_storage/cache_storage_dispatcher_host.h @@ -70,7 +70,7 @@ class CONTENT_EXPORT CacheStorageDispatcherHost void Has(const base::string16& cache_name, blink::mojom::CacheStorage::HasCallback callback) override; void Match(const content::ServiceWorkerFetchRequest& request, - const content::CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, blink::mojom::CacheStorage::MatchCallback callback) override; void Open(const base::string16& cache_name, blink::mojom::CacheStorage::OpenCallback callback) override; diff --git a/chromium/content/browser/cache_storage/cache_storage_manager.cc b/chromium/content/browser/cache_storage/cache_storage_manager.cc index d87786e203f..eb0fbd228dd 100644 --- a/chromium/content/browser/cache_storage/cache_storage_manager.cc +++ b/chromium/content/browser/cache_storage/cache_storage_manager.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -71,7 +72,8 @@ int64_t GetCacheStorageSize(const proto::CacheStorageIndex& index) { // sizes (if current), and last modified times. void ListOriginsAndLastModifiedOnTaskRunner( std::vector* usages, - base::FilePath root_path) { + base::FilePath root_path, + CacheStorageOwner owner) { base::FileEnumerator file_enum(root_path, false /* recursive */, base::FileEnumerator::DIRECTORIES); @@ -88,21 +90,26 @@ void ListOriginsAndLastModifiedOnTaskRunner( proto::CacheStorageIndex index; if (index.ParseFromString(protobuf)) { if (index.has_origin()) { - if (base::GetFileInfo(path, &file_info)) { - int64_t storage_size = CacheStorage::kSizeUnknown; - if (file_info.last_modified < index_last_modified) - storage_size = GetCacheStorageSize(index); - usages->push_back(CacheStorageUsageInfo( - GURL(index.origin()), storage_size, file_info.last_modified)); + if (path == + CacheStorageManager::ConstructOriginPath( + root_path, url::Origin::Create(GURL(index.origin())), owner)) { + if (base::GetFileInfo(path, &file_info)) { + int64_t storage_size = CacheStorage::kSizeUnknown; + if (file_info.last_modified < index_last_modified) + storage_size = GetCacheStorageSize(index); + usages->push_back(CacheStorageUsageInfo( + GURL(index.origin()), storage_size, file_info.last_modified)); + } } } } } } -std::set ListOriginsOnTaskRunner(base::FilePath root_path) { +std::set ListOriginsOnTaskRunner(base::FilePath root_path, + CacheStorageOwner owner) { std::vector usages; - ListOriginsAndLastModifiedOnTaskRunner(&usages, root_path); + ListOriginsAndLastModifiedOnTaskRunner(&usages, root_path, owner); std::set out_origins; for (const CacheStorageUsageInfo& usage : usages) @@ -177,67 +184,89 @@ CacheStorageManager::~CacheStorageManager() = default; void CacheStorageManager::OpenCache( const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, CacheStorage::CacheAndErrorCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); + CacheStorage* cache_storage = FindOrCreateCacheStorage(origin, owner); cache_storage->OpenCache(cache_name, std::move(callback)); } void CacheStorageManager::HasCache( const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, CacheStorage::BoolAndErrorCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); + CacheStorage* cache_storage = FindOrCreateCacheStorage(origin, owner); cache_storage->HasCache(cache_name, std::move(callback)); } void CacheStorageManager::DeleteCache(const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, CacheStorage::ErrorCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); + CacheStorage* cache_storage = FindOrCreateCacheStorage(origin, owner); cache_storage->DoomCache(cache_name, std::move(callback)); } void CacheStorageManager::EnumerateCaches( const url::Origin& origin, + CacheStorageOwner owner, CacheStorage::IndexCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); + CacheStorage* cache_storage = FindOrCreateCacheStorage(origin, owner); cache_storage->EnumerateCaches(std::move(callback)); } void CacheStorageManager::MatchCache( const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback) { - CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); + CacheStorage* cache_storage = FindOrCreateCacheStorage(origin, owner); - cache_storage->MatchCache(cache_name, std::move(request), match_params, - std::move(callback)); + cache_storage->MatchCache(cache_name, std::move(request), + std::move(match_params), std::move(callback)); } void CacheStorageManager::MatchAllCaches( const url::Origin& origin, + CacheStorageOwner owner, std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback) { - CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); + CacheStorage* cache_storage = FindOrCreateCacheStorage(origin, owner); - cache_storage->MatchAllCaches(std::move(request), match_params, + cache_storage->MatchAllCaches(std::move(request), std::move(match_params), std::move(callback)); } +void CacheStorageManager::WriteToCache( + const url::Origin& origin, + CacheStorageOwner owner, + const std::string& cache_name, + std::unique_ptr request, + std::unique_ptr response, + CacheStorage::ErrorCallback callback) { + // Cache API should write through the dispatcher. + DCHECK_NE(owner, CacheStorageOwner::kCacheAPI); + + CacheStorage* cache_storage = FindOrCreateCacheStorage(origin, owner); + + cache_storage->WriteToCache(cache_name, std::move(request), + std::move(response), std::move(callback)); +} + void CacheStorageManager::SetBlobParametersForCache( scoped_refptr request_context_getter, base::WeakPtr blob_storage_context) { @@ -273,6 +302,7 @@ void CacheStorageManager::NotifyCacheContentChanged(const url::Origin& origin, } void CacheStorageManager::GetAllOriginsUsage( + CacheStorageOwner owner, CacheStorageContext::GetUsageInfoCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -281,9 +311,11 @@ void CacheStorageManager::GetAllOriginsUsage( if (IsMemoryBacked()) { for (const auto& origin_details : cache_storage_map_) { - usages->push_back( - CacheStorageUsageInfo(origin_details.first.GetURL(), 0 /* size */, - base::Time() /* last modified */)); + if (origin_details.first.second != owner) + continue; + usages->push_back(CacheStorageUsageInfo( + origin_details.first.first.GetURL(), 0 /* size */, + base::Time() /* last modified */)); } GetAllOriginsUsageGetSizes(std::move(usages), std::move(callback)); return; @@ -293,7 +325,7 @@ void CacheStorageManager::GetAllOriginsUsage( cache_task_runner_->PostTaskAndReply( FROM_HERE, base::BindOnce(&ListOriginsAndLastModifiedOnTaskRunner, usages_ptr, - root_path_), + root_path_, owner), base::BindOnce(&CacheStorageManager::GetAllOriginsUsageGetSizes, weak_ptr_factory_.GetWeakPtr(), std::move(usages), std::move(callback))); @@ -325,8 +357,8 @@ void CacheStorageManager::GetAllOriginsUsageGetSizes( base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, barrier_closure); continue; } - CacheStorage* cache_storage = - FindOrCreateCacheStorage(url::Origin::Create(usage.origin)); + CacheStorage* cache_storage = FindOrCreateCacheStorage( + url::Origin::Create(usage.origin), CacheStorageOwner::kCacheAPI); cache_storage->Size( base::BindOnce(&OneOriginSizeReported, barrier_closure, &usage)); } @@ -334,22 +366,25 @@ void CacheStorageManager::GetAllOriginsUsageGetSizes( void CacheStorageManager::GetOriginUsage( const url::Origin& origin, + CacheStorageOwner owner, storage::QuotaClient::GetUsageCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); + CacheStorage* cache_storage = FindOrCreateCacheStorage(origin, owner); cache_storage->Size(std::move(callback)); } void CacheStorageManager::GetOrigins( + CacheStorageOwner owner, storage::QuotaClient::GetOriginsCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (IsMemoryBacked()) { std::set origins; for (const auto& key_value : cache_storage_map_) - origins.insert(key_value.first); + if (key_value.first.second == owner) + origins.insert(key_value.first.first); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), origins)); @@ -358,20 +393,23 @@ void CacheStorageManager::GetOrigins( PostTaskAndReplyWithResult( cache_task_runner_.get(), FROM_HERE, - base::BindOnce(&ListOriginsOnTaskRunner, root_path_), + base::BindOnce(&ListOriginsOnTaskRunner, root_path_, owner), std::move(callback)); } void CacheStorageManager::GetOriginsForHost( const std::string& host, + CacheStorageOwner owner, storage::QuotaClient::GetOriginsCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (IsMemoryBacked()) { std::set origins; for (const auto& key_value : cache_storage_map_) { - if (host == net::GetHostOrSpecFromURL(key_value.first.GetURL())) - origins.insert(key_value.first); + if (key_value.first.second != owner) + continue; + if (host == net::GetHostOrSpecFromURL(key_value.first.first.GetURL())) + origins.insert(key_value.first.first); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), origins)); @@ -380,38 +418,41 @@ void CacheStorageManager::GetOriginsForHost( PostTaskAndReplyWithResult( cache_task_runner_.get(), FROM_HERE, - base::BindOnce(&ListOriginsOnTaskRunner, root_path_), + base::BindOnce(&ListOriginsOnTaskRunner, root_path_, owner), base::BindOnce(&GetOriginsForHostDidListOrigins, host, std::move(callback))); } void CacheStorageManager::DeleteOriginData( const url::Origin& origin, + CacheStorageOwner owner, storage::QuotaClient::DeletionCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // Create the CacheStorage for the origin if it hasn't been loaded yet. - FindOrCreateCacheStorage(origin); + FindOrCreateCacheStorage(origin, owner); - CacheStorageMap::iterator it = cache_storage_map_.find(origin); + CacheStorageMap::iterator it = cache_storage_map_.find({origin, owner}); DCHECK(it != cache_storage_map_.end()); CacheStorage* cache_storage = it->second.release(); cache_storage->ResetManager(); - cache_storage_map_.erase(origin); + cache_storage_map_.erase({origin, owner}); cache_storage->GetSizeThenCloseAllCaches( base::BindOnce(&CacheStorageManager::DeleteOriginDidClose, - weak_ptr_factory_.GetWeakPtr(), origin, + weak_ptr_factory_.GetWeakPtr(), origin, owner, std::move(callback), base::WrapUnique(cache_storage))); } -void CacheStorageManager::DeleteOriginData(const url::Origin& origin) { +void CacheStorageManager::DeleteOriginData(const url::Origin& origin, + CacheStorageOwner owner) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DeleteOriginData(origin, base::DoNothing()); + DeleteOriginData(origin, owner, base::DoNothing()); } void CacheStorageManager::DeleteOriginDidClose( const url::Origin& origin, + CacheStorageOwner owner, storage::QuotaClient::DeletionCallback callback, std::unique_ptr cache_storage, int64_t origin_size) { @@ -421,9 +462,11 @@ void CacheStorageManager::DeleteOriginDidClose( cache_storage.reset(); quota_manager_proxy_->NotifyStorageModified( - storage::QuotaClient::kServiceWorkerCache, origin, + CacheStorageQuotaClient::GetIDFromOwner(owner), origin, blink::mojom::StorageType::kTemporary, -1 * origin_size); - NotifyCacheListChanged(origin); + + if (owner == CacheStorageOwner::kCacheAPI) + NotifyCacheListChanged(origin); if (IsMemoryBacked()) { base::ThreadTaskRunnerHandle::Get()->PostTask( @@ -434,7 +477,8 @@ void CacheStorageManager::DeleteOriginDidClose( PostTaskAndReplyWithResult( cache_task_runner_.get(), FROM_HERE, - base::BindOnce(&DeleteDir, ConstructOriginPath(root_path_, origin)), + base::BindOnce(&DeleteDir, + ConstructOriginPath(root_path_, origin, owner)), base::BindOnce(&DeleteOriginDidDeleteDir, std::move(callback))); } @@ -447,23 +491,25 @@ CacheStorageManager::CacheStorageManager( quota_manager_proxy_(std::move(quota_manager_proxy)), weak_ptr_factory_(this) { if (quota_manager_proxy_.get()) { - quota_manager_proxy_->RegisterClient( - new CacheStorageQuotaClient(weak_ptr_factory_.GetWeakPtr())); + quota_manager_proxy_->RegisterClient(new CacheStorageQuotaClient( + weak_ptr_factory_.GetWeakPtr(), CacheStorageOwner::kCacheAPI)); + quota_manager_proxy_->RegisterClient(new CacheStorageQuotaClient( + weak_ptr_factory_.GetWeakPtr(), CacheStorageOwner::kBackgroundFetch)); } } CacheStorage* CacheStorageManager::FindOrCreateCacheStorage( - const url::Origin& origin) { + const url::Origin& origin, + CacheStorageOwner owner) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(request_context_getter_); - CacheStorageMap::const_iterator it = cache_storage_map_.find(origin); + CacheStorageMap::const_iterator it = cache_storage_map_.find({origin, owner}); if (it == cache_storage_map_.end()) { CacheStorage* cache_storage = new CacheStorage( - ConstructOriginPath(root_path_, origin), IsMemoryBacked(), + ConstructOriginPath(root_path_, origin, owner), IsMemoryBacked(), cache_task_runner_.get(), request_context_getter_, quota_manager_proxy_, - blob_context_, this, origin); - cache_storage_map_.insert( - std::make_pair(origin, base::WrapUnique(cache_storage))); + blob_context_, this, origin, owner); + cache_storage_map_[{origin, owner}] = base::WrapUnique(cache_storage); return cache_storage; } return it->second.get(); @@ -472,8 +518,12 @@ CacheStorage* CacheStorageManager::FindOrCreateCacheStorage( // static base::FilePath CacheStorageManager::ConstructOriginPath( const base::FilePath& root_path, - const url::Origin& origin) { - const std::string identifier = storage::GetIdentifierFromOrigin(origin); + const url::Origin& origin, + CacheStorageOwner owner) { + std::string identifier = storage::GetIdentifierFromOrigin(origin); + if (owner != CacheStorageOwner::kCacheAPI) { + identifier += "-" + std::to_string(static_cast(owner)); + } const std::string origin_hash = base::SHA1HashString(identifier); const std::string origin_hash_hex = base::ToLowerASCII( base::HexEncode(origin_hash.c_str(), origin_hash.length())); diff --git a/chromium/content/browser/cache_storage/cache_storage_manager.h b/chromium/content/browser/cache_storage/cache_storage_manager.h index f053d4c1dbd..e94dfa631b4 100644 --- a/chromium/content/browser/cache_storage/cache_storage_manager.h +++ b/chromium/content/browser/cache_storage/cache_storage_manager.h @@ -39,6 +39,18 @@ namespace cache_storage_manager_unittest { class CacheStorageManagerTest; } +enum class CacheStorageOwner { + kMinValue, + + // Caches that can be accessed by the JS CacheStorage API (developer facing). + kCacheAPI = kMinValue, + + // Private cache to store background fetch downloads. + kBackgroundFetch, + + kMaxValue = kBackgroundFetch +}; + // Keeps track of a CacheStorage per origin. There is one // CacheStorageManager per ServiceWorkerContextCore. // TODO(jkarlin): Remove CacheStorage from memory once they're no @@ -55,33 +67,50 @@ class CONTENT_EXPORT CacheStorageManager { // Map a database identifier (computed from an origin) to the path. static base::FilePath ConstructOriginPath(const base::FilePath& root_path, - const url::Origin& origin); + const url::Origin& origin, + CacheStorageOwner owner); virtual ~CacheStorageManager(); // Methods to support the CacheStorage spec. These methods call the // corresponding CacheStorage method on the appropriate thread. void OpenCache(const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, CacheStorage::CacheAndErrorCallback callback); void HasCache(const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, CacheStorage::BoolAndErrorCallback callback); void DeleteCache(const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, CacheStorage::ErrorCallback callback); void EnumerateCaches(const url::Origin& origin, + CacheStorageOwner owner, CacheStorage::IndexCallback callback); void MatchCache(const url::Origin& origin, + CacheStorageOwner owner, const std::string& cache_name, std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback); void MatchAllCaches(const url::Origin& origin, + CacheStorageOwner owner, std::unique_ptr request, - const CacheStorageCacheQueryParams& match_params, + blink::mojom::QueryParamsPtr match_params, CacheStorageCache::ResponseCallback callback); + // Method to support writing to a cache directly from CacheStorageManager. + // This should be used by non-CacheAPI owners. The Cache API writes are + // handled via the dispatcher. + void WriteToCache(const url::Origin& origin, + CacheStorageOwner owner, + const std::string& cache_name, + std::unique_ptr request, + std::unique_ptr response, + CacheStorage::ErrorCallback callback); + // This must be called before creating any of the public *Cache functions // above. void SetBlobParametersForCache( @@ -106,7 +135,9 @@ class CONTENT_EXPORT CacheStorageManager { friend class cache_storage_manager_unittest::CacheStorageManagerTest; friend class CacheStorageQuotaClient; - typedef std::map> CacheStorageMap; + typedef std::map, + std::unique_ptr> + CacheStorageMap; CacheStorageManager( const base::FilePath& path, @@ -114,23 +145,30 @@ class CONTENT_EXPORT CacheStorageManager { scoped_refptr quota_manager_proxy); // The returned CacheStorage* is owned by this manager. - CacheStorage* FindOrCreateCacheStorage(const url::Origin& origin); + CacheStorage* FindOrCreateCacheStorage(const url::Origin& origin, + CacheStorageOwner owner); // QuotaClient and Browsing Data Deletion support - void GetAllOriginsUsage(CacheStorageContext::GetUsageInfoCallback callback); + void GetAllOriginsUsage(CacheStorageOwner owner, + CacheStorageContext::GetUsageInfoCallback callback); void GetAllOriginsUsageGetSizes( std::unique_ptr> usage_info, CacheStorageContext::GetUsageInfoCallback callback); void GetOriginUsage(const url::Origin& origin_url, + CacheStorageOwner owner, storage::QuotaClient::GetUsageCallback callback); - void GetOrigins(storage::QuotaClient::GetOriginsCallback callback); + void GetOrigins(CacheStorageOwner owner, + storage::QuotaClient::GetOriginsCallback callback); void GetOriginsForHost(const std::string& host, + CacheStorageOwner owner, storage::QuotaClient::GetOriginsCallback callback); void DeleteOriginData(const url::Origin& origin, + CacheStorageOwner owner, storage::QuotaClient::DeletionCallback callback); - void DeleteOriginData(const url::Origin& origin); + void DeleteOriginData(const url::Origin& origin, CacheStorageOwner owner); void DeleteOriginDidClose(const url::Origin& origin, + CacheStorageOwner owner, storage::QuotaClient::DeletionCallback callback, std::unique_ptr cache_storage, int64_t origin_size); diff --git a/chromium/content/browser/cache_storage/cache_storage_manager_unittest.cc b/chromium/content/browser/cache_storage/cache_storage_manager_unittest.cc index 2ccc79e2a13..e779d23257c 100644 --- a/chromium/content/browser/cache_storage/cache_storage_manager_unittest.cc +++ b/chromium/content/browser/cache_storage/cache_storage_manager_unittest.cc @@ -63,6 +63,31 @@ namespace cache_storage_manager_unittest { using blink::mojom::StorageType; +class MockCacheStorageQuotaManagerProxy : public MockQuotaManagerProxy { + public: + MockCacheStorageQuotaManagerProxy(MockQuotaManager* quota_manager, + base::SingleThreadTaskRunner* task_runner) + : MockQuotaManagerProxy(quota_manager, task_runner) {} + + void RegisterClient(QuotaClient* client) override { + registered_clients_.push_back(client); + } + + void SimulateQuotaManagerDestroyed() override { + for (auto* client : registered_clients_) { + client->OnQuotaManagerDestroyed(); + } + registered_clients_.clear(); + } + + private: + ~MockCacheStorageQuotaManagerProxy() override { + DCHECK(registered_clients_.empty()); + } + + std::vector registered_clients_; +}; + bool IsIndexFileCurrent(const base::FilePath& cache_dir) { base::File::Info info; const base::FilePath index_path = @@ -234,7 +259,7 @@ class CacheStorageManagerTest : public testing::Test { mock_quota_manager_->SetQuota(origin2_.GetURL(), StorageType::kTemporary, 1024 * 1024 * 100); - quota_manager_proxy_ = new MockQuotaManagerProxy( + quota_manager_proxy_ = new MockCacheStorageQuotaManagerProxy( mock_quota_manager_.get(), base::ThreadTaskRunnerHandle::Get().get()); cache_manager_ = CacheStorageManager::Create( @@ -281,10 +306,12 @@ class CacheStorageManagerTest : public testing::Test { cache_manager_ = nullptr; } - bool Open(const url::Origin& origin, const std::string& cache_name) { + bool Open(const url::Origin& origin, + const std::string& cache_name, + CacheStorageOwner owner = CacheStorageOwner::kCacheAPI) { base::RunLoop loop; cache_manager_->OpenCache( - origin, cache_name, + origin, owner, cache_name, base::BindOnce(&CacheStorageManagerTest::CacheAndErrorCallback, base::Unretained(this), base::Unretained(&loop))); loop.Run(); @@ -297,10 +324,12 @@ class CacheStorageManagerTest : public testing::Test { return !error; } - bool Has(const url::Origin& origin, const std::string& cache_name) { + bool Has(const url::Origin& origin, + const std::string& cache_name, + CacheStorageOwner owner = CacheStorageOwner::kCacheAPI) { base::RunLoop loop; cache_manager_->HasCache( - origin, cache_name, + origin, owner, cache_name, base::BindOnce(&CacheStorageManagerTest::BoolAndErrorCallback, base::Unretained(this), base::Unretained(&loop))); loop.Run(); @@ -308,10 +337,12 @@ class CacheStorageManagerTest : public testing::Test { return callback_bool_; } - bool Delete(const url::Origin& origin, const std::string& cache_name) { + bool Delete(const url::Origin& origin, + const std::string& cache_name, + CacheStorageOwner owner = CacheStorageOwner::kCacheAPI) { base::RunLoop loop; cache_manager_->DeleteCache( - origin, cache_name, + origin, owner, cache_name, base::BindOnce(&CacheStorageManagerTest::ErrorCallback, base::Unretained(this), base::Unretained(&loop))); loop.Run(); @@ -319,10 +350,11 @@ class CacheStorageManagerTest : public testing::Test { return callback_bool_; } - size_t Keys(const url::Origin& origin) { + size_t Keys(const url::Origin& origin, + CacheStorageOwner owner = CacheStorageOwner::kCacheAPI) { base::RunLoop loop; cache_manager_->EnumerateCaches( - origin, + origin, owner, base::BindOnce(&CacheStorageManagerTest::CacheMetadataCallback, base::Unretained(this), base::Unretained(&loop))); loop.Run(); @@ -332,25 +364,27 @@ class CacheStorageManagerTest : public testing::Test { bool StorageMatch(const url::Origin& origin, const std::string& cache_name, const GURL& url, - const CacheStorageCacheQueryParams& match_params = - CacheStorageCacheQueryParams()) { + blink::mojom::QueryParamsPtr match_params = nullptr, + CacheStorageOwner owner = CacheStorageOwner::kCacheAPI) { ServiceWorkerFetchRequest request; request.url = url; - return StorageMatchWithRequest(origin, cache_name, request, match_params); + return StorageMatchWithRequest(origin, cache_name, request, + std::move(match_params), owner); } bool StorageMatchWithRequest( const url::Origin& origin, const std::string& cache_name, const ServiceWorkerFetchRequest& request, - const CacheStorageCacheQueryParams& match_params = - CacheStorageCacheQueryParams()) { + blink::mojom::QueryParamsPtr match_params = nullptr, + CacheStorageOwner owner = CacheStorageOwner::kCacheAPI) { std::unique_ptr unique_request = std::make_unique(request); base::RunLoop loop; cache_manager_->MatchCache( - origin, cache_name, std::move(unique_request), match_params, + origin, owner, cache_name, std::move(unique_request), + std::move(match_params), base::BindOnce(&CacheStorageManagerTest::CacheMatchCallback, base::Unretained(this), base::Unretained(&loop))); loop.Run(); @@ -360,23 +394,22 @@ class CacheStorageManagerTest : public testing::Test { bool StorageMatchAll(const url::Origin& origin, const GURL& url, - const CacheStorageCacheQueryParams& match_params = - CacheStorageCacheQueryParams()) { + blink::mojom::QueryParamsPtr match_params = nullptr) { ServiceWorkerFetchRequest request; request.url = url; - return StorageMatchAllWithRequest(origin, request, match_params); + return StorageMatchAllWithRequest(origin, request, std::move(match_params)); } bool StorageMatchAllWithRequest( const url::Origin& origin, const ServiceWorkerFetchRequest& request, - const CacheStorageCacheQueryParams& match_params = - CacheStorageCacheQueryParams()) { + blink::mojom::QueryParamsPtr match_params = nullptr, + CacheStorageOwner owner = CacheStorageOwner::kCacheAPI) { std::unique_ptr unique_request = std::make_unique(request); base::RunLoop loop; cache_manager_->MatchAllCaches( - origin, std::move(unique_request), match_params, + origin, owner, std::move(unique_request), std::move(match_params), base::BindOnce(&CacheStorageManagerTest::CacheMatchCallback, base::Unretained(this), base::Unretained(&loop))); loop.Run(); @@ -384,6 +417,24 @@ class CacheStorageManagerTest : public testing::Test { return callback_error_ == CacheStorageError::kSuccess; } + bool Write(const url::Origin& origin, + CacheStorageOwner owner, + const std::string& cache_name, + const std::string& request_url) { + auto request = std::make_unique(); + request->url = GURL(request_url); + + auto response = std::make_unique(); + + base::RunLoop loop; + cache_manager_->WriteToCache( + origin, owner, cache_name, std::move(request), std::move(response), + base::BindOnce(&CacheStorageManagerTest::ErrorCallback, + base::Unretained(this), base::Unretained(&loop))); + loop.Run(); + return callback_bool_; + } + bool CachePut(CacheStorageCache* cache, const GURL& url, FetchResponseType response_type = FetchResponseType::kDefault) { @@ -435,14 +486,17 @@ class CacheStorageManagerTest : public testing::Test { std::make_unique< ServiceWorkerHeaderList>() /* cors_exposed_header_names */); - CacheStorageBatchOperation operation; - operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - operation.request = request; - operation.response = response; + blink::mojom::BatchOperationPtr operation = + blink::mojom::BatchOperation::New(); + operation->operation_type = blink::mojom::OperationType::kPut; + operation->request = request; + operation->response = response; + std::vector operations; + operations.emplace_back(std::move(operation)); base::RunLoop loop; cache->BatchOperation( - std::vector(1, operation), + std::move(operations), base::BindOnce(&CacheStorageManagerTest::CachePutCallback, base::Unretained(this), base::Unretained(&loop)), CacheStorageCache::BadMessageCallback()); @@ -456,14 +510,17 @@ class CacheStorageManagerTest : public testing::Test { request.url = url; ServiceWorkerResponse response; - CacheStorageBatchOperation operation; - operation.operation_type = CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE; - operation.request = request; - operation.response = response; + blink::mojom::BatchOperationPtr operation = + blink::mojom::BatchOperation::New(); + operation->operation_type = blink::mojom::OperationType::kDelete; + operation->request = request; + operation->response = response; + std::vector operations; + operations.emplace_back(std::move(operation)); base::RunLoop loop; cache->BatchOperation( - std::vector(1, operation), + std::move(operations), base::BindOnce(&CacheStorageManagerTest::CacheDeleteCallback, base::Unretained(this), base::Unretained(&loop)), CacheStorageCache::BadMessageCallback()); @@ -478,7 +535,7 @@ class CacheStorageManagerTest : public testing::Test { request->url = url; base::RunLoop loop; cache->Match( - std::move(request), CacheStorageCacheQueryParams(), + std::move(request), nullptr, base::BindOnce(&CacheStorageManagerTest::CacheMatchCallback, base::Unretained(this), base::Unretained(&loop))); loop.Run(); @@ -487,13 +544,16 @@ class CacheStorageManagerTest : public testing::Test { } CacheStorage* CacheStorageForOrigin(const url::Origin& origin) { - return cache_manager_->FindOrCreateCacheStorage(origin); + return cache_manager_->FindOrCreateCacheStorage( + origin, CacheStorageOwner::kCacheAPI); } - int64_t GetOriginUsage(const url::Origin& origin) { + int64_t GetOriginUsage( + const url::Origin& origin, + CacheStorageOwner owner = CacheStorageOwner::kCacheAPI) { base::RunLoop loop; cache_manager_->GetOriginUsage( - origin, + origin, owner, base::BindOnce(&CacheStorageManagerTest::UsageCallback, base::Unretained(this), base::Unretained(&loop))); loop.Run(); @@ -505,11 +565,12 @@ class CacheStorageManagerTest : public testing::Test { run_loop->Quit(); } - std::vector GetAllOriginsUsage() { + std::vector GetAllOriginsUsage( + CacheStorageOwner owner = CacheStorageOwner::kCacheAPI) { base::RunLoop loop; cache_manager_->GetAllOriginsUsage( - base::Bind(&CacheStorageManagerTest::AllOriginsUsageCallback, - base::Unretained(this), base::Unretained(&loop))); + owner, base::Bind(&CacheStorageManagerTest::AllOriginsUsageCallback, + base::Unretained(this), base::Unretained(&loop))); loop.Run(); return callback_all_origins_usage_; } @@ -574,7 +635,7 @@ class CacheStorageManagerTest : public testing::Test { scoped_refptr quota_policy_; scoped_refptr mock_quota_manager_; - scoped_refptr quota_manager_proxy_; + scoped_refptr quota_manager_proxy_; std::unique_ptr cache_manager_; CacheStorageCacheHandle callback_cache_handle_; @@ -618,6 +679,13 @@ TEST_P(CacheStorageManagerTestP, OpenTwoCaches) { EXPECT_TRUE(Open(origin1_, "bar")); } +TEST_P(CacheStorageManagerTestP, OpenSameCacheDifferentOwners) { + EXPECT_TRUE(Open(origin1_, "foo", CacheStorageOwner::kCacheAPI)); + CacheStorageCacheHandle cache_handle = std::move(callback_cache_handle_); + EXPECT_TRUE(Open(origin1_, "foo", CacheStorageOwner::kBackgroundFetch)); + EXPECT_NE(callback_cache_handle_.value(), cache_handle.value()); +} + TEST_P(CacheStorageManagerTestP, CachePointersDiffer) { EXPECT_TRUE(Open(origin1_, "foo")); CacheStorageCacheHandle cache_handle = std::move(callback_cache_handle_); @@ -645,6 +713,21 @@ TEST_P(CacheStorageManagerTestP, HasCache) { EXPECT_TRUE(callback_bool_); } +TEST_P(CacheStorageManagerTestP, HasCacheDifferentOwners) { + EXPECT_TRUE(Open(origin1_, "public", CacheStorageOwner::kCacheAPI)); + EXPECT_TRUE(Open(origin1_, "bgf", CacheStorageOwner::kBackgroundFetch)); + + EXPECT_TRUE(Has(origin1_, "public", CacheStorageOwner::kCacheAPI)); + EXPECT_TRUE(callback_bool_); + EXPECT_FALSE(Has(origin1_, "bgf", CacheStorageOwner::kCacheAPI)); + EXPECT_FALSE(callback_bool_); + + EXPECT_TRUE(Has(origin1_, "bgf", CacheStorageOwner::kBackgroundFetch)); + EXPECT_TRUE(callback_bool_); + EXPECT_FALSE(Has(origin1_, "public", CacheStorageOwner::kBackgroundFetch)); + EXPECT_FALSE(callback_bool_); +} + TEST_P(CacheStorageManagerTestP, HasNonExistent) { EXPECT_FALSE(Has(origin1_, "foo")); } @@ -749,6 +832,27 @@ TEST_P(CacheStorageManagerTestP, StorageMatchAllNoCaches) { EXPECT_EQ(CacheStorageError::kErrorNotFound, callback_error_); } +TEST_P(CacheStorageManagerTestP, StorageMatchDifferentOwners) { + EXPECT_TRUE(Open(origin1_, "foo", CacheStorageOwner::kCacheAPI)); + EXPECT_TRUE(CachePut(callback_cache_handle_.value(), + GURL("http://example.com/public"))); + EXPECT_TRUE(Open(origin1_, "foo", CacheStorageOwner::kBackgroundFetch)); + EXPECT_TRUE( + CachePut(callback_cache_handle_.value(), GURL("http://example.com/bgf"))); + + // Check the public cache. + EXPECT_TRUE(StorageMatch(origin1_, "foo", GURL("http://example.com/public"), + nullptr, CacheStorageOwner::kCacheAPI)); + EXPECT_FALSE(StorageMatch(origin1_, "foo", GURL("http://example.com/bgf"), + nullptr, CacheStorageOwner::kCacheAPI)); + + // Check the internal cache. + EXPECT_FALSE(StorageMatch(origin1_, "foo", GURL("http://example.com/public"), + nullptr, CacheStorageOwner::kBackgroundFetch)); + EXPECT_TRUE(StorageMatch(origin1_, "foo", GURL("http://example.com/bgf"), + nullptr, CacheStorageOwner::kBackgroundFetch)); +} + TEST_F(CacheStorageManagerTest, StorageReuseCacheName) { // Deleting a cache and creating one with the same name and adding an entry // with the same URL should work. (see crbug.com/542668) @@ -1194,7 +1298,7 @@ TEST_P(CacheStorageManagerTestP, OpenRunsSerially) { base::RunLoop open_loop; cache_manager_->OpenCache( - origin1_, "foo", + origin1_, CacheStorageOwner::kCacheAPI, "foo", base::BindOnce(&CacheStorageManagerTest::CacheAndErrorCallback, base::Unretained(this), base::Unretained(&open_loop))); @@ -1257,6 +1361,55 @@ TEST_P(CacheStorageManagerTestP, GetAllOriginsUsage) { } } +TEST_P(CacheStorageManagerTestP, GetAllOriginsUsageDifferentOwners) { + EXPECT_EQ(0ULL, GetAllOriginsUsage(CacheStorageOwner::kCacheAPI).size()); + EXPECT_EQ(0ULL, + GetAllOriginsUsage(CacheStorageOwner::kBackgroundFetch).size()); + + // Put one entry in a cache of owner 1. + EXPECT_TRUE(Open(origin1_, "foo", CacheStorageOwner::kCacheAPI)); + EXPECT_TRUE( + CachePut(callback_cache_handle_.value(), GURL("http://example.com/foo"))); + + // Put two entries (of identical size) in two origins in a cache of owner 2. + EXPECT_TRUE(Open(origin1_, "foo", CacheStorageOwner::kBackgroundFetch)); + EXPECT_TRUE( + CachePut(callback_cache_handle_.value(), GURL("http://example.com/foo"))); + EXPECT_TRUE(Open(origin2_, "foo", CacheStorageOwner::kBackgroundFetch)); + EXPECT_TRUE( + CachePut(callback_cache_handle_.value(), GURL("http://example.com/bar"))); + + std::vector usage_cache = + GetAllOriginsUsage(CacheStorageOwner::kCacheAPI); + EXPECT_EQ(1ULL, usage_cache.size()); + std::vector usage_bgf = + GetAllOriginsUsage(CacheStorageOwner::kBackgroundFetch); + EXPECT_EQ(2ULL, usage_bgf.size()); + + int origin1_index = + url::Origin::Create(usage_bgf[0].origin) == origin1_ ? 0 : 1; + int origin2_index = + url::Origin::Create(usage_bgf[1].origin) == origin2_ ? 1 : 0; + EXPECT_NE(origin1_index, origin2_index); + + EXPECT_EQ(usage_cache[0].origin, origin1_.GetURL()); + EXPECT_EQ(usage_bgf[origin1_index].origin, origin1_.GetURL()); + EXPECT_EQ(usage_bgf[origin2_index].origin, origin2_.GetURL()); + + EXPECT_EQ(usage_cache[0].total_size_bytes, + usage_bgf[origin1_index].total_size_bytes); + + if (MemoryOnly()) { + EXPECT_TRUE(usage_cache[0].last_modified.is_null()); + EXPECT_TRUE(usage_bgf[origin1_index].last_modified.is_null()); + EXPECT_TRUE(usage_bgf[origin2_index].last_modified.is_null()); + } else { + EXPECT_FALSE(usage_cache[0].last_modified.is_null()); + EXPECT_FALSE(usage_bgf[origin1_index].last_modified.is_null()); + EXPECT_FALSE(usage_bgf[origin2_index].last_modified.is_null()); + } +} + TEST_F(CacheStorageManagerTest, GetAllOriginsUsageWithOldIndex) { // Write a single value (V1) to the cache. const GURL kFooURL = origin1_.GetURL().Resolve("foo"); @@ -1392,6 +1545,23 @@ TEST_P(CacheStorageManagerTestP, GetSizeThenCloseAllCaches) { CachePut(callback_cache_handle_.value(), GURL("http://example.com/baz"))); } +TEST_P(CacheStorageManagerTestP, GetSizeThenCloseAllCachesTwoOwners) { + EXPECT_TRUE(Open(origin1_, "foo", CacheStorageOwner::kCacheAPI)); + CacheStorageCacheHandle public_handle = std::move(callback_cache_handle_); + EXPECT_TRUE(Open(origin1_, "foo", CacheStorageOwner::kBackgroundFetch)); + CacheStorageCacheHandle bgf_handle = std::move(callback_cache_handle_); + + EXPECT_TRUE( + CachePut(public_handle.value(), GURL("http://example.com/public"))); + EXPECT_TRUE(CachePut(bgf_handle.value(), GURL("http://example.com/bgf"))); + + int64_t origin_size = GetOriginUsage(origin1_); + EXPECT_LT(0, origin_size); + + EXPECT_EQ(origin_size, GetSizeThenCloseAllCaches(origin1_)); + EXPECT_FALSE(CachePut(public_handle.value(), GURL("http://example.com/baz"))); +} + TEST_P(CacheStorageManagerTestP, GetSizeThenCloseAllCachesAfterDelete) { // Tests that doomed caches are also deleted by GetSizeThenCloseAllCaches. EXPECT_TRUE(Open(origin1_, "foo")); @@ -1422,7 +1592,7 @@ TEST_F(CacheStorageManagerTest, DeleteUnreferencedCacheDirectories) { // Create an unreferenced directory next to the referenced one. base::FilePath origin_path = CacheStorageManager::ConstructOriginPath( - cache_manager_->root_path(), origin1_); + cache_manager_->root_path(), origin1_, CacheStorageOwner::kCacheAPI); base::FilePath unreferenced_path = origin_path.AppendASCII("bar"); EXPECT_TRUE(CreateDirectory(unreferenced_path)); EXPECT_TRUE(base::DirectoryExists(unreferenced_path)); @@ -1594,10 +1764,10 @@ TEST_P(CacheStorageManagerTestP, StorageMatch_IgnoreSearch) { EXPECT_FALSE(StorageMatch(origin1_, "foo", GURL("http://example.com/foo"))); - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = true; + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_search = true; EXPECT_TRUE(StorageMatch(origin1_, "foo", GURL("http://example.com/foo"), - match_params)); + std::move(match_params))); } TEST_P(CacheStorageManagerTestP, StorageMatch_IgnoreMethod) { @@ -1610,10 +1780,10 @@ TEST_P(CacheStorageManagerTestP, StorageMatch_IgnoreMethod) { post_request.method = "POST"; EXPECT_FALSE(StorageMatchWithRequest(origin1_, "foo", post_request)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_method = true; - EXPECT_TRUE( - StorageMatchWithRequest(origin1_, "foo", post_request, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_method = true; + EXPECT_TRUE(StorageMatchWithRequest(origin1_, "foo", post_request, + std::move(match_params))); } TEST_P(CacheStorageManagerTestP, StorageMatch_IgnoreVary) { @@ -1634,9 +1804,10 @@ TEST_P(CacheStorageManagerTestP, StorageMatch_IgnoreVary) { request.headers["vary_foo"] = "bar"; EXPECT_FALSE(StorageMatchWithRequest(origin1_, "foo", request)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_vary = true; - EXPECT_TRUE(StorageMatchWithRequest(origin1_, "foo", request, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_vary = true; + EXPECT_TRUE(StorageMatchWithRequest(origin1_, "foo", request, + std::move(match_params))); } TEST_P(CacheStorageManagerTestP, StorageMatchAll_IgnoreSearch) { @@ -1646,10 +1817,10 @@ TEST_P(CacheStorageManagerTestP, StorageMatchAll_IgnoreSearch) { EXPECT_FALSE(StorageMatchAll(origin1_, GURL("http://example.com/foo"))); - CacheStorageCacheQueryParams match_params; - match_params.ignore_search = true; - EXPECT_TRUE( - StorageMatchAll(origin1_, GURL("http://example.com/foo"), match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_search = true; + EXPECT_TRUE(StorageMatchAll(origin1_, GURL("http://example.com/foo"), + std::move(match_params))); } TEST_P(CacheStorageManagerTestP, StorageMatchAll_IgnoreMethod) { @@ -1662,9 +1833,10 @@ TEST_P(CacheStorageManagerTestP, StorageMatchAll_IgnoreMethod) { post_request.method = "POST"; EXPECT_FALSE(StorageMatchAllWithRequest(origin1_, post_request)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_method = true; - EXPECT_TRUE(StorageMatchAllWithRequest(origin1_, post_request, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_method = true; + EXPECT_TRUE(StorageMatchAllWithRequest(origin1_, post_request, + std::move(match_params))); } TEST_P(CacheStorageManagerTestP, StorageMatchAll_IgnoreVary) { @@ -1685,9 +1857,34 @@ TEST_P(CacheStorageManagerTestP, StorageMatchAll_IgnoreVary) { request.headers["vary_foo"] = "bar"; EXPECT_FALSE(StorageMatchAllWithRequest(origin1_, request)); - CacheStorageCacheQueryParams match_params; - match_params.ignore_vary = true; - EXPECT_TRUE(StorageMatchAllWithRequest(origin1_, request, match_params)); + blink::mojom::QueryParamsPtr match_params = blink::mojom::QueryParams::New(); + match_params->ignore_vary = true; + EXPECT_TRUE( + StorageMatchAllWithRequest(origin1_, request, std::move(match_params))); +} + +TEST_P(CacheStorageManagerTestP, StorageWriteToCache) { + EXPECT_TRUE(Open(origin1_, "foo", CacheStorageOwner::kBackgroundFetch)); + + EXPECT_TRUE(Write(origin1_, CacheStorageOwner::kBackgroundFetch, "foo", + "http://example.com/foo")); + + // Match request we just wrote. + EXPECT_TRUE(StorageMatch(origin1_, "foo", GURL("http://example.com/foo"), + nullptr, CacheStorageOwner::kBackgroundFetch)); + + // Don't match with different origin. + EXPECT_FALSE(StorageMatch(origin2_, "foo", GURL("http://example.com/foo"), + nullptr, CacheStorageOwner::kBackgroundFetch)); + // Don't match with different cache name. + EXPECT_FALSE(StorageMatch(origin1_, "bar", GURL("http://example.com/foo"), + nullptr, CacheStorageOwner::kBackgroundFetch)); + // Don't match with different request. + EXPECT_FALSE(StorageMatch(origin1_, "foo", GURL("http://example.com/bar"), + nullptr, CacheStorageOwner::kBackgroundFetch)); + // Don't match with different owner. + EXPECT_FALSE(StorageMatch(origin1_, "foo", GURL("http://example.com/foo"), + nullptr, CacheStorageOwner::kCacheAPI)); } class CacheStorageQuotaClientTest : public CacheStorageManagerTest { @@ -1696,8 +1893,8 @@ class CacheStorageQuotaClientTest : public CacheStorageManagerTest { void SetUp() override { CacheStorageManagerTest::SetUp(); - quota_client_.reset( - new CacheStorageQuotaClient(cache_manager_->AsWeakPtr())); + quota_client_.reset(new CacheStorageQuotaClient( + cache_manager_->AsWeakPtr(), CacheStorageOwner::kCacheAPI)); } void QuotaUsageCallback(base::RunLoop* run_loop, int64_t usage) { @@ -1801,6 +1998,15 @@ TEST_P(CacheStorageQuotaClientTestP, QuotaGetOriginsForType) { EXPECT_EQ(2u, QuotaGetOriginsForType()); } +TEST_P(CacheStorageQuotaClientTestP, QuotaGetOriginsForTypeDifferentOwners) { + EXPECT_EQ(0u, QuotaGetOriginsForType()); + EXPECT_TRUE(Open(origin1_, "foo")); + // The |quota_client_| is registered for CacheStorageOwner::kCacheAPI, so this + // Open is ignored. + EXPECT_TRUE(Open(origin2_, "bar", CacheStorageOwner::kBackgroundFetch)); + EXPECT_EQ(1u, QuotaGetOriginsForType()); +} + TEST_P(CacheStorageQuotaClientTestP, QuotaGetOriginsForHost) { EXPECT_EQ(0u, QuotaGetOriginsForHost("example.com")); EXPECT_TRUE( @@ -1857,7 +2063,8 @@ TEST_F(CacheStorageQuotaClientDiskOnlyTest, QuotaDeleteUnloadedOriginData) { // Create a new CacheStorageManager that hasn't yet loaded the origin. quota_manager_proxy_->SimulateQuotaManagerDestroyed(); cache_manager_ = CacheStorageManager::Create(cache_manager_.get()); - quota_client_.reset(new CacheStorageQuotaClient(cache_manager_->AsWeakPtr())); + quota_client_.reset(new CacheStorageQuotaClient( + cache_manager_->AsWeakPtr(), CacheStorageOwner::kCacheAPI)); EXPECT_TRUE(QuotaDeleteOriginData(origin1_)); EXPECT_EQ(0, QuotaGetOriginUsage(origin1_)); diff --git a/chromium/content/browser/cache_storage/cache_storage_quota_client.cc b/chromium/content/browser/cache_storage/cache_storage_quota_client.cc index 1eea5a76e35..50086c0cd9e 100644 --- a/chromium/content/browser/cache_storage/cache_storage_quota_client.cc +++ b/chromium/content/browser/cache_storage/cache_storage_quota_client.cc @@ -12,16 +12,15 @@ namespace content { CacheStorageQuotaClient::CacheStorageQuotaClient( - base::WeakPtr cache_manager) - : cache_manager_(cache_manager) { -} + base::WeakPtr cache_manager, + CacheStorageOwner owner) + : cache_manager_(cache_manager), owner_(owner) {} -CacheStorageQuotaClient::~CacheStorageQuotaClient() { -} +CacheStorageQuotaClient::~CacheStorageQuotaClient() {} storage::QuotaClient::ID CacheStorageQuotaClient::id() const { DCHECK_CURRENTLY_ON(BrowserThread::IO); - return kServiceWorkerCache; + return GetIDFromOwner(owner_); } void CacheStorageQuotaClient::OnQuotaManagerDestroyed() { @@ -38,7 +37,7 @@ void CacheStorageQuotaClient::GetOriginUsage(const url::Origin& origin, return; } - cache_manager_->GetOriginUsage(origin, std::move(callback)); + cache_manager_->GetOriginUsage(origin, owner_, std::move(callback)); } void CacheStorageQuotaClient::GetOriginsForType(blink::mojom::StorageType type, @@ -50,7 +49,7 @@ void CacheStorageQuotaClient::GetOriginsForType(blink::mojom::StorageType type, return; } - cache_manager_->GetOrigins(std::move(callback)); + cache_manager_->GetOrigins(owner_, std::move(callback)); } void CacheStorageQuotaClient::GetOriginsForHost(blink::mojom::StorageType type, @@ -63,7 +62,7 @@ void CacheStorageQuotaClient::GetOriginsForHost(blink::mojom::StorageType type, return; } - cache_manager_->GetOriginsForHost(host, std::move(callback)); + cache_manager_->GetOriginsForHost(host, owner_, std::move(callback)); } void CacheStorageQuotaClient::DeleteOriginData(const url::Origin& origin, @@ -81,7 +80,7 @@ void CacheStorageQuotaClient::DeleteOriginData(const url::Origin& origin, return; } - cache_manager_->DeleteOriginData(origin, std::move(callback)); + cache_manager_->DeleteOriginData(origin, owner_, std::move(callback)); } bool CacheStorageQuotaClient::DoesSupport( @@ -91,4 +90,15 @@ bool CacheStorageQuotaClient::DoesSupport( return type == blink::mojom::StorageType::kTemporary; } +// static +storage::QuotaClient::ID CacheStorageQuotaClient::GetIDFromOwner( + CacheStorageOwner owner) { + switch (owner) { + case CacheStorageOwner::kCacheAPI: + return kServiceWorkerCache; + case CacheStorageOwner::kBackgroundFetch: + return kBackgroundFetch; + } +} + } // namespace content diff --git a/chromium/content/browser/cache_storage/cache_storage_quota_client.h b/chromium/content/browser/cache_storage/cache_storage_quota_client.h index 6eab4ce0324..d3c1620441d 100644 --- a/chromium/content/browser/cache_storage/cache_storage_quota_client.h +++ b/chromium/content/browser/cache_storage/cache_storage_quota_client.h @@ -13,15 +13,17 @@ #include "url/origin.h" namespace content { + class CacheStorageManager; +enum class CacheStorageOwner; // CacheStorageQuotaClient is owned by the QuotaManager. There is one per // CacheStorageManager, and therefore one per // ServiceWorkerContextCore. class CONTENT_EXPORT CacheStorageQuotaClient : public storage::QuotaClient { public: - explicit CacheStorageQuotaClient( - base::WeakPtr cache_manager); + CacheStorageQuotaClient(base::WeakPtr cache_manager, + CacheStorageOwner owner); ~CacheStorageQuotaClient() override; // QuotaClient overrides @@ -40,8 +42,11 @@ class CONTENT_EXPORT CacheStorageQuotaClient : public storage::QuotaClient { DeletionCallback callback) override; bool DoesSupport(blink::mojom::StorageType type) const override; + static ID GetIDFromOwner(CacheStorageOwner owner); + private: base::WeakPtr cache_manager_; + CacheStorageOwner owner_; DISALLOW_COPY_AND_ASSIGN(CacheStorageQuotaClient); }; diff --git a/chromium/content/browser/child_process_launcher.cc b/chromium/content/browser/child_process_launcher.cc index fcc37f21745..b1869a2723c 100644 --- a/chromium/content/browser/child_process_launcher.cc +++ b/chromium/content/browser/child_process_launcher.cc @@ -4,6 +4,8 @@ #include "content/browser/child_process_launcher.h" +#include + #include "base/bind.h" #include "base/command_line.h" #include "base/files/file_util.h" @@ -12,8 +14,8 @@ #include "base/process/launch.h" #include "build/build_config.h" #include "content/public/browser/child_process_launcher_utils.h" -#include "content/public/common/result_codes.h" #include "content/public/common/sandboxed_process_launcher_delegate.h" +#include "services/service_manager/embedder/result_codes.h" namespace content { @@ -29,12 +31,11 @@ ChildProcessLauncher::ChildProcessLauncher( const mojo::edk::ProcessErrorCallback& process_error_callback, bool terminate_on_shutdown) : client_(client), - termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), - exit_code_(RESULT_CODE_NORMAL_EXIT), starting_(true), + start_time_(base::TimeTicks::Now()), #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ - defined(UNDEFINED_SANITIZER) + defined(UNDEFINED_SANITIZER) || defined(CLANG_COVERAGE) terminate_child_on_shutdown_(false), #else terminate_child_on_shutdown_(terminate_on_shutdown), @@ -80,7 +81,7 @@ void ChildProcessLauncher::Notify( if (process_.process.IsValid()) { client_->OnProcessLaunched(); } else { - termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED; + termination_info_.status = base::TERMINATION_STATUS_LAUNCH_FAILED; // NOTE: May delete |this|. client_->OnProcessLaunchFailed(error_code); @@ -99,40 +100,39 @@ const base::Process& ChildProcessLauncher::GetProcess() const { return process_.process; } -base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( - bool known_dead, - int* exit_code) { +ChildProcessTerminationInfo ChildProcessLauncher::GetChildTerminationInfo( + bool known_dead) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!process_.process.IsValid()) { // Make sure to avoid using the default termination status if the process // hasn't even started yet. - if (IsStarting()) - termination_status_ = base::TERMINATION_STATUS_STILL_RUNNING; - - // Process doesn't exist, so return the cached termination status. - if (exit_code) - *exit_code = exit_code_; - return termination_status_; + if (IsStarting()) { + termination_info_.status = base::TERMINATION_STATUS_STILL_RUNNING; + termination_info_.uptime = base::TimeTicks::Now() - start_time_; + DCHECK_LE(base::TimeDelta::FromSeconds(0), termination_info_.uptime); + } + + // Process doesn't exist, so return the cached termination info. + return termination_info_; } - termination_status_ = - helper_->GetTerminationStatus(process_, known_dead, &exit_code_); - if (exit_code) - *exit_code = exit_code_; + termination_info_ = helper_->GetTerminationInfo(process_, known_dead); + termination_info_.uptime = base::TimeTicks::Now() - start_time_; + DCHECK_LE(base::TimeDelta::FromSeconds(0), termination_info_.uptime); // POSIX: If the process crashed, then the kernel closed the socket for it and // so the child has already died by the time we get here. Since - // GetTerminationStatus called waitpid with WNOHANG, it'll reap the process. - // However, if GetTerminationStatus didn't reap the child (because it was + // GetTerminationInfo called waitpid with WNOHANG, it'll reap the process. + // However, if GetTerminationInfo didn't reap the child (because it was // still running), we'll need to Terminate via ProcessWatcher. So we can't // close the handle here. - if (termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) { - process_.process.Exited(exit_code_); + if (termination_info_.status != base::TERMINATION_STATUS_STILL_RUNNING) { + process_.process.Exited(termination_info_.exit_code); process_.process.Close(); } - return termination_status_; + return termination_info_; } bool ChildProcessLauncher::Terminate(int exit_code) { @@ -160,13 +160,6 @@ void ChildProcessLauncher::ResetRegisteredFilesForTesting() { ChildProcessLauncherHelper::ResetRegisteredFilesForTesting(); } -#if defined(OS_ANDROID) -// static -size_t ChildProcessLauncher::GetNumberOfRendererSlots() { - return ChildProcessLauncherHelper::GetNumberOfRendererSlots(); -} -#endif // OS_ANDROID - ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( Client* client) { Client* ret = client_; diff --git a/chromium/content/browser/child_process_launcher.h b/chromium/content/browser/child_process_launcher.h index c9d243c37ea..04787554d23 100644 --- a/chromium/content/browser/child_process_launcher.h +++ b/chromium/content/browser/child_process_launcher.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_H_ #include +#include #include "base/macros.h" #include "base/memory/ref_counted.h" @@ -14,10 +15,12 @@ #include "base/process/kill.h" #include "base/process/process.h" #include "base/sequence_checker.h" +#include "base/time/time.h" #include "build/build_config.h" #include "content/browser/child_process_launcher_helper.h" #include "content/common/content_export.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/child_process_termination_info.h" #include "content/public/common/result_codes.h" #include "mojo/edk/embedder/outgoing_broker_client_invitation.h" #include "mojo/edk/embedder/scoped_platform_handle.h" @@ -78,7 +81,7 @@ class CONTENT_EXPORT ChildProcessLauncher { // constructed on. virtual void OnProcessLaunched() = 0; - virtual void OnProcessLaunchFailed(int error_code) {}; + virtual void OnProcessLaunchFailed(int error_code) {} protected: virtual ~Client() {} @@ -118,11 +121,7 @@ class CONTENT_EXPORT ChildProcessLauncher { // process could be seen as running. With |known_dead| set to true, the // process will be killed if it was still running. See ZygoteHostImpl for // more discussion of Linux implementation details. - // |exit_code| is the exit code of the process if it exited (e.g. status from - // waitpid if on posix, from GetExitCodeProcess on Windows). |exit_code| may - // be NULL. - base::TerminationStatus GetChildTerminationStatus(bool known_dead, - int* exit_code); + ChildProcessTerminationInfo GetChildTerminationInfo(bool known_dead); // Changes whether the process runs in the background or not. Only call // this after the process has started. @@ -153,11 +152,6 @@ class CONTENT_EXPORT ChildProcessLauncher { // support multiple shell context creation in unit_tests. static void ResetRegisteredFilesForTesting(); -#if defined(OS_ANDROID) - // Temporary until crbug.com/693484 is fixed. - static size_t GetNumberOfRendererSlots(); -#endif // OS_ANDROID - private: friend class internal::ChildProcessLauncherHelper; @@ -172,9 +166,9 @@ class CONTENT_EXPORT ChildProcessLauncher { // ChildProcessLauncherHelper once the process was started. internal::ChildProcessLauncherHelper::Process process_; - base::TerminationStatus termination_status_; - int exit_code_; + ChildProcessTerminationInfo termination_info_; bool starting_; + base::TimeTicks start_time_; // Controls whether the child process should be terminated on browser // shutdown. Default behavior is to terminate the child. diff --git a/chromium/content/browser/child_process_launcher_helper.h b/chromium/content/browser/child_process_launcher_helper.h index b9b7a82652b..51d54bb4ab6 100644 --- a/chromium/content/browser/child_process_launcher_helper.h +++ b/chromium/content/browser/child_process_launcher_helper.h @@ -14,11 +14,11 @@ #include "build/build_config.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/result_codes.h" -#include "content/public/common/zygote_buildflags.h" #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/embedder/outgoing_broker_client_invitation.h" #include "mojo/edk/embedder/scoped_platform_handle.h" #include "services/catalog/public/cpp/manifest_parsing_util.h" +#include "services/service_manager/zygote/common/zygote_buildflags.h" #if defined(OS_ANDROID) #include "base/android/scoped_java_ref.h" @@ -35,7 +35,7 @@ #endif #if BUILDFLAG(USE_ZYGOTE_HANDLE) -#include "content/public/common/zygote_handle.h" +#include "services/service_manager/zygote/common/zygote_handle.h" // nogncheck #endif namespace base { @@ -47,14 +47,15 @@ namespace content { class ChildProcessLauncher; class SandboxedProcessLauncherDelegate; struct ChildProcessLauncherPriority; +struct ChildProcessTerminationInfo; -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) class PosixFileDescriptorInfo; #endif namespace internal { -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(OS_FUCHSIA) using FileMappedForLaunch = PosixFileDescriptorInfo; #else using FileMappedForLaunch = base::HandlesToInheritVector; @@ -78,7 +79,7 @@ class ChildProcessLauncherHelper : base::Process process; #if BUILDFLAG(USE_ZYGOTE_HANDLE) - ZygoteHandle zygote = nullptr; + service_manager::ZygoteHandle zygote = nullptr; #endif // BUILDFLAG(USE_ZYGOTE_HANDLE) }; @@ -103,7 +104,8 @@ class ChildProcessLauncherHelper : // Called in to give implementors a chance at creating a server pipe. // Platform specific. - mojo::edk::ScopedPlatformHandle PrepareMojoPipeHandlesOnClientThread(); + mojo::edk::ScopedInternalPlatformHandle + PrepareMojoPipeHandlesOnClientThread(); // Returns the list of files that should be mapped in the child process. // Platform specific. @@ -146,12 +148,10 @@ class ChildProcessLauncherHelper : int client_thread_id() const { return client_thread_id_; } - // Returns the termination status and sets |exit_code| if non null. - // See ChildProcessLauncher::GetChildTerminationStatus for more info. - base::TerminationStatus GetTerminationStatus( + // See ChildProcessLauncher::GetChildTerminationInfo for more info. + ChildProcessTerminationInfo GetTerminationInfo( const ChildProcessLauncherHelper::Process& process, - bool known_dead, - int* exit_code); + bool known_dead); // Terminates |process|. // Returns true if the process was stopped, false if the process had not been @@ -180,7 +180,6 @@ class ChildProcessLauncherHelper : void OnChildProcessStarted(JNIEnv* env, const base::android::JavaParamRef& obj, jint handle); - static size_t GetNumberOfRendererSlots(); #endif // OS_ANDROID private: @@ -190,7 +189,7 @@ class ChildProcessLauncherHelper : void LaunchOnLauncherThread(); - const mojo::edk::PlatformHandle& mojo_client_handle() const { + const mojo::edk::InternalPlatformHandle& mojo_client_handle() const { return mojo_client_handle_.get(); } base::CommandLine* command_line() { return command_line_.get(); } @@ -213,8 +212,8 @@ class ChildProcessLauncherHelper : std::unique_ptr command_line_; std::unique_ptr delegate_; base::WeakPtr child_process_launcher_; - mojo::edk::ScopedPlatformHandle mojo_client_handle_; - mojo::edk::ScopedPlatformHandle mojo_server_handle_; + mojo::edk::ScopedInternalPlatformHandle mojo_client_handle_; + mojo::edk::ScopedInternalPlatformHandle mojo_server_handle_; bool terminate_on_shutdown_; std::unique_ptr broker_client_invitation_; diff --git a/chromium/content/browser/child_process_launcher_helper_android.cc b/chromium/content/browser/child_process_launcher_helper_android.cc index 1954e362d8c..07c1a74cec2 100644 --- a/chromium/content/browser/child_process_launcher_helper_android.cc +++ b/chromium/content/browser/child_process_launcher_helper_android.cc @@ -20,6 +20,7 @@ #include "content/public/common/content_descriptors.h" #include "content/public/common/content_switches.h" #include "jni/ChildProcessLauncherHelper_jni.h" +#include "services/service_manager/sandbox/switches.h" using base::android::AttachCurrentThread; using base::android::JavaParamRef; @@ -55,12 +56,12 @@ void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() { // Non-sandboxed utility or renderer process are currently not supported. DCHECK(process_type == switches::kGpuProcess || - !command_line()->HasSwitch(switches::kNoSandbox)); + !command_line()->HasSwitch(service_manager::switches::kNoSandbox)); } -mojo::edk::ScopedPlatformHandle +mojo::edk::ScopedInternalPlatformHandle ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() { - return mojo::edk::ScopedPlatformHandle(); + return mojo::edk::ScopedInternalPlatformHandle(); } std::unique_ptr @@ -148,18 +149,30 @@ void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread( const base::LaunchOptions& options) { } -base::TerminationStatus ChildProcessLauncherHelper::GetTerminationStatus( +ChildProcessTerminationInfo ChildProcessLauncherHelper::GetTerminationInfo( const ChildProcessLauncherHelper::Process& process, - bool known_dead, - int* exit_code) { - if (java_peer_avaiable_on_client_thread_ && - Java_ChildProcessLauncherHelper_isOomProtected(AttachCurrentThread(), - java_peer_)) { - return base::TERMINATION_STATUS_OOM_PROTECTED; + bool known_dead) { + ChildProcessTerminationInfo info; + info.has_oom_protection_bindings = + java_peer_avaiable_on_client_thread_ && + Java_ChildProcessLauncherHelper_hasOomProtectionBindings( + AttachCurrentThread(), java_peer_); + info.was_killed_intentionally_by_browser = + java_peer_avaiable_on_client_thread_ && + Java_ChildProcessLauncherHelper_isKilledByUs(AttachCurrentThread(), + java_peer_); + bool app_foreground = + java_peer_avaiable_on_client_thread_ && + Java_ChildProcessLauncherHelper_isApplicationInForeground( + AttachCurrentThread(), java_peer_); + if (app_foreground && info.has_oom_protection_bindings) { + info.status = base::TERMINATION_STATUS_OOM_PROTECTED; + } else { + // Note waitpid does not work on Android since these are not actually child + // processes. So there is no need for base::GetTerminationInfo. + info.status = base::TERMINATION_STATUS_NORMAL_TERMINATION; } - // Note waitpid does not work on Android since these are not actually child - // processes. So there is no need for base::GetTerminationStatus. - return base::TERMINATION_STATUS_NORMAL_TERMINATION; + return info; } // static @@ -228,13 +241,6 @@ void ChildProcessLauncherHelper::OnChildProcessStarted( PostLaunchOnLauncherThread(std::move(process), launch_result); } -// static -size_t ChildProcessLauncherHelper::GetNumberOfRendererSlots() { - return static_cast( - Java_ChildProcessLauncherHelper_getNumberOfRendererSlots( - AttachCurrentThread())); -} - } // namespace internal } // namespace content diff --git a/chromium/content/browser/child_process_launcher_helper_fuchsia.cc b/chromium/content/browser/child_process_launcher_helper_fuchsia.cc index 281f4609d10..cd3095ce794 100644 --- a/chromium/content/browser/child_process_launcher_helper_fuchsia.cc +++ b/chromium/content/browser/child_process_launcher_helper_fuchsia.cc @@ -11,6 +11,7 @@ #include "content/public/browser/child_process_launcher_utils.h" #include "content/public/common/sandboxed_process_launcher_delegate.h" #include "mojo/edk/embedder/platform_channel_pair.h" +#include "services/service_manager/embedder/result_codes.h" namespace content { namespace internal { @@ -23,11 +24,13 @@ void ChildProcessLauncherHelper::SetProcessPriorityOnLauncherThread( NOTIMPLEMENTED(); } -base::TerminationStatus ChildProcessLauncherHelper::GetTerminationStatus( +ChildProcessTerminationInfo ChildProcessLauncherHelper::GetTerminationInfo( const ChildProcessLauncherHelper::Process& process, - bool known_dead, - int* exit_code) { - return base::GetTerminationStatus(process.process.Handle(), exit_code); + bool known_dead) { + ChildProcessTerminationInfo info; + info.status = + base::GetTerminationStatus(process.process.Handle(), &info.exit_code); + return info; } // static @@ -54,13 +57,13 @@ void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() { DCHECK_CURRENTLY_ON(client_thread_id_); } -mojo::edk::ScopedPlatformHandle +mojo::edk::ScopedInternalPlatformHandle ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() { DCHECK_CURRENTLY_ON(client_thread_id_); // By doing nothing here, StartLaunchOnClientThread() will construct a channel // pair instead. - return mojo::edk::ScopedPlatformHandle(); + return mojo::edk::ScopedInternalPlatformHandle(); } std::unique_ptr @@ -114,7 +117,7 @@ void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread( void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync( ChildProcessLauncherHelper::Process process) { DCHECK(CurrentlyOnProcessLauncherTaskRunner()); - process.process.Terminate(RESULT_CODE_NORMAL_EXIT, true); + process.process.Terminate(service_manager::RESULT_CODE_NORMAL_EXIT, true); } } // namespace internal diff --git a/chromium/content/browser/child_process_launcher_helper_linux.cc b/chromium/content/browser/child_process_launcher_helper_linux.cc index 732970c0e83..5db82c367aa 100644 --- a/chromium/content/browser/child_process_launcher_helper_linux.cc +++ b/chromium/content/browser/child_process_launcher_helper_linux.cc @@ -9,26 +9,26 @@ #include "content/browser/child_process_launcher_helper.h" #include "content/browser/child_process_launcher_helper_posix.h" #include "content/browser/sandbox_host_linux.h" -#include "content/browser/zygote_host/zygote_communication_linux.h" -#include "content/browser/zygote_host/zygote_host_impl_linux.h" #include "content/public/browser/child_process_launcher_utils.h" #include "content/public/browser/content_browser_client.h" -#include "content/public/common/common_sandbox_support_linux.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 "content/public/common/zygote_handle.h" #include "gpu/config/gpu_switches.h" #include "services/service_manager/sandbox/linux/sandbox_linux.h" +#include "services/service_manager/zygote/common/common_sandbox_support_linux.h" +#include "services/service_manager/zygote/common/zygote_handle.h" +#include "services/service_manager/zygote/host/zygote_communication_linux.h" +#include "services/service_manager/zygote/host/zygote_host_impl_linux.h" namespace content { namespace internal { -mojo::edk::ScopedPlatformHandle +mojo::edk::ScopedInternalPlatformHandle ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() { DCHECK_CURRENTLY_ON(client_thread_id_); - return mojo::edk::ScopedPlatformHandle(); + return mojo::edk::ScopedInternalPlatformHandle(); } void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() { @@ -55,7 +55,8 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread( base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableOOPRasterization))) { const int sandbox_fd = SandboxHostLinux::GetInstance()->GetChildSocket(); - options->fds_to_remap.push_back(std::make_pair(sandbox_fd, GetSandboxFD())); + options->fds_to_remap.push_back( + std::make_pair(sandbox_fd, service_manager::GetSandboxFD())); } options->environ = delegate_->GetEnvironment(); @@ -71,7 +72,7 @@ ChildProcessLauncherHelper::LaunchProcessOnLauncherThread( int* launch_result) { *is_synchronous_launch = true; - ZygoteHandle zygote_handle = + service_manager::ZygoteHandle zygote_handle = base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoZygote) ? nullptr : delegate_->GetZygote(); @@ -92,7 +93,7 @@ ChildProcessLauncherHelper::LaunchProcessOnLauncherThread( // chrome::kLowestRendererOomScore in chrome/chrome_constants.h, but // that's not something we can include here.) const int kLowestRendererOomScore = 300; - ZygoteHostImpl::GetInstance()->AdjustRendererOOMScore( + service_manager::ZygoteHostImpl::GetInstance()->AdjustRendererOOMScore( handle, kLowestRendererOomScore); } #endif @@ -115,19 +116,21 @@ void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread( const base::LaunchOptions& options) { } -base::TerminationStatus ChildProcessLauncherHelper::GetTerminationStatus( +ChildProcessTerminationInfo ChildProcessLauncherHelper::GetTerminationInfo( const ChildProcessLauncherHelper::Process& process, - bool known_dead, - int* exit_code) { + bool known_dead) { + ChildProcessTerminationInfo info; if (process.zygote) { - return process.zygote->GetTerminationStatus( - process.process.Handle(), known_dead, exit_code); - } - if (known_dead) { - return base::GetKnownDeadTerminationStatus( - process.process.Handle(), exit_code); + info.status = process.zygote->GetTerminationStatus( + process.process.Handle(), known_dead, &info.exit_code); + } else if (known_dead) { + info.status = base::GetKnownDeadTerminationStatus(process.process.Handle(), + &info.exit_code); + } else { + info.status = + base::GetTerminationStatus(process.process.Handle(), &info.exit_code); } - return base::GetTerminationStatus(process.process.Handle(), exit_code); + return info; } // static @@ -142,7 +145,7 @@ bool ChildProcessLauncherHelper::TerminateProcess(const base::Process& process, void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync( ChildProcessLauncherHelper::Process process) { DCHECK(CurrentlyOnProcessLauncherTaskRunner()); - process.process.Terminate(RESULT_CODE_NORMAL_EXIT, false); + process.process.Terminate(service_manager::RESULT_CODE_NORMAL_EXIT, false); // On POSIX, we must additionally reap the child. if (process.zygote) { // If the renderer was created via a zygote, we have to proxy the reaping diff --git a/chromium/content/browser/child_process_launcher_helper_mac.cc b/chromium/content/browser/child_process_launcher_helper_mac.cc index 61f9aa4ec40..902177996e1 100644 --- a/chromium/content/browser/child_process_launcher_helper_mac.cc +++ b/chromium/content/browser/child_process_launcher_helper_mac.cc @@ -21,6 +21,7 @@ #include "content/public/common/sandboxed_process_launcher_delegate.h" #include "mojo/edk/embedder/scoped_platform_handle.h" #include "sandbox/mac/seatbelt_exec.h" +#include "services/service_manager/embedder/result_codes.h" #include "services/service_manager/sandbox/mac/cdm.sb.h" #include "services/service_manager/sandbox/mac/common_v2.sb.h" #include "services/service_manager/sandbox/mac/gpu_v2.sb.h" @@ -31,14 +32,15 @@ #include "services/service_manager/sandbox/mac/utility.sb.h" #include "services/service_manager/sandbox/sandbox.h" #include "services/service_manager/sandbox/sandbox_type.h" +#include "services/service_manager/sandbox/switches.h" namespace content { namespace internal { -mojo::edk::ScopedPlatformHandle +mojo::edk::ScopedInternalPlatformHandle ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() { DCHECK_CURRENTLY_ON(client_thread_id_); - return mojo::edk::ScopedPlatformHandle(); + return mojo::edk::ScopedInternalPlatformHandle(); } void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() { @@ -66,8 +68,9 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread( auto sandbox_type = service_manager::SandboxTypeFromCommandLine(*command_line_); - bool no_sandbox = command_line_->HasSwitch(switches::kNoSandbox) || - service_manager::IsUnsandboxedSandboxType(sandbox_type); + bool no_sandbox = + command_line_->HasSwitch(service_manager::switches::kNoSandbox) || + service_manager::IsUnsandboxedSandboxType(sandbox_type); // TODO(kerrnel): Delete this switch once the V2 sandbox is always enabled. bool v2_process = false; @@ -161,7 +164,8 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread( } base::FilePath helper_executable; - CHECK(PathService::Get(content::CHILD_PROCESS_EXE, &helper_executable)); + CHECK( + base::PathService::Get(content::CHILD_PROCESS_EXE, &helper_executable)); options->fds_to_remap.push_back(std::make_pair(pipe, pipe)); @@ -221,13 +225,15 @@ void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread( broker->GetLock().Release(); } -base::TerminationStatus ChildProcessLauncherHelper::GetTerminationStatus( +ChildProcessTerminationInfo ChildProcessLauncherHelper::GetTerminationInfo( const ChildProcessLauncherHelper::Process& process, - bool known_dead, - int* exit_code) { - return known_dead - ? base::GetKnownDeadTerminationStatus(process.process.Handle(), exit_code) - : base::GetTerminationStatus(process.process.Handle(), exit_code); + bool known_dead) { + ChildProcessTerminationInfo info; + info.status = known_dead ? base::GetKnownDeadTerminationStatus( + process.process.Handle(), &info.exit_code) + : base::GetTerminationStatus( + process.process.Handle(), &info.exit_code); + return info; } // static @@ -244,7 +250,7 @@ void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync( DCHECK(CurrentlyOnProcessLauncherTaskRunner()); // Client has gone away, so just kill the process. Using exit code 0 means // that UMA won't treat this as a crash. - process.process.Terminate(RESULT_CODE_NORMAL_EXIT, false); + process.process.Terminate(service_manager::RESULT_CODE_NORMAL_EXIT, false); base::EnsureProcessTerminated(std::move(process.process)); } diff --git a/chromium/content/browser/child_process_launcher_helper_posix.cc b/chromium/content/browser/child_process_launcher_helper_posix.cc index 42e1c9e4cf1..60aedf2a364 100644 --- a/chromium/content/browser/child_process_launcher_helper_posix.cc +++ b/chromium/content/browser/child_process_launcher_helper_posix.cc @@ -73,7 +73,7 @@ base::PlatformFile OpenFileIfNecessary(const base::FilePath& path, std::unique_ptr CreateDefaultPosixFilesToMap( int child_process_id, - const mojo::edk::PlatformHandle& mojo_client_handle, + const mojo::edk::InternalPlatformHandle& mojo_client_handle, bool include_service_required_files, const std::string& process_type, base::CommandLine* command_line) { @@ -83,12 +83,13 @@ std::unique_ptr CreateDefaultPosixFilesToMap( base::SharedMemoryHandle shm = base::FieldTrialList::GetFieldTrialHandle(); if (shm.IsValid()) { files_to_register->Share( - kFieldTrialDescriptor, + service_manager::kFieldTrialDescriptor, base::SharedMemory::GetFdFromSharedMemoryHandle(shm)); } DCHECK(mojo_client_handle.is_valid()); - files_to_register->Share(kMojoIPCChannel, mojo_client_handle.handle); + files_to_register->Share(service_manager::kMojoIPCChannel, + mojo_client_handle.handle); // TODO(jcivelli): remove this "if defined" by making // GetAdditionalMappedFilesForChildProcess a no op on Mac. diff --git a/chromium/content/browser/child_process_launcher_helper_posix.h b/chromium/content/browser/child_process_launcher_helper_posix.h index 7f08758cae4..df353a54a11 100644 --- a/chromium/content/browser/child_process_launcher_helper_posix.h +++ b/chromium/content/browser/child_process_launcher_helper_posix.h @@ -18,7 +18,7 @@ class FilePath; namespace mojo { namespace edk { -struct PlatformHandle; +struct InternalPlatformHandle; } // namespace mojo } // namespace edk @@ -33,7 +33,7 @@ namespace internal { std::unique_ptr CreateDefaultPosixFilesToMap( int child_process_id, - const mojo::edk::PlatformHandle& mojo_client_handle, + const mojo::edk::InternalPlatformHandle& mojo_client_handle, bool include_service_required_files, const std::string& process_type, base::CommandLine* command_line); diff --git a/chromium/content/browser/child_process_launcher_helper_win.cc b/chromium/content/browser/child_process_launcher_helper_win.cc index ca73f6006dc..46c2dbea83d 100644 --- a/chromium/content/browser/child_process_launcher_helper_win.cc +++ b/chromium/content/browser/child_process_launcher_helper_win.cc @@ -18,6 +18,7 @@ #include "mojo/edk/embedder/platform_channel_pair.h" #include "mojo/edk/embedder/scoped_platform_handle.h" #include "sandbox/win/src/sandbox_types.h" +#include "services/service_manager/embedder/result_codes.h" #include "services/service_manager/sandbox/win/sandbox_win.h" namespace content { @@ -27,12 +28,12 @@ void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() { DCHECK_CURRENTLY_ON(client_thread_id_); } -mojo::edk::ScopedPlatformHandle +mojo::edk::ScopedInternalPlatformHandle ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() { DCHECK_CURRENTLY_ON(client_thread_id_); if (!delegate_->ShouldLaunchElevated()) - return mojo::edk::ScopedPlatformHandle(); + return mojo::edk::ScopedInternalPlatformHandle(); mojo::edk::NamedPlatformChannelPair named_pair; named_pair.PrepareToPassClientHandleToChildProcess(command_line()); @@ -89,11 +90,13 @@ void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread( DCHECK(CurrentlyOnProcessLauncherTaskRunner()); } -base::TerminationStatus ChildProcessLauncherHelper::GetTerminationStatus( +ChildProcessTerminationInfo ChildProcessLauncherHelper::GetTerminationInfo( const ChildProcessLauncherHelper::Process& process, - bool known_dead, - int* exit_code) { - return base::GetTerminationStatus(process.process.Handle(), exit_code); + bool known_dead) { + ChildProcessTerminationInfo info; + info.status = + base::GetTerminationStatus(process.process.Handle(), &info.exit_code); + return info; } // static @@ -107,7 +110,7 @@ void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync( DCHECK(CurrentlyOnProcessLauncherTaskRunner()); // Client has gone away, so just kill the process. Using exit code 0 means // that UMA won't treat this as a crash. - process.process.Terminate(RESULT_CODE_NORMAL_EXIT, false); + process.process.Terminate(service_manager::RESULT_CODE_NORMAL_EXIT, false); } void ChildProcessLauncherHelper::SetProcessPriorityOnLauncherThread( diff --git a/chromium/content/browser/child_process_security_policy_impl.cc b/chromium/content/browser/child_process_security_policy_impl.cc index 09d1adb47f3..4bf51fd3352 100644 --- a/chromium/content/browser/child_process_security_policy_impl.cc +++ b/chromium/content/browser/child_process_security_policy_impl.cc @@ -683,12 +683,12 @@ bool ChildProcessSecurityPolicyImpl::CanRedirectToURL(const GURL& url) { // Note about redirects and special URLs: // * data-url: Blocked by net::DataProtocolHandler::IsSafeRedirectTarget(). + // * filesystem-url: Blocked by + // storage::FilesystemProtocolHandler::IsSafeRedirectTarget(). // Depending on their inner origins and if the request is browser-initiated or - // renderer-initiated, blob-urls and filesystem-urls might get blocked by - // CanCommitURL or in DocumentLoader::RedirectReceived. - // * blob-url: If not blocked, a 'file not found' response will be - // generated in net::BlobURLRequestJob::DidStart(). - // * filesystem-url: If not blocked, the response is displayed. + // renderer-initiated, blob-urls might get blocked by CanCommitURL or in + // DocumentLoader::RedirectReceived. If not blocked, a 'file not found' + // response will be generated in net::BlobURLRequestJob::DidStart(). return true; } diff --git a/chromium/content/browser/cocoa/system_hotkey_helper_mac.mm b/chromium/content/browser/cocoa/system_hotkey_helper_mac.mm index 258f0ff7b32..36a0ee2f3bc 100644 --- a/chromium/content/browser/cocoa/system_hotkey_helper_mac.mm +++ b/chromium/content/browser/cocoa/system_hotkey_helper_mac.mm @@ -7,7 +7,6 @@ #include "base/bind.h" #include "base/files/file_path.h" #include "base/mac/foundation_util.h" -#include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" #include "base/task_scheduler/post_task.h" #include "base/task_scheduler/task_traits.h" diff --git a/chromium/content/browser/cocoa/system_hotkey_map_unittest.mm b/chromium/content/browser/cocoa/system_hotkey_map_unittest.mm index a0798bec241..d4b2a798c0c 100644 --- a/chromium/content/browser/cocoa/system_hotkey_map_unittest.mm +++ b/chromium/content/browser/cocoa/system_hotkey_map_unittest.mm @@ -21,7 +21,7 @@ class SystemHotkeyMapTest : public ::testing::Test { NSData* DataFromTestFile(const char* file) { base::FilePath test_data_dir; - bool result = PathService::Get(DIR_TEST_DATA, &test_data_dir); + bool result = base::PathService::Get(DIR_TEST_DATA, &test_data_dir); if (!result) return nil; diff --git a/chromium/content/browser/compositor/browser_compositor_output_surface.cc b/chromium/content/browser/compositor/browser_compositor_output_surface.cc index f7e37d38bfb..924e82dadab 100644 --- a/chromium/content/browser/compositor/browser_compositor_output_surface.cc +++ b/chromium/content/browser/compositor/browser_compositor_output_surface.cc @@ -36,12 +36,14 @@ BrowserCompositorOutputSurface::BrowserCompositorOutputSurface( update_vsync_parameters_callback_(update_vsync_parameters_callback), reflector_(nullptr) {} +#if BUILDFLAG(ENABLE_VULKAN) BrowserCompositorOutputSurface::BrowserCompositorOutputSurface( const scoped_refptr& vulkan_context_provider, const UpdateVSyncParametersCallback& update_vsync_parameters_callback) : OutputSurface(std::move(vulkan_context_provider)), update_vsync_parameters_callback_(update_vsync_parameters_callback), reflector_(nullptr) {} +#endif BrowserCompositorOutputSurface::~BrowserCompositorOutputSurface() { if (reflector_) diff --git a/chromium/content/browser/compositor/browser_compositor_output_surface.h b/chromium/content/browser/compositor/browser_compositor_output_surface.h index 0df130ac73b..941d70bd2a7 100644 --- a/chromium/content/browser/compositor/browser_compositor_output_surface.h +++ b/chromium/content/browser/compositor/browser_compositor_output_surface.h @@ -9,6 +9,7 @@ #include "build/build_config.h" #include "components/viz/service/display/output_surface.h" #include "content/common/content_export.h" +#include "gpu/vulkan/buildflags.h" namespace cc { class SoftwareOutputDevice; @@ -43,10 +44,6 @@ class CONTENT_EXPORT BrowserCompositorOutputSurface // Called when |reflector_| was updated. virtual void OnReflectorChanged(); -#if defined(OS_MACOSX) - virtual void SetSurfaceSuspendedForRecycle(bool suspended) = 0; -#endif - protected: // Constructor used by the accelerated implementation. BrowserCompositorOutputSurface( @@ -60,10 +57,12 @@ class CONTENT_EXPORT BrowserCompositorOutputSurface std::unique_ptr software_device, const UpdateVSyncParametersCallback& update_vsync_parameters_callback); +#if BUILDFLAG(ENABLE_VULKAN) // Constructor used by the Vulkan implementation. BrowserCompositorOutputSurface( const scoped_refptr& vulkan_context_provider, const UpdateVSyncParametersCallback& update_vsync_parameters_callback); +#endif const UpdateVSyncParametersCallback update_vsync_parameters_callback_; ReflectorImpl* reflector_; diff --git a/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc index 2694224b27f..34fbb3a9521 100644 --- a/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc +++ b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.cc @@ -16,6 +16,7 @@ #include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/common/swap_buffers_complete_params.h" +#include "gpu/command_buffer/common/swap_buffers_flags.h" #include "gpu/ipc/client/command_buffer_proxy_impl.h" #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h" #include "ui/gl/gl_utils.h" @@ -30,7 +31,7 @@ GpuBrowserCompositorOutputSurface::GpuBrowserCompositorOutputSurface( : BrowserCompositorOutputSurface(std::move(context), update_vsync_parameters_callback, std::move(overlay_candidate_validator)), - latency_info_cache_(this) { + weak_ptr_factory_(this) { if (capabilities_.uses_default_gl_framebuffer) { capabilities_.flipped_output_surface = context_provider()->ContextCapabilities().flips_vertically; @@ -39,16 +40,8 @@ GpuBrowserCompositorOutputSurface::GpuBrowserCompositorOutputSurface( context_provider()->ContextCapabilities().num_stencil_bits > 0; } -GpuBrowserCompositorOutputSurface::~GpuBrowserCompositorOutputSurface() { - // Reset GetCommandBufferProxy() callbacks to avoid calling those callbacks - // from dtor of the base class. - GetCommandBufferProxy()->SetSwapBuffersCompletionCallback( - gpu::CommandBufferProxyImpl::SwapBuffersCompletionCallback()); - GetCommandBufferProxy()->SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback()); - GetCommandBufferProxy()->SetPresentationCallback( - gpu::CommandBufferProxyImpl::PresentationCallback()); -} +GpuBrowserCompositorOutputSurface::~GpuBrowserCompositorOutputSurface() = + default; void GpuBrowserCompositorOutputSurface::SetNeedsVSync(bool needs_vsync) { #if defined(OS_WIN) @@ -59,18 +52,16 @@ void GpuBrowserCompositorOutputSurface::SetNeedsVSync(bool needs_vsync) { } void GpuBrowserCompositorOutputSurface::OnGpuSwapBuffersCompleted( + std::vector latency_info, const gpu::SwapBuffersCompleteParams& params) { if (!params.ca_layer_params.is_empty) client_->DidReceiveCALayerParams(params.ca_layer_params); if (!params.texture_in_use_responses.empty()) client_->DidReceiveTextureInUseResponses(params.texture_in_use_responses); - client_->DidReceiveSwapBuffersAck(params.swap_response.swap_id); - latency_info_cache_.OnSwapBuffersCompleted(params.swap_response); -} - -void GpuBrowserCompositorOutputSurface::LatencyInfoCompleted( - const std::vector& latency_info) { + client_->DidReceiveSwapBuffersAck(); + UpdateLatencyInfoOnSwap(params.swap_response, &latency_info); RenderWidgetHostImpl::OnGpuSwapBuffersCompleted(latency_info); + latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); } void GpuBrowserCompositorOutputSurface::OnReflectorChanged() { @@ -89,16 +80,9 @@ void GpuBrowserCompositorOutputSurface::BindToClient( DCHECK(!client_); client_ = client; - // CommandBufferProxy() will always call below callbacks directly (no - // PostTask), so it is safe to use base::Unretained(this). - GetCommandBufferProxy()->SetSwapBuffersCompletionCallback( - base::Bind(&GpuBrowserCompositorOutputSurface::OnGpuSwapBuffersCompleted, - base::Unretained(this))); - GetCommandBufferProxy()->SetUpdateVSyncParametersCallback( - update_vsync_parameters_callback_); - GetCommandBufferProxy()->SetPresentationCallback( - base::Bind(&GpuBrowserCompositorOutputSurface::OnPresentation, - base::Unretained(this))); + GetCommandBufferProxy()->SetUpdateVSyncParametersCallback(base::BindRepeating( + &GpuBrowserCompositorOutputSurface::OnUpdateVSyncParameters, + weak_ptr_factory_.GetWeakPtr())); } void GpuBrowserCompositorOutputSurface::EnsureBackbuffer() {} @@ -126,7 +110,7 @@ void GpuBrowserCompositorOutputSurface::Reshape( void GpuBrowserCompositorOutputSurface::SwapBuffers( viz::OutputSurfaceFrame frame) { - if (latency_info_cache_.WillSwap(std::move(frame.latency_info))) + if (LatencyInfoHasSnapshotRequest(frame.latency_info)) GetCommandBufferProxy()->SetSnapshotRequested(); gfx::Size surface_size = frame.size; @@ -143,14 +127,29 @@ void GpuBrowserCompositorOutputSurface::SwapBuffers( set_draw_rectangle_for_frame_ = false; + auto swap_callback = base::BindOnce( + &GpuBrowserCompositorOutputSurface::OnGpuSwapBuffersCompleted, + weak_ptr_factory_.GetWeakPtr(), std::move(frame.latency_info)); + uint32_t flags = gpu::SwapBuffersFlags::kVSyncParams; + gpu::ContextSupport::PresentationCallback presentation_callback; + if (frame.need_presentation_feedback) { + flags |= gpu::SwapBuffersFlags::kPresentationFeedback; + presentation_callback = + base::BindOnce(&GpuBrowserCompositorOutputSurface::OnPresentation, + weak_ptr_factory_.GetWeakPtr()); + } if (frame.sub_buffer_rect) { DCHECK(frame.content_bounds.empty()); context_provider_->ContextSupport()->PartialSwapBuffers( - *frame.sub_buffer_rect); + *frame.sub_buffer_rect, flags, std::move(swap_callback), + std::move(presentation_callback)); } else if (!frame.content_bounds.empty()) { - context_provider_->ContextSupport()->SwapWithBounds(frame.content_bounds); + context_provider_->ContextSupport()->SwapWithBounds( + frame.content_bounds, flags, std::move(swap_callback), + std::move(presentation_callback)); } else { - context_provider_->ContextSupport()->Swap(); + context_provider_->ContextSupport()->Swap(flags, std::move(swap_callback), + std::move(presentation_callback)); } } @@ -172,15 +171,6 @@ gfx::BufferFormat GpuBrowserCompositorOutputSurface::GetOverlayBufferFormat() return gfx::BufferFormat::RGBX_8888; } -bool GpuBrowserCompositorOutputSurface::SurfaceIsSuspendForRecycle() const { - return false; -} - -#if defined(OS_MACOSX) -void GpuBrowserCompositorOutputSurface::SetSurfaceSuspendedForRecycle( - bool suspended) {} -#endif - void GpuBrowserCompositorOutputSurface::SetDrawRectangle( const gfx::Rect& rect) { if (set_draw_rectangle_for_frame_) @@ -195,10 +185,16 @@ void GpuBrowserCompositorOutputSurface::SetDrawRectangle( } void GpuBrowserCompositorOutputSurface::OnPresentation( - uint64_t swap_id, const gfx::PresentationFeedback& feedback) { DCHECK(client_); - client_->DidReceivePresentationFeedback(swap_id, feedback); + client_->DidReceivePresentationFeedback(feedback); +} + +void GpuBrowserCompositorOutputSurface::OnUpdateVSyncParameters( + base::TimeTicks timebase, + base::TimeDelta interval) { + if (update_vsync_parameters_callback_) + update_vsync_parameters_callback_.Run(timebase, interval); } gpu::CommandBufferProxyImpl* @@ -218,4 +214,8 @@ gpu::VulkanSurface* GpuBrowserCompositorOutputSurface::GetVulkanSurface() { } #endif +unsigned GpuBrowserCompositorOutputSurface::UpdateGpuFence() { + return 0; +} + } // namespace content diff --git a/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h index 95aa6d1983e..1ec2b3e3fdf 100644 --- a/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h +++ b/chromium/content/browser/compositor/gpu_browser_compositor_output_surface.h @@ -13,6 +13,7 @@ #include "content/browser/compositor/gpu_vsync_begin_frame_source.h" #include "gpu/vulkan/buildflags.h" #include "ui/gfx/swap_result.h" +#include "ui/latency/latency_tracker.h" namespace viz { class CompositorOverlayCandidateValidator; @@ -37,10 +38,8 @@ class ReflectorTexture; // Adapts a WebGraphicsContext3DCommandBufferImpl into a // viz::OutputSurface that also handles vsync parameter updates // arriving from the GPU process. -class GpuBrowserCompositorOutputSurface - : public BrowserCompositorOutputSurface, - public GpuVSyncControl, - public viz::OutputSurface::LatencyInfoCache::Client { +class GpuBrowserCompositorOutputSurface : public BrowserCompositorOutputSurface, + public GpuVSyncControl { public: GpuBrowserCompositorOutputSurface( scoped_refptr context, @@ -52,13 +51,11 @@ class GpuBrowserCompositorOutputSurface // Called when a swap completion is sent from the GPU process. virtual void OnGpuSwapBuffersCompleted( + std::vector latency_info, const gpu::SwapBuffersCompleteParams& params); // BrowserCompositorOutputSurface implementation. void OnReflectorChanged() override; -#if defined(OS_MACOSX) - void SetSurfaceSuspendedForRecycle(bool suspended) override; -#endif // viz::OutputSurface implementation. void BindToClient(viz::OutputSurfaceClient* client) override; @@ -75,8 +72,8 @@ class GpuBrowserCompositorOutputSurface bool IsDisplayedAsOverlayPlane() const override; unsigned GetOverlayTextureId() const override; gfx::BufferFormat GetOverlayBufferFormat() const override; + unsigned UpdateGpuFence() override; - bool SurfaceIsSuspendForRecycle() const override; void SetDrawRectangle(const gfx::Rect& rect) override; // GpuVSyncControl implementation. @@ -85,13 +82,10 @@ class GpuBrowserCompositorOutputSurface gpu::VulkanSurface* GetVulkanSurface() override; #endif - // OutputSurface::LatencyInfoCache::Client implementation. - void LatencyInfoCompleted( - const std::vector& latency_info) override; - protected: - void OnPresentation(uint64_t swap_id, - const gfx::PresentationFeedback& feedback); + void OnPresentation(const gfx::PresentationFeedback& feedback); + void OnUpdateVSyncParameters(base::TimeTicks timebase, + base::TimeDelta interval); gpu::CommandBufferProxyImpl* GetCommandBufferProxy(); viz::OutputSurfaceClient* client_ = nullptr; @@ -101,9 +95,11 @@ class GpuBrowserCompositorOutputSurface // True if the draw rectangle has been set at all since the last resize. bool has_set_draw_rectangle_since_last_resize_ = false; gfx::Size size_; - LatencyInfoCache latency_info_cache_; + ui::LatencyTracker latency_tracker_; private: + base::WeakPtrFactory weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(GpuBrowserCompositorOutputSurface); }; diff --git a/chromium/content/browser/compositor/gpu_output_surface_mac.cc b/chromium/content/browser/compositor/gpu_output_surface_mac.cc index 75429bb7c6d..7c5b22d9df7 100644 --- a/chromium/content/browser/compositor/gpu_output_surface_mac.cc +++ b/chromium/content/browser/compositor/gpu_output_surface_mac.cc @@ -9,13 +9,10 @@ #include "components/viz/service/display_embedder/compositor_overlay_candidate_validator.h" #include "gpu/GLES2/gl2extchromium.h" #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h" -#include "ui/accelerated_widget_mac/ca_layer_frame_sink.h" -#include "ui/compositor/compositor.h" namespace content { GpuOutputSurfaceMac::GpuOutputSurfaceMac( - gfx::AcceleratedWidget widget, scoped_refptr context, gpu::SurfaceHandle surface_handle, const UpdateVSyncParametersCallback& update_vsync_parameters_callback, @@ -30,53 +27,8 @@ GpuOutputSurfaceMac::GpuOutputSurfaceMac( GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, gfx::BufferFormat::RGBA_8888, - gpu_memory_buffer_manager), - widget_(widget) {} + gpu_memory_buffer_manager) {} GpuOutputSurfaceMac::~GpuOutputSurfaceMac() {} -void GpuOutputSurfaceMac::SwapBuffers(viz::OutputSurfaceFrame frame) { - GpuSurfacelessBrowserCompositorOutputSurface::SwapBuffers(std::move(frame)); - if (should_show_frames_state_ == - SHOULD_NOT_SHOW_FRAMES_NO_SWAP_AFTER_SUSPENDED) { - should_show_frames_state_ = SHOULD_SHOW_FRAMES; - ui::CALayerFrameSink* ca_layer_frame_sink = - ui::CALayerFrameSink::FromAcceleratedWidget(widget_); - if (ca_layer_frame_sink) - ca_layer_frame_sink->SetSuspended(false); - } -} - -void GpuOutputSurfaceMac::SetSurfaceSuspendedForRecycle(bool suspended) { - if (suspended) { - // It may be that there are frames in-flight from the GPU process back to - // the browser. Make sure that these frames are not displayed by ignoring - // them in GpuProcessHostUIShim, until the browser issues a SwapBuffers for - // the new content. - should_show_frames_state_ = SHOULD_NOT_SHOW_FRAMES_SUSPENDED; - ui::CALayerFrameSink* ca_layer_frame_sink = - ui::CALayerFrameSink::FromAcceleratedWidget(widget_); - if (ca_layer_frame_sink) - ca_layer_frame_sink->SetSuspended(true); - } else { - // Discard the backbuffer before drawing the new frame. This is necessary - // only when using a ImageTransportSurfaceFBO with a - // CALayerStorageProvider. Discarding the backbuffer results in the next - // frame using a new CALayer and CAContext, which guarantees that the - // browser will not flash stale content when adding the remote CALayer to - // the NSView hierarchy (it could flash stale content because the system - // window server is not synchronized with any signals we control or - // observe). - if (should_show_frames_state_ == SHOULD_NOT_SHOW_FRAMES_SUSPENDED) { - DiscardBackbuffer(); - should_show_frames_state_ = - SHOULD_NOT_SHOW_FRAMES_NO_SWAP_AFTER_SUSPENDED; - } - } -} - -bool GpuOutputSurfaceMac::SurfaceIsSuspendForRecycle() const { - return should_show_frames_state_ == SHOULD_NOT_SHOW_FRAMES_SUSPENDED; -} - } // namespace content diff --git a/chromium/content/browser/compositor/gpu_output_surface_mac.h b/chromium/content/browser/compositor/gpu_output_surface_mac.h index efe446e7cd1..73ad778a50a 100644 --- a/chromium/content/browser/compositor/gpu_output_surface_mac.h +++ b/chromium/content/browser/compositor/gpu_output_surface_mac.h @@ -15,7 +15,6 @@ class GpuOutputSurfaceMac : public GpuSurfacelessBrowserCompositorOutputSurface { public: GpuOutputSurfaceMac( - gfx::AcceleratedWidget widget, scoped_refptr context, gpu::SurfaceHandle surface_handle, const UpdateVSyncParametersCallback& update_vsync_parameters_callback, @@ -24,29 +23,7 @@ class GpuOutputSurfaceMac gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager); ~GpuOutputSurfaceMac() override; - // viz::OutputSurface implementation. - void SwapBuffers(viz::OutputSurfaceFrame frame) override; - bool SurfaceIsSuspendForRecycle() const override; - - // BrowserCompositorOutputSurface implementation. - void SetSurfaceSuspendedForRecycle(bool suspended) override; - private: - gfx::AcceleratedWidget widget_; - - enum ShouldShowFramesState { - // Frames that come from the GPU process should appear on-screen. - SHOULD_SHOW_FRAMES, - // The compositor has been suspended. Any frames that come from the GPU - // process are for the pre-suspend content and should not be displayed. - SHOULD_NOT_SHOW_FRAMES_SUSPENDED, - // The compositor has been un-suspended, but has not yet issued a swap - // since being un-suspended, so any frames that come from the GPU process - // are for pre-suspend content and should not be displayed. - SHOULD_NOT_SHOW_FRAMES_NO_SWAP_AFTER_SUSPENDED, - }; - ShouldShowFramesState should_show_frames_state_ = SHOULD_SHOW_FRAMES; - DISALLOW_COPY_AND_ASSIGN(GpuOutputSurfaceMac); }; diff --git a/chromium/content/browser/compositor/gpu_process_transport_factory.cc b/chromium/content/browser/compositor/gpu_process_transport_factory.cc index 7b9fc4114c5..e679dbb02a8 100644 --- a/chromium/content/browser/compositor/gpu_process_transport_factory.cc +++ b/chromium/content/browser/compositor/gpu_process_transport_factory.cc @@ -6,6 +6,7 @@ #include #include +#include #include "base/bind.h" #include "base/command_line.h" @@ -25,7 +26,6 @@ #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/frame_sinks/delay_based_time_source.h" #include "components/viz/common/gl_helper.h" -#include "components/viz/common/gpu/vulkan_in_process_context_provider.h" #include "components/viz/common/switches.h" #include "components/viz/host/host_frame_sink_manager.h" #include "components/viz/host/renderer_settings_creation.h" @@ -85,6 +85,7 @@ #if defined(OS_WIN) #include "base/win/windows_version.h" #include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_win.h" +#include "components/viz/service/display_embedder/output_device_backing.h" #include "components/viz/service/display_embedder/software_output_device_win.h" #include "ui/gfx/win/rendering_window_manager.h" #elif defined(USE_OZONE) @@ -112,7 +113,9 @@ #endif #if BUILDFLAG(ENABLE_VULKAN) +#include "components/viz/common/gpu/vulkan_in_process_context_provider.h" #include "content/browser/compositor/vulkan_browser_compositor_output_surface.h" +#include "gpu/vulkan/init/vulkan_factory.cc" #endif using viz::ContextProvider; @@ -187,16 +190,8 @@ GpuProcessTransportFactory::GpuProcessTransportFactory( cc::SetClientNameForMetrics("Browser"); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kDisableGpuVsync)) { - std::string display_vsync_string = - command_line->GetSwitchValueASCII(switches::kDisableGpuVsync); - // See comments in gl_switches about this flag. The browser compositor - // is only unthrottled when "gpu" or no switch value is passed, as it - // is driven directly by the display compositor. - if (display_vsync_string != "beginframe") { - disable_display_vsync_ = true; - } - } + if (command_line->HasSwitch(switches::kDisableFrameRateLimit)) + disable_frame_rate_limit_ = true; if (command_line->HasSwitch(switches::kRunAllCompositorStagesBeforeDraw)) wait_for_all_pipeline_stages_before_draw_ = true; @@ -227,7 +222,8 @@ GpuProcessTransportFactory::~GpuProcessTransportFactory() { std::unique_ptr GpuProcessTransportFactory::CreateSoftwareOutputDevice( - gfx::AcceleratedWidget widget) { + gfx::AcceleratedWidget widget, + scoped_refptr task_runner) { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kHeadless)) return base::WrapUnique(new viz::SoftwareOutputDevice); @@ -241,8 +237,7 @@ GpuProcessTransportFactory::CreateSoftwareOutputDevice( DCHECK_CURRENTLY_ON(BrowserThread::UI); #if defined(OS_WIN) - return std::make_unique(software_backing_.get(), - widget); + return CreateSoftwareOutputDeviceWinBrowser(widget, software_backing_.get()); #elif defined(USE_OZONE) ui::SurfaceFactoryOzone* factory = ui::OzonePlatform::GetInstance()->GetSurfaceFactoryOzone(); @@ -254,7 +249,7 @@ GpuProcessTransportFactory::CreateSoftwareOutputDevice( #elif defined(USE_X11) return std::make_unique(widget); #elif defined(OS_MACOSX) - return std::make_unique(widget); + return std::make_unique(std::move(task_runner)); #else NOTREACHED(); return std::unique_ptr(); @@ -272,13 +267,21 @@ CreateOverlayCandidateValidator( std::unique_ptr validator; #if defined(USE_OZONE) base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kEnableHardwareOverlays)) { - std::string enable_overlay_flag = - command_line->GetSwitchValueASCII(switches::kEnableHardwareOverlays); + + std::string enable_overlay_flag = + command_line->GetSwitchValueASCII(switches::kEnableHardwareOverlays); + + ui::OzonePlatform* ozone_platform = ui::OzonePlatform::GetInstance(); + DCHECK(ozone_platform); + ui::OverlayManagerOzone* overlay_manager = + ozone_platform->GetOverlayManager(); + if (!command_line->HasSwitch(switches::kEnableHardwareOverlays) && + overlay_manager->SupportsOverlays()) { + enable_overlay_flag = "single-fullscreen,single-on-top"; + } + if (!enable_overlay_flag.empty()) { std::unique_ptr overlay_candidates = - ui::OzonePlatform::GetInstance() - ->GetOverlayManager() - ->CreateOverlayCandidates(widget); + ozone_platform->GetOverlayManager()->CreateOverlayCandidates(widget); validator.reset(new viz::CompositorOverlayCandidateValidatorOzone( std::move(overlay_candidates), enable_overlay_flag)); } @@ -319,7 +322,11 @@ void GpuProcessTransportFactory::CreateLayerTreeFrameSink( compositor->widget()); #endif +#if BUILDFLAG(ENABLE_VULKAN) const bool use_vulkan = static_cast(SharedVulkanContextProvider()); +#else + const bool use_vulkan = false; +#endif const bool use_gpu_compositing = !compositor->force_software_compositor() && !is_gpu_compositing_disabled_; if (use_gpu_compositing && !use_vulkan) { @@ -372,11 +379,16 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( compositor->widget()); #endif +#if BUILDFLAG(ENABLE_VULKAN) scoped_refptr vulkan_context_provider = SharedVulkanContextProvider(); + bool use_vulkan = vulkan_context_provider != nullptr; +#else + bool use_vulkan = false; +#endif scoped_refptr context_provider; - if (!use_gpu_compositing || vulkan_context_provider) { + if (!use_gpu_compositing || use_vulkan) { // If not using GL compositing, don't keep the old shared worker context. shared_worker_context_provider_ = nullptr; } else if (!gpu_channel_host) { @@ -390,11 +402,12 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( shared_worker_context_provider_ = nullptr; if (!shared_worker_context_provider_) { - bool need_alpha_channel = false; - bool support_locking = true; - bool support_gles2_interface = false; - bool support_raster_interface = true; - bool support_grcontext = false; + const bool need_alpha_channel = false; + const bool support_locking = true; + const bool support_gles2_interface = + features::IsUiGpuRasterizationEnabled(); + const bool support_raster_interface = true; + const bool support_grcontext = features::IsUiGpuRasterizationEnabled(); shared_worker_context_provider_ = CreateContextCommon( gpu_channel_host, gpu::kNullSurfaceHandle, need_alpha_channel, false /* support_stencil */, support_locking, support_gles2_interface, @@ -417,11 +430,11 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( // front buffer into a mailbox, insert a sync token, and send the // mailbox+sync to the ui service process. gpu::SurfaceHandle surface_handle = data->surface_handle; - bool need_alpha_channel = false; - bool support_locking = false; - bool support_gles2_interface = true; - bool support_raster_interface = false; - bool support_grcontext = true; + const bool need_alpha_channel = false; + const bool support_locking = false; + const bool support_gles2_interface = true; + const bool support_raster_interface = false; + const bool support_grcontext = true; context_provider = CreateContextCommon( std::move(gpu_channel_host), surface_handle, need_alpha_channel, support_stencil, support_locking, support_gles2_interface, @@ -440,8 +453,7 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( } bool gpu_compositing_ready = - vulkan_context_provider || - (context_provider && shared_worker_context_provider_); + use_vulkan || (context_provider && shared_worker_context_provider_); UMA_HISTOGRAM_BOOLEAN("Aura.CreatedGpuBrowserCompositor", gpu_compositing_ready); if (!gpu_compositing_ready) { @@ -497,8 +509,9 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( } display_output_surface = std::make_unique( - CreateSoftwareOutputDevice(compositor->widget()), - std::move(vsync_callback), compositor->task_runner()); + CreateSoftwareOutputDevice(compositor->widget(), + compositor->task_runner()), + std::move(vsync_callback)); } else { DCHECK(context_provider); const auto& capabilities = context_provider->ContextCapabilities(); @@ -513,8 +526,7 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( bool disable_overlay_ca_layers = gpu_feature_info.IsWorkaroundEnabled( gpu::DISABLE_OVERLAY_CA_LAYERS); display_output_surface = std::make_unique( - compositor->widget(), context_provider, data->surface_handle, - vsync_callback, + context_provider, data->surface_handle, vsync_callback, CreateOverlayCandidateValidator(compositor->widget(), disable_overlay_ca_layers), GetGpuMemoryBufferManager()); @@ -575,7 +587,13 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( std::move(request), external_begin_frame_controller_client->GetBoundPtr()); begin_frame_source = external_begin_frame_controller->begin_frame_source(); - } else if (!disable_display_vsync_) { + } else if (disable_frame_rate_limit_) { + synthetic_begin_frame_source = + std::make_unique( + std::make_unique( + compositor->task_runner().get())); + begin_frame_source = synthetic_begin_frame_source.get(); + } else { if (gpu_vsync_control && IsGpuVSyncSignalSupported()) { gpu_vsync_begin_frame_source = std::make_unique(gpu_vsync_control); @@ -588,12 +606,6 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( viz::BeginFrameSource::kNotRestartableId); begin_frame_source = synthetic_begin_frame_source.get(); } - } else { - synthetic_begin_frame_source = - std::make_unique( - std::make_unique( - compositor->task_runner().get())); - begin_frame_source = synthetic_begin_frame_source.get(); } #if defined(OS_WIN) @@ -645,7 +657,6 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( GetFrameSinkManager(), data->display.get(), data->display_client.get(), context_provider, shared_worker_context_provider_, compositor->task_runner(), GetGpuMemoryBufferManager(), - viz::ServerSharedBitmapManager::current(), features::IsVizHitTestingEnabled()); data->display->Resize(compositor->size()); data->display->SetOutputIsSecure(data->output_is_secure); @@ -654,6 +665,8 @@ void GpuProcessTransportFactory::EstablishedGpuChannel( void GpuProcessTransportFactory::DisableGpuCompositing( ui::Compositor* guilty_compositor) { + DLOG(ERROR) << "Switching to software compositing."; + // Change the result of IsGpuCompositingDisabled() before notifying anything. is_gpu_compositing_disabled_ = true; @@ -933,20 +946,6 @@ viz::GLHelper* GpuProcessTransportFactory::GetGLHelper() { return gl_helper_.get(); } -#if defined(OS_MACOSX) -void GpuProcessTransportFactory::SetCompositorSuspendedForRecycle( - ui::Compositor* compositor, - bool suspended) { - PerCompositorDataMap::iterator it = per_compositor_data_.find(compositor); - if (it == per_compositor_data_.end()) - return; - PerCompositorData* data = it->second.get(); - DCHECK(data); - if (data->display_output_surface) - data->display_output_surface->SetSurfaceSuspendedForRecycle(suspended); -} -#endif - scoped_refptr GpuProcessTransportFactory::SharedMainThreadContextProvider() { if (is_gpu_compositing_disabled_) @@ -1036,19 +1035,28 @@ void GpuProcessTransportFactory::OnLostMainThreadSharedContext() { lost_shared_main_thread_contexts = nullptr; } +#if BUILDFLAG(ENABLE_VULKAN) scoped_refptr GpuProcessTransportFactory::SharedVulkanContextProvider() { if (!shared_vulkan_context_provider_initialized_) { if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableVulkan)) { - shared_vulkan_context_provider_ = - viz::VulkanInProcessContextProvider::Create(); + vulkan_implementation_ = gpu::CreateVulkanImplementation(); + if (vulkan_implementation_ && + vulkan_implementation_->InitializeVulkanInstance()) { + shared_vulkan_context_provider_ = + viz::VulkanInProcessContextProvider::Create( + vulkan_implementation_.get()); + } else { + vulkan_implementation_.reset(); + } } shared_vulkan_context_provider_initialized_ = true; } return shared_vulkan_context_provider_; } +#endif void GpuProcessTransportFactory::OnContextLost() { base::ThreadTaskRunnerHandle::Get()->PostTask( @@ -1107,8 +1115,7 @@ GpuProcessTransportFactory::CreateContextCommon( return base::MakeRefCounted( std::move(gpu_channel_host), GetGpuMemoryBufferManager(), stream_id, stream_priority, surface_handle, url, automatic_flushes, support_locking, - support_grcontext, gpu::SharedMemoryLimits(), attributes, - nullptr /* share_context */, type); + support_grcontext, gpu::SharedMemoryLimits(), attributes, type); } } // namespace content diff --git a/chromium/content/browser/compositor/gpu_process_transport_factory.h b/chromium/content/browser/compositor/gpu_process_transport_factory.h index bc7e7750003..02c1ec0d59b 100644 --- a/chromium/content/browser/compositor/gpu_process_transport_factory.h +++ b/chromium/content/browser/compositor/gpu_process_transport_factory.h @@ -22,6 +22,7 @@ #include "components/viz/host/host_frame_sink_manager.h" #include "content/browser/compositor/image_transport_factory.h" #include "gpu/ipc/client/gpu_channel_host.h" +#include "gpu/vulkan/buildflags.h" #include "services/ui/public/cpp/gpu/command_buffer_metrics.h" #include "ui/compositor/compositor.h" @@ -36,6 +37,7 @@ class SurfaceManager; namespace gpu { class GpuChannelEstablishFactory; +class VulkanImplementation; } namespace ui { @@ -105,17 +107,14 @@ class GpuProcessTransportFactory : public ui::ContextFactory, ui::ContextFactoryPrivate* GetContextFactoryPrivate() override; viz::FrameSinkManagerImpl* GetFrameSinkManager() override; viz::GLHelper* GetGLHelper() override; -#if defined(OS_MACOSX) - void SetCompositorSuspendedForRecycle(ui::Compositor* compositor, - bool suspended) override; -#endif private: struct PerCompositorData; PerCompositorData* CreatePerCompositorData(ui::Compositor* compositor); std::unique_ptr CreateSoftwareOutputDevice( - gfx::AcceleratedWidget widget); + gfx::AcceleratedWidget widget, + scoped_refptr task_runner); void EstablishedGpuChannel( base::WeakPtr compositor, bool use_gpu_compositing, @@ -125,8 +124,10 @@ class GpuProcessTransportFactory : public ui::ContextFactory, void OnLostMainThreadSharedContext(); +#if BUILDFLAG(ENABLE_VULKAN) scoped_refptr SharedVulkanContextProvider(); +#endif // viz::ContextLostObserver implementation. void OnContextLost() override; @@ -149,6 +150,10 @@ class GpuProcessTransportFactory : public ui::ContextFactory, std::unique_ptr software_backing_; #endif +#if BUILDFLAG(ENABLE_VULKAN) + std::unique_ptr vulkan_implementation_; +#endif + // Depends on SurfaceManager. typedef std::map> PerCompositorDataMap; @@ -163,11 +168,13 @@ class GpuProcessTransportFactory : public ui::ContextFactory, scoped_refptr shared_worker_context_provider_; bool is_gpu_compositing_disabled_ = false; - bool disable_display_vsync_ = false; + bool disable_frame_rate_limit_ = false; bool wait_for_all_pipeline_stages_before_draw_ = false; +#if BUILDFLAG(ENABLE_VULKAN) bool shared_vulkan_context_provider_initialized_ = false; scoped_refptr shared_vulkan_context_provider_; +#endif gpu::GpuChannelEstablishFactory* const gpu_channel_factory_; // Service-side impl that controls the compositing mode based on what mode the diff --git a/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc b/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc index c1cb69e4b42..c2a3a8d7b5e 100644 --- a/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc +++ b/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.cc @@ -33,6 +33,11 @@ GpuSurfacelessBrowserCompositorOutputSurface:: : GpuBrowserCompositorOutputSurface(std::move(context), update_vsync_parameters_callback, std::move(overlay_candidate_validator)), + use_gpu_fence_( + context_provider_->ContextCapabilities().chromium_gpu_fence && + context_provider_->ContextCapabilities() + .use_gpu_fences_for_overlay_planes), + gpu_fence_id_(0), gpu_memory_buffer_manager_(gpu_memory_buffer_manager) { capabilities_.uses_default_gl_framebuffer = false; capabilities_.flipped_output_surface = true; @@ -55,6 +60,8 @@ GpuSurfacelessBrowserCompositorOutputSurface:: GpuSurfacelessBrowserCompositorOutputSurface:: ~GpuSurfacelessBrowserCompositorOutputSurface() { + if (gpu_fence_id_ > 0) + context_provider_->ContextGL()->DestroyGpuFenceCHROMIUM(gpu_fence_id_); } bool GpuSurfacelessBrowserCompositorOutputSurface::IsDisplayedAsOverlayPlane() @@ -112,6 +119,7 @@ void GpuSurfacelessBrowserCompositorOutputSurface::Reshape( } void GpuSurfacelessBrowserCompositorOutputSurface::OnGpuSwapBuffersCompleted( + std::vector latency_info, const gpu::SwapBuffersCompleteParams& params) { gpu::SwapBuffersCompleteParams modified_params(params); bool force_swap = false; @@ -124,9 +132,22 @@ void GpuSurfacelessBrowserCompositorOutputSurface::OnGpuSwapBuffersCompleted( force_swap = true; } buffer_queue_->PageFlipComplete(); - GpuBrowserCompositorOutputSurface::OnGpuSwapBuffersCompleted(modified_params); + GpuBrowserCompositorOutputSurface::OnGpuSwapBuffersCompleted( + std::move(latency_info), modified_params); if (force_swap) client_->SetNeedsRedrawRect(gfx::Rect(swap_size_)); } +unsigned GpuSurfacelessBrowserCompositorOutputSurface::UpdateGpuFence() { + if (!use_gpu_fence_) + return 0; + + if (gpu_fence_id_ > 0) + context_provider_->ContextGL()->DestroyGpuFenceCHROMIUM(gpu_fence_id_); + + gpu_fence_id_ = context_provider_->ContextGL()->CreateGpuFenceCHROMIUM(); + + return gpu_fence_id_; +} + } // namespace content diff --git a/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h b/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h index 48a7356fc69..439c8553ab1 100644 --- a/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h +++ b/chromium/content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h @@ -48,14 +48,18 @@ class GpuSurfacelessBrowserCompositorOutputSurface bool IsDisplayedAsOverlayPlane() const override; unsigned GetOverlayTextureId() const override; gfx::BufferFormat GetOverlayBufferFormat() const override; + unsigned UpdateGpuFence() override; // BrowserCompositorOutputSurface implementation. void OnGpuSwapBuffersCompleted( + std::vector latency_info, const gpu::SwapBuffersCompleteParams& params) override; private: gfx::Size reshape_size_; gfx::Size swap_size_; + bool use_gpu_fence_; + unsigned gpu_fence_id_; std::unique_ptr gl_helper_; std::unique_ptr buffer_queue_; diff --git a/chromium/content/browser/compositor/image_transport_factory.h b/chromium/content/browser/compositor/image_transport_factory.h index 1ac2c66a4e5..0ccaf358643 100644 --- a/chromium/content/browser/compositor/image_transport_factory.h +++ b/chromium/content/browser/compositor/image_transport_factory.h @@ -11,7 +11,6 @@ #include "content/common/content_export.h" namespace ui { -class Compositor; class ContextFactory; class ContextFactoryPrivate; } @@ -56,17 +55,6 @@ class CONTENT_EXPORT ImageTransportFactory { // GLHelper will get destroyed whenever the shared context is lost // (ImageTransportFactoryObserver::OnLostResources is called). virtual viz::GLHelper* GetGLHelper() = 0; - -#if defined(OS_MACOSX) - // Called with |suspended| as true when the ui::Compositor has been - // disconnected from an NSView and may be attached to another one. Called - // with |suspended| as false after the ui::Compositor has been connected to - // a new NSView and the first commit targeted at the new NSView has - // completed. This ensures that content and frames intended for the old - // NSView will not flash in the new NSView. - virtual void SetCompositorSuspendedForRecycle(ui::Compositor* compositor, - bool suspended) = 0; -#endif }; } // namespace content diff --git a/chromium/content/browser/compositor/in_process_display_client.cc b/chromium/content/browser/compositor/in_process_display_client.cc index 388998baa97..3190c0d90ff 100644 --- a/chromium/content/browser/compositor/in_process_display_client.cc +++ b/chromium/content/browser/compositor/in_process_display_client.cc @@ -4,15 +4,25 @@ #include "content/browser/compositor/in_process_display_client.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" + #if defined(OS_MACOSX) #include "ui/accelerated_widget_mac/ca_layer_frame_sink.h" #endif +#if defined(OS_WIN) +#include + +#include "components/viz/common/display/use_layered_window.h" +#include "components/viz/host/layered_window_updater_impl.h" +#include "ui/base/win/internal_constants.h" +#endif + namespace content { InProcessDisplayClient::InProcessDisplayClient(gfx::AcceleratedWidget widget) : binding_(this) { -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) || defined(OS_WIN) widget_ = widget; #endif } @@ -40,4 +50,24 @@ void InProcessDisplayClient::OnDisplayReceivedCALayerParams( #endif } +void InProcessDisplayClient::DidSwapAfterSnapshotRequestReceived( + const std::vector& latency_info) { + RenderWidgetHostImpl::OnGpuSwapBuffersCompleted(latency_info); +} + +void InProcessDisplayClient::CreateLayeredWindowUpdater( + viz::mojom::LayeredWindowUpdaterRequest request) { +#if defined(OS_WIN) + if (!viz::NeedsToUseLayerWindow(widget_)) { + DLOG(ERROR) << "HWND shouldn't be using a layered window"; + return; + } + + layered_window_updater_ = std::make_unique( + widget_, std::move(request)); +#else +// This should never happen on non-Windows platforms. +#endif +} + } // namespace content diff --git a/chromium/content/browser/compositor/in_process_display_client.h b/chromium/content/browser/compositor/in_process_display_client.h index dd2ef76cddc..baa83594ed5 100644 --- a/chromium/content/browser/compositor/in_process_display_client.h +++ b/chromium/content/browser/compositor/in_process_display_client.h @@ -5,18 +5,26 @@ #ifndef CONTENT_BROWSER_COMPOSITOR_IN_PROCESS_DISPLAY_CLIENT_H_ #define CONTENT_BROWSER_COMPOSITOR_IN_PROCESS_DISPLAY_CLIENT_H_ +#include +#include + +#include "base/single_thread_task_runner.h" #include "build/build_config.h" #include "mojo/public/cpp/bindings/binding.h" #include "services/viz/privileged/interfaces/compositing/display_private.mojom.h" #include "ui/gfx/native_widget_types.h" +namespace viz { +class LayeredWindowUpdaterImpl; +} + namespace content { // A DisplayClient that can be used to display received // gfx::CALayerParams in a CALayer tree in this process. class InProcessDisplayClient : public viz::mojom::DisplayClient { public: - InProcessDisplayClient(gfx::AcceleratedWidget widget); + explicit InProcessDisplayClient(gfx::AcceleratedWidget widget); ~InProcessDisplayClient() override; viz::mojom::DisplayClientPtr GetBoundPtr( @@ -26,11 +34,19 @@ class InProcessDisplayClient : public viz::mojom::DisplayClient { // viz::mojom::DisplayClient implementation: void OnDisplayReceivedCALayerParams( const gfx::CALayerParams& ca_layer_params) override; + void DidSwapAfterSnapshotRequestReceived( + const std::vector& latency_info) override; + void CreateLayeredWindowUpdater( + viz::mojom::LayeredWindowUpdaterRequest request) override; mojo::Binding binding_; -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) || defined(OS_WIN) gfx::AcceleratedWidget widget_; #endif + +#if defined(OS_WIN) + std::unique_ptr layered_window_updater_; +#endif }; } // namespace content diff --git a/chromium/content/browser/compositor/offscreen_browser_compositor_output_surface.cc b/chromium/content/browser/compositor/offscreen_browser_compositor_output_surface.cc index 05f9a5f3149..2792d4646f5 100644 --- a/chromium/content/browser/compositor/offscreen_browser_compositor_output_surface.cc +++ b/chromium/content/browser/compositor/offscreen_browser_compositor_output_surface.cc @@ -8,7 +8,6 @@ #include "base/logging.h" #include "build/build_config.h" -#include "cc/resources/resource_provider.h" #include "components/viz/common/resources/resource_format_utils.h" #include "components/viz/service/display/output_surface_client.h" #include "components/viz/service/display/output_surface_frame.h" @@ -161,7 +160,8 @@ void OffscreenBrowserCompositorOutputSurface::SwapBuffers( sync_token, base::BindOnce( &OffscreenBrowserCompositorOutputSurface::OnSwapBuffersComplete, - weak_ptr_factory_.GetWeakPtr(), frame.latency_info, ++swap_id_)); + weak_ptr_factory_.GetWeakPtr(), frame.latency_info, + frame.need_presentation_feedback)); } bool OffscreenBrowserCompositorOutputSurface::IsDisplayedAsOverlayPlane() @@ -178,11 +178,6 @@ OffscreenBrowserCompositorOutputSurface::GetOverlayBufferFormat() const { return gfx::BufferFormat::RGBX_8888; } -bool OffscreenBrowserCompositorOutputSurface::SurfaceIsSuspendForRecycle() - const { - return false; -} - GLenum OffscreenBrowserCompositorOutputSurface::GetFramebufferCopyTextureFormat() { return GLCopyTextureInternalFormat(kFboTextureFormat); @@ -197,10 +192,12 @@ void OffscreenBrowserCompositorOutputSurface::OnReflectorChanged() { void OffscreenBrowserCompositorOutputSurface::OnSwapBuffersComplete( const std::vector& latency_info, - uint64_t swap_id) { + bool need_presentation_feedback) { RenderWidgetHostImpl::OnGpuSwapBuffersCompleted(latency_info); - client_->DidReceiveSwapBuffersAck(swap_id); - client_->DidReceivePresentationFeedback(swap_id, gfx::PresentationFeedback()); + latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); + client_->DidReceiveSwapBuffersAck(); + if (need_presentation_feedback) + client_->DidReceivePresentationFeedback(gfx::PresentationFeedback()); } #if BUILDFLAG(ENABLE_VULKAN) @@ -211,4 +208,8 @@ OffscreenBrowserCompositorOutputSurface::GetVulkanSurface() { } #endif +unsigned OffscreenBrowserCompositorOutputSurface::UpdateGpuFence() { + return 0; +} + } // namespace content diff --git a/chromium/content/browser/compositor/offscreen_browser_compositor_output_surface.h b/chromium/content/browser/compositor/offscreen_browser_compositor_output_surface.h index a1f4e6df47f..f123692aebb 100644 --- a/chromium/content/browser/compositor/offscreen_browser_compositor_output_surface.h +++ b/chromium/content/browser/compositor/offscreen_browser_compositor_output_surface.h @@ -16,6 +16,7 @@ #include "content/browser/compositor/browser_compositor_output_surface.h" #include "gpu/vulkan/buildflags.h" #include "ui/latency/latency_info.h" +#include "ui/latency/latency_tracker.h" namespace ui { class ContextProviderCommandBuffer; @@ -51,28 +52,26 @@ class OffscreenBrowserCompositorOutputSurface bool IsDisplayedAsOverlayPlane() const override; unsigned GetOverlayTextureId() const override; gfx::BufferFormat GetOverlayBufferFormat() const override; - bool SurfaceIsSuspendForRecycle() const override; uint32_t GetFramebufferCopyTextureFormat() override; // BrowserCompositorOutputSurface implementation. void OnReflectorChanged() override; -#if defined(OS_MACOSX) - void SetSurfaceSuspendedForRecycle(bool suspended) override {} -#endif #if BUILDFLAG(ENABLE_VULKAN) gpu::VulkanSurface* GetVulkanSurface() override; #endif + unsigned UpdateGpuFence() override; + void OnSwapBuffersComplete(const std::vector& latency_info, - uint64_t swap_id); + bool need_presentation_feedback); viz::OutputSurfaceClient* client_ = nullptr; gfx::Size reshape_size_; uint32_t fbo_ = 0; bool reflector_changed_ = false; std::unique_ptr reflector_texture_; - uint64_t swap_id_ = 0; + ui::LatencyTracker latency_tracker_; base::WeakPtrFactory weak_ptr_factory_; diff --git a/chromium/content/browser/compositor/reflector_impl_unittest.cc b/chromium/content/browser/compositor/reflector_impl_unittest.cc index 3d36fdbb7be..feb02e87cfb 100644 --- a/chromium/content/browser/compositor/reflector_impl_unittest.cc +++ b/chromium/content/browser/compositor/reflector_impl_unittest.cc @@ -24,7 +24,7 @@ #include "ui/compositor/test/context_factories_for_test.h" #if defined(USE_OZONE) -#include "cc/output/overlay_candidate.h" +#include "components/viz/service/display/overlay_candidate.h" #include "components/viz/service/display_embedder/compositor_overlay_candidate_validator_ozone.h" #include "ui/ozone/public/overlay_candidates_ozone.h" #endif // defined(USE_OZONE) @@ -102,7 +102,6 @@ class TestOutputSurface : public BrowserCompositorOutputSurface { gfx::BufferFormat GetOverlayBufferFormat() const override { return gfx::BufferFormat::RGBX_8888; } - bool SurfaceIsSuspendForRecycle() const override { return false; } void OnReflectorChanged() override { if (!reflector_) { @@ -113,13 +112,10 @@ class TestOutputSurface : public BrowserCompositorOutputSurface { } } -#if defined(OS_MACOSX) - void SetSurfaceSuspendedForRecycle(bool suspended) override {} -#endif - #if BUILDFLAG(ENABLE_VULKAN) gpu::VulkanSurface* GetVulkanSurface() override { return nullptr; } #endif + unsigned UpdateGpuFence() override { return 0; } private: std::unique_ptr reflector_texture_; @@ -226,8 +222,8 @@ TEST_F(ReflectorImplTest, CheckInvertedOutputSurface) { #if defined(USE_OZONE) TEST_F(ReflectorImplTest, CheckOverlayNoReflector) { - cc::OverlayCandidateList list; - cc::OverlayCandidate plane_1, plane_2; + viz::OverlayCandidateList list; + viz::OverlayCandidate plane_1, plane_2; plane_1.plane_z_order = 0; plane_2.plane_z_order = 1; list.push_back(plane_1); @@ -238,8 +234,8 @@ TEST_F(ReflectorImplTest, CheckOverlayNoReflector) { TEST_F(ReflectorImplTest, CheckOverlaySWMirroring) { SetUpReflector(); - cc::OverlayCandidateList list; - cc::OverlayCandidate plane_1, plane_2; + viz::OverlayCandidateList list; + viz::OverlayCandidate plane_1, plane_2; plane_1.plane_z_order = 0; plane_2.plane_z_order = 1; list.push_back(plane_1); diff --git a/chromium/content/browser/compositor/software_browser_compositor_output_surface.cc b/chromium/content/browser/compositor/software_browser_compositor_output_surface.cc index d402e04aeea..b5b49363709 100644 --- a/chromium/content/browser/compositor/software_browser_compositor_output_surface.cc +++ b/chromium/content/browser/compositor/software_browser_compositor_output_surface.cc @@ -9,7 +9,6 @@ #include "base/bind.h" #include "base/location.h" #include "base/memory/ref_counted.h" -#include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" #include "components/viz/service/display/output_surface_client.h" @@ -23,11 +22,9 @@ namespace content { SoftwareBrowserCompositorOutputSurface::SoftwareBrowserCompositorOutputSurface( std::unique_ptr software_device, - const UpdateVSyncParametersCallback& update_vsync_parameters_callback, - scoped_refptr task_runner) + const UpdateVSyncParametersCallback& update_vsync_parameters_callback) : BrowserCompositorOutputSurface(std::move(software_device), update_vsync_parameters_callback), - task_runner_(std::move(task_runner)), weak_factory_(this) {} SoftwareBrowserCompositorOutputSurface:: @@ -74,15 +71,11 @@ void SoftwareBrowserCompositorOutputSurface::SwapBuffers( base::TimeTicks swap_time = base::TimeTicks::Now(); for (auto& latency : frame.latency_info) { latency.AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, 0, 0, swap_time, 1); + ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, 0, swap_time, 1); latency.AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0, - swap_time, 1); + ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, swap_time, + 1); } - task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&RenderWidgetHostImpl::OnGpuSwapBuffersCompleted, - frame.latency_info)); gfx::VSyncProvider* vsync_provider = software_device()->GetVSyncProvider(); if (vsync_provider) { @@ -91,20 +84,22 @@ void SoftwareBrowserCompositorOutputSurface::SwapBuffers( weak_factory_.GetWeakPtr())); } - ++swap_id_; - task_runner_->PostTask( - FROM_HERE, - base::BindOnce( - &SoftwareBrowserCompositorOutputSurface::SwapBuffersCallback, - weak_factory_.GetWeakPtr(), swap_id_)); + software_device()->OnSwapBuffers(base::BindOnce( + &SoftwareBrowserCompositorOutputSurface::SwapBuffersCallback, + weak_factory_.GetWeakPtr(), frame.latency_info, + frame.need_presentation_feedback)); } void SoftwareBrowserCompositorOutputSurface::SwapBuffersCallback( - uint64_t swap_id) { - client_->DidReceiveSwapBuffersAck(swap_id); - client_->DidReceivePresentationFeedback( - swap_id, - gfx::PresentationFeedback(base::TimeTicks::Now(), refresh_interval_, 0u)); + const std::vector& latency_info, + bool need_presentation_feedback) { + RenderWidgetHostImpl::OnGpuSwapBuffersCompleted(latency_info); + latency_tracker_.OnGpuSwapBuffersCompleted(latency_info); + client_->DidReceiveSwapBuffersAck(); + if (need_presentation_feedback) { + client_->DidReceivePresentationFeedback(gfx::PresentationFeedback( + base::TimeTicks::Now(), refresh_interval_, 0u)); + } } void SoftwareBrowserCompositorOutputSurface::UpdateVSyncCallback( @@ -127,11 +122,6 @@ SoftwareBrowserCompositorOutputSurface::GetOverlayBufferFormat() const { return gfx::BufferFormat::RGBX_8888; } -bool SoftwareBrowserCompositorOutputSurface::SurfaceIsSuspendForRecycle() - const { - return false; -} - uint32_t SoftwareBrowserCompositorOutputSurface::GetFramebufferCopyTextureFormat() { // Not used for software surfaces. @@ -139,12 +129,6 @@ SoftwareBrowserCompositorOutputSurface::GetFramebufferCopyTextureFormat() { return 0; } -#if defined(OS_MACOSX) -void SoftwareBrowserCompositorOutputSurface::SetSurfaceSuspendedForRecycle( - bool suspended) { -} -#endif - #if BUILDFLAG(ENABLE_VULKAN) gpu::VulkanSurface* SoftwareBrowserCompositorOutputSurface::GetVulkanSurface() { NOTIMPLEMENTED(); @@ -152,4 +136,8 @@ gpu::VulkanSurface* SoftwareBrowserCompositorOutputSurface::GetVulkanSurface() { } #endif +unsigned SoftwareBrowserCompositorOutputSurface::UpdateGpuFence() { + return 0; +} + } // namespace content diff --git a/chromium/content/browser/compositor/software_browser_compositor_output_surface.h b/chromium/content/browser/compositor/software_browser_compositor_output_surface.h index 79bc9f5f6b8..80e0836a9d6 100644 --- a/chromium/content/browser/compositor/software_browser_compositor_output_surface.h +++ b/chromium/content/browser/compositor/software_browser_compositor_output_surface.h @@ -7,15 +7,11 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "base/single_thread_task_runner.h" #include "build/build_config.h" #include "content/browser/compositor/browser_compositor_output_surface.h" #include "content/common/content_export.h" #include "gpu/vulkan/buildflags.h" - -namespace cc { -class SoftwareOutputDevice; -} +#include "ui/latency/latency_tracker.h" namespace content { @@ -24,8 +20,7 @@ class CONTENT_EXPORT SoftwareBrowserCompositorOutputSurface public: SoftwareBrowserCompositorOutputSurface( std::unique_ptr software_device, - const UpdateVSyncParametersCallback& update_vsync_parameters_callback, - scoped_refptr task_runner); + const UpdateVSyncParametersCallback& update_vsync_parameters_callback); ~SoftwareBrowserCompositorOutputSurface() override; @@ -44,26 +39,21 @@ class CONTENT_EXPORT SoftwareBrowserCompositorOutputSurface bool IsDisplayedAsOverlayPlane() const override; unsigned GetOverlayTextureId() const override; gfx::BufferFormat GetOverlayBufferFormat() const override; - bool SurfaceIsSuspendForRecycle() const override; uint32_t GetFramebufferCopyTextureFormat() override; #if BUILDFLAG(ENABLE_VULKAN) gpu::VulkanSurface* GetVulkanSurface() override; #endif + unsigned UpdateGpuFence() override; private: - // BrowserCompositorOutputSurface implementation. -#if defined(OS_MACOSX) - void SetSurfaceSuspendedForRecycle(bool suspended) override; -#endif - - void SwapBuffersCallback(uint64_t swap_id); + void SwapBuffersCallback(const std::vector& latency_info, + bool need_presentation_feedback); void UpdateVSyncCallback(const base::TimeTicks timebase, const base::TimeDelta interval); viz::OutputSurfaceClient* client_ = nullptr; - scoped_refptr task_runner_; - uint64_t swap_id_ = 0; base::TimeDelta refresh_interval_; + ui::LatencyTracker latency_tracker_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(SoftwareBrowserCompositorOutputSurface); diff --git a/chromium/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc b/chromium/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc index 103c816895e..60ece2f2458 100644 --- a/chromium/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc +++ b/chromium/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc @@ -130,8 +130,7 @@ SoftwareBrowserCompositorOutputSurfaceTest::CreateSurface( std::move(device), base::Bind( &SoftwareBrowserCompositorOutputSurfaceTest::UpdateVSyncParameters, - base::Unretained(this)), - base::ThreadTaskRunnerHandle::Get()); + base::Unretained(this))); } void SoftwareBrowserCompositorOutputSurfaceTest::UpdateVSyncParameters( diff --git a/chromium/content/browser/compositor/viz_process_transport_factory.cc b/chromium/content/browser/compositor/viz_process_transport_factory.cc index a2b4b4095c4..7f02b82c3a4 100644 --- a/chromium/content/browser/compositor/viz_process_transport_factory.cc +++ b/chromium/content/browser/compositor/viz_process_transport_factory.cc @@ -5,6 +5,7 @@ #include "content/browser/compositor/viz_process_transport_factory.h" #include +#include #include "base/command_line.h" #include "base/debug/dump_without_crashing.h" @@ -30,9 +31,9 @@ #include "content/public/common/content_switches.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/raster_interface.h" -#include "gpu/command_buffer/common/context_result.h" #include "gpu/ipc/client/gpu_channel_host.h" #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h" +#include "ui/base/ui_base_features.h" #include "ui/compositor/reflector.h" #if defined(OS_WIN) @@ -73,7 +74,7 @@ scoped_refptr CreateContextProviderImpl( std::move(gpu_channel_host), gpu_memory_buffer_manager, kGpuStreamIdDefault, kGpuStreamPriorityUI, gpu::kNullSurfaceHandle, std::move(url), kAutomaticFlushes, support_locking, support_grcontext, - gpu::SharedMemoryLimits(), attributes, nullptr /* share_context */, type); + gpu::SharedMemoryLimits(), attributes, type); } bool IsContextLost(viz::ContextProvider* context_provider) { @@ -128,7 +129,7 @@ VizProcessTransportFactory::VizProcessTransportFactory( base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kDisableGpu) || command_line->HasSwitch(switches::kDisableGpuCompositing)) { - CompositingModeFallbackToSoftware(); + DisableGpuCompositing(nullptr); } } @@ -195,8 +196,15 @@ VizProcessTransportFactory::SharedMainThreadContextProvider() { return nullptr; if (!main_context_provider_) { - CreateContextProviders( - gpu_channel_establish_factory_->EstablishGpuChannelSync()); + auto context_result = gpu::ContextResult::kTransientFailure; + while (context_result == gpu::ContextResult::kTransientFailure) { + context_result = TryCreateContextsForGpuCompositing( + gpu_channel_establish_factory_->EstablishGpuChannelSync()); + + if (context_result == gpu::ContextResult::kFatalFailure) + DisableGpuCompositing(nullptr); + } + // On kFatalFailure |main_context_provider_| will be null. } return main_context_provider_; @@ -359,19 +367,16 @@ viz::GLHelper* VizProcessTransportFactory::GetGLHelper() { return nullptr; } -#if defined(OS_MACOSX) -void VizProcessTransportFactory::SetCompositorSuspendedForRecycle( - ui::Compositor* compositor, - bool suspended) { - NOTIMPLEMENTED(); +void VizProcessTransportFactory::OnContextLost() { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&VizProcessTransportFactory::OnLostMainThreadSharedContext, + weak_ptr_factory_.GetWeakPtr())); } -#endif -void VizProcessTransportFactory::CompositingModeFallbackToSoftware() { - // This may happen multiple times, since when the viz process (re)starts, it - // will send this notification if gpu is disabled. - if (is_gpu_compositing_disabled_) - return; +void VizProcessTransportFactory::DisableGpuCompositing( + ui::Compositor* guilty_compositor) { + DLOG(ERROR) << "Switching to software compositing."; // Change the result of IsGpuCompositingDisabled() before notifying anything. is_gpu_compositing_disabled_ = true; @@ -401,8 +406,15 @@ void VizProcessTransportFactory::CompositingModeFallbackToSoftware() { to_release.reserve(compositor_data_map_.size()); for (auto& pair : compositor_data_map_) { ui::Compositor* compositor = pair.first; - if (!compositor->force_software_compositor()) + // The |guilty_compositor| is in the process of setting up its FrameSink + // so removing it from |compositor_data_map_| would be both pointless and + // the cause of a crash. + // Compositors with force_software_compositor() do not follow the global + // compositing mode, so they do not need to changed. + if (compositor != guilty_compositor && + !compositor->force_software_compositor()) { to_release.push_back(compositor); + } } for (ui::Compositor* compositor : to_release) { // Compositor expects to be not visible when releasing its FrameSink. @@ -417,13 +429,6 @@ void VizProcessTransportFactory::CompositingModeFallbackToSoftware() { GpuDataManagerImpl::GetInstance()->NotifyGpuInfoUpdate(); } -void VizProcessTransportFactory::OnContextLost() { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&VizProcessTransportFactory::OnLostMainThreadSharedContext, - weak_ptr_factory_.GetWeakPtr())); -} - void VizProcessTransportFactory::OnGpuProcessLost() { // Reconnect HostFrameSinkManager to new GPU process. ConnectHostFrameSinkManager(); @@ -439,18 +444,18 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel( bool gpu_compositing = !is_gpu_compositing_disabled_ && !compositor->force_software_compositor(); - // Only try to make contexts for gpu compositing. if (gpu_compositing) { - // TODO(kylechar): Check GPU compositing status from GpuFeatureInfo. - - if (!gpu_channel_host || - !CreateContextProviders(std::move(gpu_channel_host))) { - // Retry on failure. If this isn't possible we should hear that we're - // falling back to software compositing from the viz process eventually. + auto context_result = + TryCreateContextsForGpuCompositing(std::move(gpu_channel_host)); + if (context_result == gpu::ContextResult::kTransientFailure) { + // Get a new GpuChannelHost and retry context creation. gpu_channel_establish_factory_->EstablishGpuChannel( base::BindOnce(&VizProcessTransportFactory::OnEstablishedGpuChannel, weak_ptr_factory_.GetWeakPtr(), compositor_weak_ptr)); return; + } else if (context_result == gpu::ContextResult::kFatalFailure) { + DisableGpuCompositing(compositor); + gpu_compositing = false; } } @@ -511,11 +516,6 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel( viz::ClientLayerTreeFrameSink::InitParams params; params.compositor_task_runner = compositor->task_runner(); params.gpu_memory_buffer_manager = GetGpuMemoryBufferManager(); - // TODO(crbug.com/730660): Adding ServerSharedBitmapManager here is just to - // stop software compositing from crashing the browser process. Software - // composited areas will be blank since the ShareBitmaps aren't shared with - // the viz process. This mechanism is being rewritten currently. - params.shared_bitmap_manager = viz::ServerSharedBitmapManager::current(); params.pipes.compositor_frame_sink_associated_info = std::move(sink_info); params.pipes.client_request = std::move(client_request); params.local_surface_id_provider = @@ -542,27 +542,34 @@ void VizProcessTransportFactory::OnEstablishedGpuChannel( #endif } -bool VizProcessTransportFactory::CreateContextProviders( +gpu::ContextResult +VizProcessTransportFactory::TryCreateContextsForGpuCompositing( scoped_refptr gpu_channel_host) { - constexpr bool kSharedWorkerContextSupportsLocking = true; - constexpr bool kSharedWorkerContextSupportsGLES2 = false; - constexpr bool kSharedWorkerContextSupportsRaster = true; - constexpr bool kSharedWorkerContextSupportsGrContext = false; - constexpr bool kCompositorContextSupportsLocking = false; - constexpr bool kCompositorContextSupportsGLES2 = true; - constexpr bool kCompositorContextSupportsRaster = false; - constexpr bool kCompositorContextSupportsGrContext = true; + DCHECK(!is_gpu_compositing_disabled_); - if (main_context_provider_ && IsContextLost(main_context_provider_.get())) { - main_context_provider_->RemoveObserver(this); - main_context_provider_ = nullptr; - } + // Fallback to software compositing if there is no IPC channel. + if (!gpu_channel_host) + return gpu::ContextResult::kFatalFailure; + + // Fallback to software compositing if GPU compositing is blacklisted. + auto gpu_compositing_status = + gpu_channel_host->gpu_feature_info() + .status_values[gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING]; + if (gpu_compositing_status != gpu::kGpuFeatureStatusEnabled) + return gpu::ContextResult::kFatalFailure; if (worker_context_provider_ && IsWorkerContextLost(worker_context_provider_.get())) worker_context_provider_ = nullptr; if (!worker_context_provider_) { + constexpr bool kSharedWorkerContextSupportsLocking = true; + constexpr bool kSharedWorkerContextSupportsRaster = true; + const bool kSharedWorkerContextSupportsGLES2 = + features::IsUiGpuRasterizationEnabled(); + const bool kSharedWorkerContextSupportsGrContext = + features::IsUiGpuRasterizationEnabled(); + worker_context_provider_ = CreateContextProviderImpl( gpu_channel_host, GetGpuMemoryBufferManager(), kSharedWorkerContextSupportsLocking, kSharedWorkerContextSupportsGLES2, @@ -573,32 +580,42 @@ bool VizProcessTransportFactory::CreateContextProviders( // Don't observer context loss on |worker_context_provider_| here, that is // already observered by LayerTreeFrameSink. The lost context will be caught // when recreating LayerTreeFrameSink(s). - auto result = worker_context_provider_->BindToCurrentThread(); - if (result != gpu::ContextResult::kSuccess) { + auto context_result = worker_context_provider_->BindToCurrentThread(); + if (context_result != gpu::ContextResult::kSuccess) { worker_context_provider_ = nullptr; - return false; + return context_result; } } + if (main_context_provider_ && IsContextLost(main_context_provider_.get())) { + main_context_provider_->RemoveObserver(this); + main_context_provider_ = nullptr; + } + if (!main_context_provider_) { + constexpr bool kCompositorContextSupportsLocking = false; + constexpr bool kCompositorContextSupportsGLES2 = true; + constexpr bool kCompositorContextSupportsRaster = false; + constexpr bool kCompositorContextSupportsGrContext = true; + main_context_provider_ = CreateContextProviderImpl( std::move(gpu_channel_host), GetGpuMemoryBufferManager(), kCompositorContextSupportsLocking, kCompositorContextSupportsGLES2, kCompositorContextSupportsRaster, kCompositorContextSupportsGrContext, ui::command_buffer_metrics::UI_COMPOSITOR_CONTEXT); main_context_provider_->SetDefaultTaskRunner(resize_task_runner_); - main_context_provider_->AddObserver(this); - auto result = main_context_provider_->BindToCurrentThread(); - if (result != gpu::ContextResult::kSuccess) { - main_context_provider_->RemoveObserver(this); - main_context_provider_ = nullptr; + auto context_result = main_context_provider_->BindToCurrentThread(); + if (context_result != gpu::ContextResult::kSuccess) { worker_context_provider_ = nullptr; - return false; + main_context_provider_ = nullptr; + return context_result; } + + main_context_provider_->AddObserver(this); } - return true; + return gpu::ContextResult::kSuccess; } void VizProcessTransportFactory::OnLostMainThreadSharedContext() { diff --git a/chromium/content/browser/compositor/viz_process_transport_factory.h b/chromium/content/browser/compositor/viz_process_transport_factory.h index 44c00cbc446..229b760eb08 100644 --- a/chromium/content/browser/compositor/viz_process_transport_factory.h +++ b/chromium/content/browser/compositor/viz_process_transport_factory.h @@ -15,9 +15,9 @@ #include "components/viz/common/surfaces/frame_sink_id_allocator.h" #include "content/browser/compositor/image_transport_factory.h" #include "content/browser/compositor/in_process_display_client.h" +#include "gpu/command_buffer/common/context_result.h" #include "mojo/public/cpp/bindings/binding.h" #include "services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom.h" -#include "services/viz/public/interfaces/compositing/compositing_mode_watcher.mojom.h" #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h" #include "ui/compositor/compositor.h" @@ -53,7 +53,6 @@ class ExternalBeginFrameControllerClientImpl; class VizProcessTransportFactory : public ui::ContextFactory, public ui::ContextFactoryPrivate, public ImageTransportFactory, - public viz::mojom::CompositingModeWatcher, public viz::ContextLostObserver { public: VizProcessTransportFactory( @@ -106,13 +105,6 @@ class VizProcessTransportFactory : public ui::ContextFactory, ui::ContextFactory* GetContextFactory() override; ui::ContextFactoryPrivate* GetContextFactoryPrivate() override; viz::GLHelper* GetGLHelper() override; -#if defined(OS_MACOSX) - void SetCompositorSuspendedForRecycle(ui::Compositor* compositor, - bool suspended) override; -#endif - - // viz::mojom::CompositingModeWatcher implementation. - void CompositingModeFallbackToSoftware() override; // viz::ContextLostObserver implementation. void OnContextLost() override; @@ -138,6 +130,12 @@ class VizProcessTransportFactory : public ui::ContextFactory, DISALLOW_COPY_AND_ASSIGN(CompositorData); }; + // Disables GPU compositing. This notifies UI and renderer compositors to drop + // LayerTreeFrameSinks and request new ones. If fallback happens while + // creating a new LayerTreeFrameSink for UI compositor it should be passed in + // as |guilty_compositor| to avoid extra work and reentrancy problems. + void DisableGpuCompositing(ui::Compositor* guilty_compositor); + // Provided as a callback when the GPU process has crashed. void OnGpuProcessLost(); @@ -147,10 +145,15 @@ class VizProcessTransportFactory : public ui::ContextFactory, base::WeakPtr compositor_weak_ptr, scoped_refptr gpu_channel); - // Creates the necessary shared worker and compositor ContextProviders. If the - // ContextProviders already exist and haven't been lost then it will do - // nothing. Returns true if ContextProviders exist. - bool CreateContextProviders( + // Tries to create the raster and main thread ContextProviders. If the + // ContextProviders already exist and haven't been lost then this will do + // nothing. Also verifies |gpu_channel_host| and checks if GPU compositing is + // blacklisted. + // + // Returns kSuccess if caller can use GPU compositing, kTransientFailure if + // caller should try again or kFatalFailure if caller should fallback to + // software compositing. + gpu::ContextResult TryCreateContextsForGpuCompositing( scoped_refptr gpu_channel_host); void OnLostMainThreadSharedContext(); diff --git a/chromium/content/browser/compositor/vulkan_browser_compositor_output_surface.cc b/chromium/content/browser/compositor/vulkan_browser_compositor_output_surface.cc index 72e1b33e041..50ea53d2cec 100644 --- a/chromium/content/browser/compositor/vulkan_browser_compositor_output_surface.cc +++ b/chromium/content/browser/compositor/vulkan_browser_compositor_output_surface.cc @@ -7,6 +7,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "components/viz/service/display/output_surface_client.h" #include "content/browser/renderer_host/render_widget_host_impl.h" +#include "gpu/vulkan/vulkan_implementation.h" #include "gpu/vulkan/vulkan_surface.h" namespace content { @@ -25,8 +26,9 @@ VulkanBrowserCompositorOutputSurface::~VulkanBrowserCompositorOutputSurface() { bool VulkanBrowserCompositorOutputSurface::Initialize( gfx::AcceleratedWidget widget) { DCHECK(!surface_); - std::unique_ptr surface( - gpu::VulkanSurface::CreateViewSurface(widget)); + std::unique_ptr surface = + vulkan_context_provider()->GetVulkanImplementation()->CreateViewSurface( + widget); if (!surface->Initialize(vulkan_context_provider()->GetDeviceQueue(), gpu::VulkanSurface::DEFAULT_SURFACE_FORMAT)) { return false; @@ -78,11 +80,6 @@ gfx::BufferFormat VulkanBrowserCompositorOutputSurface::GetOverlayBufferFormat() return gfx::BufferFormat::RGBX_8888; } -bool VulkanBrowserCompositorOutputSurface::SurfaceIsSuspendForRecycle() const { - NOTIMPLEMENTED(); - return false; -} - void VulkanBrowserCompositorOutputSurface::Reshape( const gfx::Size& size, float device_scale_factor, @@ -106,17 +103,15 @@ VulkanBrowserCompositorOutputSurface::GetFramebufferCopyTextureFormat() { void VulkanBrowserCompositorOutputSurface::SwapBuffers( viz::OutputSurfaceFrame frame) { surface_->SwapBuffers(); - ++swap_id_; - base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&VulkanBrowserCompositorOutputSurface::SwapBuffersAck, - weak_ptr_factory_.GetWeakPtr(), swap_id_)); + weak_ptr_factory_.GetWeakPtr())); } -void VulkanBrowserCompositorOutputSurface::SwapBuffersAck(uint64_t swap_id) { +void VulkanBrowserCompositorOutputSurface::SwapBuffersAck() { DCHECK(client_); - client_->DidReceiveSwapBuffersAck(swap_id); + client_->DidReceiveSwapBuffersAck(); } gpu::VulkanSurface* VulkanBrowserCompositorOutputSurface::GetVulkanSurface() { diff --git a/chromium/content/browser/compositor/vulkan_browser_compositor_output_surface.h b/chromium/content/browser/compositor/vulkan_browser_compositor_output_surface.h index 9398765c250..7de13ae0e6a 100644 --- a/chromium/content/browser/compositor/vulkan_browser_compositor_output_surface.h +++ b/chromium/content/browser/compositor/vulkan_browser_compositor_output_surface.h @@ -39,7 +39,6 @@ class VulkanBrowserCompositorOutputSurface bool IsDisplayedAsOverlayPlane() const override; unsigned GetOverlayTextureId() const override; gfx::BufferFormat GetOverlayBufferFormat() const override; - bool SurfaceIsSuspendForRecycle() const override; void Reshape(const gfx::Size& size, float device_scale_factor, const gfx::ColorSpace& color_space, @@ -52,11 +51,10 @@ class VulkanBrowserCompositorOutputSurface gpu::VulkanSurface* GetVulkanSurface() override; private: - void SwapBuffersAck(uint64_t swap_id); + void SwapBuffersAck(); std::unique_ptr surface_; viz::OutputSurfaceClient* client_ = nullptr; - uint64_t swap_id_ = 0; base::WeakPtrFactory weak_ptr_factory_; diff --git a/chromium/content/browser/cookie_store/BUILD.gn b/chromium/content/browser/cookie_store/BUILD.gn new file mode 100644 index 00000000000..d5dbdd7fca9 --- /dev/null +++ b/chromium/content/browser/cookie_store/BUILD.gn @@ -0,0 +1,11 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/protobuf/proto_library.gni") + +proto_library("cookie_store_proto") { + sources = [ + "cookie_change_subscriptions.proto", + ] +} diff --git a/chromium/content/browser/cookie_store/OWNERS b/chromium/content/browser/cookie_store/OWNERS new file mode 100644 index 00000000000..b23c10fd54c --- /dev/null +++ b/chromium/content/browser/cookie_store/OWNERS @@ -0,0 +1,8 @@ +# Primary +pwnall@chromium.org + +# Secondary +jsbell@chromium.org + +# TEAM: storage-dev@chromium.org +# COMPONENT: Blink>Storage>CookiesAPI diff --git a/chromium/content/browser/cookie_store/cookie_change_subscription.cc b/chromium/content/browser/cookie_store/cookie_change_subscription.cc new file mode 100644 index 00000000000..78c410148a3 --- /dev/null +++ b/chromium/content/browser/cookie_store/cookie_change_subscription.cc @@ -0,0 +1,182 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/cookie_store/cookie_change_subscription.h" + +#include + +#include "content/browser/cookie_store/cookie_change_subscriptions.pb.h" + +namespace content { + +namespace { + +#define STATIC_ASSERT_ENUM(a, b) \ + static_assert(static_cast(a) == static_cast(b), \ + "mismatching enums: " #a) + +STATIC_ASSERT_ENUM(network::mojom::CookieMatchType::EQUALS, + proto::CookieMatchType::EQUALS); +STATIC_ASSERT_ENUM(network::mojom::CookieMatchType::STARTS_WITH, + proto::CookieMatchType::STARTS_WITH); + +proto::CookieMatchType CookieMatchTypeToProto( + network::mojom::CookieMatchType match_type) { + switch (match_type) { + case network::mojom::CookieMatchType::EQUALS: + return proto::CookieMatchType::EQUALS; + case ::network::mojom::CookieMatchType::STARTS_WITH: + return proto::CookieMatchType::STARTS_WITH; + } + NOTREACHED(); + return proto::CookieMatchType::EQUALS; +} + +network::mojom::CookieMatchType CookieMatchTypeFromProto( + proto::CookieMatchType match_type_proto) { + switch (match_type_proto) { + case proto::CookieMatchType::EQUALS: + return network::mojom::CookieMatchType::EQUALS; + case proto::CookieMatchType::STARTS_WITH: + return ::network::mojom::CookieMatchType::STARTS_WITH; + } + NOTREACHED(); + return network::mojom::CookieMatchType::EQUALS; +} + +} // namespace + +// static +base::Optional> +CookieChangeSubscription::DeserializeVector( + const std::string& proto_string, + int64_t service_worker_registration_id) { + proto::CookieChangeSubscriptionsProto subscriptions_proto; + if (!subscriptions_proto.ParseFromString(proto_string)) + return base::nullopt; + + std::vector subscriptions; + int subscription_count = subscriptions_proto.subscriptions_size(); + subscriptions.reserve(subscription_count); + for (int i = 0; i < subscription_count; ++i) { + base::Optional subscription_opt = + CookieChangeSubscription::Create(subscriptions_proto.subscriptions(i), + service_worker_registration_id); + if (!subscription_opt.has_value()) + continue; + subscriptions.emplace_back(std::move(subscription_opt).value()); + } + + return base::make_optional( + std::vector(std::move(subscriptions))); +} + +// static +std::vector CookieChangeSubscription::FromMojoVector( + std::vector mojo_subscriptions, + int64_t service_worker_registration_id) { + std::vector subscriptions; + subscriptions.reserve(mojo_subscriptions.size()); + for (const auto& mojo_subscription : mojo_subscriptions) { + subscriptions.emplace_back( + std::move(mojo_subscription->url), std::move(mojo_subscription->name), + mojo_subscription->match_type, service_worker_registration_id); + } + return subscriptions; +} + +// static +std::string CookieChangeSubscription::SerializeVector( + const std::vector& subscriptions) { + proto::CookieChangeSubscriptionsProto subscriptions_proto; + for (const auto& subscription : subscriptions) + subscription.Serialize(subscriptions_proto.add_subscriptions()); + return subscriptions_proto.SerializeAsString(); +} + +// static +std::vector +CookieChangeSubscription::ToMojoVector( + const std::vector& subscriptions) { + std::vector mojo_subscriptions; + mojo_subscriptions.reserve(subscriptions.size()); + for (const auto& subscription : subscriptions) { + auto mojo_subscription = blink::mojom::CookieChangeSubscription::New(); + subscription.Serialize(mojo_subscription.get()); + mojo_subscriptions.emplace_back(std::move(mojo_subscription)); + } + return mojo_subscriptions; +} + +// static +base::Optional CookieChangeSubscription::Create( + proto::CookieChangeSubscriptionProto proto, + int64_t service_worker_registration_id) { + if (!proto.has_url()) + return base::nullopt; + GURL url = GURL(proto.url()); + if (!url.is_valid()) + return base::nullopt; + + std::string name = proto.has_name() ? proto.name() : ""; + ::network::mojom::CookieMatchType match_type = + proto.has_match_type() ? CookieMatchTypeFromProto(proto.match_type()) + : ::network::mojom::CookieMatchType::EQUALS; + + return CookieChangeSubscription(std::move(url), std::move(name), match_type, + service_worker_registration_id); +} + +CookieChangeSubscription::CookieChangeSubscription(CookieChangeSubscription&&) = + default; + +CookieChangeSubscription::~CookieChangeSubscription() = default; + +CookieChangeSubscription::CookieChangeSubscription( + GURL url, + std::string name, + ::network::mojom::CookieMatchType match_type, + int64_t service_worker_registration_id) + : url_(std::move(url)), + name_(std::move(name)), + match_type_(match_type), + service_worker_registration_id_(service_worker_registration_id) {} + +void CookieChangeSubscription::Serialize( + proto::CookieChangeSubscriptionProto* proto) const { + proto->set_match_type(CookieMatchTypeToProto(match_type_)); + proto->set_name(name_); + proto->set_url(url_.spec()); +} + +void CookieChangeSubscription::Serialize( + blink::mojom::CookieChangeSubscription* mojo_subscription) const { + mojo_subscription->url = url_; + mojo_subscription->name = name_; + mojo_subscription->match_type = match_type_; +} + +bool CookieChangeSubscription::ShouldObserveChangeTo( + const net::CanonicalCookie& cookie) const { + switch (match_type_) { + case ::network::mojom::CookieMatchType::EQUALS: + if (cookie.Name() != name_) + return false; + break; + case ::network::mojom::CookieMatchType::STARTS_WITH: + if (!base::StartsWith(cookie.Name(), name_, base::CompareCase::SENSITIVE)) + return false; + break; + } + + net::CookieOptions net_options; + net_options.set_same_site_cookie_mode( + net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); + if (!cookie.IncludeForRequestURL(url_, net_options)) + return false; + + return true; +} + +} // namespace content diff --git a/chromium/content/browser/cookie_store/cookie_change_subscription.h b/chromium/content/browser/cookie_store/cookie_change_subscription.h new file mode 100644 index 00000000000..c2c4839a26d --- /dev/null +++ b/chromium/content/browser/cookie_store/cookie_change_subscription.h @@ -0,0 +1,114 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_COOKIE_STORE_COOKIE_CHANGE_SUBSCRIPTION_H_ +#define CONTENT_BROWSER_COOKIE_STORE_COOKIE_CHANGE_SUBSCRIPTION_H_ + +#include +#include + +#include "base/containers/linked_list.h" +#include "base/macros.h" +#include "base/optional.h" +#include "third_party/blink/public/mojom/cookie_store/cookie_store.mojom.h" +#include "url/gurl.h" + +namespace content { + +namespace proto { + +class CookieChangeSubscriptionProto; + +} // namespace proto + +// Represents a single subscription to the list of cookies sent to a URL. +// +// The included linked list node and service worker registration ID are used by +// CookieStoreManager. +class CookieChangeSubscription + : public base::LinkNode { + public: + // Used to read a service worker's subscriptions from the persistent store. + static base::Optional> + DeserializeVector(const std::string& proto_string, + int64_t service_worker_registration_id); + + // Converts subscriptions from a Mojo API call. + static std::vector FromMojoVector( + std::vector mojo_subscriptions, + int64_t service_worker_registration_id); + + // Used to write a service worker's subscriptions to the service worker store. + // + // Returns the empty string in case of a serialization error. + static std::string SerializeVector( + const std::vector&); + + // Converts a service worker's subscriptions to a Mojo API call result. + static std::vector ToMojoVector( + const std::vector&); + + // Public for testing. + // + // Production code should use the vector-based factory methods above. + static base::Optional Create( + proto::CookieChangeSubscriptionProto proto, + int64_t service_worker_registration_id); + + // Public for testing. + // + // Production code should use the vector-based factory methods above. + CookieChangeSubscription(GURL url, + std::string name, + ::network::mojom::CookieMatchType match_type, + int64_t service_worker_registration_id); + + // LinkNode supports move-construction, but not move assignment. + CookieChangeSubscription(CookieChangeSubscription&&); + CookieChangeSubscription& operator=(CookieChangeSubscription&&) = delete; + + ~CookieChangeSubscription(); + + // The URL whose cookie list is watched for changes. + const GURL& url() const { return url_; } + + // Operator for name-based matching. + // + // This is used to implement both equality and prefix-based name matching. + // Supporting the latter helps avoid wasting battery by waking up service + // workers unnecessarily. + ::network::mojom::CookieMatchType match_type() const { return match_type_; } + + // Operand for the name-based matching operator above. + // + // For EQUAL matching, the cookie name must precisely match name(). For + // STARTS_WITH matching, the cookie name must be prefixed by name(). + const std::string& name() const { return name_; } + + // The service worker registration that this subscription belongs to. + int64_t service_worker_registration_id() const { + return service_worker_registration_id_; + } + + // Writes the subscription to the given protobuf. + void Serialize(proto::CookieChangeSubscriptionProto* proto) const; + // Writes the subscription to the given Mojo object. + void Serialize( + blink::mojom::CookieChangeSubscription* mojo_subscription) const; + + // True if the subscription covers a change to the given cookie. + bool ShouldObserveChangeTo(const net::CanonicalCookie& cookie) const; + + private: + const GURL url_; + const std::string name_; + const ::network::mojom::CookieMatchType match_type_; + const int64_t service_worker_registration_id_; + + DISALLOW_COPY_AND_ASSIGN(CookieChangeSubscription); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_COOKIE_STORE_COOKIE_CHANGE_SUBSCRIPTION_H_ diff --git a/chromium/content/browser/cookie_store/cookie_change_subscriptions.proto b/chromium/content/browser/cookie_store/cookie_change_subscriptions.proto new file mode 100644 index 00000000000..4ee027f6075 --- /dev/null +++ b/chromium/content/browser/cookie_store/cookie_change_subscriptions.proto @@ -0,0 +1,27 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package content.proto; + +// Proto equivalent of network::mojom::CookieMatchType. Values must match. +enum CookieMatchType { + EQUALS = 0; + STARTS_WITH = 1; +} + +// A single cookie change subscription. +message CookieChangeSubscriptionProto { + required string url = 1; + optional string name = 2; + optional CookieMatchType match_type = 3; +} + +// All cookie change subscriptions belonging to a service worker registration. +message CookieChangeSubscriptionsProto { + repeated CookieChangeSubscriptionProto subscriptions = 1; +} diff --git a/chromium/content/browser/cookie_store/cookie_store_context.cc b/chromium/content/browser/cookie_store/cookie_store_context.cc new file mode 100644 index 00000000000..eeea0d01a97 --- /dev/null +++ b/chromium/content/browser/cookie_store/cookie_store_context.cc @@ -0,0 +1,117 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/cookie_store/cookie_store_context.h" + +#include "content/browser/service_worker/service_worker_context_wrapper.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/storage_partition.h" + +namespace content { + +CookieStoreContext::CookieStoreContext() + : base::RefCountedDeleteOnSequence( + BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)) {} + +CookieStoreContext::~CookieStoreContext() { + // The destructor must be called on the IO thread, because it runs + // cookie_store_manager_'s destructor, and the latter is only accessed on the + // IO thread. + DCHECK_CURRENTLY_ON(BrowserThread::IO); +} + +void CookieStoreContext::Initialize( + scoped_refptr service_worker_context, + base::OnceCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +#if DCHECK_IS_ON() + DCHECK(!initialize_called_) << __func__ << " called twice"; + initialize_called_ = true; +#endif // DCHECK_IS_ON() + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &CookieStoreContext::InitializeOnIOThread, this, + std::move(service_worker_context), + base::BindOnce( + [](scoped_refptr task_runner, + base::OnceCallback callback, bool result) { + task_runner->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), result)); + }, + base::SequencedTaskRunnerHandle::Get(), std::move(callback)))); +} + +void CookieStoreContext::ListenToCookieChanges( + ::network::mojom::NetworkContext* network_context, + base::OnceCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +#if DCHECK_IS_ON() + DCHECK(initialize_called_) << __func__ << " called before Initialize()"; +#endif // DCHECK_IS_ON() + + ::network::mojom::CookieManagerPtrInfo cookie_manager_ptr_info; + network_context->GetCookieManager( + mojo::MakeRequest(&cookie_manager_ptr_info)); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &CookieStoreContext::ListenToCookieChangesOnIOThread, this, + std::move(cookie_manager_ptr_info), + base::BindOnce( + [](scoped_refptr task_runner, + base::OnceCallback callback, bool result) { + task_runner->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), result)); + }, + base::SequencedTaskRunnerHandle::Get(), std::move(callback)))); +} + +void CookieStoreContext::CreateService(blink::mojom::CookieStoreRequest request, + const url::Origin& origin) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +#if DCHECK_IS_ON() + DCHECK(initialize_called_) << __func__ << " called before Initialize()"; +#endif // DCHECK_IS_ON() + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&CookieStoreContext::CreateServiceOnIOThread, this, + std::move(request), origin)); +} + +void CookieStoreContext::InitializeOnIOThread( + scoped_refptr service_worker_context, + base::OnceCallback callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!cookie_store_manager_) << __func__ << " called more than once"; + + cookie_store_manager_ = + std::make_unique(std::move(service_worker_context)); + cookie_store_manager_->LoadAllSubscriptions(std::move(callback)); +} + +void CookieStoreContext::ListenToCookieChangesOnIOThread( + ::network::mojom::CookieManagerPtrInfo cookie_manager_ptr_info, + base::OnceCallback callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(cookie_store_manager_); + + cookie_store_manager_->ListenToCookieChanges( + ::network::mojom::CookieManagerPtr(std::move(cookie_manager_ptr_info)), + std::move(callback)); +} + +void CookieStoreContext::CreateServiceOnIOThread( + blink::mojom::CookieStoreRequest request, + const url::Origin& origin) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(cookie_store_manager_); + + cookie_store_manager_->CreateService(std::move(request), origin); +} + +} // namespace content diff --git a/chromium/content/browser/cookie_store/cookie_store_context.h b/chromium/content/browser/cookie_store/cookie_store_context.h new file mode 100644 index 00000000000..0317b9f0773 --- /dev/null +++ b/chromium/content/browser/cookie_store/cookie_store_context.h @@ -0,0 +1,98 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_CONTEXT_H_ +#define CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_CONTEXT_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted_delete_on_sequence.h" +#include "base/memory/scoped_refptr.h" +#include "content/browser/cookie_store/cookie_store_manager.h" +#include "content/common/content_export.h" +#include "services/network/public/mojom/network_service.mojom.h" +#include "third_party/blink/public/mojom/cookie_store/cookie_store.mojom.h" +#include "url/origin.h" + +namespace content { + +class CookieStoreManager; +class ServiceWorkerContextWrapper; + +// UI thread handle to a CookieStoreManager. +// +// This class is RefCountedDeleteOnSequence because it has members that must be +// accessed on the IO thread, and therefore must be destroyed on the IO thread. +// Conceptually, CookieStoreContext instances are owned by StoragePartitionImpl. +class CONTENT_EXPORT CookieStoreContext + : public base::RefCountedDeleteOnSequence { + public: + REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); + + // Creates an empty CookieStoreContext shell. + // + // Newly created instances must be initialized via Initialize() before any + // other methods are used. + CookieStoreContext(); + + // Creates the underlying CookieStoreManager. + // + // This must be called before any other CookieStoreContext method. + // + // The newly created CookieStoreManager starts loading any persisted cookie + // change subscriptions from ServiceWorkerStorage. When the loading completes, + // the given callback is called with a boolean indicating whether the loading + // succeeded. + // + // It is safe to call all the other methods during the loading operation. This + // includes creating and using CookieStore mojo services. The + // CookieStoreManager has well-defined semantics if loading from + // ServiceWorkerStorage fails, so the caller does not need to handle loading + // errors. + void Initialize( + scoped_refptr service_worker_context, + base::OnceCallback callback); + + // Starts listening to cookie changes from a network service instance. + // + // The callback is called with the (success / failure) result of subscribing. + void ListenToCookieChanges(::network::mojom::NetworkContext* network_context, + base::OnceCallback callback); + + // Routes a mojo request to the CookieStoreManager on the IO thread. + void CreateService(blink::mojom::CookieStoreRequest request, + const url::Origin& origin); + + private: + friend class base::RefCountedDeleteOnSequence; + friend class base::DeleteHelper; + ~CookieStoreContext(); + + void InitializeOnIOThread( + scoped_refptr service_worker_context, + base::OnceCallback callback); + + void ListenToCookieChangesOnIOThread( + ::network::mojom::CookieManagerPtrInfo cookie_manager_ptr_info, + base::OnceCallback callback); + + void CreateServiceOnIOThread(blink::mojom::CookieStoreRequest request, + const url::Origin& origin); + + // Only accessed on the IO thread. + std::unique_ptr cookie_store_manager_; + +#if DCHECK_IS_ON() + // Only accesssed on the UI thread. + bool initialize_called_ = false; +#endif // DCHECK_IS_ON() + + SEQUENCE_CHECKER(sequence_checker_); + + DISALLOW_COPY_AND_ASSIGN(CookieStoreContext); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_CONTEXT_H_ diff --git a/chromium/content/browser/cookie_store/cookie_store_host.cc b/chromium/content/browser/cookie_store/cookie_store_host.cc new file mode 100644 index 00000000000..685abad2e34 --- /dev/null +++ b/chromium/content/browser/cookie_store/cookie_store_host.cc @@ -0,0 +1,38 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/cookie_store/cookie_store_host.h" + +#include + +#include "content/browser/cookie_store/cookie_store_manager.h" +#include "url/origin.h" + +namespace content { + +CookieStoreHost::CookieStoreHost(CookieStoreManager* manager, + const url::Origin& origin) + : manager_(manager), origin_(origin) {} + +CookieStoreHost::~CookieStoreHost() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +void CookieStoreHost::AppendSubscriptions( + int64_t service_worker_registration_id, + std::vector subscriptions, + AppendSubscriptionsCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + manager_->AppendSubscriptions(service_worker_registration_id, origin_, + std::move(subscriptions), std::move(callback)); +} + +void CookieStoreHost::GetSubscriptions(int64_t service_worker_registration_id, + GetSubscriptionsCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + manager_->GetSubscriptions(service_worker_registration_id, origin_, + std::move(callback)); +} + +} // namespace content diff --git a/chromium/content/browser/cookie_store/cookie_store_host.h b/chromium/content/browser/cookie_store/cookie_store_host.h new file mode 100644 index 00000000000..af259ddc465 --- /dev/null +++ b/chromium/content/browser/cookie_store/cookie_store_host.h @@ -0,0 +1,59 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_HOST_H_ +#define CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_HOST_H_ + +#include + +#include "base/macros.h" +#include "base/sequence_checker.h" +#include "third_party/blink/public/mojom/cookie_store/cookie_store.mojom.h" +#include "url/origin.h" + +namespace content { + +class CookieStoreManager; + +// Stores the state associated with each CookieStore mojo connection. +// +// The bulk of the CookieStore implementation is in the CookieStoreManager +// class. Each StoragePartition has a single associated CookieStoreManager +// instance. By contrast, each CookieStore mojo connection has an associated +// CoookieStoreHost instance, which stores the per-connection state. +// +// Instances of this class must be accessed exclusively on the IO thread, +// because they call into CookieStoreManager directly. +class CookieStoreHost : public blink::mojom::CookieStore { + public: + CookieStoreHost(CookieStoreManager* manager, const url::Origin& origin); + ~CookieStoreHost() override; + + // content::mojom::CookieStore + void AppendSubscriptions( + int64_t service_worker_registration_id, + std::vector, + AppendSubscriptionsCallback callback) override; + void GetSubscriptions(int64_t service_worker_registration_id, + GetSubscriptionsCallback callback) override; + + private: + // The raw pointer is safe because CookieStoreManager owns this instance via a + // mojo::BindingSet. + CookieStoreManager* const manager_; + + const url::Origin origin_; + + // Instances of this class are currently bound to the IO thread, because they + // call ServiceWorkerContextWrapper methods that are restricted to the IO + // thread. However, the class implementation itself is thread-friendly, so it + // only checks that methods are called on the same sequence. + SEQUENCE_CHECKER(sequence_checker_); + + DISALLOW_COPY_AND_ASSIGN(CookieStoreHost); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_HOST_H_ diff --git a/chromium/content/browser/cookie_store/cookie_store_manager.cc b/chromium/content/browser/cookie_store/cookie_store_manager.cc new file mode 100644 index 00000000000..8e08a32ed55 --- /dev/null +++ b/chromium/content/browser/cookie_store/cookie_store_manager.cc @@ -0,0 +1,530 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/cookie_store/cookie_store_manager.h" + +#include + +#include "base/optional.h" +#include "content/browser/cookie_store/cookie_change_subscriptions.pb.h" +#include "content/browser/service_worker/embedded_worker_status.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" +#include "content/browser/service_worker/service_worker_metrics.h" +#include "content/browser/service_worker/service_worker_registration.h" +#include "content/browser/service_worker/service_worker_version.h" +#include "content/common/service_worker/service_worker_status_code.h" +#include "content/public/browser/browser_context.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h" +#include "url/gurl.h" + +namespace content { + +namespace { + +// ServiceWorkerStorage user data key for cookie change subscriptions. +const char kSubscriptionsUserKey[] = "cookie_store_subscriptions"; + +// Handles the result of ServiceWorkerContextWrapper::StoreRegistrationUserData. +void HandleStoreRegistrationUserDataStatus(ServiceWorkerStatusCode status) { + // The current implementation does not have a good way to handle errors in + // StoreRegistrationUserData. Cookie change subscriptions have been added to + // the registration during the install event, so it's too late to surface the + // error to the renderer. The registration has already been persisted, and the + // Service Worker is likely active by now. + DLOG_IF(ERROR, status != SERVICE_WORKER_OK) + << "StoreRegistrationUserData failed"; +} + +} // namespace + +CookieStoreManager::CookieStoreManager( + scoped_refptr service_worker_context) + : service_worker_context_(std::move(service_worker_context)), + cookie_change_listener_binding_(this), + registration_user_data_key_(kSubscriptionsUserKey), + weak_factory_(this) { + service_worker_context_->AddObserver(this); +} + +CookieStoreManager::~CookieStoreManager() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + service_worker_context_->RemoveObserver(this); +} + +void CookieStoreManager::CreateService(blink::mojom::CookieStoreRequest request, + const url::Origin& origin) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + bindings_.AddBinding(std::make_unique(this, origin), + std::move(request)); +} + +void CookieStoreManager::LoadAllSubscriptions( + base::OnceCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + DCHECK(!done_loading_subscriptions_) << __func__ << " already called"; + + service_worker_context_->GetUserDataForAllRegistrations( + registration_user_data_key_, + base::BindOnce(&CookieStoreManager::ProcessOnDiskSubscriptions, + weak_factory_.GetWeakPtr(), std::move(callback))); +} + +void CookieStoreManager::ListenToCookieChanges( + ::network::mojom::CookieManagerPtr cookie_manager, + base::OnceCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + DCHECK(!cookie_manager_) << __func__ << " already called"; + cookie_manager_ = std::move(cookie_manager); + + DCHECK(!cookie_change_listener_binding_.is_bound()); + ::network::mojom::CookieChangeListenerPtr cookie_change_listener; + cookie_change_listener_binding_.Bind( + mojo::MakeRequest(&cookie_change_listener)); + + // TODO(pwnall): Switch to an API with subscription confirmation. + cookie_manager_->AddGlobalChangeListener(std::move(cookie_change_listener)); + std::move(callback).Run(true); +} + +void CookieStoreManager::ProcessOnDiskSubscriptions( + base::OnceCallback load_callback, + const std::vector>& user_data, + ServiceWorkerStatusCode status) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + DCHECK(!done_loading_subscriptions_) << __func__ << " already called"; + done_loading_subscriptions_ = true; + + if (status != SERVICE_WORKER_OK) { + DidLoadAllSubscriptions(false, std::move(load_callback)); + return; + } + + DCHECK(subscriptions_by_registration_.empty()); + subscriptions_by_registration_.reserve(user_data.size()); + bool load_success = true; + for (const auto& pair : user_data) { + int64_t service_worker_registration_id = pair.first; + const std::string& proto_string = pair.second; + + base::Optional> subscriptions_opt = + CookieChangeSubscription::DeserializeVector( + proto_string, service_worker_registration_id); + if (!subscriptions_opt.has_value()) { + load_success = false; + continue; + } + + ActivateSubscriptions(&subscriptions_opt.value()); + DCHECK( + !subscriptions_by_registration_.count(service_worker_registration_id)); + subscriptions_by_registration_.emplace( + std::move(service_worker_registration_id), + std::move(subscriptions_opt).value()); + } + + DidLoadAllSubscriptions(load_success, std::move(load_callback)); +} + +void CookieStoreManager::DidLoadAllSubscriptions( + bool succeeded, + base::OnceCallback load_callback) { + DCHECK(done_loading_subscriptions_); + succeeded_loading_subscriptions_ = succeeded; + + for (auto& callback : subscriptions_loaded_callbacks_) + std::move(callback).Run(); + subscriptions_loaded_callbacks_.clear(); + + std::move(load_callback).Run(succeeded); +} + +void CookieStoreManager::AppendSubscriptions( + int64_t service_worker_registration_id, + const url::Origin& origin, + std::vector mojo_subscriptions, + blink::mojom::CookieStore::AppendSubscriptionsCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!done_loading_subscriptions_) { + subscriptions_loaded_callbacks_.emplace_back(base::BindOnce( + &CookieStoreManager::AppendSubscriptions, weak_factory_.GetWeakPtr(), + service_worker_registration_id, origin, std::move(mojo_subscriptions), + std::move(callback))); + return; + } + + if (!succeeded_loading_subscriptions_) { + std::move(callback).Run(false); + return; + } + + // GetLiveRegistration() is sufficient here (as opposed to a flavor of + // FindRegistration()) because AppendSubscriptions is only called from the + // implementation of the Cookie Store API, which is exposed to + // ServiceWorkerGlobalScope. ServiceWorkerGlobalScope references the + // service worker's registration via a ServiceWorkerRegistration JavaScript + // object, so the registration is guaranteed to be live while the service + // worker is executing. + // + // It is possible for the service worker to get killed while this API call is + // in progress, for example, if the service worker code exceeds an event + // handling time limit. In that case, the return value will not be observed, + // so a false negative is acceptable. + ServiceWorkerRegistration* service_worker_registration = + service_worker_context_->GetLiveRegistration( + service_worker_registration_id); + if (!service_worker_registration || + !origin.IsSameOriginWith( + url::Origin::Create(service_worker_registration->pattern()))) { + // This error case is a good fit for mojo::ReportBadMessage(), because the + // renderer has passed an invalid registration ID. However, the code here + // might run without a mojo call context, if the original call was delayed + // while loading on-disk subscription data. + // + // While it would be possible to have two code paths for the two situations, + // the extra complexity doesn't seem warranted for the limited debuggig + // benefits provided by mojo::ReportBadMessage. + std::move(callback).Run(false); + return; + } + + // TODO(crbug.com/843079): This check incorrectly allows an active service + // worker version to call the API, if another version + // is installing at the same time. + if (!service_worker_registration->installing_version()) { + // A service worker's cookie change subscriptions can only be modified while + // the service worker's install event is handled. + std::move(callback).Run(false); + return; + } + + if (mojo_subscriptions.empty()) { + // Empty subscriptions are special-cased so we never have to serialize an + // empty array of subscriptions. This is advantageous because the protobuf + // serialization of an empty array is the empty string, which is also used + // by the convenience protobuf serialization API to signal serialization + // failure. So, supporting serializing an empty array would mean we can't + // use the convenience serialization API. + std::move(callback).Run(true); + return; + } + + std::vector new_subscriptions = + CookieChangeSubscription::FromMojoVector( + std::move(mojo_subscriptions), service_worker_registration->id()); + DCHECK(!new_subscriptions.empty()); + + auto old_subscriptions_it = + subscriptions_by_registration_.find(service_worker_registration_id); + if (old_subscriptions_it == subscriptions_by_registration_.end()) { + subscriptions_by_registration_.emplace(service_worker_registration_id, + std::move(new_subscriptions)); + std::move(callback).Run(true); + return; + } + + std::vector& old_subscriptions = + old_subscriptions_it->second; + old_subscriptions.reserve(old_subscriptions.size() + + new_subscriptions.size()); + for (auto& new_subscription : new_subscriptions) + old_subscriptions.emplace_back(std::move(new_subscription)); + + std::move(callback).Run(true); +} + +void CookieStoreManager::GetSubscriptions( + int64_t service_worker_registration_id, + const url::Origin& origin, + blink::mojom::CookieStore::GetSubscriptionsCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!done_loading_subscriptions_) { + subscriptions_loaded_callbacks_.emplace_back(base::BindOnce( + &CookieStoreManager::GetSubscriptions, weak_factory_.GetWeakPtr(), + service_worker_registration_id, origin, std::move(callback))); + return; + } + + if (!succeeded_loading_subscriptions_) { + std::move(callback).Run( + std::vector(), false); + return; + } + + auto it = subscriptions_by_registration_.find(service_worker_registration_id); + if (it == subscriptions_by_registration_.end() || it->second.empty()) { + std::move(callback).Run( + std::vector(), true); + return; + } + + const url::Origin& first_origin = url::Origin::Create(it->second[0].url()); +#if DCHECK_IS_ON() + for (const auto& subscription : it->second) { + DCHECK( + first_origin.IsSameOriginWith(url::Origin::Create(subscription.url()))) + << "Service worker's change subscriptions don't have the same origin"; + } +#endif // DCHECK_IS_ON() + + if (!origin.IsSameOriginWith(first_origin)) { + // This error case is a good fit for mojo::ReportBadMessage(), because the + // renderer has passed an invalid registration ID. However, the code here + // might run without a mojo call context, if the original call was delayed + // while loading on-disk subscription data. + // + // While it would be possible to have two code paths for the two situations, + // the extra complexity doesn't seem warranted for the limited debuggig + // benefits provided by mojo::ReportBadMessage. + std::move(callback).Run( + std::vector(), false); + return; + } + + std::move(callback).Run(CookieChangeSubscription::ToMojoVector(it->second), + true); +} + +void CookieStoreManager::OnNewLiveRegistration( + int64_t service_worker_registration_id, + const GURL& pattern) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +void CookieStoreManager::OnRegistrationStored( + int64_t service_worker_registration_id, + const GURL& pattern) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // Waiting for the on-disk subscriptions to be loaded ensures that the + // registration's subscriptions aren't activated twice. Without waiting, + // there's a risk that LoadAllSubscriptions() sees the result of the + // StoreRegistrationUserData() call below. + if (!done_loading_subscriptions_) { + subscriptions_loaded_callbacks_.emplace_back(base::BindOnce( + &CookieStoreManager::OnRegistrationStored, weak_factory_.GetWeakPtr(), + service_worker_registration_id, pattern)); + return; + } + + auto it = subscriptions_by_registration_.find(service_worker_registration_id); + if (it == subscriptions_by_registration_.end()) + return; + + ActivateSubscriptions(&it->second); + + std::string subscriptions_data = + CookieChangeSubscription::SerializeVector(it->second); + DCHECK(!subscriptions_data.empty()) + << "Failed to create cookie change subscriptions protobuf"; + + service_worker_context_->StoreRegistrationUserData( + service_worker_registration_id, pattern.GetOrigin(), + std::vector>( + {{registration_user_data_key_, subscriptions_data}}), + base::BindOnce(&HandleStoreRegistrationUserDataStatus)); +} + +void CookieStoreManager::OnRegistrationDeleted( + int64_t service_worker_registration_id, + const GURL& pattern) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // Waiting for the on-disk subscriptions to be loaded ensures that the + // registration's subscriptions are removed. Without waiting, there's a risk + // that a registration's subscriptions will finish loading (and thus remain + // active) right after this function runs. + if (!done_loading_subscriptions_) { + subscriptions_loaded_callbacks_.emplace_back(base::BindOnce( + &CookieStoreManager::OnRegistrationDeleted, weak_factory_.GetWeakPtr(), + service_worker_registration_id, pattern)); + return; + } + + auto it = subscriptions_by_registration_.find(service_worker_registration_id); + if (it == subscriptions_by_registration_.end()) + return; + + DeactivateSubscriptions(&it->second); + subscriptions_by_registration_.erase(it); +} + +void CookieStoreManager::ActivateSubscriptions( + std::vector* subscriptions) { + if (subscriptions->empty()) + return; + + // Service workers can only observe changes to cookies for URLs under their + // scope. This means all the URLs that the worker is observing must map to the + // same domain key (eTLD+1). + // + // TODO(pwnall): This is the same as implementation as + // net::CookieMonsterChangeDispatcher::DomainKey. Extract that + // implementation into net/cookies.cookie_util.h and call it. + std::string url_key = net::registry_controlled_domains::GetDomainAndRegistry( + + (*subscriptions)[0].url(), + net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); + base::LinkedList& url_key_subscriptions_list = + subscriptions_by_url_key_[url_key]; + + for (auto& subscription : *subscriptions) { + DCHECK(!subscription.next() && !subscription.previous()) + << "Subscription passed to " << __func__ << " already activated"; + DCHECK_EQ(url_key, + net::registry_controlled_domains::GetDomainAndRegistry( + subscription.url(), + net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) + << __func__ << " subscriptions belong to different registrations"; + url_key_subscriptions_list.Append(&subscription); + } +} + +void CookieStoreManager::DeactivateSubscriptions( + std::vector* subscriptions) { + if (subscriptions->empty()) + return; + + // Service workers can only observe changes to cookies for URLs under their + // scope. This means all the URLs that the worker is observing must map to the + // same domain key (eTLD+1). + // + // TODO(pwnall): This has the same implementation as + // net::CookieMonsterChangeDispatcher::DomainKey. Extract that + // implementation into net/cookies.cookie_util.h and call it. + std::string url_key = net::registry_controlled_domains::GetDomainAndRegistry( + (*subscriptions)[0].url(), + net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); + for (auto& subscription : *subscriptions) { + DCHECK(subscription.next() && subscription.previous()) + << "Subscription passed to " << __func__ << " not previously activated"; + DCHECK_EQ(url_key, + net::registry_controlled_domains::GetDomainAndRegistry( + subscription.url(), + net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) + << __func__ << " subscriptions belong to different registrations"; + subscription.RemoveFromList(); + } + auto it = subscriptions_by_url_key_.find(url_key); + DCHECK(it != subscriptions_by_url_key_.end()); + if (it->second.empty()) + subscriptions_by_url_key_.erase(it); +} + +void CookieStoreManager::OnStorageWiped() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // Waiting for the on-disk subscriptions to be loaded ensures that all + // subscriptions are removed. Without waiting, there's a risk that some + // subscriptions will finish loading (and thus remain active) after this + // function runs. + if (!done_loading_subscriptions_) { + subscriptions_loaded_callbacks_.emplace_back(base::BindOnce( + &CookieStoreManager::OnStorageWiped, weak_factory_.GetWeakPtr())); + return; + } + + subscriptions_by_url_key_.clear(); + subscriptions_by_registration_.clear(); +} + +void CookieStoreManager::OnCookieChange( + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause) { + // Waiting for on-disk subscriptions to be loaded ensures that changes are + // delivered to all service workers that subscribed to them in previous + // browser sessions. Without waiting, workers might miss cookie changes. + if (!done_loading_subscriptions_) { + subscriptions_loaded_callbacks_.emplace_back( + base::BindOnce(&CookieStoreManager::OnCookieChange, + weak_factory_.GetWeakPtr(), cookie, cause)); + return; + } + + // Compute the list of service workers interested in this change. A worker + // might have multiple subscriptions that cover this change, but should still + // receive a single change event. + // TODO(pwnall): This has same as implementation as + // net::CookieMonsterChangeDispatcher::DomainKey. Extract that + // implementation into net/cookies.cookie_util.h and call it. + std::string url_key = net::registry_controlled_domains::GetDomainAndRegistry( + cookie.Domain(), + net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); + auto it = subscriptions_by_url_key_.find(url_key); + if (it == subscriptions_by_url_key_.end()) + return; + std::set interested_registration_ids; + const base::LinkedList& subscriptions = it->second; + for (const base::LinkNode* node = + subscriptions.head(); + node != subscriptions.end(); node = node->next()) { + const CookieChangeSubscription* subscription = node->value(); + if (subscription->ShouldObserveChangeTo(cookie)) { + interested_registration_ids.insert( + subscription->service_worker_registration_id()); + } + } + + // Dispatch the change to interested workers. + for (int64_t registration_id : interested_registration_ids) { + service_worker_context_->FindReadyRegistrationForIdOnly( + registration_id, + base::BindOnce( + [](base::WeakPtr manager, + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause, + ServiceWorkerStatusCode find_status, + scoped_refptr registration) { + if (find_status != SERVICE_WORKER_OK) + return; + + DCHECK(registration); + if (!manager) + return; + manager->DispatchChangeEvent(std::move(registration), cookie, + cause); + }, + weak_factory_.GetWeakPtr(), cookie, cause)); + } +} + +void CookieStoreManager::DispatchChangeEvent( + scoped_refptr registration, + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause) { + scoped_refptr active_version = + registration->active_version(); + if (active_version->running_status() != EmbeddedWorkerStatus::RUNNING) { + active_version->RunAfterStartWorker( + ServiceWorkerMetrics::EventType::COOKIE_CHANGE, + base::BindOnce(&CookieStoreManager::DidStartWorkerForChangeEvent, + weak_factory_.GetWeakPtr(), std::move(registration), + cookie, cause)); + return; + } + + int request_id = active_version->StartRequest( + ServiceWorkerMetrics::EventType::COOKIE_CHANGE, base::DoNothing()); + + active_version->event_dispatcher()->DispatchCookieChangeEvent( + cookie, cause, active_version->CreateSimpleEventCallback(request_id)); +} + +void CookieStoreManager::DidStartWorkerForChangeEvent( + scoped_refptr registration, + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause, + ServiceWorkerStatusCode start_worker_status) { + if (start_worker_status != SERVICE_WORKER_OK) + return; + DispatchChangeEvent(std::move(registration), cookie, cause); +} + +} // namespace content diff --git a/chromium/content/browser/cookie_store/cookie_store_manager.h b/chromium/content/browser/cookie_store/cookie_store_manager.h new file mode 100644 index 00000000000..19ccf7333b1 --- /dev/null +++ b/chromium/content/browser/cookie_store/cookie_store_manager.h @@ -0,0 +1,228 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_MANAGER_H_ +#define CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_MANAGER_H_ + +#include +#include +#include + +#include "base/callback.h" +#include "base/containers/linked_list.h" +#include "base/macros.h" +#include "base/memory/scoped_refptr.h" +#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" +#include "content/browser/cookie_store/cookie_change_subscription.h" +#include "content/browser/cookie_store/cookie_store_host.h" +#include "content/browser/service_worker/service_worker_context_core_observer.h" +#include "mojo/public/cpp/bindings/strong_binding_set.h" +#include "services/network/public/mojom/network_service.mojom.h" +#include "third_party/blink/public/mojom/cookie_store/cookie_store.mojom.h" +#include "url/origin.h" + +class GURL; + +namespace content { + +class ServiceWorkerContextWrapper; +class ServiceWorkerRegistration; + +// Manages cookie change subscriptions for a StoragePartition's service workers. +// +// Subscriptions are stored along with their associated service worker +// registrations in ServiceWorkerStorage, as user data. When a service worker is +// unregistered, its cookie change subscriptions are removed. The storage method +// (user data) is an implementation detail. Callers should not rely on it, as +// the storage method may change in the future. +// +// Instances of this class must be accessed exclusively on the IO thread, +// because they call into ServiceWorkerContextWrapper methods that are +// restricted to the IO thread. +class CookieStoreManager : public ServiceWorkerContextCoreObserver, + public ::network::mojom::CookieChangeListener { + public: + // Creates a CookieStoreManager with an empty in-memory subscription database. + // + // The in-memory subscription database must be populated with data from disk, + // by calling ReadAllSubscriptions(). + CookieStoreManager( + scoped_refptr service_worker_context); + + ~CookieStoreManager() override; + + // Creates a mojo connection to a service worker. + // + // This is called when service workers use the Cookie Store API to subscribe + // to cookie changes or obtain the list of cookie changes. + void CreateService(blink::mojom::CookieStoreRequest request, + const url::Origin& origin); + + // Starts loading the on-disk subscription data. + // + // Returns after scheduling the work. The callback is called with a boolean + // that indicates if the load operation succeeded. + // + // It is safe to call all the other CookieStoreManager methods during the + // loading operation. The CookieStoreManager has well-defined semantics if + // loading fails, so it is not necessary to handle loading errors. + void LoadAllSubscriptions(base::OnceCallback callback); + + // Processes cookie changes from a network service instance. + void ListenToCookieChanges(::network::mojom::CookieManagerPtr cookie_manager, + base::OnceCallback callback); + + // content::mojom::CookieStore implementation + void AppendSubscriptions( + int64_t service_worker_registration_id, + const url::Origin& origin, + std::vector mojo_subscriptions, + blink::mojom::CookieStore::AppendSubscriptionsCallback callback); + void GetSubscriptions( + int64_t service_worker_registration_id, + const url::Origin& origin, + blink::mojom::CookieStore::GetSubscriptionsCallback callback); + + // ServiceWorkerContextCoreObserver + void OnRegistrationStored(int64_t service_worker_registration_id, + const GURL& pattern) override; + void OnRegistrationDeleted(int64_t service_worker_registration_id, + const GURL& pattern) override; + void OnNewLiveRegistration(int64_t service_worker_registration_id, + const GURL& pattern) override; + void OnStorageWiped() override; + + // ::network::mojom::CookieChangeListener + void OnCookieChange(const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause) override; + + private: + // Updates internal state with the result of loading disk subscription data. + // + // Called exactly once. + void ProcessOnDiskSubscriptions( + base::OnceCallback load_callback, + const std::vector>& user_data, + ServiceWorkerStatusCode status); + + // Runs all the callbacks waiting for on-disk subscription data. + // + // Called exactly once, after on-disk subcriptions have been loaded. + void DidLoadAllSubscriptions(bool succeeded, + base::OnceCallback load_callback); + + // Starts sending cookie change events to a service worker. + // + // All subscriptions must belong to the same service worker registration. This + // method is not idempotent. + void ActivateSubscriptions( + std::vector* subscriptions); + + // Stops sending cookie change events to a service worker. + // + // All subscriptions must belong to the same service worker registration. This + // method is not idempotent. + void DeactivateSubscriptions( + std::vector* subscriptions); + + // Sends a cookie change to interested service workers. + // + // Must only be called after the on-disk subscription data is successfully + // loaded. + void DispatchCookieChange(const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause); + + // Sends a cookie change event to one service worker. + void DispatchChangeEvent( + scoped_refptr registration, + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause); + + // Called after a service worker was started so it can get a cookie change. + void DidStartWorkerForChangeEvent( + scoped_refptr registration, + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause, + ServiceWorkerStatusCode start_worker_status); + + // Used to efficiently implement OnRegistrationDeleted(). + // + // When a service worker registration is removed from the system, the + // CookieStoreManager needs to remove all the cookie change subscriptions + // associated with the registration. Looking up the registration ID in the + // |subscriptions_by_registration_| map is done in O(1) time, and then each + // subscription is removed from a LinkedList in |subscription_by_url_key_| in + // O(1) time. + std::unordered_map> + subscriptions_by_registration_; + + // Used to efficiently implement DispatchCookieChange(). + // + // When a cookie change notification comes from the network service, the + // CookieStoreManager needs to dispatch events to the workers with relevant + // subscriptions. |subscriptions_by_url_key_| indexes change subscriptions + // according to the eTLD+1 of the subscription's scope URL, so each cookie + // change only needs to be checked against the subscriptions of the service + // workers in the same eTLD+1. The reduction in work is signficant, given that + // checking whether a subscription matches a cookie isn't very cheap. + // + // The current implementation's performance profile could have been achieved + // with a map from eTLD+1 to registration IDs, which would not have required + // linked lists. However, the current approach is more amenable to future + // optimizations, such as partitioning by (eTLD+1, cookie name). + std::map> + subscriptions_by_url_key_; + + // Used to look up and modify service worker registration data. + scoped_refptr service_worker_context_; + + // Tracks the open mojo pipes created by CreateService(). + // + // Each pipe is associated with the CookieStoreHost instance that it is + // connected to. When the pipe is closed, the StrongBindingSet automatically + // deletes the CookieStoreHost. + mojo::StrongBindingSet bindings_; + + // Used to receive cookie changes from the network service. + ::network::mojom::CookieManagerPtr cookie_manager_; + mojo::Binding<::network::mojom::CookieChangeListener> + cookie_change_listener_binding_; + + // The service worker registration user data key for subscription data. + // + // All the subscriptions associated with a registration are stored in a single + // user data entry whose key is |registration_user_data_key_|, and whose value + // is a serialized CookieChangeSubscriptionsProto. + const std::string registration_user_data_key_; + + // Called after all subscriptions have been loaded. + // + // Callbacks can assume that |done_loading_subscriptions_| is true + // and |succeeded_loading_subscriptions_| is set. If the latter is true, + // |subscriptions_by_registration_| and |subscriptions_by_url_key_| will also + // be populated. + std::vector subscriptions_loaded_callbacks_; + + // Set to true once all subscriptions have been loaded. + bool done_loading_subscriptions_ = false; + + // Only defined when |done_loading_subscriptions_| is true. + bool succeeded_loading_subscriptions_ = false; + + // Instances of this class are currently bound to the IO thread, because they + // call ServiceWorkerContextWrapper methods that are restricted to the IO + // thread. However, the class implementation itself is thread-friendly, so it + // only checks that methods are called on the same sequence. + SEQUENCE_CHECKER(sequence_checker_); + + // Supports having the manager destroyed while waiting for disk I/O. + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(CookieStoreManager); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_COOKIE_STORE_COOKIE_STORE_MANAGER_H_ diff --git a/chromium/content/browser/cookie_store/cookie_store_manager_unittest.cc b/chromium/content/browser/cookie_store/cookie_store_manager_unittest.cc new file mode 100644 index 00000000000..68c926cab07 --- /dev/null +++ b/chromium/content/browser/cookie_store/cookie_store_manager_unittest.cc @@ -0,0 +1,863 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/bind.h" +#include "base/files/scoped_temp_dir.h" +#include "base/macros.h" +#include "base/memory/scoped_refptr.h" +#include "content/browser/cookie_store/cookie_store_context.h" +#include "content/browser/cookie_store/cookie_store_manager.h" +#include "content/browser/service_worker/embedded_worker_test_helper.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" +#include "content/browser/storage_partition_impl.h" +#include "content/common/service_worker/service_worker_event_dispatcher.mojom.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 "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" +#include "url/gurl.h" +#include "url/origin.h" + +namespace content { + +namespace { + +// Synchronous proxies to a wrapped CookieStore service's methods. +class CookieStoreSync { + public: + using Subscriptions = std::vector; + + // The caller must ensure that the CookieStore service outlives this. + explicit CookieStoreSync(blink::mojom::CookieStore* cookie_store_service) + : cookie_store_service_(cookie_store_service) {} + ~CookieStoreSync() = default; + + bool AppendSubscriptions(int64_t service_worker_registration_id, + Subscriptions subscriptions) { + bool success; + base::RunLoop run_loop; + cookie_store_service_->AppendSubscriptions( + service_worker_registration_id, std::move(subscriptions), + base::BindOnce( + [](base::RunLoop* run_loop, bool* success, bool service_success) { + *success = service_success; + run_loop->Quit(); + }, + &run_loop, &success)); + run_loop.Run(); + return success; + } + + base::Optional GetSubscriptions( + int64_t service_worker_registration_id) { + base::Optional result; + base::RunLoop run_loop; + cookie_store_service_->GetSubscriptions( + service_worker_registration_id, + base::BindOnce( + [](base::RunLoop* run_loop, base::Optional* result, + Subscriptions service_result, bool service_success) { + if (service_success) + *result = std::move(service_result); + run_loop->Quit(); + }, + &run_loop, &result)); + run_loop.Run(); + return result; + } + + private: + blink::mojom::CookieStore* cookie_store_service_; + + DISALLOW_COPY_AND_ASSIGN(CookieStoreSync); +}; + +const char kExampleScope[] = "https://example.com/a"; +const char kExampleWorkerScript[] = "https://example.com/a/script.js"; +const char kGoogleScope[] = "https://google.com/a"; +const char kGoogleWorkerScript[] = "https://google.com/a/script.js"; + +// Mocks a service worker that uses the cookieStore API. +class CookieStoreWorkerTestHelper : public EmbeddedWorkerTestHelper { + public: + using EmbeddedWorkerTestHelper::EmbeddedWorkerTestHelper; + + // Sets the cookie change subscriptions requested in the next install event. + void SetOnInstallSubscriptions( + std::vector subscription_batches, + blink::mojom::CookieStore* cookie_store_service) { + install_subscription_batches_ = std::move(subscription_batches); + cookie_store_service_ = cookie_store_service; + } + + // Spins inside a run loop until a service worker activate event is received. + void WaitForActivateEvent() { + base::RunLoop run_loop; + quit_on_activate_ = &run_loop; + run_loop.Run(); + } + + // The data in the CookieChangeEvents received by the worker. + std::vector< + std::pair>& + changes() { + return changes_; + } + + protected: + // Collects the worker's registration ID for OnInstallEvent(). + void OnStartWorker( + int embedded_worker_id, + int64_t service_worker_version_id, + const GURL& scope, + const GURL& script_url, + bool pause_after_download, + mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, + mojom::ControllerServiceWorkerRequest controller_request, + mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, + mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, + blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) + override { + ServiceWorkerVersion* service_worker_version = + context()->GetLiveVersion(service_worker_version_id); + DCHECK(service_worker_version); + service_worker_registration_id_ = service_worker_version->registration_id(); + + EmbeddedWorkerTestHelper::OnStartWorker( + embedded_worker_id, service_worker_version_id, scope, script_url, + pause_after_download, std::move(dispatcher_request), + std::move(controller_request), std::move(instance_host), + std::move(provider_info), std::move(installed_scripts_info)); + } + + // Cookie change subscriptions can only be created in this event handler. + void OnInstallEvent( + mojom::ServiceWorkerEventDispatcher::DispatchInstallEventCallback + callback) override { + for (auto& subscriptions : install_subscription_batches_) { + cookie_store_service_->AppendSubscriptions( + service_worker_registration_id_, std::move(subscriptions), + base::BindOnce([](bool success) { + CHECK(success) << "AppendSubscriptions failed"; + })); + } + install_subscription_batches_.clear(); + + EmbeddedWorkerTestHelper::OnInstallEvent(std::move(callback)); + } + + // Used to implement WaitForActivateEvent(). + void OnActivateEvent( + mojom::ServiceWorkerEventDispatcher::DispatchActivateEventCallback + callback) override { + if (quit_on_activate_) { + quit_on_activate_->Quit(); + quit_on_activate_ = nullptr; + } + + EmbeddedWorkerTestHelper::OnActivateEvent(std::move(callback)); + } + + void OnCookieChangeEvent( + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause, + mojom::ServiceWorkerEventDispatcher::DispatchCookieChangeEventCallback + callback) override { + changes_.emplace_back(cookie, cause); + std::move(callback).Run(blink::mojom::ServiceWorkerEventStatus::COMPLETED, + base::Time::Now()); + } + + private: + // Used to add cookie change subscriptions during OnInstallEvent(). + blink::mojom::CookieStore* cookie_store_service_ = nullptr; + std::vector install_subscription_batches_; + int64_t service_worker_registration_id_; + + // Set by WaitForActivateEvent(), used in OnActivateEvent(). + base::RunLoop* quit_on_activate_ = nullptr; + + // Collects the changes reported to OnCookieChangeEvent(). + std::vector< + std::pair> + changes_; +}; + +} // namespace + +// This class cannot be in an anonymous namespace because it needs to be a +// friend of StoragePartitionImpl, to access its constructor. +class CookieStoreManagerTest + : public testing::Test, + public testing::WithParamInterface { + public: + CookieStoreManagerTest() + : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {} + + void SetUp() override { + // Use an on-disk service worker storage to test saving and loading. + ASSERT_TRUE(user_data_directory_.CreateUniqueTempDir()); + + ResetServiceWorkerContext(); + } + + void TearDown() override { + thread_bundle_.RunUntilIdle(); + + // Smart pointers are reset manually in destruction order because this is + // called by ResetServiceWorkerContext(). + example_service_.reset(); + google_service_.reset(); + example_service_ptr_.reset(); + google_service_ptr_.reset(); + cookie_manager_.reset(); + cookie_store_context_ = nullptr; + storage_partition_impl_.reset(); + worker_test_helper_.reset(); + } + + void ResetServiceWorkerContext() { + if (cookie_store_context_) + TearDown(); + + worker_test_helper_ = std::make_unique( + user_data_directory_.GetPath()); + cookie_store_context_ = base::MakeRefCounted(); + cookie_store_context_->Initialize(worker_test_helper_->context_wrapper(), + base::BindOnce([](bool success) { + CHECK(success) << "Initialize failed"; + })); + storage_partition_impl_ = base::WrapUnique( + new StoragePartitionImpl(worker_test_helper_->browser_context(), + user_data_directory_.GetPath(), nullptr)); + storage_partition_impl_->SetURLRequestContext( + worker_test_helper_->browser_context() + ->CreateRequestContextForStoragePartition( + user_data_directory_.GetPath(), false, nullptr, + URLRequestInterceptorScopedVector())); + ::network::mojom::NetworkContext* network_context = + storage_partition_impl_->GetNetworkContext(); + cookie_store_context_->ListenToCookieChanges( + network_context, base::BindOnce([](bool success) { + CHECK(success) << "ListenToCookieChanges failed"; + })); + network_context->GetCookieManager(mojo::MakeRequest(&cookie_manager_)); + + cookie_store_context_->CreateService( + mojo::MakeRequest(&example_service_ptr_), + url::Origin::Create(GURL(kExampleScope))); + example_service_ = + std::make_unique(example_service_ptr_.get()); + + cookie_store_context_->CreateService( + mojo::MakeRequest(&google_service_ptr_), + url::Origin::Create(GURL(kGoogleScope))); + google_service_ = + std::make_unique(google_service_ptr_.get()); + } + + int64_t RegisterServiceWorker(const char* scope, const char* script_url) { + bool success = false; + int64_t registration_id; + blink::mojom::ServiceWorkerRegistrationOptions options; + options.scope = GURL(scope); + base::RunLoop run_loop; + worker_test_helper_->context()->RegisterServiceWorker( + GURL(script_url), options, + base::BindOnce( + [](base::RunLoop* run_loop, bool* success, int64_t* registration_id, + ServiceWorkerStatusCode status, + const std::string& status_message, + int64_t service_worker_registration_id) { + *success = (status == SERVICE_WORKER_OK); + *registration_id = service_worker_registration_id; + EXPECT_EQ(SERVICE_WORKER_OK, status) + << ServiceWorkerStatusToString(status); + run_loop->Quit(); + }, + &run_loop, &success, ®istration_id)); + run_loop.Run(); + if (!success) + return kInvalidRegistrationId; + + worker_test_helper_->WaitForActivateEvent(); + return registration_id; + } + + // Simplified helper for SetCanonicalCookie. + // + // Creates a CanonicalCookie that is not secure, not http-only, + // and not restricted to first parties. Returns false if creation fails. + bool SetSessionCookie(const char* name, + const char* value, + const char* domain, + const char* path) { + net::CanonicalCookie cookie( + name, value, domain, path, base::Time(), base::Time(), base::Time(), + /* secure = */ false, + /* httponly = */ false, net::CookieSameSite::NO_RESTRICTION, + net::COOKIE_PRIORITY_DEFAULT); + base::RunLoop run_loop; + bool success = false; + cookie_manager_->SetCanonicalCookie( + cookie, /* secure_source = */ true, /* can_modify_httponly = */ true, + base::BindOnce( + [](base::RunLoop* run_loop, bool* success, bool service_success) { + *success = success; + run_loop->Quit(); + }, + &run_loop, &success)); + run_loop.Run(); + return success; + } + + bool reset_context_during_test() const { return GetParam(); } + + static constexpr const int64_t kInvalidRegistrationId = -1; + + protected: + TestBrowserThreadBundle thread_bundle_; + base::ScopedTempDir user_data_directory_; + std::unique_ptr worker_test_helper_; + std::unique_ptr storage_partition_impl_; + scoped_refptr cookie_store_context_; + ::network::mojom::CookieManagerPtr cookie_manager_; + + blink::mojom::CookieStorePtr example_service_ptr_, google_service_ptr_; + std::unique_ptr example_service_, google_service_; +}; + +const int64_t CookieStoreManagerTest::kInvalidRegistrationId; + +namespace { + +// Useful for sorting a vector of cookie change subscriptions. +bool CookieChangeSubscriptionLessThan( + const blink::mojom::CookieChangeSubscriptionPtr& lhs, + const blink::mojom::CookieChangeSubscriptionPtr& rhs) { + return std::tie(lhs->name, lhs->match_type, lhs->url) < + std::tie(rhs->name, rhs->match_type, rhs->url); +} + +TEST_P(CookieStoreManagerTest, NoSubscriptions) { + worker_test_helper_->SetOnInstallSubscriptions( + std::vector(), + example_service_ptr_.get()); + int64_t registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(registration_id, kInvalidRegistrationId); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + EXPECT_EQ(0u, all_subscriptions_opt.value().size()); +} + +TEST_P(CookieStoreManagerTest, EmptySubscriptions) { + std::vector batches; + batches.emplace_back(); + worker_test_helper_->SetOnInstallSubscriptions(std::move(batches), + example_service_ptr_.get()); + int64_t registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(registration_id, kInvalidRegistrationId); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + EXPECT_EQ(0u, all_subscriptions_opt.value().size()); +} + +TEST_P(CookieStoreManagerTest, OneSubscription) { + std::vector batches; + batches.emplace_back(); + + CookieStoreSync::Subscriptions& subscriptions = batches.back(); + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "cookie_name_prefix"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL(kExampleScope); + + worker_test_helper_->SetOnInstallSubscriptions(std::move(batches), + example_service_ptr_.get()); + int64_t registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(registration_id, kInvalidRegistrationId); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + CookieStoreSync::Subscriptions all_subscriptions = + std::move(all_subscriptions_opt).value(); + EXPECT_EQ(1u, all_subscriptions.size()); + EXPECT_EQ("cookie_name_prefix", all_subscriptions[0]->name); + EXPECT_EQ(::network::mojom::CookieMatchType::STARTS_WITH, + all_subscriptions[0]->match_type); + EXPECT_EQ(GURL(kExampleScope), all_subscriptions[0]->url); +} + +TEST_P(CookieStoreManagerTest, AppendSubscriptionsAfterEmptyInstall) { + worker_test_helper_->SetOnInstallSubscriptions( + std::vector(), + example_service_ptr_.get()); + int64_t registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(registration_id, kInvalidRegistrationId); + + CookieStoreSync::Subscriptions subscriptions; + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "cookie_name_prefix"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL(kExampleScope); + + EXPECT_FALSE(example_service_->AppendSubscriptions(registration_id, + std::move(subscriptions))); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + EXPECT_EQ(0u, all_subscriptions_opt.value().size()); +} + +TEST_P(CookieStoreManagerTest, AppendSubscriptionsAfterInstall) { + { + std::vector batches; + batches.emplace_back(); + + CookieStoreSync::Subscriptions& subscriptions = batches.back(); + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "cookie_name_prefix"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL(kExampleScope); + + worker_test_helper_->SetOnInstallSubscriptions(std::move(batches), + example_service_ptr_.get()); + } + int64_t registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(registration_id, kInvalidRegistrationId); + + { + CookieStoreSync::Subscriptions subscriptions; + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "cookie_name"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::EQUALS; + subscriptions.back()->url = GURL(kExampleScope); + + EXPECT_FALSE(example_service_->AppendSubscriptions( + registration_id, std::move(subscriptions))); + } + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + CookieStoreSync::Subscriptions all_subscriptions = + std::move(all_subscriptions_opt).value(); + EXPECT_EQ(1u, all_subscriptions.size()); + EXPECT_EQ("cookie_name_prefix", all_subscriptions[0]->name); + EXPECT_EQ(::network::mojom::CookieMatchType::STARTS_WITH, + all_subscriptions[0]->match_type); + EXPECT_EQ(GURL(kExampleScope), all_subscriptions[0]->url); +} + +TEST_P(CookieStoreManagerTest, AppendSubscriptionsFromWrongOrigin) { + worker_test_helper_->SetOnInstallSubscriptions( + std::vector(), + example_service_ptr_.get()); + int64_t example_registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(example_registration_id, kInvalidRegistrationId); + + CookieStoreSync::Subscriptions subscriptions; + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "cookie_name_prefix"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL(kExampleScope); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + EXPECT_FALSE(google_service_->AppendSubscriptions(example_registration_id, + std::move(subscriptions))); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(example_registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + EXPECT_EQ(0u, all_subscriptions_opt.value().size()); +} + +TEST_P(CookieStoreManagerTest, AppendSubscriptionsInvalidRegistrationId) { + worker_test_helper_->SetOnInstallSubscriptions( + std::vector(), + example_service_ptr_.get()); + int64_t registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(registration_id, kInvalidRegistrationId); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + CookieStoreSync::Subscriptions subscriptions; + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "cookie_name_prefix"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL(kExampleScope); + + EXPECT_FALSE(example_service_->AppendSubscriptions(registration_id + 100, + std::move(subscriptions))); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + EXPECT_EQ(0u, all_subscriptions_opt.value().size()); +} + +TEST_P(CookieStoreManagerTest, MultiWorkerSubscriptions) { + { + std::vector batches; + batches.emplace_back(); + + CookieStoreSync::Subscriptions& subscriptions = batches.back(); + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "cookie_name_prefix"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL(kExampleScope); + + worker_test_helper_->SetOnInstallSubscriptions(std::move(batches), + example_service_ptr_.get()); + } + int64_t example_registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(example_registration_id, kInvalidRegistrationId); + + { + std::vector batches; + batches.emplace_back(); + + CookieStoreSync::Subscriptions& subscriptions = batches.back(); + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "cookie_name"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::EQUALS; + subscriptions.back()->url = GURL(kGoogleScope); + + worker_test_helper_->SetOnInstallSubscriptions(std::move(batches), + google_service_ptr_.get()); + } + int64_t google_registration_id = + RegisterServiceWorker(kGoogleScope, kGoogleWorkerScript); + ASSERT_NE(google_registration_id, kInvalidRegistrationId); + EXPECT_NE(example_registration_id, google_registration_id); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + base::Optional example_subscriptions_opt = + example_service_->GetSubscriptions(example_registration_id); + ASSERT_TRUE(example_subscriptions_opt.has_value()); + CookieStoreSync::Subscriptions example_subscriptions = + std::move(example_subscriptions_opt).value(); + EXPECT_EQ(1u, example_subscriptions.size()); + EXPECT_EQ("cookie_name_prefix", example_subscriptions[0]->name); + EXPECT_EQ(::network::mojom::CookieMatchType::STARTS_WITH, + example_subscriptions[0]->match_type); + EXPECT_EQ(GURL(kExampleScope), example_subscriptions[0]->url); + + base::Optional google_subscriptions_opt = + google_service_->GetSubscriptions(google_registration_id); + ASSERT_TRUE(google_subscriptions_opt.has_value()); + CookieStoreSync::Subscriptions google_subscriptions = + std::move(google_subscriptions_opt).value(); + EXPECT_EQ(1u, google_subscriptions.size()); + EXPECT_EQ("cookie_name", google_subscriptions[0]->name); + EXPECT_EQ(::network::mojom::CookieMatchType::EQUALS, + google_subscriptions[0]->match_type); + EXPECT_EQ(GURL(kGoogleScope), google_subscriptions[0]->url); +} + +TEST_P(CookieStoreManagerTest, MultipleSubscriptions) { + std::vector batches; + + { + batches.emplace_back(); + CookieStoreSync::Subscriptions& subscriptions = batches.back(); + + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "name1"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL("https://example.com/a/1"); + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "name2"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::EQUALS; + subscriptions.back()->url = GURL("https://example.com/a/2"); + } + + batches.emplace_back(); + + { + batches.emplace_back(); + CookieStoreSync::Subscriptions& subscriptions = batches.back(); + + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "name3"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL("https://example.com/a/3"); + } + + worker_test_helper_->SetOnInstallSubscriptions(std::move(batches), + example_service_ptr_.get()); + int64_t registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(registration_id, kInvalidRegistrationId); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + CookieStoreSync::Subscriptions all_subscriptions = + std::move(all_subscriptions_opt).value(); + + std::sort(all_subscriptions.begin(), all_subscriptions.end(), + CookieChangeSubscriptionLessThan); + + EXPECT_EQ(3u, all_subscriptions.size()); + EXPECT_EQ("name1", all_subscriptions[0]->name); + EXPECT_EQ(::network::mojom::CookieMatchType::STARTS_WITH, + all_subscriptions[0]->match_type); + EXPECT_EQ(GURL("https://example.com/a/1"), all_subscriptions[0]->url); + EXPECT_EQ("name2", all_subscriptions[1]->name); + EXPECT_EQ(::network::mojom::CookieMatchType::EQUALS, + all_subscriptions[1]->match_type); + EXPECT_EQ(GURL("https://example.com/a/2"), all_subscriptions[1]->url); + EXPECT_EQ("name3", all_subscriptions[2]->name); + EXPECT_EQ(::network::mojom::CookieMatchType::STARTS_WITH, + all_subscriptions[2]->match_type); + EXPECT_EQ(GURL("https://example.com/a/3"), all_subscriptions[2]->url); +} + +TEST_P(CookieStoreManagerTest, OneCookieChange) { + std::vector batches; + batches.emplace_back(); + + CookieStoreSync::Subscriptions& subscriptions = batches.back(); + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = ""; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL(kExampleScope); + + worker_test_helper_->SetOnInstallSubscriptions(std::move(batches), + example_service_ptr_.get()); + int64_t registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(registration_id, kInvalidRegistrationId); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + ASSERT_EQ(1u, all_subscriptions_opt.value().size()); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + ASSERT_TRUE( + SetSessionCookie("cookie-name", "cookie-value", "example.com", "/")); + thread_bundle_.RunUntilIdle(); + + ASSERT_EQ(1u, worker_test_helper_->changes().size()); + EXPECT_EQ("cookie-name", worker_test_helper_->changes()[0].first.Name()); + EXPECT_EQ("cookie-value", worker_test_helper_->changes()[0].first.Value()); + EXPECT_EQ("example.com", worker_test_helper_->changes()[0].first.Domain()); + EXPECT_EQ("/", worker_test_helper_->changes()[0].first.Path()); + EXPECT_EQ(::network::mojom::CookieChangeCause::INSERTED, + worker_test_helper_->changes()[0].second); +} + +TEST_P(CookieStoreManagerTest, CookieChangeNameStartsWith) { + std::vector batches; + batches.emplace_back(); + + CookieStoreSync::Subscriptions& subscriptions = batches.back(); + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "cookie-name-2"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL(kExampleScope); + + worker_test_helper_->SetOnInstallSubscriptions(std::move(batches), + example_service_ptr_.get()); + int64_t registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(registration_id, kInvalidRegistrationId); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + ASSERT_EQ(1u, all_subscriptions_opt.value().size()); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + ASSERT_TRUE( + SetSessionCookie("cookie-name-1", "cookie-value-1", "example.com", "/")); + thread_bundle_.RunUntilIdle(); + EXPECT_EQ(0u, worker_test_helper_->changes().size()); + + worker_test_helper_->changes().clear(); + ASSERT_TRUE( + SetSessionCookie("cookie-name-2", "cookie-value-2", "example.com", "/")); + thread_bundle_.RunUntilIdle(); + + ASSERT_EQ(1u, worker_test_helper_->changes().size()); + EXPECT_EQ("cookie-name-2", worker_test_helper_->changes()[0].first.Name()); + EXPECT_EQ("cookie-value-2", worker_test_helper_->changes()[0].first.Value()); + EXPECT_EQ("example.com", worker_test_helper_->changes()[0].first.Domain()); + EXPECT_EQ("/", worker_test_helper_->changes()[0].first.Path()); + EXPECT_EQ(::network::mojom::CookieChangeCause::INSERTED, + worker_test_helper_->changes()[0].second); + + worker_test_helper_->changes().clear(); + ASSERT_TRUE(SetSessionCookie("cookie-name-22", "cookie-value-22", + "example.com", "/")); + thread_bundle_.RunUntilIdle(); + + ASSERT_EQ(1u, worker_test_helper_->changes().size()); + EXPECT_EQ("cookie-name-22", worker_test_helper_->changes()[0].first.Name()); + EXPECT_EQ("cookie-value-22", worker_test_helper_->changes()[0].first.Value()); + EXPECT_EQ("example.com", worker_test_helper_->changes()[0].first.Domain()); + EXPECT_EQ("/", worker_test_helper_->changes()[0].first.Path()); + EXPECT_EQ(::network::mojom::CookieChangeCause::INSERTED, + worker_test_helper_->changes()[0].second); +} + +TEST_P(CookieStoreManagerTest, CookieChangeUrl) { + std::vector batches; + batches.emplace_back(); + + CookieStoreSync::Subscriptions& subscriptions = batches.back(); + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = ""; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL(kExampleScope); + + worker_test_helper_->SetOnInstallSubscriptions(std::move(batches), + example_service_ptr_.get()); + int64_t registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(registration_id, kInvalidRegistrationId); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + ASSERT_EQ(1u, all_subscriptions_opt.value().size()); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + ASSERT_TRUE( + SetSessionCookie("cookie-name-1", "cookie-value-1", "google.com", "/")); + thread_bundle_.RunUntilIdle(); + ASSERT_EQ(0u, worker_test_helper_->changes().size()); + + worker_test_helper_->changes().clear(); + ASSERT_TRUE(SetSessionCookie("cookie-name-2", "cookie-value-2", "example.com", + "/a/subpath")); + thread_bundle_.RunUntilIdle(); + EXPECT_EQ(0u, worker_test_helper_->changes().size()); + + worker_test_helper_->changes().clear(); + ASSERT_TRUE( + SetSessionCookie("cookie-name-3", "cookie-value-3", "example.com", "/")); + thread_bundle_.RunUntilIdle(); + + ASSERT_EQ(1u, worker_test_helper_->changes().size()); + EXPECT_EQ("cookie-name-3", worker_test_helper_->changes()[0].first.Name()); + EXPECT_EQ("cookie-value-3", worker_test_helper_->changes()[0].first.Value()); + EXPECT_EQ("example.com", worker_test_helper_->changes()[0].first.Domain()); + EXPECT_EQ("/", worker_test_helper_->changes()[0].first.Path()); + EXPECT_EQ(::network::mojom::CookieChangeCause::INSERTED, + worker_test_helper_->changes()[0].second); + + worker_test_helper_->changes().clear(); + ASSERT_TRUE( + SetSessionCookie("cookie-name-4", "cookie-value-4", "example.com", "/a")); + thread_bundle_.RunUntilIdle(); + + ASSERT_EQ(1u, worker_test_helper_->changes().size()); + EXPECT_EQ("cookie-name-4", worker_test_helper_->changes()[0].first.Name()); + EXPECT_EQ("cookie-value-4", worker_test_helper_->changes()[0].first.Value()); + EXPECT_EQ("example.com", worker_test_helper_->changes()[0].first.Domain()); + EXPECT_EQ("/a", worker_test_helper_->changes()[0].first.Path()); + EXPECT_EQ(::network::mojom::CookieChangeCause::INSERTED, + worker_test_helper_->changes()[0].second); +} + +TEST_P(CookieStoreManagerTest, GetSubscriptionsFromWrongOrigin) { + std::vector batches; + batches.emplace_back(); + + CookieStoreSync::Subscriptions& subscriptions = batches.back(); + subscriptions.emplace_back(blink::mojom::CookieChangeSubscription::New()); + subscriptions.back()->name = "cookie_name_prefix"; + subscriptions.back()->match_type = + ::network::mojom::CookieMatchType::STARTS_WITH; + subscriptions.back()->url = GURL(kExampleScope); + + worker_test_helper_->SetOnInstallSubscriptions(std::move(batches), + example_service_ptr_.get()); + int64_t example_registration_id = + RegisterServiceWorker(kExampleScope, kExampleWorkerScript); + ASSERT_NE(example_registration_id, kInvalidRegistrationId); + + if (reset_context_during_test()) + ResetServiceWorkerContext(); + + base::Optional all_subscriptions_opt = + example_service_->GetSubscriptions(example_registration_id); + ASSERT_TRUE(all_subscriptions_opt.has_value()); + EXPECT_EQ(1u, all_subscriptions_opt.value().size()); + + base::Optional wrong_subscriptions_opt = + google_service_->GetSubscriptions(example_registration_id); + EXPECT_FALSE(wrong_subscriptions_opt.has_value()); +} + +INSTANTIATE_TEST_CASE_P(CookieStoreManagerTest, + CookieStoreManagerTest, + testing::Bool() /* reset_storage_during_test */); + +} // namespace + +} // namespace content diff --git a/chromium/content/browser/dedicated_worker/dedicated_worker_host.cc b/chromium/content/browser/dedicated_worker/dedicated_worker_host.cc index 2f89c680233..86b2b534245 100644 --- a/chromium/content/browser/dedicated_worker/dedicated_worker_host.cc +++ b/chromium/content/browser/dedicated_worker/dedicated_worker_host.cc @@ -7,11 +7,14 @@ #include "content/browser/dedicated_worker/dedicated_worker_host.h" +#include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/interface_provider_filtering.h" #include "content/browser/renderer_interface_binders.h" +#include "content/browser/websockets/websocket_manager.h" #include "content/public/browser/render_process_host.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/system/message_pipe.h" +#include "services/service_manager/public/cpp/binder_registry.h" #include "services/service_manager/public/mojom/interface_provider.mojom.h" #include "url/origin.h" @@ -23,8 +26,14 @@ namespace { // StrongBinding. class DedicatedWorkerHost : public service_manager::mojom::InterfaceProvider { public: - DedicatedWorkerHost(int process_id, const url::Origin& origin) - : process_id_(process_id), origin_(origin) {} + DedicatedWorkerHost(int process_id, + int parent_render_frame_id, + const url::Origin& origin) + : process_id_(process_id), + parent_render_frame_id_(parent_render_frame_id), + origin_(origin) { + RegisterMojoInterfaces(); + } // service_manager::mojom::InterfaceProvider: void GetInterface(const std::string& interface_name, @@ -33,14 +42,37 @@ class DedicatedWorkerHost : public service_manager::mojom::InterfaceProvider { if (!process) return; + // See if the registry that is specific to this worker host wants to handle + // the interface request. + if (registry_.TryBindInterface(interface_name, &interface_pipe)) + return; + BindWorkerInterface(interface_name, std::move(interface_pipe), process, origin_); } private: + void RegisterMojoInterfaces() { + registry_.AddInterface( + base::BindRepeating(&WebSocketManager::CreateWebSocket, process_id_, + parent_render_frame_id_, origin_)); + registry_.AddInterface(base::BindRepeating( + &DedicatedWorkerHost::CreateUsbDeviceManager, base::Unretained(this))); + } + + void CreateUsbDeviceManager(device::mojom::UsbDeviceManagerRequest request) { + auto* host = + RenderFrameHostImpl::FromID(process_id_, parent_render_frame_id_); + GetContentClient()->browser()->CreateUsbDeviceManager(host, + std::move(request)); + } + const int process_id_; + const int parent_render_frame_id_; const url::Origin origin_; + service_manager::BinderRegistry registry_; + DISALLOW_COPY_AND_ASSIGN(DedicatedWorkerHost); }; @@ -49,8 +81,10 @@ class DedicatedWorkerHost : public service_manager::mojom::InterfaceProvider { class DedicatedWorkerFactoryImpl : public blink::mojom::DedicatedWorkerFactory { public: DedicatedWorkerFactoryImpl(int process_id, + int parent_render_frame_id, const url::Origin& parent_context_origin) : process_id_(process_id), + parent_render_frame_id_(parent_render_frame_id), parent_context_origin_(parent_context_origin) {} // blink::mojom::DedicatedWorkerFactory: @@ -60,15 +94,16 @@ class DedicatedWorkerFactoryImpl : public blink::mojom::DedicatedWorkerFactory { // TODO(crbug.com/729021): Once |parent_context_origin_| is no longer races // with the request for |DedicatedWorkerFactory|, enforce that the worker's // origin either matches the creating document's origin, or is unique. - mojo::MakeStrongBinding( - std::make_unique(process_id_, origin), - FilterRendererExposedInterfaces( - blink::mojom::kNavigation_DedicatedWorkerSpec, process_id_, - std::move(request))); + mojo::MakeStrongBinding(std::make_unique( + process_id_, parent_render_frame_id_, origin), + FilterRendererExposedInterfaces( + blink::mojom::kNavigation_DedicatedWorkerSpec, + process_id_, std::move(request))); } private: const int process_id_; + const int parent_render_frame_id_; const url::Origin parent_context_origin_; DISALLOW_COPY_AND_ASSIGN(DedicatedWorkerFactoryImpl); @@ -77,12 +112,13 @@ class DedicatedWorkerFactoryImpl : public blink::mojom::DedicatedWorkerFactory { } // namespace void CreateDedicatedWorkerHostFactory( - blink::mojom::DedicatedWorkerFactoryRequest request, - RenderProcessHost* host, - const url::Origin& origin) { - mojo::MakeStrongBinding( - std::make_unique(host->GetID(), origin), - std::move(request)); + int process_id, + int parent_render_frame_id, + const url::Origin& origin, + blink::mojom::DedicatedWorkerFactoryRequest request) { + mojo::MakeStrongBinding(std::make_unique( + process_id, parent_render_frame_id, origin), + std::move(request)); } } // namespace content diff --git a/chromium/content/browser/dedicated_worker/dedicated_worker_host.h b/chromium/content/browser/dedicated_worker/dedicated_worker_host.h index 60bbdd54536..587ab20cb26 100644 --- a/chromium/content/browser/dedicated_worker/dedicated_worker_host.h +++ b/chromium/content/browser/dedicated_worker/dedicated_worker_host.h @@ -12,12 +12,12 @@ class Origin; } namespace content { -class RenderProcessHost; void CreateDedicatedWorkerHostFactory( - blink::mojom::DedicatedWorkerFactoryRequest request, - RenderProcessHost* host, - const url::Origin& origin); + int process_id, + int parent_render_frame_id, + const url::Origin& origin, + blink::mojom::DedicatedWorkerFactoryRequest request); } // namespace content diff --git a/chromium/content/browser/device_sensors/device_sensor_browsertest.cc b/chromium/content/browser/device_sensors/device_sensor_browsertest.cc index b2b99a142b5..494c1c7acc1 100644 --- a/chromium/content/browser/device_sensors/device_sensor_browsertest.cc +++ b/chromium/content/browser/device_sensors/device_sensor_browsertest.cc @@ -8,7 +8,6 @@ #include "base/command_line.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" diff --git a/chromium/content/browser/devtools/devtools_agent_host_impl.cc b/chromium/content/browser/devtools/devtools_agent_host_impl.cc index 7c9b58aa5e1..a67148429f1 100644 --- a/chromium/content/browser/devtools/devtools_agent_host_impl.cc +++ b/chromium/content/browser/devtools/devtools_agent_host_impl.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/json/json_writer.h" #include "base/lazy_instance.h" -#include "base/message_loop/message_loop.h" #include "base/observer_list.h" #include "content/browser/devtools/devtools_manager.h" #include "content/browser/devtools/devtools_session.h" @@ -209,16 +208,6 @@ bool DevToolsAgentHostImpl::AttachRestrictedClient( return InnerAttachClient(client, true /* restricted */); } -void DevToolsAgentHostImpl::ForceAttachClient(DevToolsAgentHostClient* client) { - if (SessionByClient(client)) - return; - scoped_refptr protect(this); - if (!sessions_.empty()) - ForceDetachAllSessions(); - DCHECK(sessions_.empty()); - InnerAttachClient(client, false /* restricted */); -} - bool DevToolsAgentHostImpl::DetachClient(DevToolsAgentHostClient* client) { if (!SessionByClient(client)) return false; diff --git a/chromium/content/browser/devtools/devtools_agent_host_impl.h b/chromium/content/browser/devtools/devtools_agent_host_impl.h index c69bf5a70f2..6831704222e 100644 --- a/chromium/content/browser/devtools/devtools_agent_host_impl.h +++ b/chromium/content/browser/devtools/devtools_agent_host_impl.h @@ -38,7 +38,6 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost { // DevToolsAgentHost implementation. void AttachClient(DevToolsAgentHostClient* client) override; bool AttachRestrictedClient(DevToolsAgentHostClient* client) override; - void ForceAttachClient(DevToolsAgentHostClient* client) override; bool DetachClient(DevToolsAgentHostClient* client) override; bool DispatchProtocolMessage(DevToolsAgentHostClient* client, const std::string& message) override; diff --git a/chromium/content/browser/devtools/devtools_http_handler.cc b/chromium/content/browser/devtools/devtools_http_handler.cc index cae7c566146..c9187e77495 100644 --- a/chromium/content/browser/devtools/devtools_http_handler.cc +++ b/chromium/content/browser/devtools/devtools_http_handler.cc @@ -277,7 +277,7 @@ void StartServerOnHandlerThread( fflush(stderr); // Write this port to a well-known file in the profile directory - // so Telemetry can pick it up. + // so Telemetry, ChromeDriver, etc. can pick it up. if (!output_directory.empty()) { base::FilePath path = output_directory.Append(kDevToolsActivePortFileName); diff --git a/chromium/content/browser/devtools/devtools_io_context.cc b/chromium/content/browser/devtools/devtools_io_context.cc index 1e0ae3d9eee..02bc1faddb1 100644 --- a/chromium/content/browser/devtools/devtools_io_context.cc +++ b/chromium/content/browser/devtools/devtools_io_context.cc @@ -4,257 +4,65 @@ #include "content/browser/devtools/devtools_io_context.h" -#include "base/base64.h" -#include "base/containers/queue.h" -#include "base/files/file.h" -#include "base/files/file_util.h" +#include "base/bind.h" #include "base/sequenced_task_runner.h" #include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/task_scheduler/lazy_task_runner.h" -#include "base/task_scheduler/post_task.h" -#include "base/third_party/icu/icu_utf.h" -#include "base/threading/thread_restrictions.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/storage_partition.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "storage/browser/blob/blob_data_handle.h" -#include "storage/browser/blob/blob_reader.h" -#include "storage/browser/blob/blob_storage_context.h" -#include "storage/browser/fileapi/file_system_context.h" -#include "storage/common/blob_storage/blob_storage_constants.h" - -#include +#include "content/browser/devtools/devtools_stream_blob.h" +#include "content/browser/devtools/devtools_stream_file.h" namespace content { -namespace { +DevToolsIOContext::Stream::Stream( + scoped_refptr task_runner) + : RefCountedDeleteOnSequence( + std::move(task_runner)) {} -base::SequencedTaskRunner* impl_task_runner() { - constexpr base::TaskTraits kBlockingTraits = {base::MayBlock(), - base::TaskPriority::BACKGROUND}; - static base::LazySequencedTaskRunner s_sequenced_task_unner = - LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(kBlockingTraits); - return s_sequenced_task_unner.Get().get(); +std::string DevToolsIOContext::Stream::Register(DevToolsIOContext* context) { + static unsigned s_last_stream_handle = 0; + const std::string handle = base::UintToString(++s_last_stream_handle); + Register(context, handle); + return handle; } -using storage::BlobReader; - -unsigned s_last_stream_handle = 0; - -class TempFileStream : public DevToolsIOContext::RWStream { - public: - explicit TempFileStream(bool binary); - - void Read(off_t position, size_t max_size, ReadCallback callback) override; - void Close(bool invoke_pending_callbacks) override {} - void Append(std::unique_ptr data) override; - const std::string& handle() override { return handle_; } - - private: - ~TempFileStream() override; - - void ReadOnFileSequence(off_t pos, size_t max_size, ReadCallback callback); - void AppendOnFileSequence(std::unique_ptr data); - bool InitOnFileSequenceIfNeeded(); - - const std::string handle_; - base::File file_; - scoped_refptr task_runner_; - bool had_errors_; - off_t last_read_pos_; - bool binary_; - - DISALLOW_COPY_AND_ASSIGN(TempFileStream); -}; - -TempFileStream::TempFileStream(bool binary) - : DevToolsIOContext::RWStream(impl_task_runner()), - handle_(base::UintToString(++s_last_stream_handle)), - task_runner_(impl_task_runner()), - had_errors_(false), - last_read_pos_(0), - binary_(binary) {} - -TempFileStream::~TempFileStream() { - DCHECK(task_runner_->RunsTasksInCurrentSequence()); +void DevToolsIOContext::Stream::Register(DevToolsIOContext* context, + const std::string& handle) { + context->RegisterStream(this, handle); } -bool TempFileStream::InitOnFileSequenceIfNeeded() { - DCHECK(task_runner_->RunsTasksInCurrentSequence()); - base::AssertBlockingAllowed(); - if (had_errors_) - return false; - if (file_.IsValid()) - return true; - base::FilePath temp_path; - if (!base::CreateTemporaryFile(&temp_path)) { - LOG(ERROR) << "Failed to create temporary file"; - had_errors_ = true; - return false; - } - const unsigned flags = base::File::FLAG_OPEN_TRUNCATED | - base::File::FLAG_WRITE | base::File::FLAG_READ | - base::File::FLAG_DELETE_ON_CLOSE; - file_.Initialize(temp_path, flags); - if (!file_.IsValid()) { - LOG(ERROR) << "Failed to open temporary file: " << temp_path.value() - << ", " << base::File::ErrorToString(file_.error_details()); - had_errors_ = true; - DeleteFile(temp_path, false); - return false; - } +bool DevToolsIOContext::Stream::SupportsSeek() const { return true; } -void TempFileStream::Read(off_t position, - size_t max_size, - ReadCallback callback) { - task_runner_->PostTask( - FROM_HERE, base::BindOnce(&TempFileStream::ReadOnFileSequence, this, - position, max_size, std::move(callback))); -} +DevToolsIOContext::Stream::~Stream() = default; -void TempFileStream::Append(std::unique_ptr data) { - task_runner_->PostTask( - FROM_HERE, base::BindOnce(&TempFileStream::AppendOnFileSequence, this, - std::move(data))); -} +DevToolsIOContext::DevToolsIOContext() = default; -void TempFileStream::ReadOnFileSequence(off_t position, - size_t max_size, - ReadCallback callback) { - DCHECK(task_runner_->RunsTasksInCurrentSequence()); - Status status = StatusFailure; - std::unique_ptr data; - bool base64_encoded = false; +DevToolsIOContext::~DevToolsIOContext() = default; - if (file_.IsValid()) { - std::string buffer; - buffer.resize(max_size); - if (position < 0) - position = last_read_pos_; - int size_got = file_.ReadNoBestEffort(position, &*buffer.begin(), max_size); - if (size_got < 0) { - LOG(ERROR) << "Failed to read temporary file"; - had_errors_ = true; - file_.Close(); - } else { - // Provided client has requested sufficient large block, make their - // life easier by not truncating in the middle of a UTF-8 character. - if (size_got > 6 && !CBU8_IS_SINGLE(buffer[size_got - 1])) { - base::TruncateUTF8ToByteSize(buffer, size_got, &buffer); - size_got = buffer.size(); - } else { - buffer.resize(size_got); - } - data.reset(new std::string(std::move(buffer))); - status = size_got ? StatusSuccess : StatusEOF; - last_read_pos_ = position + size_got; - } - } - if (binary_) { - std::string raw_data(std::move(*data)); - base::Base64Encode(raw_data, data.get()); - base64_encoded = true; - } - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::BindOnce(std::move(callback), std::move(data), - base64_encoded, status)); +void DevToolsIOContext::RegisterStream(scoped_refptr stream, + const std::string& id) { + bool inserted = streams_.emplace(id, std::move(stream)).second; + DCHECK(inserted); } -void TempFileStream::AppendOnFileSequence(std::unique_ptr data) { - if (!InitOnFileSequenceIfNeeded()) - return; - int size_written = file_.WriteAtCurrentPos(&*data->begin(), data->length()); - if (size_written != static_cast(data->length())) { - LOG(ERROR) << "Failed to write temporary file"; - had_errors_ = true; - file_.Close(); - } +scoped_refptr DevToolsIOContext::GetByHandle( + const std::string& handle) { + auto it = streams_.find(handle); + return it == streams_.end() ? nullptr : it->second; } -class BlobStream : public DevToolsIOContext::ROStream { - public: - using OpenCallback = base::OnceCallback; - - BlobStream() - : DevToolsIOContext::ROStream( - BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)), - last_read_pos_(0), - failed_(false), - is_binary_(false) {} - - void Open(scoped_refptr context, - StoragePartition* partition, - const std::string& handle, - OpenCallback callback); - - void Read(off_t position, size_t max_size, ReadCallback callback) override; - void Close(bool invoke_pending_callbacks) override; - - private: - struct ReadRequest { - off_t position; - size_t max_size; - ReadCallback callback; - - void Fail(); - - ReadRequest(off_t position, size_t max_size, ReadCallback callback) - : position(position), - max_size(max_size), - callback(std::move(callback)) {} - }; - - ~BlobStream() override = default; - - void OpenOnIO(scoped_refptr blob_context, - const std::string& uuid, - OpenCallback callback); - void ReadOnIO(std::unique_ptr request); - void CloseOnIO(bool invoke_pending_callbacks); - - void FailOnIO(); - void FailOnIO(OpenCallback callback) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::BindOnce(std::move(callback), false)); - FailOnIO(); - } - - void StartReadRequest(); - void CreateReader(); - void BeginRead(); - - void OnReadComplete(int bytes_read); - void OnBlobConstructionComplete(storage::BlobStatus status); - void OnCalculateSizeComplete(int net_error); - - static bool IsTextMimeType(const std::string& mime_type); - - std::unique_ptr blob_handle_; - OpenCallback open_callback_; - std::unique_ptr blob_reader_; - base::queue> pending_reads_; - scoped_refptr io_buf_; - off_t last_read_pos_; - bool failed_; - bool is_binary_; - - DISALLOW_COPY_AND_ASSIGN(BlobStream); -}; +bool DevToolsIOContext::Close(const std::string& handle) { + size_t erased_count = streams_.erase(handle); + return !!erased_count; +} -void BlobStream::ReadRequest::Fail() { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::BindOnce(std::move(callback), nullptr, false, - ROStream::StatusFailure)); +void DevToolsIOContext::DiscardAllStreams() { + streams_.clear(); } // static -bool BlobStream::IsTextMimeType(const std::string& mime_type) { +bool DevToolsIOContext::IsTextMimeType(const std::string& mime_type) { static const char* kTextMIMETypePrefixes[] = { "text/", "application/x-javascript", "application/json", "application/xml"}; @@ -266,267 +74,4 @@ bool BlobStream::IsTextMimeType(const std::string& mime_type) { return false; } -void BlobStream::Open(scoped_refptr context, - StoragePartition* partition, - const std::string& handle, - OpenCallback callback) { - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::BindOnce(&BlobStream::OpenOnIO, this, context, - handle, std::move(callback))); -} - -void BlobStream::Read(off_t position, size_t max_size, ReadCallback callback) { - std::unique_ptr request( - new ReadRequest(position, max_size, std::move(callback))); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&BlobStream::ReadOnIO, this, std::move(request))); -} - -void BlobStream::Close(bool invoke_pending_callbacks) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&BlobStream::CloseOnIO, this, invoke_pending_callbacks)); -} - -void BlobStream::OpenOnIO(scoped_refptr blob_context, - const std::string& uuid, - OpenCallback callback) { - DCHECK(!blob_handle_); - - storage::BlobStorageContext* bsc = blob_context->context(); - blob_handle_ = bsc->GetBlobDataFromUUID(uuid); - if (!blob_handle_) { - LOG(ERROR) << "No blob with uuid: " << uuid; - FailOnIO(std::move(callback)); - return; - } - is_binary_ = !IsTextMimeType(blob_handle_->content_type()); - open_callback_ = std::move(callback); - blob_handle_->RunOnConstructionComplete( - base::BindOnce(&BlobStream::OnBlobConstructionComplete, this)); -} - -void BlobStream::OnBlobConstructionComplete(storage::BlobStatus status) { - DCHECK(!BlobStatusIsPending(status)); - if (BlobStatusIsError(status)) { - LOG(ERROR) << "Blob building failed: " << static_cast(status); - FailOnIO(std::move(open_callback_)); - return; - } - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::BindOnce(std::move(open_callback_), true)); - if (!pending_reads_.empty()) - StartReadRequest(); -} - -void BlobStream::ReadOnIO(std::unique_ptr request) { - if (failed_) { - request->Fail(); - return; - } - pending_reads_.push(std::move(request)); - if (pending_reads_.size() > 1 || open_callback_) - return; - StartReadRequest(); -} - -void BlobStream::FailOnIO() { - failed_ = true; - while (!pending_reads_.empty()) { - pending_reads_.front()->Fail(); - pending_reads_.pop(); - } -} - -void BlobStream::CloseOnIO(bool invoke_pending_callbacks) { - if (blob_reader_) { - blob_reader_->Kill(); - blob_reader_.reset(); - } - if (blob_handle_) - blob_handle_.reset(); - if (invoke_pending_callbacks) { - FailOnIO(); - return; - } - failed_ = true; - pending_reads_ = base::queue>(); - open_callback_ = OpenCallback(); -} - -void BlobStream::StartReadRequest() { - DCHECK_GE(pending_reads_.size(), 1UL); - DCHECK(blob_handle_); - DCHECK(!failed_); - - ReadRequest& request = *pending_reads_.front(); - if (request.position < 0) - request.position = last_read_pos_; - if (request.position != last_read_pos_) - blob_reader_.reset(); - if (!blob_reader_) - CreateReader(); - else - BeginRead(); -} - -void BlobStream::BeginRead() { - DCHECK_GE(pending_reads_.size(), 1UL); - ReadRequest& request = *pending_reads_.front(); - if (!io_buf_ || static_cast(io_buf_->size()) < request.max_size) - io_buf_ = new net::IOBufferWithSize(request.max_size); - int bytes_read; - BlobReader::Status status = - blob_reader_->Read(io_buf_.get(), request.max_size, &bytes_read, - base::BindOnce(&BlobStream::OnReadComplete, this)); - if (status == BlobReader::Status::IO_PENDING) - return; - // This is for uniformity with the asynchronous case. - if (status == BlobReader::Status::NET_ERROR) { - bytes_read = blob_reader_->net_error(); - DCHECK_LT(0, bytes_read); - } - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&BlobStream::OnReadComplete, this, bytes_read)); -} - -void BlobStream::OnReadComplete(int bytes_read) { - std::unique_ptr request = std::move(pending_reads_.front()); - pending_reads_.pop(); - - Status status; - std::unique_ptr data(new std::string()); - bool base64_encoded = false; - - if (bytes_read < 0) { - status = StatusFailure; - LOG(ERROR) << "Error reading blob: " << net::ErrorToString(bytes_read); - } else if (!bytes_read) { - status = StatusEOF; - } else { - last_read_pos_ += bytes_read; - status = blob_reader_->remaining_bytes() ? StatusSuccess : StatusEOF; - if (is_binary_) { - base64_encoded = true; - Base64Encode(base::StringPiece(io_buf_->data(), bytes_read), data.get()); - } else { - // TODO(caseq): truncate at UTF8 boundary. - *data = std::string(io_buf_->data(), bytes_read); - } - } - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(std::move(request->callback), std::move(data), - base64_encoded, status)); - if (!pending_reads_.empty()) - StartReadRequest(); -} - -void BlobStream::CreateReader() { - DCHECK(!blob_reader_); - blob_reader_ = blob_handle_->CreateReader(); - BlobReader::Status status = blob_reader_->CalculateSize( - base::BindOnce(&BlobStream::OnCalculateSizeComplete, this)); - if (status != BlobReader::Status::IO_PENDING) { - OnCalculateSizeComplete(status == BlobReader::Status::NET_ERROR - ? blob_reader_->net_error() - : net::OK); - } -} - -void BlobStream::OnCalculateSizeComplete(int net_error) { - if (net_error != net::OK) { - FailOnIO(); - return; - } - off_t seek_to = pending_reads_.front()->position; - if (seek_to != 0UL) { - if (seek_to >= static_cast(blob_reader_->total_size())) { - OnReadComplete(0); - return; - } - BlobReader::Status status = blob_reader_->SetReadRange( - seek_to, blob_reader_->total_size() - seek_to); - if (status != BlobReader::Status::DONE) { - FailOnIO(); - return; - } - } - BeginRead(); -} - -} // namespace - -DevToolsIOContext::ROStream::ROStream( - scoped_refptr task_runner) - : RefCountedDeleteOnSequence( - std::move(task_runner)) {} - -DevToolsIOContext::ROStream::~ROStream() = default; - -DevToolsIOContext::RWStream::RWStream( - scoped_refptr task_runner) - : DevToolsIOContext::ROStream(std::move(task_runner)) {} - -DevToolsIOContext::RWStream::~RWStream() = default; - -DevToolsIOContext::DevToolsIOContext() : weak_factory_(this) {} - -DevToolsIOContext::~DevToolsIOContext() { - DiscardAllStreams(); -} - -scoped_refptr -DevToolsIOContext::CreateTempFileBackedStream(bool binary) { - scoped_refptr result = new TempFileStream(binary); - bool inserted = - streams_.insert(std::make_pair(result->handle(), result)).second; - DCHECK(inserted); - return result; -} - -scoped_refptr DevToolsIOContext::GetByHandle( - const std::string& handle) { - StreamsMap::const_iterator it = streams_.find(handle); - return it == streams_.end() ? scoped_refptr() : it->second; -} - -scoped_refptr DevToolsIOContext::OpenBlob( - ChromeBlobStorageContext* context, - StoragePartition* partition, - const std::string& handle, - const std::string& uuid) { - scoped_refptr result = new BlobStream(); - bool inserted = streams_.insert(std::make_pair(handle, result)).second; - - result->Open(context, partition, uuid, - base::BindOnce(&DevToolsIOContext::OnBlobOpenComplete, - weak_factory_.GetWeakPtr(), handle)); - DCHECK(inserted); - return std::move(result); -} - -void DevToolsIOContext::OnBlobOpenComplete(const std::string& handle, - bool success) { - if (!success) - Close(handle); -} - -bool DevToolsIOContext::Close(const std::string& handle) { - StreamsMap::iterator it = streams_.find(handle); - if (it == streams_.end()) - return false; - it->second->Close(false); - streams_.erase(it); - return true; -} - -void DevToolsIOContext::DiscardAllStreams() { - for (auto& entry : streams_) - entry.second->Close(true); - return streams_.clear(); -} - } // namespace content diff --git a/chromium/content/browser/devtools/devtools_io_context.h b/chromium/content/browser/devtools/devtools_io_context.h index 7467760000c..be7a2afe4b8 100644 --- a/chromium/content/browser/devtools/devtools_io_context.h +++ b/chromium/content/browser/devtools/devtools_io_context.h @@ -5,12 +5,9 @@ #ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_IO_CONTEXT_H_ #define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_IO_CONTEXT_H_ -#include - #include #include "base/callback.h" -#include "base/files/file.h" #include "base/memory/ref_counted_delete_on_sequence.h" #include "base/memory/ref_counted_memory.h" #include "base/memory/weak_ptr.h" @@ -20,12 +17,10 @@ class SequencedTaskRunner; } namespace content { -class ChromeBlobStorageContext; -class StoragePartition; -class DevToolsIOContext { +class DevToolsIOContext : public base::SupportsWeakPtr { public: - class ROStream : public base::RefCountedDeleteOnSequence { + class Stream : public base::RefCountedDeleteOnSequence { public: enum Status { StatusSuccess, @@ -38,50 +33,42 @@ class DevToolsIOContext { bool base64_encoded, int status)>; + virtual bool SupportsSeek() const; virtual void Read(off_t position, size_t max_size, ReadCallback callback) = 0; - virtual void Close(bool invoke_pending_callbacks) = 0; protected: - friend class base::DeleteHelper; - friend class base::RefCountedDeleteOnSequence; + friend class base::DeleteHelper; + friend class base::RefCountedDeleteOnSequence; - explicit ROStream(scoped_refptr task_runner); - virtual ~ROStream() = 0; + explicit Stream(scoped_refptr task_runner); + virtual ~Stream() = 0; - DISALLOW_COPY_AND_ASSIGN(ROStream); - }; + // Sub-class API: - class RWStream : public ROStream { - public: - virtual void Append(std::unique_ptr data) = 0; - virtual const std::string& handle() = 0; + // Caller is reposnsible for generating a unique handle. + void Register(DevToolsIOContext* context, const std::string& handle); + // We generate handle for the caller and return it. + std::string Register(DevToolsIOContext* context); - protected: - explicit RWStream(scoped_refptr task_runner); - ~RWStream() override = 0; - DISALLOW_COPY_AND_ASSIGN(RWStream); + DISALLOW_COPY_AND_ASSIGN(Stream); }; DevToolsIOContext(); ~DevToolsIOContext(); - scoped_refptr CreateTempFileBackedStream(bool binary); - scoped_refptr GetByHandle(const std::string& handle); - scoped_refptr OpenBlob(ChromeBlobStorageContext*, - StoragePartition*, - const std::string& handle, - const std::string& uuid); - + scoped_refptr GetByHandle(const std::string& handle); bool Close(const std::string& handle); void DiscardAllStreams(); - void OnBlobOpenComplete(const std::string& handle, bool success); + + static bool IsTextMimeType(const std::string& mime_type); private: - using StreamsMap = std::map>; - StreamsMap streams_; - base::WeakPtrFactory weak_factory_; + // Registration can only be done by Stream subclasses through Stream methods. + void RegisterStream(scoped_refptr stream, const std::string& handle); + + std::map> streams_; }; } // namespace content diff --git a/chromium/content/browser/devtools/devtools_manager.cc b/chromium/content/browser/devtools/devtools_manager.cc index 241e5039fa9..49bb469207f 100644 --- a/chromium/content/browser/devtools/devtools_manager.cc +++ b/chromium/content/browser/devtools/devtools_manager.cc @@ -5,7 +5,6 @@ #include "content/browser/devtools/devtools_manager.h" #include "base/bind.h" -#include "base/message_loop/message_loop.h" #include "content/browser/devtools/devtools_agent_host_impl.h" #include "content/browser/devtools/devtools_http_handler.h" #include "content/browser/devtools/devtools_pipe_handler.h" diff --git a/chromium/content/browser/devtools/devtools_manager_unittest.cc b/chromium/content/browser/devtools/devtools_manager_unittest.cc index a37bd100c05..b945b03f214 100644 --- a/chromium/content/browser/devtools/devtools_manager_unittest.cc +++ b/chromium/content/browser/devtools/devtools_manager_unittest.cc @@ -9,7 +9,6 @@ #include "base/guid.h" #include "base/location.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" @@ -146,7 +145,7 @@ TEST_F(DevToolsManagerTest, NoUnresponsiveDialogInInspectedContents) { TimeDelta::FromMilliseconds(10)); // Wait long enough for first timeout and see if it fired. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), + FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated(), TimeDelta::FromMilliseconds(10)); base::RunLoop().Run(); EXPECT_FALSE(delegate.renderer_unresponsive_received()); @@ -158,7 +157,7 @@ TEST_F(DevToolsManagerTest, NoUnresponsiveDialogInInspectedContents) { TimeDelta::FromMilliseconds(10)); // Wait long enough for first timeout and see if it fired. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), + FROM_HERE, base::RunLoop::QuitCurrentWhenIdleClosureDeprecated(), TimeDelta::FromMilliseconds(10)); base::RunLoop().Run(); EXPECT_TRUE(delegate.renderer_unresponsive_received()); diff --git a/chromium/content/browser/devtools/devtools_network_interceptor.cc b/chromium/content/browser/devtools/devtools_network_interceptor.cc index 8213acf5d97..e4206b1f0bd 100644 --- a/chromium/content/browser/devtools/devtools_network_interceptor.cc +++ b/chromium/content/browser/devtools/devtools_network_interceptor.cc @@ -10,7 +10,7 @@ namespace content { InterceptedRequestInfo::InterceptedRequestInfo() - : response_error_code(net::OK) {} + : is_navigation(false), response_error_code(net::OK) {} InterceptedRequestInfo::~InterceptedRequestInfo() = default; diff --git a/chromium/content/browser/devtools/devtools_network_interceptor.h b/chromium/content/browser/devtools/devtools_network_interceptor.h index 97c64bc6257..43dbf902448 100644 --- a/chromium/content/browser/devtools/devtools_network_interceptor.h +++ b/chromium/content/browser/devtools/devtools_network_interceptor.h @@ -11,6 +11,7 @@ #include "base/unguessable_token.h" #include "content/browser/devtools/protocol/network.h" #include "content/public/common/resource_type.h" +#include "mojo/public/cpp/system/data_pipe.h" #include "net/base/net_errors.h" namespace content { @@ -24,6 +25,7 @@ struct InterceptedRequestInfo { base::UnguessableToken frame_id; ResourceType resource_type; bool is_navigation; + protocol::Maybe is_download; protocol::Maybe redirect_headers; protocol::Maybe redirect_status_code; protocol::Maybe redirect_url; @@ -43,6 +45,10 @@ class DevToolsNetworkInterceptor { protocol::Network::Backend::ContinueInterceptedRequestCallback; using GetResponseBodyForInterceptionCallback = protocol::Network::Backend::GetResponseBodyForInterceptionCallback; + using TakeResponseBodyPipeCallback = + base::OnceCallback; struct Modifications { Modifications(); diff --git a/chromium/content/browser/devtools/devtools_session.cc b/chromium/content/browser/devtools/devtools_session.cc index b31ca0ada8b..10a84a5ef0a 100644 --- a/chromium/content/browser/devtools/devtools_session.cc +++ b/chromium/content/browser/devtools/devtools_session.cc @@ -207,7 +207,11 @@ void DevToolsSession::DispatchProtocolResponse( // |this| may be deleted at this point. } -void DevToolsSession::DispatchProtocolNotification(const std::string& message) { +void DevToolsSession::DispatchProtocolNotification( + const std::string& message, + const base::Optional& state) { + if (state.has_value()) + state_cookie_ = state.value(); client_->DispatchProtocolMessage(agent_host_, message); // |this| may be deleted at this point. } diff --git a/chromium/content/browser/devtools/devtools_session.h b/chromium/content/browser/devtools/devtools_session.h index ea900e26bb3..9ae28ad0097 100644 --- a/chromium/content/browser/devtools/devtools_session.h +++ b/chromium/content/browser/devtools/devtools_session.h @@ -80,7 +80,9 @@ class DevToolsSession : public protocol::FrontendChannel, const std::string& message, int call_id, const base::Optional& state) override; - void DispatchProtocolNotification(const std::string& message) override; + void DispatchProtocolNotification( + const std::string& message, + const base::Optional& state) override; mojo::AssociatedBinding binding_; blink::mojom::DevToolsSessionAssociatedPtr session_ptr_; diff --git a/chromium/content/browser/devtools/devtools_stream_blob.cc b/chromium/content/browser/devtools/devtools_stream_blob.cc new file mode 100644 index 00000000000..2dca3fb2bfb --- /dev/null +++ b/chromium/content/browser/devtools/devtools_stream_blob.cc @@ -0,0 +1,251 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/devtools/devtools_stream_blob.h" + +#include "base/base64.h" +#include "base/bind.h" +#include "base/strings/string_piece.h" +#include "content/browser/blob_storage/chrome_blob_storage_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/storage_partition.h" +#include "net/base/io_buffer.h" +#include "storage/browser/blob/blob_data_handle.h" +#include "storage/browser/blob/blob_reader.h" +#include "storage/browser/blob/blob_storage_context.h" +#include "storage/common/blob_storage/blob_storage_constants.h" + +namespace content { + +using storage::BlobReader; + +DevToolsStreamBlob::ReadRequest::ReadRequest(off_t position, + size_t max_size, + ReadCallback callback) + : position(position), max_size(max_size), callback(std::move(callback)) {} + +DevToolsStreamBlob::ReadRequest::~ReadRequest() = default; + +DevToolsStreamBlob::DevToolsStreamBlob() + : DevToolsIOContext::Stream( + BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)), + last_read_pos_(0), + failed_(false), + is_binary_(false) {} + +DevToolsStreamBlob::~DevToolsStreamBlob() { + if (blob_reader_) + blob_reader_->Kill(); +} + +namespace { +void UnregisterIfOpenFailed(base::WeakPtr context, + const std::string& handle, + bool success) { + if (!success && context) + context->Close(handle); +} +} // namespace + +// static +scoped_refptr DevToolsStreamBlob::Create( + DevToolsIOContext* io_context, + ChromeBlobStorageContext* blob_context, + StoragePartition* partition, + const std::string& handle, + const std::string& uuid) { + scoped_refptr result = new DevToolsStreamBlob(); + result->Register(io_context, handle); + result->Open( + blob_context, partition, uuid, + base::BindOnce(&UnregisterIfOpenFailed, io_context->AsWeakPtr(), handle)); + return std::move(result); +} + +void DevToolsStreamBlob::ReadRequest::Fail() { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::BindOnce(std::move(callback), nullptr, false, + Stream::StatusFailure)); +} + +void DevToolsStreamBlob::Open(scoped_refptr context, + StoragePartition* partition, + const std::string& handle, + OpenCallback callback) { + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::BindOnce(&DevToolsStreamBlob::OpenOnIO, this, + context, handle, std::move(callback))); +} + +void DevToolsStreamBlob::Read(off_t position, + size_t max_size, + ReadCallback callback) { + std::unique_ptr request( + new ReadRequest(position, max_size, std::move(callback))); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&DevToolsStreamBlob::ReadOnIO, this, std::move(request))); +} + +void DevToolsStreamBlob::OpenOnIO( + scoped_refptr blob_context, + const std::string& uuid, + OpenCallback callback) { + DCHECK(!blob_handle_); + + storage::BlobStorageContext* bsc = blob_context->context(); + blob_handle_ = bsc->GetBlobDataFromUUID(uuid); + if (!blob_handle_) { + LOG(ERROR) << "No blob with uuid: " << uuid; + FailOnIO(std::move(callback)); + return; + } + is_binary_ = !DevToolsIOContext::IsTextMimeType(blob_handle_->content_type()); + open_callback_ = std::move(callback); + blob_handle_->RunOnConstructionComplete( + base::BindOnce(&DevToolsStreamBlob::OnBlobConstructionComplete, this)); +} + +void DevToolsStreamBlob::OnBlobConstructionComplete( + storage::BlobStatus status) { + DCHECK(!BlobStatusIsPending(status)); + if (BlobStatusIsError(status)) { + LOG(ERROR) << "Blob building failed: " << static_cast(status); + FailOnIO(std::move(open_callback_)); + return; + } + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::BindOnce(std::move(open_callback_), true)); + if (!pending_reads_.empty()) + StartReadRequest(); +} + +void DevToolsStreamBlob::ReadOnIO(std::unique_ptr request) { + if (failed_) { + request->Fail(); + return; + } + pending_reads_.push(std::move(request)); + if (pending_reads_.size() > 1 || open_callback_) + return; + StartReadRequest(); +} + +void DevToolsStreamBlob::FailOnIO() { + failed_ = true; + while (!pending_reads_.empty()) { + pending_reads_.front()->Fail(); + pending_reads_.pop(); + } +} + +void DevToolsStreamBlob::FailOnIO(OpenCallback callback) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::BindOnce(std::move(callback), false)); + FailOnIO(); +} + +void DevToolsStreamBlob::StartReadRequest() { + DCHECK_GE(pending_reads_.size(), 1UL); + DCHECK(blob_handle_); + DCHECK(!failed_); + + ReadRequest& request = *pending_reads_.front(); + if (request.position < 0) + request.position = last_read_pos_; + if (request.position != last_read_pos_) + blob_reader_.reset(); + if (!blob_reader_) + CreateReader(); + else + BeginRead(); +} + +void DevToolsStreamBlob::BeginRead() { + DCHECK_GE(pending_reads_.size(), 1UL); + ReadRequest& request = *pending_reads_.front(); + if (!io_buf_ || static_cast(io_buf_->size()) < request.max_size) + io_buf_ = new net::IOBufferWithSize(request.max_size); + int bytes_read; + BlobReader::Status status = blob_reader_->Read( + io_buf_.get(), request.max_size, &bytes_read, + base::BindOnce(&DevToolsStreamBlob::OnReadComplete, this)); + if (status == BlobReader::Status::IO_PENDING) + return; + // This is for uniformity with the asynchronous case. + if (status == BlobReader::Status::NET_ERROR) { + bytes_read = blob_reader_->net_error(); + DCHECK_LT(0, bytes_read); + } + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&DevToolsStreamBlob::OnReadComplete, this, bytes_read)); +} + +void DevToolsStreamBlob::OnReadComplete(int bytes_read) { + std::unique_ptr request = std::move(pending_reads_.front()); + pending_reads_.pop(); + + Status status; + std::unique_ptr data(new std::string()); + bool base64_encoded = false; + + if (bytes_read < 0) { + status = StatusFailure; + LOG(ERROR) << "Error reading blob: " << net::ErrorToString(bytes_read); + } else if (!bytes_read) { + status = StatusEOF; + } else { + last_read_pos_ += bytes_read; + status = blob_reader_->remaining_bytes() ? StatusSuccess : StatusEOF; + if (is_binary_) { + base64_encoded = true; + Base64Encode(base::StringPiece(io_buf_->data(), bytes_read), data.get()); + } else { + // TODO(caseq): truncate at UTF8 boundary. + *data = std::string(io_buf_->data(), bytes_read); + } + } + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(std::move(request->callback), std::move(data), + base64_encoded, status)); + if (!pending_reads_.empty()) + StartReadRequest(); +} + +void DevToolsStreamBlob::CreateReader() { + DCHECK(!blob_reader_); + blob_reader_ = blob_handle_->CreateReader(); + BlobReader::Status status = blob_reader_->CalculateSize( + base::BindOnce(&DevToolsStreamBlob::OnCalculateSizeComplete, this)); + if (status != BlobReader::Status::IO_PENDING) { + OnCalculateSizeComplete(status == BlobReader::Status::NET_ERROR + ? blob_reader_->net_error() + : net::OK); + } +} + +void DevToolsStreamBlob::OnCalculateSizeComplete(int net_error) { + if (net_error != net::OK) { + FailOnIO(); + return; + } + off_t seek_to = pending_reads_.front()->position; + if (seek_to != 0UL) { + if (seek_to >= static_cast(blob_reader_->total_size())) { + OnReadComplete(0); + return; + } + BlobReader::Status status = blob_reader_->SetReadRange( + seek_to, blob_reader_->total_size() - seek_to); + if (status != BlobReader::Status::DONE) { + FailOnIO(); + return; + } + } + BeginRead(); +} + +} // namespace content diff --git a/chromium/content/browser/devtools/devtools_stream_blob.h b/chromium/content/browser/devtools/devtools_stream_blob.h new file mode 100644 index 00000000000..298f857b868 --- /dev/null +++ b/chromium/content/browser/devtools/devtools_stream_blob.h @@ -0,0 +1,94 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_STREAM_BLOB_H_ +#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_STREAM_BLOB_H_ + +#include "base/callback.h" +#include "base/containers/queue.h" +#include "base/memory/scoped_refptr.h" +#include "content/browser/devtools/devtools_io_context.h" +#include "net/base/net_errors.h" +#include "storage/common/blob_storage/blob_storage_constants.h" + +#include + +namespace net { +class IOBufferWithSize; +} + +namespace storage { +class BlobDataHandle; +class BlobReader; +} // namespace storage + +namespace content { +class ChromeBlobStorageContext; +class StoragePartition; + +class DevToolsStreamBlob : public DevToolsIOContext::Stream { + public: + using OpenCallback = base::OnceCallback; + + static scoped_refptr Create( + DevToolsIOContext* io_context, + ChromeBlobStorageContext* blob_context, + StoragePartition* partition, + const std::string& handle, + const std::string& uuid); + + private: + DevToolsStreamBlob(); + + void Open(scoped_refptr context, + StoragePartition* partition, + const std::string& handle, + OpenCallback callback); + + void Read(off_t position, size_t max_size, ReadCallback callback) override; + + struct ReadRequest { + off_t position; + size_t max_size; + ReadCallback callback; + + void Fail(); + + ReadRequest() = delete; + ReadRequest(off_t position, size_t max_size, ReadCallback callback); + ~ReadRequest(); + }; + + ~DevToolsStreamBlob() override; + + void OpenOnIO(scoped_refptr blob_context, + const std::string& uuid, + OpenCallback callback); + void ReadOnIO(std::unique_ptr request); + void CloseOnIO(bool invoke_pending_callbacks); + + void FailOnIO(); + void FailOnIO(OpenCallback callback); + + void StartReadRequest(); + void CreateReader(); + void BeginRead(); + + void OnReadComplete(int bytes_read); + void OnBlobConstructionComplete(storage::BlobStatus status); + void OnCalculateSizeComplete(int net_error); + + std::unique_ptr blob_handle_; + OpenCallback open_callback_; + std::unique_ptr blob_reader_; + base::queue> pending_reads_; + scoped_refptr io_buf_; + off_t last_read_pos_; + bool failed_; + bool is_binary_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_STREAM_BLOB_H_ diff --git a/chromium/content/browser/devtools/devtools_stream_file.cc b/chromium/content/browser/devtools/devtools_stream_file.cc new file mode 100644 index 00000000000..c32261e092a --- /dev/null +++ b/chromium/content/browser/devtools/devtools_stream_file.cc @@ -0,0 +1,142 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/devtools/devtools_stream_file.h" + +#include "base/base64.h" +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/sequenced_task_runner.h" +#include "base/strings/string_util.h" +#include "base/task_scheduler/lazy_task_runner.h" +#include "base/task_scheduler/post_task.h" +#include "base/third_party/icu/icu_utf.h" +#include "base/threading/thread_restrictions.h" +#include "content/public/browser/browser_thread.h" +#include "storage/browser/fileapi/file_system_context.h" + +namespace content { + +scoped_refptr impl_task_runner() { + constexpr base::TaskTraits kBlockingTraits = {base::MayBlock(), + base::TaskPriority::BACKGROUND}; + static base::LazySequencedTaskRunner s_sequenced_task_unner = + LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(kBlockingTraits); + return s_sequenced_task_unner.Get(); +} + +scoped_refptr DevToolsStreamFile::Create( + DevToolsIOContext* context, + bool binary) { + return new DevToolsStreamFile(context, binary); +} + +DevToolsStreamFile::DevToolsStreamFile(DevToolsIOContext* context, bool binary) + : DevToolsIOContext::Stream(impl_task_runner()), + handle_(Register(context)), + binary_(binary), + task_runner_(impl_task_runner()), + had_errors_(false), + last_read_pos_(0) {} + +DevToolsStreamFile::~DevToolsStreamFile() { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); +} + +bool DevToolsStreamFile::InitOnFileSequenceIfNeeded() { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + base::AssertBlockingAllowed(); + if (had_errors_) + return false; + if (file_.IsValid()) + return true; + base::FilePath temp_path; + if (!base::CreateTemporaryFile(&temp_path)) { + LOG(ERROR) << "Failed to create temporary file"; + had_errors_ = true; + return false; + } + const unsigned flags = base::File::FLAG_OPEN_TRUNCATED | + base::File::FLAG_WRITE | base::File::FLAG_READ | + base::File::FLAG_DELETE_ON_CLOSE; + file_.Initialize(temp_path, flags); + if (!file_.IsValid()) { + LOG(ERROR) << "Failed to open temporary file: " << temp_path.value() << ", " + << base::File::ErrorToString(file_.error_details()); + had_errors_ = true; + DeleteFile(temp_path, false); + return false; + } + return true; +} + +void DevToolsStreamFile::Read(off_t position, + size_t max_size, + ReadCallback callback) { + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&DevToolsStreamFile::ReadOnFileSequence, this, + position, max_size, std::move(callback))); +} + +void DevToolsStreamFile::Append(std::unique_ptr data) { + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&DevToolsStreamFile::AppendOnFileSequence, this, + std::move(data))); +} + +void DevToolsStreamFile::ReadOnFileSequence(off_t position, + size_t max_size, + ReadCallback callback) { + DCHECK(task_runner_->RunsTasksInCurrentSequence()); + Status status = StatusFailure; + std::unique_ptr data; + bool base64_encoded = false; + + if (file_.IsValid()) { + std::string buffer; + buffer.resize(max_size); + if (position < 0) + position = last_read_pos_; + int size_got = file_.ReadNoBestEffort(position, &*buffer.begin(), max_size); + if (size_got < 0) { + LOG(ERROR) << "Failed to read temporary file"; + had_errors_ = true; + file_.Close(); + } else { + // Provided client has requested sufficient large block, make their + // life easier by not truncating in the middle of a UTF-8 character. + if (size_got > 6 && !CBU8_IS_SINGLE(buffer[size_got - 1])) { + base::TruncateUTF8ToByteSize(buffer, size_got, &buffer); + size_got = buffer.size(); + } else { + buffer.resize(size_got); + } + data.reset(new std::string(std::move(buffer))); + status = size_got ? StatusSuccess : StatusEOF; + last_read_pos_ = position + size_got; + } + } + if (binary_) { + std::string raw_data(std::move(*data)); + base::Base64Encode(raw_data, data.get()); + base64_encoded = true; + } + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::BindOnce(std::move(callback), std::move(data), + base64_encoded, status)); +} + +void DevToolsStreamFile::AppendOnFileSequence( + std::unique_ptr data) { + if (!InitOnFileSequenceIfNeeded()) + return; + int size_written = file_.WriteAtCurrentPos(&*data->begin(), data->length()); + if (size_written != static_cast(data->length())) { + LOG(ERROR) << "Failed to write temporary file"; + had_errors_ = true; + file_.Close(); + } +} + +} // namespace content diff --git a/chromium/content/browser/devtools/devtools_stream_file.h b/chromium/content/browser/devtools/devtools_stream_file.h new file mode 100644 index 00000000000..1c63e39f5d0 --- /dev/null +++ b/chromium/content/browser/devtools/devtools_stream_file.h @@ -0,0 +1,44 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_STREAM_FILE_H_ +#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_STREAM_FILE_H_ + +#include "base/files/file.h" +#include "base/memory/scoped_refptr.h" +#include "content/browser/devtools/devtools_io_context.h" + +#include + +namespace content { + +class DevToolsStreamFile : public DevToolsIOContext::Stream { + public: + static scoped_refptr Create(DevToolsIOContext* context, + bool binary); + const std::string& handle() const { return handle_; } + void Append(std::unique_ptr data); + + private: + DevToolsStreamFile(DevToolsIOContext* context, bool binary); + ~DevToolsStreamFile() override; + + void Read(off_t position, size_t max_size, ReadCallback callback) override; + + void ReadOnFileSequence(off_t pos, size_t max_size, ReadCallback callback); + void AppendOnFileSequence(std::unique_ptr data); + bool InitOnFileSequenceIfNeeded(); + + const std::string handle_; + const bool binary_; + + base::File file_; + scoped_refptr task_runner_; + bool had_errors_; + off_t last_read_pos_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_STREAM_FILE_H_ diff --git a/chromium/content/browser/devtools/devtools_stream_pipe.cc b/chromium/content/browser/devtools/devtools_stream_pipe.cc new file mode 100644 index 00000000000..62c662bb566 --- /dev/null +++ b/chromium/content/browser/devtools/devtools_stream_pipe.cc @@ -0,0 +1,133 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/devtools/devtools_stream_pipe.h" + +#include "base/base64.h" +#include "base/bind.h" +#include "base/threading/sequenced_task_runner_handle.h" + +namespace content { + +struct DevToolsStreamPipe::ReadRequest { + ReadRequest() = delete; + ReadRequest(uint32_t max_size, ReadCallback read_callback) + : max_size(max_size), read_callback(std::move(read_callback)) {} + + uint32_t max_size; + ReadCallback read_callback; +}; + +// static +scoped_refptr DevToolsStreamPipe::Create( + DevToolsIOContext* context, + mojo::ScopedDataPipeConsumerHandle pipe, + bool is_binary) { + return new DevToolsStreamPipe(context, std::move(pipe), is_binary); +} + +DevToolsStreamPipe::DevToolsStreamPipe(DevToolsIOContext* context, + mojo::ScopedDataPipeConsumerHandle pipe, + bool is_binary) + : DevToolsIOContext::Stream(base::SequencedTaskRunnerHandle::Get()), + handle_(Register(context)), + pipe_(std::move(pipe)), + is_binary_(is_binary), + pipe_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL), + last_status_(StatusSuccess) { + MojoResult res = pipe_watcher_.Watch( + pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + base::BindRepeating(&DevToolsStreamPipe::OnPipeSignalled, + base::Unretained(this))); + DCHECK_EQ(MOJO_RESULT_OK, res); +} + +DevToolsStreamPipe::~DevToolsStreamPipe() = default; + +bool DevToolsStreamPipe::SupportsSeek() const { + return false; +} + +void DevToolsStreamPipe::Read(off_t position, + size_t max_size, + ReadCallback callback) { + DCHECK(position == -1); + if (last_status_ != StatusSuccess) { + DCHECK(read_requests_.empty()); + std::move(callback).Run(std::make_unique(), false, + last_status_); + return; + } + read_requests_.emplace(max_size, std::move(callback)); + if (read_requests_.size() == 1lu) + pipe_watcher_.ArmOrNotify(); +} + +void DevToolsStreamPipe::OnPipeSignalled( + MojoResult result, + const mojo::HandleSignalsState& state) { + DCHECK_EQ(StatusSuccess, last_status_); + DCHECK(!read_requests_.empty()); + + if (result != MOJO_RESULT_OK) { + DispatchEOFOrError(state.peer_closed()); + return; + } + while (!read_requests_.empty()) { + const void* pipe_bytes = nullptr; + uint32_t bytes_available = 0; + MojoResult res = pipe_->BeginReadData(&pipe_bytes, &bytes_available, + MOJO_READ_DATA_FLAG_NONE); + if (res == MOJO_RESULT_FAILED_PRECONDITION) { + DCHECK(state.peer_closed()); + DispatchEOFOrError(state.peer_closed()); + return; + } + DCHECK_EQ(MOJO_RESULT_OK, res); + auto& request = read_requests_.front(); + const uint32_t bytes_to_read = + std::min(bytes_available, + request.max_size - static_cast(buffer_.size())); + // Dispatch available bytes (but no more than requested), when there are + // multiple requests pending. If we just have a single read request, it's + // more efficient (and easier for client) to only dispatch when enough bytes + // are available or eof has been reached. + const bool fulfill_entire_request = read_requests_.size() == 1ul; + if (fulfill_entire_request) + buffer_.reserve(request.max_size); + buffer_.append(static_cast(pipe_bytes), bytes_to_read); + pipe_->EndReadData(bytes_to_read); + DCHECK_LE(buffer_.size(), request.max_size); + if (buffer_.size() < request.max_size && fulfill_entire_request) + break; + DispatchResponse(); + if (bytes_to_read == bytes_available) + break; + } + if (!read_requests_.empty()) + pipe_watcher_.ArmOrNotify(); +} + +void DevToolsStreamPipe::DispatchResponse() { + auto data = std::make_unique(std::move(buffer_)); + if (is_binary_ && !data->empty()) + base::Base64Encode(*data, data.get()); + std::move(read_requests_.front().read_callback) + .Run(std::move(data), is_binary_, last_status_); + read_requests_.pop(); +} + +void DevToolsStreamPipe::DispatchEOFOrError(bool is_eof) { + // For consistency with other implementation, do not report EOF or failure + // condition along with actual data, do it for the next request instead. + if (!buffer_.empty()) + DispatchResponse(); + last_status_ = is_eof ? StatusEOF : StatusFailure; + + while (!read_requests_.empty()) + DispatchResponse(); +} + +} // namespace content diff --git a/chromium/content/browser/devtools/devtools_stream_pipe.h b/chromium/content/browser/devtools/devtools_stream_pipe.h new file mode 100644 index 00000000000..b931eaec116 --- /dev/null +++ b/chromium/content/browser/devtools/devtools_stream_pipe.h @@ -0,0 +1,55 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_STREAM_PIPE_H_ +#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_STREAM_PIPE_H_ + +#include "base/callback.h" +#include "base/containers/queue.h" +#include "base/memory/scoped_refptr.h" +#include "content/browser/devtools/devtools_io_context.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/simple_watcher.h" + +#include + +namespace content { + +class DevToolsStreamPipe : public DevToolsIOContext::Stream { + public: + static scoped_refptr Create( + DevToolsIOContext* context, + mojo::ScopedDataPipeConsumerHandle pipe, + bool is_binary); + const std::string& handle() const { return handle_; } + + private: + struct ReadRequest; + + DevToolsStreamPipe(DevToolsIOContext* context, + mojo::ScopedDataPipeConsumerHandle pipe, + bool is_binary); + ~DevToolsStreamPipe() override; + + bool SupportsSeek() const override; + void Read(off_t position, size_t max_size, ReadCallback callback) override; + + void OnPipeSignalled(MojoResult result, + const mojo::HandleSignalsState& state); + void DispatchResponse(); + void DispatchEOFOrError(bool is_eof); + + const std::string handle_; + const mojo::ScopedDataPipeConsumerHandle pipe_; + const bool is_binary_; + + mojo::SimpleWatcher pipe_watcher_; + base::queue read_requests_; + std::string buffer_; + Status last_status_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_STREAM_PIPE_H_ diff --git a/chromium/content/browser/devtools/devtools_target_registry.cc b/chromium/content/browser/devtools/devtools_target_registry.cc index a426d9e1924..851f75527c4 100644 --- a/chromium/content/browser/devtools/devtools_target_registry.cc +++ b/chromium/content/browser/devtools/devtools_target_registry.cc @@ -149,10 +149,12 @@ class DevToolsTargetRegistry::ContentsObserver : public ObserverBase, void WebContentsDestroyed() override { NOTREACHED() << "DevToolsTarget Registry clients should be destroyed " "before WebContents"; + registry_->UnregisterWebContents(web_contents()); } ~ContentsObserver() override { - registry_->UnregisterWebContents(web_contents()); + if (web_contents()) + registry_->UnregisterWebContents(web_contents()); } DevToolsTargetRegistry* registry_; diff --git a/chromium/content/browser/devtools/devtools_url_interceptor_request_job.cc b/chromium/content/browser/devtools/devtools_url_interceptor_request_job.cc index 3c9d31b784b..abfa5795a0c 100644 --- a/chromium/content/browser/devtools/devtools_url_interceptor_request_job.cc +++ b/chromium/content/browser/devtools/devtools_url_interceptor_request_job.cc @@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "content/browser/devtools/protocol/network_handler.h" #include "content/browser/devtools/protocol/page.h" +#include "content/browser/loader/navigation_loader_util.h" #include "content/browser/loader/resource_request_info_impl.h" #include "ipc/ipc_channel.h" #include "net/base/completion_once_callback.h" @@ -21,6 +22,7 @@ #include "net/http/http_util.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "net/url_request/url_request_context.h" +#include "third_party/blink/public/platform/resource_request_blocked_reason.h" namespace { static const int kInitialBufferSize = 4096; @@ -119,6 +121,13 @@ DevToolsURLInterceptorRequestJob::SubRequest::SubRequest( request_->SetResponseHeadersCallback( devtools_interceptor_request_job->response_headers_callback_); + net::URLRequest* original_request = + devtools_interceptor_request_job_->request(); + request_->set_attach_same_site_cookies( + original_request->attach_same_site_cookies()); + request_->set_site_for_cookies(original_request->site_for_cookies()); + request_->set_initiator(original_request->initiator()); + // Mimic the ResourceRequestInfoImpl of the original request. const ResourceRequestInfoImpl* resource_request_info = static_cast( @@ -134,7 +143,6 @@ DevToolsURLInterceptorRequestJob::SubRequest::SubRequest( resource_request_info->IsMainFrame(), resource_request_info->GetResourceType(), resource_request_info->GetPageTransition(), - resource_request_info->should_replace_current_entry(), resource_request_info->IsDownload(), resource_request_info->is_stream(), resource_request_info->allow_download(), resource_request_info->HasUserGesture(), @@ -146,10 +154,10 @@ DevToolsURLInterceptorRequestJob::SubRequest::SubRequest( resource_request_info->IsPrerendering(), resource_request_info->GetContext(), resource_request_info->ShouldReportRawHeaders(), + resource_request_info->ShouldReportSecurityInfo(), resource_request_info->IsAsync(), resource_request_info->GetPreviewsState(), resource_request_info->body(), - resource_request_info->initiated_in_secure_context(), - resource_request_info->suggested_filename()); + resource_request_info->initiated_in_secure_context()); extra_data->AssociateWithRequest(request_.get()); if (request_details.post_data) @@ -531,6 +539,22 @@ void SetDevToolsStatus(net::URLRequest* request, resource_request_info->set_devtools_status(devtools_status); } +bool IsDownload(net::URLRequest* orig_request, net::URLRequest* subrequest) { + auto* req_info = ResourceRequestInfoImpl::ForRequest(orig_request); + // Only happens to downloads that are initiated by the download manager. + if (req_info->IsDownload()) + return true; + + // Note this will not correctly identify a download for the MIME types + // inferred with content sniffing. The new interception implementation + // should not have this problem, as it's on top of MIME sniffer. + std::string mime_type; + subrequest->GetMimeType(&mime_type); + return req_info->allow_download() && + navigation_loader_util::IsDownload( + orig_request->url(), subrequest->response_headers(), mime_type); +} + } // namespace DevToolsURLInterceptorRequestJob::DevToolsURLInterceptorRequestJob( @@ -622,9 +646,7 @@ void DevToolsURLInterceptorRequestJob::Start() { } void DevToolsURLInterceptorRequestJob::Kill() { - if (sub_request_) - sub_request_->Cancel(); - + sub_request_.reset(); URLRequestJob::Kill(); } @@ -839,6 +861,7 @@ void DevToolsURLInterceptorRequestJob::OnInterceptedRequestResponseStarted( sub_request_->request()->GetResponseCode(); request_info->response_headers = protocol::Object::fromValue(headers_dict.get(), nullptr); + request_info->is_download = IsDownload(request(), sub_request_->request()); } BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::BindOnce(callback_, std::move(request_info))); @@ -1056,6 +1079,16 @@ void DevToolsURLInterceptorRequestJob::ProcessInterceptionResponse( sub_request_->Cancel(); sub_request_.reset(); } + if (modifications->error_reason == net::ERR_BLOCKED_BY_CLIENT) { + // So we know that these modifications originated from devtools + // (also known as inspector), and can therefore annotate the + // request. We only do this for one specific error code thus + // far, to minimize risk of breaking other usages. + ResourceRequestInfoImpl* resource_request_info = + ResourceRequestInfoImpl::ForRequest(request()); + resource_request_info->set_resource_request_blocked_reason( + blink::ResourceRequestBlockedReason::kInspector); + } NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED, *modifications->error_reason)); return; diff --git a/chromium/content/browser/devtools/devtools_url_loader_interceptor.cc b/chromium/content/browser/devtools/devtools_url_loader_interceptor.cc index ced25eb9169..bc91e2d6d59 100644 --- a/chromium/content/browser/devtools/devtools_url_loader_interceptor.cc +++ b/chromium/content/browser/devtools/devtools_url_loader_interceptor.cc @@ -10,6 +10,7 @@ #include "base/unguessable_token.h" #include "content/browser/devtools/protocol/network_handler.h" #include "content/browser/frame_host/frame_tree_node.h" +#include "content/browser/loader/navigation_loader_util.h" #include "content/public/browser/browser_thread.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "mojo/public/cpp/system/data_pipe_drainer.h" @@ -17,6 +18,7 @@ #include "net/http/http_util.h" #include "net/url_request/url_request.h" #include "services/network/public/cpp/resource_request_body.h" +#include "third_party/blink/public/platform/resource_request_blocked_reason.h" namespace content { @@ -28,6 +30,8 @@ using ContinueInterceptedRequestCallback = DevToolsNetworkInterceptor::ContinueInterceptedRequestCallback; using GetResponseBodyForInterceptionCallback = DevToolsNetworkInterceptor::GetResponseBodyForInterceptionCallback; +using TakeResponseBodyPipeCallback = + DevToolsNetworkInterceptor::TakeResponseBodyPipeCallback; using Modifications = DevToolsNetworkInterceptor::Modifications; using InterceptionStage = DevToolsNetworkInterceptor::InterceptionStage; using protocol::Response; @@ -169,12 +173,14 @@ class InterceptionJob : public network::mojom::URLLoaderClient, const base::UnguessableToken& frame_token, int32_t process_id, std::unique_ptr create_loader_params, + bool is_download, network::mojom::URLLoaderRequest loader_request, network::mojom::URLLoaderClientPtr client, network::mojom::URLLoaderFactoryPtr target_factory); void GetResponseBody( std::unique_ptr callback); + void TakeResponseBodyPipe(TakeResponseBodyPipeCallback callback); void ContinueInterceptedRequest( std::unique_ptr modifications, std::unique_ptr callback); @@ -215,13 +221,16 @@ class InterceptionJob : public network::mojom::URLLoaderClient, void ResponseBodyComplete(); bool ShouldBypassForResponse() const { + if (state_ == State::kResponseTaken) + return false; DCHECK_EQ(!!response_metadata_, !!body_reader_); DCHECK_EQ(state_, State::kResponseReceived); return !response_metadata_; } // network::mojom::URLLoader methods - void FollowRedirect() override; + void FollowRedirect(const base::Optional& + modified_request_headers) override; void ProceedWithResponse() override; void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override; @@ -244,6 +253,8 @@ class InterceptionJob : public network::mojom::URLLoaderClient, mojo::ScopedDataPipeConsumerHandle body) override; void OnComplete(const network::URLLoaderCompletionStatus& status) override; + bool CanGetResponseBody(std::string* error_reason); + const std::string id_; const GlobalRequestId global_req_id_; const base::UnguessableToken frame_token_; @@ -255,6 +266,7 @@ class InterceptionJob : public network::mojom::URLLoaderClient, InterceptionStage stage_; std::unique_ptr create_loader_params_; + const bool is_download_; mojo::Binding client_binding_; mojo::Binding loader_binding_; @@ -269,6 +281,7 @@ class InterceptionJob : public network::mojom::URLLoaderClient, kRedirectReceived, kAuthRequired, kResponseReceived, + kResponseTaken, }; State state_; @@ -280,6 +293,7 @@ class InterceptionJob : public network::mojom::URLLoaderClient, base::Optional> priority_; DevToolsURLLoaderInterceptor::HandleAuthRequestCallback pending_auth_callback_; + TakeResponseBodyPipeCallback pending_response_body_pipe_callback_; DISALLOW_COPY_AND_ASSIGN(InterceptionJob); }; @@ -298,6 +312,7 @@ class DevToolsURLLoaderInterceptor::Impl void CreateJob(const base::UnguessableToken& frame_token, int32_t process_id, + bool is_download, std::unique_ptr create_params, network::mojom::URLLoaderRequest loader_request, network::mojom::URLLoaderClientPtr client, @@ -307,10 +322,10 @@ class DevToolsURLLoaderInterceptor::Impl static int last_id = 0; std::string id = base::StringPrintf("interception-job-%d", ++last_id); - InterceptionJob* job = - new InterceptionJob(this, id, frame_token, process_id, - std::move(create_params), std::move(loader_request), - std::move(client), std::move(target_factory)); + InterceptionJob* job = new InterceptionJob( + this, id, frame_token, process_id, std::move(create_params), + is_download, std::move(loader_request), std::move(client), + std::move(target_factory)); jobs_.emplace(std::move(id), job); } @@ -336,6 +351,19 @@ class DevToolsURLLoaderInterceptor::Impl job->GetResponseBody(std::move(callback)); } + void TakeResponseBodyPipe( + const std::string& interception_id, + DevToolsNetworkInterceptor::TakeResponseBodyPipeCallback callback) { + auto it = jobs_.find(interception_id); + if (it == jobs_.end()) { + std::move(callback).Run( + protocol::Response::InvalidParams("Invalid InterceptionId."), + mojo::ScopedDataPipeConsumerHandle(), std::string()); + return; + } + it->second->TakeResponseBodyPipe(std::move(callback)); + } + void ContinueInterceptedRequest( const std::string& interception_id, std::unique_ptr modifications, @@ -377,6 +405,7 @@ class DevToolsURLLoaderFactoryProxy : public network::mojom::URLLoaderFactory { DevToolsURLLoaderFactoryProxy( const base::UnguessableToken& frame_token, int32_t process_id, + bool is_download, network::mojom::URLLoaderFactoryRequest loader_request, network::mojom::URLLoaderFactoryPtrInfo target_factory_info, base::WeakPtr interceptor); @@ -401,6 +430,7 @@ class DevToolsURLLoaderFactoryProxy : public network::mojom::URLLoaderFactory { const base::UnguessableToken frame_token_; const int32_t process_id_; + const bool is_download_; network::mojom::URLLoaderFactoryPtr target_factory_; base::WeakPtr interceptor_; @@ -412,11 +442,13 @@ class DevToolsURLLoaderFactoryProxy : public network::mojom::URLLoaderFactory { DevToolsURLLoaderFactoryProxy::DevToolsURLLoaderFactoryProxy( const base::UnguessableToken& frame_token, int32_t process_id, + bool is_download, network::mojom::URLLoaderFactoryRequest loader_request, network::mojom::URLLoaderFactoryPtrInfo target_factory_info, base::WeakPtr interceptor) : frame_token_(frame_token), process_id_(process_id), + is_download_(is_download), interceptor_(std::move(interceptor)) { DETACH_FROM_SEQUENCE(sequence_checker_); BrowserThread::PostTask( @@ -449,9 +481,9 @@ void DevToolsURLLoaderFactoryProxy::CreateLoaderAndStart( routing_id, request_id, options, request, traffic_annotation); network::mojom::URLLoaderFactoryPtr factory_clone; target_factory_->Clone(MakeRequest(&factory_clone)); - interceptor->CreateJob(frame_token_, process_id_, std::move(creation_params), - std::move(loader), std::move(client), - std::move(factory_clone)); + interceptor->CreateJob(frame_token_, process_id_, is_download_, + std::move(creation_params), std::move(loader), + std::move(client), std::move(factory_clone)); } void DevToolsURLLoaderFactoryProxy::StartOnIO( @@ -475,7 +507,6 @@ void DevToolsURLLoaderFactoryProxy::Clone( } void DevToolsURLLoaderFactoryProxy::OnTargetFactoryError() { - DCHECK(!target_factory_.is_bound()); delete this; } @@ -548,6 +579,15 @@ void DevToolsURLLoaderInterceptor::GetResponseBody( interception_id, std::move(callback))); } +void DevToolsURLLoaderInterceptor::TakeResponseBodyPipe( + const std::string& interception_id, + DevToolsNetworkInterceptor::TakeResponseBodyPipeCallback callback) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&Impl::TakeResponseBodyPipe, base::Unretained(impl_.get()), + interception_id, std::move(callback))); +} + void DevToolsURLLoaderInterceptor::ContinueInterceptedRequest( const std::string& interception_id, std::unique_ptr modifications, @@ -562,6 +602,7 @@ void DevToolsURLLoaderInterceptor::ContinueInterceptedRequest( bool DevToolsURLLoaderInterceptor::CreateProxyForInterception( const base::UnguessableToken frame_token, int process_id, + bool is_download, network::mojom::URLLoaderFactoryRequest* request) const { if (!enabled_) return false; @@ -570,7 +611,7 @@ bool DevToolsURLLoaderInterceptor::CreateProxyForInterception( network::mojom::URLLoaderFactoryPtrInfo target_ptr_info; *request = MakeRequest(&target_ptr_info); - new DevToolsURLLoaderFactoryProxy(frame_token, process_id, + new DevToolsURLLoaderFactoryProxy(frame_token, process_id, is_download, std::move(original_request), std::move(target_ptr_info), weak_impl_); return true; @@ -582,6 +623,7 @@ InterceptionJob::InterceptionJob( const base::UnguessableToken& frame_token, int process_id, std::unique_ptr create_loader_params, + bool is_download, network::mojom::URLLoaderRequest loader_request, network::mojom::URLLoaderClientPtr client, network::mojom::URLLoaderFactoryPtr target_factory) @@ -596,6 +638,7 @@ InterceptionJob::InterceptionJob( report_upload_(!!create_loader_params->request.request_body), interceptor_(interceptor), create_loader_params_(std::move(create_loader_params)), + is_download_(is_download), client_binding_(this), loader_binding_(this), client_(std::move(client)), @@ -622,21 +665,26 @@ InterceptionJob::InterceptionJob( StartRequest(); } -void InterceptionJob::GetResponseBody( - std::unique_ptr callback) { - std::string error_reason; - +bool InterceptionJob::CanGetResponseBody(std::string* error_reason) { if (!(stage_ & InterceptionStage::RESPONSE)) { - error_reason = + *error_reason = "Can only get response body on HeadersReceived pattern matched " "requests."; - } else if (state_ != kResponseReceived) { - DCHECK(waiting_for_resolution_); - error_reason = + return false; + } + if (state_ != State::kResponseReceived || !waiting_for_resolution_) { + *error_reason = "Can only get response body on requests captured after headers " "received."; + return false; } - if (!error_reason.empty()) { + return true; +} + +void InterceptionJob::GetResponseBody( + std::unique_ptr callback) { + std::string error_reason; + if (!CanGetResponseBody(&error_reason)) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::BindOnce(&GetResponseBodyForInterceptionCallback::sendFailure, @@ -644,7 +692,6 @@ void InterceptionJob::GetResponseBody( Response::Error(std::move(error_reason)))); return; } - if (!body_reader_) { body_reader_ = std::make_unique(base::BindOnce( &InterceptionJob::ResponseBodyComplete, base::Unretained(this))); @@ -654,11 +701,30 @@ void InterceptionJob::GetResponseBody( body_reader_->AddCallback(std::move(callback)); } +void InterceptionJob::TakeResponseBodyPipe( + TakeResponseBodyPipeCallback callback) { + std::string error_reason; + if (!CanGetResponseBody(&error_reason)) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(std::move(callback), + Response::Error(std::move(error_reason)), + mojo::ScopedDataPipeConsumerHandle(), std::string())); + return; + } + DCHECK_EQ(state_, State::kResponseReceived); + DCHECK(!!response_metadata_); + state_ = State::kResponseTaken; + pending_response_body_pipe_callback_ = std::move(callback); + client_binding_.ResumeIncomingMethodCallProcessing(); + loader_->ResumeReadingBodyFromNet(); +} + void InterceptionJob::ContinueInterceptedRequest( std::unique_ptr modifications, std::unique_ptr callback) { Response response = InnerContinueRequest(std::move(modifications)); - // |this| may be destroyed at this pont. + // |this| may be destroyed at this point. bool success = response.isSuccess(); base::OnceClosure task = success ? base::BindOnce(&ContinueInterceptedRequestCallback::sendSuccess, @@ -693,9 +759,9 @@ Response InterceptionJob::InnerContinueRequest( return Response::InvalidParams("authChallengeResponse required."); return ProcessAuthResponse( modifications->auth_challenge_response.fromJust()); - } else if (modifications->auth_challenge_response.isJust()) { - return Response::InvalidParams("authChallengeResponse not expected."); } + if (modifications->auth_challenge_response.isJust()) + return Response::InvalidParams("authChallengeResponse not expected."); if (modifications->mark_as_canceled || modifications->error_reason) { int error = modifications->error_reason @@ -704,6 +770,14 @@ Response InterceptionJob::InnerContinueRequest( : net::ERR_FAILED); network::URLLoaderCompletionStatus status(error); status.completion_time = base::TimeTicks::Now(); + if (modifications->error_reason == net::ERR_BLOCKED_BY_CLIENT) { + // So we know that these modifications originated from devtools + // (also known as inspector), and can therefore annotate the + // request. We only do this for one specific error code thus + // far, to minimize risk of breaking other usages. + status.extended_error_code = + static_cast(blink::ResourceRequestBlockedReason::kInspector); + } client_->OnComplete(status); Shutdown(); return Response::OK(); @@ -737,6 +811,10 @@ Response InterceptionJob::InnerContinueRequest( } if (response_metadata_) { + if (state_ == State::kResponseTaken) { + return Response::InvalidParams( + "Unable to continue request as is after body is taken"); + } // TODO(caseq): report error if other modifications are present. DCHECK_EQ(State::kResponseReceived, state_); DCHECK(!body_reader_); @@ -1007,7 +1085,11 @@ void InterceptionJob::Shutdown() { } // URLLoader methods -void InterceptionJob::FollowRedirect() { +void InterceptionJob::FollowRedirect( + const base::Optional& modified_request_headers) { + DCHECK(!modified_request_headers.has_value()) << "Redirect with modified " + "headers was not supported " + "yet. crbug.com/845683"; DCHECK(!waiting_for_resolution_); network::ResourceRequest* request = &create_loader_params_->request; @@ -1024,7 +1106,7 @@ void InterceptionJob::FollowRedirect() { } if (state_ == State::kRedirectReceived) { state_ = State::kRequestSent; - loader_->FollowRedirect(); + loader_->FollowRedirect(base::nullopt); return; } @@ -1040,17 +1122,17 @@ void InterceptionJob::SetPriority(net::RequestPriority priority, int32_t intra_priority_value) { priority_ = std::make_pair(priority, intra_priority_value); - if (state_ != State::kNotStarted) + if (loader_) loader_->SetPriority(priority, intra_priority_value); } void InterceptionJob::PauseReadingBodyFromNet() { - if (state_ != State::kNotStarted && !body_reader_) + if (!body_reader_ && loader_ && state_ != State::kResponseTaken) loader_->PauseReadingBodyFromNet(); } void InterceptionJob::ResumeReadingBodyFromNet() { - if (state_ != State::kNotStarted && !body_reader_) + if (!body_reader_ && loader_ && state_ != State::kResponseTaken) loader_->ResumeReadingBodyFromNet(); } @@ -1070,7 +1152,13 @@ void InterceptionJob::OnReceiveResponse( response_metadata_ = std::make_unique(head); response_metadata_->downloaded_file = std::move(downloaded_file); - NotifyClient(BuildRequestInfo(&head)); + auto request_info = BuildRequestInfo(&head); + const network::ResourceRequest& request = create_loader_params_->request; + request_info->is_download = + request_info->is_navigation && request.allow_download && + (is_download_ || navigation_loader_util::IsDownload( + request.url, head.headers.get(), head.mime_type)); + NotifyClient(std::move(request_info)); } void InterceptionJob::OnReceiveRedirect( @@ -1125,6 +1213,17 @@ void InterceptionJob::OnTransferSizeUpdated(int32_t transfer_size_diff) { void InterceptionJob::OnStartLoadingResponseBody( mojo::ScopedDataPipeConsumerHandle body) { + if (pending_response_body_pipe_callback_) { + DCHECK_EQ(State::kResponseTaken, state_); + DCHECK(!body_reader_); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(std::move(pending_response_body_pipe_callback_), + Response::OK(), std::move(body), + response_metadata_->head.mime_type)); + return; + } + DCHECK_EQ(State::kResponseReceived, state_); if (ShouldBypassForResponse()) client_->OnStartLoadingResponseBody(std::move(body)); else @@ -1133,12 +1232,18 @@ void InterceptionJob::OnStartLoadingResponseBody( void InterceptionJob::OnComplete( const network::URLLoaderCompletionStatus& status) { - if (ShouldBypassForResponse()) { + // Essentially ShouldBypassForResponse(), but skip DCHECKs + // since this may be called in any state during shutdown. + if (!response_metadata_) { client_->OnComplete(status); Shutdown(); return; } response_metadata_->status = status; + // No need to listen to the channel any more, so just close it, so if the pipe + // is closed by the other end, |shutdown| isn't run. + client_binding_.Close(); + loader_.reset(); } void InterceptionJob::OnAuthRequest( diff --git a/chromium/content/browser/devtools/devtools_url_loader_interceptor.h b/chromium/content/browser/devtools/devtools_url_loader_interceptor.h index a05c8bc1f56..3957091b35f 100644 --- a/chromium/content/browser/devtools/devtools_url_loader_interceptor.h +++ b/chromium/content/browser/devtools/devtools_url_loader_interceptor.h @@ -40,6 +40,9 @@ class DevToolsURLLoaderInterceptor { std::unique_ptr< DevToolsNetworkInterceptor::GetResponseBodyForInterceptionCallback> callback); + void TakeResponseBodyPipe( + const std::string& interception_id, + DevToolsNetworkInterceptor::TakeResponseBodyPipeCallback callback); void ContinueInterceptedRequest( const std::string& interception_id, std::unique_ptr modifications, @@ -50,6 +53,7 @@ class DevToolsURLLoaderInterceptor { bool CreateProxyForInterception( const base::UnguessableToken frame_token, int process_id, // 0 for navigation + bool is_download, network::mojom::URLLoaderFactoryRequest* request) const; private: diff --git a/chromium/content/browser/devtools/devtools_video_consumer.cc b/chromium/content/browser/devtools/devtools_video_consumer.cc index 8c9bdd44ade..fb91996670c 100644 --- a/chromium/content/browser/devtools/devtools_video_consumer.cc +++ b/chromium/content/browser/devtools/devtools_video_consumer.cc @@ -149,9 +149,8 @@ void DevToolsVideoConsumer::OnFrameCaptured( // Setting |frame|'s visible rect equal to |content_rect| so that only the // portion of the frame that contain content are used. frame = media::VideoFrame::WrapExternalData( - info->pixel_format, info->coded_size, info->visible_rect, - info->visible_rect.size(), static_cast(mapping.get()), - buffer_size, info->timestamp); + info->pixel_format, info->coded_size, content_rect, content_rect.size(), + static_cast(mapping.get()), buffer_size, info->timestamp); if (!frame) return; frame->AddDestructionObserver(base::BindOnce( diff --git a/chromium/content/browser/devtools/devtools_video_consumer_unittest.cc b/chromium/content/browser/devtools/devtools_video_consumer_unittest.cc index adb95d4bbab..20f9b2fdb62 100644 --- a/chromium/content/browser/devtools/devtools_video_consumer_unittest.cc +++ b/chromium/content/browser/devtools/devtools_video_consumer_unittest.cc @@ -18,7 +18,6 @@ namespace { // Capture parameters. constexpr gfx::Size kResolution = gfx::Size(320, 180); // Arbitrarily chosen. constexpr media::VideoPixelFormat kFormat = media::PIXEL_FORMAT_I420; -constexpr media::VideoPixelStorage kStorage = media::VideoPixelStorage::CPU; // A non-zero FrameSinkId to prevent validation errors when // DevToolsVideoConsumer::ChangeTarget(viz::FrameSinkId) is called @@ -170,7 +169,7 @@ class DevToolsVideoConsumerTest : public testing::Test { media::mojom::VideoFrameInfoPtr info = media::mojom::VideoFrameInfo::New( base::TimeDelta(), base::Value(base::Value::Type::DICTIONARY), kFormat, - kStorage, kResolution, gfx::Rect(kResolution)); + kResolution, gfx::Rect(kResolution)); consumer_->OnFrameCaptured(std::move(buffer), buffer_size, std::move(info), gfx::Rect(kResolution), gfx::Rect(kResolution), diff --git a/chromium/content/browser/devtools/forwarding_agent_host.cc b/chromium/content/browser/devtools/forwarding_agent_host.cc index 2b60cf23d86..1682f1cc880 100644 --- a/chromium/content/browser/devtools/forwarding_agent_host.cc +++ b/chromium/content/browser/devtools/forwarding_agent_host.cc @@ -55,12 +55,6 @@ void ForwardingAgentHost::AttachClient(DevToolsAgentHostClient* client) { session_proxies_[client].reset(new SessionProxy(this, client)); } -void ForwardingAgentHost::ForceAttachClient(DevToolsAgentHostClient* client) { - while (!session_proxies_.empty()) - session_proxies_.begin()->second->ConnectionClosed(); - AttachClient(client); -} - bool ForwardingAgentHost::DetachClient(DevToolsAgentHostClient* client) { auto it = session_proxies_.find(client); if (it == session_proxies_.end()) diff --git a/chromium/content/browser/devtools/forwarding_agent_host.h b/chromium/content/browser/devtools/forwarding_agent_host.h index b09b2cf1b5d..3368c916f36 100644 --- a/chromium/content/browser/devtools/forwarding_agent_host.h +++ b/chromium/content/browser/devtools/forwarding_agent_host.h @@ -28,7 +28,6 @@ class ForwardingAgentHost : public DevToolsAgentHostImpl { // DevToolsAgentHost implementation void AttachClient(DevToolsAgentHostClient* client) override; - void ForceAttachClient(DevToolsAgentHostClient* client) override; bool DetachClient(DevToolsAgentHostClient* client) override; bool DispatchProtocolMessage(DevToolsAgentHostClient* client, const std::string& message) override; diff --git a/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc index 5a364cef4ed..e63b7fc855d 100644 --- a/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc +++ b/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc @@ -14,7 +14,6 @@ #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" @@ -839,7 +838,7 @@ IN_PROC_BROWSER_TEST_F(CaptureScreenshotTest, CaptureScreenshot) { if (base::SysInfo::IsLowEndDevice()) return; shell()->LoadURL( - GURL("data:text/html,")); + GURL("data:text/html,")); WaitForLoadStop(shell()->web_contents()); Attach(); SkBitmap expected_bitmap; @@ -862,7 +861,7 @@ IN_PROC_BROWSER_TEST_F(CaptureScreenshotTest, CaptureScreenshotJpeg) { return; shell()->LoadURL( - GURL("data:text/html,")); + GURL("data:text/html,")); WaitForLoadStop(shell()->web_contents()); Attach(); SkBitmap expected_bitmap; @@ -1561,206 +1560,6 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, BrowserGetTargets) { EXPECT_EQ("about:blank", url); } -namespace { -class NavigationFinishedObserver : public content::WebContentsObserver { - public: - explicit NavigationFinishedObserver(WebContents* web_contents) - : WebContentsObserver(web_contents), - num_finished_(0), - num_to_wait_for_(0) {} - - ~NavigationFinishedObserver() override {} - - void DidFinishNavigation( - content::NavigationHandle* navigation_handle) override { - if (navigation_handle->WasServerRedirect()) - return; - - num_finished_++; - if (num_finished_ >= num_to_wait_for_ && num_to_wait_for_ != 0) { - base::RunLoop::QuitCurrentDeprecated(); - } - } - - void WaitForNavigationsToFinish(int num_to_wait_for) { - if (num_finished_ < num_to_wait_for) { - num_to_wait_for_ = num_to_wait_for; - RunMessageLoop(); - } - num_to_wait_for_ = 0; - } - - private: - int num_finished_; - int num_to_wait_for_; -}; - -class LoadFinishedObserver : public content::WebContentsObserver { - public: - explicit LoadFinishedObserver(WebContents* web_contents) - : WebContentsObserver(web_contents), num_finished_(0) {} - - ~LoadFinishedObserver() override {} - - void DidStopLoading() override { - num_finished_++; - if (run_loop_.running()) - run_loop_.Quit(); - } - - void WaitForLoadToFinish() { - if (num_finished_ == 0) - run_loop_.Run(); - } - - private: - int num_finished_; - base::RunLoop run_loop_; -}; - -} // namespace - -IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, PageStopLoading) { - ASSERT_TRUE(embedded_test_server()->Start()); - - // Navigate to about:blank first so we can make sure there is a target page we - // can attach to, and have Network.setRequestInterception complete - // before we start the navigations we're interested in. - NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); - Attach(); - - std::unique_ptr params(new base::DictionaryValue()); - std::unique_ptr patterns(new base::ListValue()); - patterns->Append(std::make_unique()); - params->Set("patterns", std::move(patterns)); - SendCommand("Network.setRequestInterception", std::move(params), true); - - LoadFinishedObserver load_finished_observer(shell()->web_contents()); - - // The page will try to navigate twice, however since - // Network.setRequestInterception is true, - // it'll wait for confirmation before committing to the navigation. - GURL test_url = embedded_test_server()->GetURL( - "/devtools/control_navigations/meta_tag.html"); - shell()->LoadURL(test_url); - - // Stop all navigations. - SendCommand("Page.stopLoading", nullptr); - - // Wait for the initial navigation to finish. - load_finished_observer.WaitForLoadToFinish(); -} - -IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, ControlNavigationsMainFrame) { - ASSERT_TRUE(embedded_test_server()->Start()); - - // Navigate to about:blank first so we can make sure there is a target page we - // can attach to, and have Network.setRequestInterception complete - // before we start the navigations we're interested in. - NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); - Attach(); - - std::unique_ptr params(new base::DictionaryValue()); - std::unique_ptr patterns(new base::ListValue()); - patterns->Append(std::make_unique()); - params->Set("patterns", std::move(patterns)); - SendCommand("Network.setRequestInterception", std::move(params), true); - - NavigationFinishedObserver navigation_finished_observer( - shell()->web_contents()); - - GURL test_url = embedded_test_server()->GetURL( - "/devtools/control_navigations/meta_tag.html"); - shell()->LoadURL(test_url); - - std::vector expected_navigations = { - {"http://127.0.0.1/devtools/control_navigations/meta_tag.html", - false /* expected_is_redirect */, false /* abort */}, - {"http://127.0.0.1/devtools/navigation.html", - false /* expected_is_redirect */, true /* abort */}}; - - ProcessNavigationsAnyOrder(std::move(expected_navigations)); - - // Wait for the initial navigation and the cancelled meta refresh navigation - // to finish. - navigation_finished_observer.WaitForNavigationsToFinish(2); - - // Check main frame has the expected url. - EXPECT_EQ( - "http://127.0.0.1/devtools/control_navigations/meta_tag.html", - RemovePort( - shell()->web_contents()->GetMainFrame()->GetLastCommittedURL())); -} - -class IsolatedDevToolsProtocolTest : public DevToolsProtocolTest { - public: - ~IsolatedDevToolsProtocolTest() override {} - - void SetUpCommandLine(base::CommandLine* command_line) override { - IsolateAllSitesForTesting(command_line); - } -}; - -IN_PROC_BROWSER_TEST_F(IsolatedDevToolsProtocolTest, - ControlNavigationsChildFrames) { - content::SetupCrossSiteRedirector(embedded_test_server()); - ASSERT_TRUE(embedded_test_server()->Start()); - - // Navigate to about:blank first so we can make sure there is a target page we - // can attach to, and have Network.setRequestInterception complete - // before we start the navigations we're interested in. - NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); - Attach(); - - std::unique_ptr params(new base::DictionaryValue()); - std::unique_ptr patterns(new base::ListValue()); - patterns->Append(std::make_unique()); - params->Set("patterns", std::move(patterns)); - SendCommand("Network.setRequestInterception", std::move(params), true); - - NavigationFinishedObserver navigation_finished_observer( - shell()->web_contents()); - - GURL test_url = embedded_test_server()->GetURL( - "/devtools/control_navigations/iframe_navigation.html"); - shell()->LoadURL(test_url); - - // Allow main frame navigation, and all iframe navigations to http://a.com - // Allow initial iframe navigation to http://b.com but dissallow it to - // navigate to /devtools/navigation.html. - std::vector expected_navigations = { - {"http://127.0.0.1/devtools/control_navigations/" - "iframe_navigation.html", - false /* expected_is_redirect */, false /* abort */}, - {"http://127.0.0.1/cross-site/a.com/devtools/control_navigations/" - "meta_tag.html", - false /* expected_is_redirect */, false /* abort */}, - {"http://127.0.0.1/cross-site/b.com/devtools/control_navigations/" - "meta_tag.html", - false /* expected_is_redirect */, false /* abort */}, - {"http://a.com/devtools/control_navigations/meta_tag.html", - true /* expected_is_redirect */, false /* abort */}, - {"http://b.com/devtools/control_navigations/meta_tag.html", - true /* expected_is_redirect */, false /* abort */}, - {"http://a.com/devtools/navigation.html", - false /* expected_is_redirect */, false /* abort */}, - {"http://b.com/devtools/navigation.html", - false /* expected_is_redirect */, true /* abort */}}; - - ProcessNavigationsAnyOrder(std::move(expected_navigations)); - - // Wait for each frame's navigation to finish, ignoring redirects. - navigation_finished_observer.WaitForNavigationsToFinish(3); - - // Make sure each frame has the expected url. - EXPECT_THAT( - GetAllFrameUrls(), - ElementsAre("http://127.0.0.1/devtools/control_navigations/" - "iframe_navigation.html", - "http://a.com/devtools/navigation.html", - "http://b.com/devtools/control_navigations/meta_tag.html")); -} - IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, VirtualTimeTest) { NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); Attach(); @@ -2442,11 +2241,12 @@ class CountingDownloadFile : public download::DownloadFileImpl { // until data is returned. static int GetNumberActiveFilesFromFileThread() { int result = -1; + base::RunLoop run_loop; download::GetDownloadTaskRunner()->PostTaskAndReply( FROM_HERE, base::BindOnce(&CountingDownloadFile::GetNumberActiveFiles, &result), - base::MessageLoop::current()->QuitWhenIdleClosure()); - base::RunLoop().Run(); + run_loop.QuitWhenIdleClosure()); + run_loop.Run(); DCHECK_NE(-1, result); return result; } diff --git a/chromium/content/browser/devtools/protocol/emulation_handler.cc b/chromium/content/browser/devtools/protocol/emulation_handler.cc index f61f93491cc..ca11b87a749 100644 --- a/chromium/content/browser/devtools/protocol/emulation_handler.cc +++ b/chromium/content/browser/devtools/protocol/emulation_handler.cc @@ -327,12 +327,22 @@ void EmulationHandler::UpdateTouchEventEmulationState() { return; if (host_->GetParent() && !host_->IsCrossProcessSubframe()) return; + + // We only have a single TouchEmulator for all frames, so let the main frame's + // EmulationHandler enable/disable it. + if (!host_->frame_tree_node()->IsMainFrame()) + return; + if (touch_emulation_enabled_) { - host_->GetRenderWidgetHost()->GetTouchEmulator()->Enable( - TouchEmulator::Mode::kEmulatingTouchFromMouse, - TouchEmulationConfigurationToType(touch_emulation_configuration_)); + if (auto* touch_emulator = + host_->GetRenderWidgetHost()->GetTouchEmulator()) { + touch_emulator->Enable( + TouchEmulator::Mode::kEmulatingTouchFromMouse, + TouchEmulationConfigurationToType(touch_emulation_configuration_)); + } } else { - host_->GetRenderWidgetHost()->GetTouchEmulator()->Disable(); + if (auto* touch_emulator = host_->GetRenderWidgetHost()->GetTouchEmulator()) + touch_emulator->Disable(); } if (GetWebContents()) { GetWebContents()->SetForceDisableOverscrollContent( diff --git a/chromium/content/browser/devtools/protocol/input_handler.cc b/chromium/content/browser/devtools/protocol/input_handler.cc index 3556f33c0fb..4440a13e4c4 100644 --- a/chromium/content/browser/devtools/protocol/input_handler.cc +++ b/chromium/content/browser/devtools/protocol/input_handler.cc @@ -97,10 +97,6 @@ base::TimeTicks GetEventTimeTicks(const Maybe& timestamp) { : base::TimeTicks::Now(); } -double GetEventTimestamp(const Maybe& timestamp) { - return (GetEventTimeTicks(timestamp) - base::TimeTicks()).InSecondsF(); -} - bool SetKeyboardEventText(blink::WebUChar* to, Maybe from) { if (!from.isJust()) return true; @@ -351,7 +347,7 @@ class InputHandler::InputInjector &DispatchTouchEventCallback::sendSuccess, std::move(callback)); for (size_t i = 0; i < events.size(); i++) { widget_host_->GetTouchEmulator()->InjectTouchEvent( - events[i], + events[i], widget_host_->GetView(), i == events.size() - 1 ? std::move(closure) : base::OnceClosure()); } MaybeSelfDestruct(); @@ -490,7 +486,7 @@ void InputHandler::DispatchKeyEvent( GetEventModifiers(modifiers.fromMaybe(blink::WebInputEvent::kNoModifiers), auto_repeat.fromMaybe(false), is_keypad.fromMaybe(false), location.fromMaybe(0)), - GetEventTimeTicks(std::move(timestamp))); + GetEventTimeTicks(timestamp)); if (!SetKeyboardEventText(event.text, std::move(text))) { callback->sendFailure(Response::InvalidParams("Invalid 'text' parameter")); @@ -574,7 +570,7 @@ void InputHandler::DispatchMouseEvent( maybe_modifiers.fromMaybe(blink::WebInputEvent::kNoModifiers), false, false, 0); modifiers |= button_modifiers; - double timestamp = GetEventTimestamp(maybe_timestamp); + base::TimeTicks timestamp = GetEventTimeTicks(maybe_timestamp); std::unique_ptr mouse_event; blink::WebMouseWheelEvent* wheel_event = nullptr; @@ -637,7 +633,7 @@ void InputHandler::DispatchTouchEvent( int modifiers = GetEventModifiers( maybe_modifiers.fromMaybe(blink::WebInputEvent::kNoModifiers), false, false, 0); - double timestamp = GetEventTimestamp(maybe_timestamp); + base::TimeTicks timestamp = GetEventTimeTicks(maybe_timestamp); if ((type == blink::WebInputEvent::kTouchStart || type == blink::WebInputEvent::kTouchMove) && @@ -801,7 +797,7 @@ Response InputHandler::EmulateTouchFromMouseEvent(const std::string& type, modifiers.fromMaybe(blink::WebInputEvent::kNoModifiers), false, false, 0) | button_modifiers, - GetEventTimestamp(maybe_timestamp)); + GetEventTimeTicks(maybe_timestamp)); mouse_event = wheel_event; event.reset(wheel_event); wheel_event->delta_x = static_cast(delta_x.fromJust()); @@ -817,7 +813,7 @@ Response InputHandler::EmulateTouchFromMouseEvent(const std::string& type, modifiers.fromMaybe(blink::WebInputEvent::kNoModifiers), false, false, 0) | button_modifiers, - GetEventTimestamp(maybe_timestamp)); + GetEventTimeTicks(maybe_timestamp)); event.reset(mouse_event); } diff --git a/chromium/content/browser/devtools/protocol/io_handler.cc b/chromium/content/browser/devtools/protocol/io_handler.cc index 76e77feb568..57c61cd0370 100644 --- a/chromium/content/browser/devtools/protocol/io_handler.cc +++ b/chromium/content/browser/devtools/protocol/io_handler.cc @@ -15,6 +15,7 @@ #include "base/strings/string_util.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/devtools/devtools_io_context.h" +#include "content/browser/devtools/devtools_stream_blob.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" @@ -56,21 +57,27 @@ void IOHandler::Read( static const size_t kDefaultChunkSize = 10 * 1024 * 1024; static const char kBlobPrefix[] = "blob:"; - scoped_refptr stream = + scoped_refptr stream = io_context_->GetByHandle(handle); if (!stream && browser_context_ && StartsWith(handle, kBlobPrefix, base::CompareCase::SENSITIVE)) { ChromeBlobStorageContext* blob_context = ChromeBlobStorageContext::GetFor(browser_context_); std::string uuid = handle.substr(strlen(kBlobPrefix)); - stream = - io_context_->OpenBlob(blob_context, storage_partition_, handle, uuid); + stream = DevToolsStreamBlob::Create(io_context_, blob_context, + storage_partition_, handle, uuid); } if (!stream) { callback->sendFailure(Response::InvalidParams("Invalid stream handle")); return; } + if (offset.isJust() && !stream->SupportsSeek()) { + callback->sendFailure( + Response::InvalidParams("Read offset is specificed for a stream that " + "does not support random access")); + return; + } stream->Read(offset.fromMaybe(-1), max_size.fromMaybe(kDefaultChunkSize), base::BindOnce(&IOHandler::ReadComplete, weak_factory_.GetWeakPtr(), std::move(callback))); @@ -80,11 +87,11 @@ void IOHandler::ReadComplete(std::unique_ptr callback, std::unique_ptr data, bool base64_encoded, int status) { - if (status == DevToolsIOContext::ROStream::StatusFailure) { + if (status == DevToolsIOContext::Stream::StatusFailure) { callback->sendFailure(Response::Error("Read failed")); return; } - bool eof = status == DevToolsIOContext::ROStream::StatusEOF; + bool eof = status == DevToolsIOContext::Stream::StatusEOF; callback->sendSuccess(base64_encoded, std::move(*data), eof); } diff --git a/chromium/content/browser/devtools/protocol/memory_handler.cc b/chromium/content/browser/devtools/protocol/memory_handler.cc index 56c44b1556f..06a5638dab3 100644 --- a/chromium/content/browser/devtools/protocol/memory_handler.cc +++ b/chromium/content/browser/devtools/protocol/memory_handler.cc @@ -7,14 +7,18 @@ #include "base/memory/memory_pressure_listener.h" #include "base/sampling_heap_profiler/sampling_heap_profiler.h" #include "base/strings/stringprintf.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/common/bind_interface_helpers.h" +#include "content/public/common/child_process_host.h" #include "content/public/common/content_features.h" namespace content { namespace protocol { MemoryHandler::MemoryHandler() - : DevToolsDomainHandler(Memory::Metainfo::domainName) { -} + : DevToolsDomainHandler(Memory::Metainfo::domainName), + process_host_id_(ChildProcessHost::kInvalidUniqueID), + weak_factory_(this) {} MemoryHandler::~MemoryHandler() {} @@ -22,6 +26,11 @@ void MemoryHandler::Wire(UberDispatcher* dispatcher) { Memory::Dispatcher::wire(dispatcher, this); } +void MemoryHandler::SetRenderer(int process_host_id, + RenderFrameHostImpl* frame_host) { + process_host_id_ = process_host_id; +} + Response MemoryHandler::GetBrowserSamplingProfile( std::unique_ptr* out_profile) { std::unique_ptr> samples = @@ -74,5 +83,40 @@ Response MemoryHandler::SimulatePressureNotification( return Response::OK(); } +void MemoryHandler::PrepareForLeakDetection( + std::unique_ptr callback) { + if (leak_detection_callback_) { + callback->sendFailure( + Response::Error("Another leak detection in progress")); + return; + } + RenderProcessHost* process = RenderProcessHost::FromID(process_host_id_); + if (!process) { + callback->sendFailure(Response::Error("No process to detect leaks in")); + return; + } + + leak_detection_callback_ = std::move(callback); + BindInterface(process, &leak_detector_); + leak_detector_.set_connection_error_handler(base::BindOnce( + &MemoryHandler::OnLeakDetectorIsGone, base::Unretained(this))); + leak_detector_->PerformLeakDetection(base::BindOnce( + &MemoryHandler::OnLeakDetectionComplete, weak_factory_.GetWeakPtr())); +} + +void MemoryHandler::OnLeakDetectionComplete( + blink::mojom::LeakDetectionResultPtr result) { + leak_detection_callback_->sendSuccess(); + leak_detection_callback_.reset(); + leak_detector_.reset(); +} + +void MemoryHandler::OnLeakDetectorIsGone() { + leak_detection_callback_->sendFailure( + Response::Error("Failed to run leak detection")); + leak_detection_callback_.reset(); + leak_detector_.reset(); +} + } // namespace protocol } // namespace content diff --git a/chromium/content/browser/devtools/protocol/memory_handler.h b/chromium/content/browser/devtools/protocol/memory_handler.h index 1c09b64b5ca..21fa2b92db0 100644 --- a/chromium/content/browser/devtools/protocol/memory_handler.h +++ b/chromium/content/browser/devtools/protocol/memory_handler.h @@ -6,8 +6,10 @@ #define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_MEMORY_HANDLER_H_ #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "content/browser/devtools/protocol/devtools_domain_handler.h" #include "content/browser/devtools/protocol/memory.h" +#include "third_party/blink/public/mojom/leak_detector/leak_detector.mojom.h" namespace content { namespace protocol { @@ -19,13 +21,25 @@ class MemoryHandler : public DevToolsDomainHandler, ~MemoryHandler() override; void Wire(UberDispatcher* dispatcher) override; + void SetRenderer(int process_host_id, + RenderFrameHostImpl* frame_host) override; Response GetBrowserSamplingProfile( std::unique_ptr* out_profile) override; Response SetPressureNotificationsSuppressed(bool suppressed) override; Response SimulatePressureNotification(const std::string& level) override; + void PrepareForLeakDetection( + std::unique_ptr callback) override; private: + void OnLeakDetectionComplete(blink::mojom::LeakDetectionResultPtr result); + void OnLeakDetectorIsGone(); + + int process_host_id_; + blink::mojom::LeakDetectorPtr leak_detector_; + std::unique_ptr leak_detection_callback_; + base::WeakPtrFactory weak_factory_; + DISALLOW_COPY_AND_ASSIGN(MemoryHandler); }; diff --git a/chromium/content/browser/devtools/protocol/network_handler.cc b/chromium/content/browser/devtools/protocol/network_handler.cc index 6d62f72deae..b8ea9fc9205 100644 --- a/chromium/content/browser/devtools/protocol/network_handler.cc +++ b/chromium/content/browser/devtools/protocol/network_handler.cc @@ -17,14 +17,21 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" +#include "content/browser/background_sync/background_sync_manager.h" #include "content/browser/devtools/devtools_interceptor_controller.h" +#include "content/browser/devtools/devtools_io_context.h" #include "content/browser/devtools/devtools_session.h" +#include "content/browser/devtools/devtools_stream_pipe.h" #include "content/browser/devtools/devtools_url_loader_interceptor.h" #include "content/browser/devtools/protocol/page.h" #include "content/browser/devtools/protocol/security.h" +#include "content/browser/devtools/service_worker_devtools_agent_host.h" +#include "content/browser/devtools/service_worker_devtools_manager.h" #include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/frame_host/navigation_request.h" #include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/storage_partition_impl.h" +#include "content/browser/web_package/signed_exchange_header.h" #include "content/common/navigation_params.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" @@ -61,6 +68,7 @@ #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/resource_response.h" #include "services/network/public/cpp/url_loader_completion_status.h" +#include "third_party/blink/public/platform/resource_request_blocked_reason.h" #include "third_party/boringssl/src/include/openssl/ssl.h" namespace content { @@ -620,34 +628,45 @@ net::Error NetErrorFromString(const std::string& error, bool* ok) { return net::ERR_INTERNET_DISCONNECTED; if (error == Network::ErrorReasonEnum::AddressUnreachable) return net::ERR_ADDRESS_UNREACHABLE; + if (error == Network::ErrorReasonEnum::BlockedByClient) + return net::ERR_BLOCKED_BY_CLIENT; + if (error == Network::ErrorReasonEnum::BlockedByResponse) + return net::ERR_BLOCKED_BY_RESPONSE; *ok = false; return net::ERR_FAILED; } String NetErrorToString(int net_error) { - if (net_error == net::ERR_ABORTED) - return Network::ErrorReasonEnum::Aborted; - if (net_error == net::ERR_TIMED_OUT) - return Network::ErrorReasonEnum::TimedOut; - if (net_error == net::ERR_ACCESS_DENIED) - return Network::ErrorReasonEnum::AccessDenied; - if (net_error == net::ERR_CONNECTION_CLOSED) - return Network::ErrorReasonEnum::ConnectionClosed; - if (net_error == net::ERR_CONNECTION_RESET) - return Network::ErrorReasonEnum::ConnectionReset; - if (net_error == net::ERR_CONNECTION_REFUSED) - return Network::ErrorReasonEnum::ConnectionRefused; - if (net_error == net::ERR_CONNECTION_ABORTED) - return Network::ErrorReasonEnum::ConnectionAborted; - if (net_error == net::ERR_CONNECTION_FAILED) - return Network::ErrorReasonEnum::ConnectionFailed; - if (net_error == net::ERR_NAME_NOT_RESOLVED) - return Network::ErrorReasonEnum::NameNotResolved; - if (net_error == net::ERR_INTERNET_DISCONNECTED) - return Network::ErrorReasonEnum::InternetDisconnected; - if (net_error == net::ERR_ADDRESS_UNREACHABLE) - return Network::ErrorReasonEnum::AddressUnreachable; - return Network::ErrorReasonEnum::Failed; + switch (net_error) { + case net::ERR_ABORTED: + return Network::ErrorReasonEnum::Aborted; + case net::ERR_TIMED_OUT: + return Network::ErrorReasonEnum::TimedOut; + case net::ERR_ACCESS_DENIED: + return Network::ErrorReasonEnum::AccessDenied; + case net::ERR_CONNECTION_CLOSED: + return Network::ErrorReasonEnum::ConnectionClosed; + case net::ERR_CONNECTION_RESET: + return Network::ErrorReasonEnum::ConnectionReset; + case net::ERR_CONNECTION_REFUSED: + return Network::ErrorReasonEnum::ConnectionRefused; + case net::ERR_CONNECTION_ABORTED: + return Network::ErrorReasonEnum::ConnectionAborted; + case net::ERR_CONNECTION_FAILED: + return Network::ErrorReasonEnum::ConnectionFailed; + case net::ERR_NAME_NOT_RESOLVED: + return Network::ErrorReasonEnum::NameNotResolved; + case net::ERR_INTERNET_DISCONNECTED: + return Network::ErrorReasonEnum::InternetDisconnected; + case net::ERR_ADDRESS_UNREACHABLE: + return Network::ErrorReasonEnum::AddressUnreachable; + case net::ERR_BLOCKED_BY_CLIENT: + return Network::ErrorReasonEnum::BlockedByClient; + case net::ERR_BLOCKED_BY_RESPONSE: + return Network::ErrorReasonEnum::BlockedByResponse; + default: + return Network::ErrorReasonEnum::Failed; + } } bool AddInterceptedResourceType( @@ -857,16 +876,75 @@ std::string StripFragment(const GURL& url) { } // namespace -NetworkHandler::NetworkHandler(const std::string& host_id) +class BackgroundSyncRestorer { + public: + BackgroundSyncRestorer(const std::string& host_id, + StoragePartition* storage_partition) + : host_id_(host_id), storage_partition_(storage_partition) { + SetServiceWorkerOffline(true); + } + + ~BackgroundSyncRestorer() { SetServiceWorkerOffline(false); } + + void SetStoragePartition(StoragePartition* storage_partition) { + storage_partition_ = storage_partition; + } + + private: + void SetServiceWorkerOffline(bool offline) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + scoped_refptr host = + DevToolsAgentHost::GetForId(host_id_); + if (!host || !storage_partition_ || + host->GetType() != DevToolsAgentHost::kTypeServiceWorker) { + return; + } + scoped_refptr service_worker_host = + static_cast(host.get()); + scoped_refptr sync_context = + static_cast(storage_partition_) + ->GetBackgroundSyncContext(); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &SetServiceWorkerOfflineOnIO, sync_context, + base::RetainedRef(static_cast( + storage_partition_->GetServiceWorkerContext())), + service_worker_host->version_id(), offline)); + } + + static void SetServiceWorkerOfflineOnIO( + scoped_refptr sync_context, + scoped_refptr swcontext, + int64_t version_id, + bool offline) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + ServiceWorkerVersion* version = swcontext.get()->GetLiveVersion(version_id); + if (!version) + return; + sync_context->background_sync_manager()->EmulateServiceWorkerOffline( + version->registration_id(), offline); + } + + std::string host_id_; + StoragePartition* storage_partition_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundSyncRestorer); +}; + +NetworkHandler::NetworkHandler(const std::string& host_id, + DevToolsIOContext* io_context) : DevToolsDomainHandler(Network::Metainfo::domainName), + host_id_(host_id), + io_context_(io_context), browser_context_(nullptr), storage_partition_(nullptr), host_(nullptr), enabled_(false), - host_id_(host_id), bypass_service_worker_(false), cache_disabled_(false), weak_factory_(this) { + DCHECK(io_context_); static bool have_configured_service_worker_context = false; if (have_configured_service_worker_context) return; @@ -902,6 +980,8 @@ void NetworkHandler::SetRenderer(int render_process_host_id, browser_context_ = nullptr; } host_ = frame_host; + if (background_sync_restorer_) + background_sync_restorer_->SetStoragePartition(storage_partition_); } Response NetworkHandler::Enable(Maybe max_total_size, @@ -1405,6 +1485,36 @@ std::unique_ptr BuildResponse( return response; } + +String blockedReason(blink::ResourceRequestBlockedReason reason) { + switch (reason) { + case blink::ResourceRequestBlockedReason::kCSP: + return protocol::Network::BlockedReasonEnum::Csp; + case blink::ResourceRequestBlockedReason::kMixedContent: + return protocol::Network::BlockedReasonEnum::MixedContent; + case blink::ResourceRequestBlockedReason::kOrigin: + return protocol::Network::BlockedReasonEnum::Origin; + case blink::ResourceRequestBlockedReason::kInspector: + return protocol::Network::BlockedReasonEnum::Inspector; + case blink::ResourceRequestBlockedReason::kSubresourceFilter: + return protocol::Network::BlockedReasonEnum::SubresourceFilter; + case blink::ResourceRequestBlockedReason::kContentType: + return protocol::Network::BlockedReasonEnum::ContentType; + case blink::ResourceRequestBlockedReason::kOther: + return protocol::Network::BlockedReasonEnum::Other; + } + NOTREACHED(); + return protocol::Network::BlockedReasonEnum::Other; +} + +Maybe GetBlockedReasonFor( + const network::URLLoaderCompletionStatus& status) { + if (status.error_code != net::ERR_BLOCKED_BY_CLIENT && + status.error_code != net::ERR_BLOCKED_BY_RESPONSE) + return Maybe(); + return blockedReason(static_cast( + status.extended_error_code)); +} } // namespace void NetworkHandler::NavigationRequestWillBeSent( @@ -1478,12 +1588,17 @@ void NetworkHandler::NavigationRequestWillBeSent( void NetworkHandler::RequestSent(const std::string& request_id, const std::string& loader_id, const network::ResourceRequest& request, - const char* initiator_type) { + const char* initiator_type, + const base::Optional& initiator_url) { if (!enabled_) return; std::unique_ptr headers_dict(DictionaryValue::create()); for (net::HttpRequestHeaders::Iterator it(request.headers); it.GetNext();) headers_dict->setString(it.name(), it.value()); + std::unique_ptr initiator = + Network::Initiator::Create().SetType(initiator_type).Build(); + if (initiator_url) + initiator->SetUrl(initiator_url->spec()); frontend_->RequestWillBeSent( request_id, loader_id, StripFragment(request.url), Network::Request::Create() @@ -1495,8 +1610,7 @@ void NetworkHandler::RequestSent(const std::string& request_id, .Build(), base::TimeTicks::Now().ToInternalValue() / static_cast(base::Time::kMicrosecondsPerSecond), - base::Time::Now().ToDoubleT(), - Network::Initiator::Create().SetType(initiator_type).Build(), + base::Time::Now().ToDoubleT(), std::move(initiator), std::unique_ptr(), std::string(Page::ResourceTypeEnum::Other), Maybe() /* frame_id */, request.has_user_gesture); @@ -1531,7 +1645,8 @@ void NetworkHandler::LoadingComplete( base::TimeTicks::Now().ToInternalValue() / static_cast(base::Time::kMicrosecondsPerSecond), resource_type, net::ErrorToString(status.error_code), - status.error_code == net::Error::ERR_ABORTED); + status.error_code == net::Error::ERR_ABORTED, + GetBlockedReasonFor(status)); return; } frontend_->LoadingFinished( @@ -1541,52 +1656,58 @@ void NetworkHandler::LoadingComplete( status.encoded_data_length); } -void NetworkHandler::NavigationFailed(NavigationRequest* navigation_request) { +void NetworkHandler::OnSignedExchangeReceived( + base::Optional devtools_navigation_token, + const GURL& outer_request_url, + const network::ResourceResponseHead& outer_response, + const base::Optional& header, + const base::Optional& ssl_info, + const std::vector& error_messages) { if (!enabled_) return; + std::unique_ptr signed_exchange_info = + Network::SignedExchangeInfo::Create() + .SetOuterResponse(BuildResponse(outer_request_url, outer_response)) + .Build(); - static int next_id = 0; - std::string request_id = base::IntToString(base::GetCurrentProcId()) + "." + - base::IntToString(++next_id); - std::string error_string = - net::ErrorToString(navigation_request->net_error()); - bool cancelled = navigation_request->net_error() == net::Error::ERR_ABORTED; - - std::unique_ptr headers_dict(DictionaryValue::create()); - net::HttpRequestHeaders headers; - headers.AddHeadersFromString(navigation_request->begin_params()->headers); - for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext();) - headers_dict->setString(it.name(), it.value()); - frontend_->RequestWillBeSent( - request_id, "" /* loader_id */, - StripFragment(navigation_request->common_params().url), - Network::Request::Create() - .SetUrl(StripFragment(navigation_request->common_params().url)) - .SetMethod(navigation_request->common_params().method) - .SetHeaders(Object::fromValue(headers_dict.get(), nullptr)) - // Note: the priority value is copied from - // ResourceDispatcherHostImpl::BeginNavigationRequest but there isn't - // a good way of sharing this. - .SetInitialPriority(resourcePriority(net::HIGHEST)) - .SetReferrerPolicy(referrerPolicy( - navigation_request->common_params().referrer.policy)) - .Build(), - base::TimeTicks::Now().ToInternalValue() / - static_cast(base::Time::kMicrosecondsPerSecond), - base::Time::Now().ToDoubleT(), - Network::Initiator::Create() - .SetType(Network::Initiator::TypeEnum::Parser) - .Build(), - std::unique_ptr(), - std::string(Page::ResourceTypeEnum::Document), - Maybe() /* frame_id */, - navigation_request->common_params().has_user_gesture); - - frontend_->LoadingFailed( - request_id, - base::TimeTicks::Now().ToInternalValue() / - static_cast(base::Time::kMicrosecondsPerSecond), - Page::ResourceTypeEnum::Document, error_string, cancelled); + if (header) { + std::unique_ptr headers_dict(DictionaryValue::create()); + for (const auto it : header->response_headers()) + headers_dict->setString(it.first, it.second); + + const SignedExchangeHeaderParser::Signature& sig = header->signature(); + std::unique_ptr> signatures = + Array::create(); + signatures->addItem(Network::SignedExchangeSignature::Create() + .SetLabel(sig.label) + .SetIntegrity(sig.integrity) + .SetCertUrl(sig.cert_url.spec()) + .SetValidityUrl(sig.validity_url.spec()) + .SetDate(sig.date) + .SetExpires(sig.expires) + .Build()); + + signed_exchange_info->SetHeader( + Network::SignedExchangeHeader::Create() + .SetRequestUrl(header->request_url().spec()) + .SetRequestMethod(header->request_method()) + .SetResponseCode(header->response_code()) + .SetResponseHeaders(Object::fromValue(headers_dict.get(), nullptr)) + .SetSignatures(std::move(signatures)) + .Build()); + } + if (ssl_info) + signed_exchange_info->SetSecurityDetails(BuildSecurityDetails(*ssl_info)); + if (error_messages.size()) { + std::unique_ptr> errors = Array::create(); + for (const auto& message : error_messages) + errors->addItem(message); + signed_exchange_info->SetErrors(std::move(errors)); + } + + frontend_->SignedExchangeReceived( + devtools_navigation_token ? devtools_navigation_token->ToString() : "", + std::move(signed_exchange_info)); } DispatchResponse NetworkHandler::SetRequestInterception( @@ -1723,6 +1844,38 @@ void NetworkHandler::GetResponseBodyForInterception( interceptor->GetResponseBody(interception_id, std::move(callback)); } +void NetworkHandler::TakeResponseBodyForInterceptionAsStream( + const String& interception_id, + std::unique_ptr callback) { + if (url_loader_interceptor_) { + url_loader_interceptor_->TakeResponseBodyPipe( + interception_id, + base::BindOnce(&NetworkHandler::OnResponseBodyPipeTaken, + weak_factory_.GetWeakPtr(), std::move(callback))); + return; + } + callback->sendFailure(Response::Error( + "Network.takeResponseBodyForInterceptionAsStream is only " + "currently supported with --enable-features=NetworkService")); +} + +void NetworkHandler::OnResponseBodyPipeTaken( + std::unique_ptr callback, + Response response, + mojo::ScopedDataPipeConsumerHandle pipe, + const std::string& mime_type) { + DCHECK_EQ(response.isSuccess(), pipe.is_valid()); + if (!response.isSuccess()) { + callback->sendFailure(std::move(response)); + return; + } + // The pipe stream is owned only by io_context after we return. + bool is_binary = !DevToolsIOContext::IsTextMimeType(mime_type); + auto stream = + DevToolsStreamPipe::Create(io_context_, std::move(pipe), is_binary); + callback->sendSuccess(stream->handle()); +} + // static GURL NetworkHandler::ClearUrlRef(const GURL& url) { if (!url.has_ref()) @@ -1806,10 +1959,11 @@ bool NetworkHandler::ShouldCancelNavigation( bool NetworkHandler::MaybeCreateProxyForInterception( const base::UnguessableToken& frame_token, int process_id, + bool is_download, network::mojom::URLLoaderFactoryRequest* target_factory_request) { return url_loader_interceptor_ && url_loader_interceptor_->CreateProxyForInterception( - frame_token, process_id, target_factory_request); + frame_token, process_id, is_download, target_factory_request); } void NetworkHandler::ApplyOverrides(net::HttpRequestHeaders* headers, @@ -1879,9 +2033,9 @@ void NetworkHandler::RequestIntercepted( frontend_->RequestIntercepted( info->interception_id, std::move(info->network_request), info->frame_id.ToString(), ResourceTypeToString(info->resource_type), - info->is_navigation, std::move(info->redirect_url), - std::move(info->auth_challenge), std::move(error_reason), - std::move(info->http_response_status_code), + info->is_navigation, std::move(info->is_download), + std::move(info->redirect_url), std::move(info->auth_challenge), + std::move(error_reason), std::move(info->http_response_status_code), std::move(info->response_headers)); } @@ -1891,7 +2045,14 @@ void NetworkHandler::SetNetworkConditions( return; network::mojom::NetworkContext* context = storage_partition_->GetNetworkContext(); + bool offline = conditions ? conditions->offline : false; context->SetNetworkConditions(host_id_, std::move(conditions)); + + if (offline == !!background_sync_restorer_) + return; + background_sync_restorer_.reset( + offline ? new BackgroundSyncRestorer(host_id_, storage_partition_) + : nullptr); } } // namespace protocol diff --git a/chromium/content/browser/devtools/protocol/network_handler.h b/chromium/content/browser/devtools/protocol/network_handler.h index 6bf4e7e976a..121d24f0bf5 100644 --- a/chromium/content/browser/devtools/protocol/network_handler.h +++ b/chromium/content/browser/devtools/protocol/network_handler.h @@ -6,14 +6,17 @@ #define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_NETWORK_HANDLER_H_ #include +#include #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/optional.h" #include "content/browser/devtools/devtools_url_loader_interceptor.h" #include "content/browser/devtools/protocol/devtools_domain_handler.h" #include "content/browser/devtools/protocol/network.h" +#include "mojo/public/cpp/system/data_pipe.h" #include "net/base/net_errors.h" #include "net/cookies/canonical_cookie.h" #include "services/network/public/mojom/network_service.mojom.h" @@ -25,6 +28,7 @@ class UnguessableToken; namespace net { class HttpRequestHeaders; class URLRequest; +class SSLInfo; } // namespace net namespace network { @@ -36,21 +40,24 @@ struct URLLoaderCompletionStatus; namespace content { class BrowserContext; class DevToolsAgentHostImpl; +class DevToolsIOContext; class RenderFrameHostImpl; class InterceptionHandle; class NavigationHandle; class NavigationRequest; class NavigationThrottle; +class SignedExchangeHeader; class StoragePartition; struct GlobalRequestID; struct InterceptedRequestInfo; namespace protocol { +class BackgroundSyncRestorer; class NetworkHandler : public DevToolsDomainHandler, public Network::Backend { public: - explicit NetworkHandler(const std::string& host_id); + NetworkHandler(const std::string& host_id, DevToolsIOContext* io_context); ~NetworkHandler() override; static std::vector ForAgentHost(DevToolsAgentHostImpl* host); @@ -124,10 +131,15 @@ class NetworkHandler : public DevToolsDomainHandler, const String& interception_id, std::unique_ptr callback) override; + void TakeResponseBodyForInterceptionAsStream( + const String& interception_id, + std::unique_ptr callback) + override; bool MaybeCreateProxyForInterception( const base::UnguessableToken& frame_token, int process_id, + bool is_download, network::mojom::URLLoaderFactoryRequest* target_factory_request); void ApplyOverrides(net::HttpRequestHeaders* headers, @@ -137,7 +149,8 @@ class NetworkHandler : public DevToolsDomainHandler, void RequestSent(const std::string& request_id, const std::string& loader_id, const network::ResourceRequest& request, - const char* initiator_type); + const char* initiator_type, + const base::Optional& initiator_url); void ResponseReceived(const std::string& request_id, const std::string& loader_id, const GURL& url, @@ -149,7 +162,13 @@ class NetworkHandler : public DevToolsDomainHandler, const char* resource_type, const network::URLLoaderCompletionStatus& completion_status); - void NavigationFailed(NavigationRequest* navigation_request); + void OnSignedExchangeReceived( + base::Optional devtools_navigation_token, + const GURL& outer_request_url, + const network::ResourceResponseHead& outer_response, + const base::Optional& header, + const base::Optional& ssl_info, + const std::vector& error_messages); bool enabled() const { return enabled_; } @@ -172,6 +191,15 @@ class NetworkHandler : public DevToolsDomainHandler, void RequestIntercepted(std::unique_ptr request_info); void SetNetworkConditions(network::mojom::NetworkConditionsPtr conditions); + void OnResponseBodyPipeTaken( + std::unique_ptr callback, + Response response, + mojo::ScopedDataPipeConsumerHandle pipe, + const std::string& mime_type); + + const std::string host_id_; + DevToolsIOContext* const io_context_; + std::unique_ptr frontend_; BrowserContext* browser_context_; StoragePartition* storage_partition_; @@ -179,11 +207,11 @@ class NetworkHandler : public DevToolsDomainHandler, bool enabled_; std::string user_agent_; std::vector> extra_headers_; - std::string host_id_; std::unique_ptr interception_handle_; std::unique_ptr url_loader_interceptor_; bool bypass_service_worker_; bool cache_disabled_; + std::unique_ptr background_sync_restorer_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(NetworkHandler); diff --git a/chromium/content/browser/devtools/protocol/page_handler.cc b/chromium/content/browser/devtools/protocol/page_handler.cc index 8dc4689a515..7e497a0402b 100644 --- a/chromium/content/browser/devtools/protocol/page_handler.cc +++ b/chromium/content/browser/devtools/protocol/page_handler.cc @@ -22,6 +22,8 @@ #include "base/strings/utf_string_conversions.h" #include "base/task_scheduler/post_task.h" #include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "components/viz/common/features.h" #include "content/browser/devtools/devtools_session.h" #include "content/browser/devtools/protocol/devtools_download_manager_delegate.h" #include "content/browser/devtools/protocol/devtools_download_manager_helper.h" @@ -41,11 +43,11 @@ #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/notification_types.h" +#include "content/public/browser/render_widget_host.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/common/browser_side_navigation_policy.h" +#include "content/public/common/content_features.h" #include "content/public/common/referrer.h" #include "content/public/common/result_codes.h" #include "content/public/common/use_zoom_for_dsf_policy.h" @@ -103,6 +105,77 @@ std::string EncodeSkBitmap(const SkBitmap& image, return EncodeImage(gfx::Image::CreateFrom1xBitmap(image), format, quality); } +std::unique_ptr BuildScreencastFrameMetadata( + const gfx::Size& surface_size, + float device_scale_factor, + float page_scale_factor, + const gfx::Vector2dF& root_scroll_offset, + float top_controls_height, + float top_controls_shown_ratio) { + if (surface_size.IsEmpty() || device_scale_factor == 0) + return nullptr; + + const gfx::SizeF content_size_dip = + gfx::ScaleSize(gfx::SizeF(surface_size), 1 / device_scale_factor); + float top_offset_dip = top_controls_height * top_controls_shown_ratio; + if (IsUseZoomForDSFEnabled()) + top_offset_dip /= device_scale_factor; + std::unique_ptr page_metadata = + Page::ScreencastFrameMetadata::Create() + .SetPageScaleFactor(page_scale_factor) + .SetOffsetTop(top_offset_dip) + .SetDeviceWidth(content_size_dip.width()) + .SetDeviceHeight(content_size_dip.height()) + .SetScrollOffsetX(root_scroll_offset.x()) + .SetScrollOffsetY(root_scroll_offset.y()) + .SetTimestamp(base::Time::Now().ToDoubleT()) + .Build(); + return page_metadata; +} + +// Determines the snapshot size that best-fits the Surface's content to the +// remote's requested image size. +gfx::Size DetermineSnapshotSize(const gfx::Size& surface_size, + int screencast_max_width, + int screencast_max_height) { + if (surface_size.IsEmpty()) + return gfx::Size(); // Nothing to copy (and avoid divide-by-zero below). + + double scale = 1; + if (screencast_max_width > 0) { + scale = std::min(scale, static_cast(screencast_max_width) / + surface_size.width()); + } + if (screencast_max_height > 0) { + scale = std::min(scale, static_cast(screencast_max_height) / + surface_size.height()); + } + return gfx::ToRoundedSize(gfx::ScaleSize(gfx::SizeF(surface_size), scale)); +} + +#if !defined(OS_ANDROID) +void GetMetadataFromFrame(const media::VideoFrame& frame, + double* device_scale_factor, + double* page_scale_factor, + gfx::Vector2dF* root_scroll_offset) { + // Get metadata from |frame| and ensure that no metadata is missing. + bool success = true; + double root_scroll_offset_x, root_scroll_offset_y; + success &= frame.metadata()->GetDouble( + media::VideoFrameMetadata::DEVICE_SCALE_FACTOR, device_scale_factor); + success &= frame.metadata()->GetDouble( + media::VideoFrameMetadata::PAGE_SCALE_FACTOR, page_scale_factor); + success &= frame.metadata()->GetDouble( + media::VideoFrameMetadata::ROOT_SCROLL_OFFSET_X, &root_scroll_offset_x); + success &= frame.metadata()->GetDouble( + media::VideoFrameMetadata::ROOT_SCROLL_OFFSET_Y, &root_scroll_offset_y); + DCHECK(success); + + root_scroll_offset->set_x(root_scroll_offset_x); + root_scroll_offset->set_y(root_scroll_offset_y); +} +#endif // !defined(OS_ANDROID) + } // namespace PageHandler::PageHandler(EmulationHandler* emulation_handler) @@ -118,9 +191,23 @@ PageHandler::PageHandler(EmulationHandler* emulation_handler) session_id_(0), frame_counter_(0), frames_in_flight_(0), +#if !defined(OS_ANDROID) + video_consumer_(nullptr), + last_surface_size_(gfx::Size()), +#endif // !defined(OS_ANDROID) host_(nullptr), emulation_handler_(emulation_handler), + observer_(this), weak_factory_(this) { +#if !defined(OS_ANDROID) + if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) || + base::FeatureList::IsEnabled( + features::kUseVideoCaptureApiForDevToolsSnapshots)) { + video_consumer_ = std::make_unique( + base::BindRepeating(&PageHandler::OnFrameFromVideoConsumer, + weak_factory_.GetWeakPtr())); + } +#endif // !defined(OS_ANDROID) DCHECK(emulation_handler_); } @@ -156,22 +243,21 @@ void PageHandler::SetRenderer(int process_host_id, RenderWidgetHostImpl* widget_host = host_ ? host_->GetRenderWidgetHost() : nullptr; - if (widget_host) { - registrar_.Remove( - this, - content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, - content::Source(widget_host)); - } + if (widget_host && observer_.IsObserving(widget_host)) + observer_.Remove(widget_host); host_ = frame_host; widget_host = host_ ? host_->GetRenderWidgetHost() : nullptr; - if (widget_host) { - registrar_.Add( - this, - content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, - content::Source(widget_host)); + if (widget_host) + observer_.Add(widget_host); + +#if !defined(OS_ANDROID) + if (video_consumer_ && frame_host) { + video_consumer_->SetFrameSinkId( + frame_host->GetRenderWidgetHost()->GetFrameSinkId()); } +#endif // !defined(OS_ANDROID) } void PageHandler::Wire(UberDispatcher* dispatcher) { @@ -204,14 +290,16 @@ void PageHandler::OnSynchronousSwapCompositorFrame( InnerSwapCompositorFrame(); } -void PageHandler::Observe(int type, - const NotificationSource& source, - const NotificationDetails& details) { +void PageHandler::RenderWidgetHostVisibilityChanged( + RenderWidgetHost* widget_host, + bool became_visible) { if (!screencast_enabled_) return; - DCHECK(type == content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED); - bool visible = *Details(details).ptr(); - NotifyScreencastVisibility(visible); + NotifyScreencastVisibility(became_visible); +} + +void PageHandler::RenderWidgetHostDestroyed(RenderWidgetHost* widget_host) { + observer_.Remove(widget_host); } void PageHandler::DidAttachInterstitialPage() { @@ -277,6 +365,11 @@ Response PageHandler::Disable() { enabled_ = false; screencast_enabled_ = false; +#if !defined(OS_ANDROID) + if (video_consumer_) + video_consumer_->StopCapture(); +#endif // !defined(OS_ANDROID) + if (!pending_dialog_.is_null()) { WebContentsImpl* web_contents = GetWebContents(); // Leave dialog hanging if there is a manager that can take care of it, @@ -305,6 +398,14 @@ Response PageHandler::Crash() { return Response::FallThrough(); } +Response PageHandler::Close() { + WebContentsImpl* web_contents = GetWebContents(); + if (!web_contents) + return Response::Error("Not attached to a page"); + web_contents->DispatchBeforeUnload(); + return Response::OK(); +} + Response PageHandler::Reload(Maybe bypassCache, Maybe script_to_evaluate_on_load) { WebContentsImpl* web_contents = GetWebContents(); @@ -418,14 +519,14 @@ void PageHandler::NavigationReset(NavigationRequest* navigation_request) { return; std::string frame_id = navigation_request->frame_tree_node()->devtools_frame_token().ToString(); - bool success = navigation_request->net_error() != net::OK; + bool success = navigation_request->net_error() == net::OK; std::string error_string = net::ErrorToString(navigation_request->net_error()); navigate_callback->second->sendSuccess( frame_id, Maybe( navigation_request->devtools_navigation_token().ToString()), - success ? Maybe(error_string) : Maybe()); + success ? Maybe() : Maybe(error_string)); navigate_callbacks_.erase(navigate_callback); } @@ -664,22 +765,47 @@ Response PageHandler::StartScreencast(Maybe format, frame_counter_ = 0; frames_in_flight_ = 0; capture_every_nth_frame_ = every_nth_frame.fromMaybe(1); - bool visible = !widget_host->is_hidden(); NotifyScreencastVisibility(visible); - if (visible) { - if (has_compositor_frame_metadata_) { - InnerSwapCompositorFrame(); - } else { - widget_host->Send(new ViewMsg_ForceRedraw(widget_host->GetRoutingID(), - ui::LatencyInfo())); + +#if !defined(OS_ANDROID) + if (video_consumer_) { + gfx::Size surface_size = gfx::Size(); + RenderWidgetHostViewBase* const view = + static_cast(host_->GetView()); + if (view) { + surface_size = view->GetCompositorViewportPixelSize(); + last_surface_size_ = surface_size; } + + gfx::Size snapshot_size = DetermineSnapshotSize( + surface_size, screencast_max_width_, screencast_max_height_); + if (!snapshot_size.IsEmpty()) + video_consumer_->SetMinAndMaxFrameSize(snapshot_size, snapshot_size); + + video_consumer_->StartCapture(); + return Response::FallThrough(); + } +#endif // !defined(OS_ANDROID) + + if (!visible) + return Response::FallThrough(); + + if (has_compositor_frame_metadata_) { + InnerSwapCompositorFrame(); + } else { + widget_host->Send(new ViewMsg_ForceRedraw(widget_host->GetRoutingID(), + ui::LatencyInfo())); } return Response::FallThrough(); } Response PageHandler::StopScreencast() { screencast_enabled_ = false; +#if !defined(OS_ANDROID) + if (video_consumer_) + video_consumer_->StopCapture(); +#endif // !defined(OS_ANDROID) return Response::FallThrough(); } @@ -816,45 +942,24 @@ void PageHandler::InnerSwapCompositorFrame() { if (!view || !view->IsSurfaceAvailableForCopy()) return; - // Determine the snapshot size that best-fits the Surface's content to the - // remote's requested image size. - const gfx::Size& surface_size = view->GetCompositorViewportPixelSize(); + const gfx::Size surface_size = view->GetCompositorViewportPixelSize(); if (surface_size.IsEmpty()) - return; // Nothing to copy (and avoid divide-by-zero below). - double scale = 1; - if (screencast_max_width_ > 0) { - scale = std::min(scale, static_cast(screencast_max_width_) / - surface_size.width()); - } - if (screencast_max_height_ > 0) { - scale = std::min(scale, static_cast(screencast_max_height_) / - surface_size.height()); - } - const gfx::Size snapshot_size = - gfx::ToRoundedSize(gfx::ScaleSize(gfx::SizeF(surface_size), scale)); - if (snapshot_size.IsEmpty()) return; - // Build the ScreencastFrameMetadata associated with this capture attempt. - const auto& metadata = last_compositor_frame_metadata_; - if (metadata.device_scale_factor == 0) + const gfx::Size snapshot_size = DetermineSnapshotSize( + surface_size, screencast_max_width_, screencast_max_height_); + if (snapshot_size.IsEmpty()) return; - const gfx::SizeF content_size_dip = gfx::ScaleSize( - gfx::SizeF(surface_size), 1 / metadata.device_scale_factor); - float top_offset_dip = - metadata.top_controls_height * metadata.top_controls_shown_ratio; - if (IsUseZoomForDSFEnabled()) - top_offset_dip /= metadata.device_scale_factor; + std::unique_ptr page_metadata = - Page::ScreencastFrameMetadata::Create() - .SetPageScaleFactor(metadata.page_scale_factor) - .SetOffsetTop(top_offset_dip) - .SetDeviceWidth(content_size_dip.width()) - .SetDeviceHeight(content_size_dip.height()) - .SetScrollOffsetX(metadata.root_scroll_offset.x()) - .SetScrollOffsetY(metadata.root_scroll_offset.y()) - .SetTimestamp(base::Time::Now().ToDoubleT()) - .Build(); + BuildScreencastFrameMetadata( + surface_size, last_compositor_frame_metadata_.device_scale_factor, + last_compositor_frame_metadata_.page_scale_factor, + last_compositor_frame_metadata_.root_scroll_offset, + last_compositor_frame_metadata_.top_controls_height, + last_compositor_frame_metadata_.top_controls_shown_ratio); + if (!page_metadata) + return; // Request a copy of the surface as a scaled SkBitmap. view->CopyFromSurface( @@ -864,6 +969,51 @@ void PageHandler::InnerSwapCompositorFrame() { frames_in_flight_++; } +#if !defined(OS_ANDROID) +void PageHandler::OnFrameFromVideoConsumer( + scoped_refptr frame) { + if (!host_) + return; + + RenderWidgetHostViewBase* const view = + static_cast(host_->GetView()); + if (!view) + return; + + const gfx::Size surface_size = view->GetCompositorViewportPixelSize(); + if (surface_size.IsEmpty()) + return; + + // If window has been resized, set the new dimensions. + if (surface_size != last_surface_size_) { + last_surface_size_ = surface_size; + gfx::Size snapshot_size = DetermineSnapshotSize( + surface_size, screencast_max_width_, screencast_max_height_); + if (!snapshot_size.IsEmpty()) + video_consumer_->SetMinAndMaxFrameSize(snapshot_size, snapshot_size); + return; + } + + double device_scale_factor, page_scale_factor; + gfx::Vector2dF root_scroll_offset; + GetMetadataFromFrame(*frame, &device_scale_factor, &page_scale_factor, + &root_scroll_offset); + // Top controls are only present on Android. Hence use default values of 0.f. + // TODO(dgozman): fix this when viz capture is available on Android. + const float kTopControlsHeight = 0.f; + const float kTopControlsShownRatio = 0.f; + std::unique_ptr page_metadata = + BuildScreencastFrameMetadata(surface_size, device_scale_factor, + page_scale_factor, root_scroll_offset, + kTopControlsHeight, kTopControlsShownRatio); + if (!page_metadata) + return; + + ScreencastFrameCaptured(std::move(page_metadata), + DevToolsVideoConsumer::GetSkBitmapFromFrame(frame)); +} +#endif // !defined(OS_ANDROID) + void PageHandler::ScreencastFrameCaptured( std::unique_ptr page_metadata, const SkBitmap& bitmap) { @@ -963,5 +1113,25 @@ Response PageHandler::StopLoading() { return Response::OK(); } +Response PageHandler::SetWebLifecycleState(const std::string& state) { + WebContentsImpl* web_contents = GetWebContents(); + if (!web_contents) + return Response::Error("Not attached to a page"); + if (state == Page::SetWebLifecycleState::StateEnum::Frozen) { + // TODO(fmeawad): Instead of forcing a visibility change, only allow + // freezing a page if it was already hidden. + web_contents->WasHidden(); + web_contents->FreezePage(); + return Response::OK(); + } + if (state == Page::SetWebLifecycleState::StateEnum::Active) { + // Making the page visible should make it active as visible pages cannot be + // frozen. + web_contents->WasShown(); + return Response::OK(); + } + return Response::Error("Unidentified lifecycle state"); +} + } // namespace protocol } // namespace content diff --git a/chromium/content/browser/devtools/protocol/page_handler.h b/chromium/content/browser/devtools/protocol/page_handler.h index 8e45be2d993..c9c0b1f1ed4 100644 --- a/chromium/content/browser/devtools/protocol/page_handler.h +++ b/chromium/content/browser/devtools/protocol/page_handler.h @@ -15,18 +15,23 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/scoped_observer.h" #include "base/time/time.h" +#include "build/build_config.h" #include "components/viz/common/quads/compositor_frame_metadata.h" #include "content/browser/devtools/protocol/devtools_domain_handler.h" #include "content/browser/devtools/protocol/devtools_download_manager_delegate.h" #include "content/browser/devtools/protocol/page.h" #include "content/public/browser/javascript_dialog_manager.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/render_widget_host_observer.h" #include "content/public/common/javascript_dialog_type.h" -#include "third_party/blink/public/platform/modules/manifest/manifest_manager.mojom.h" +#include "third_party/blink/public/mojom/manifest/manifest_manager.mojom.h" #include "url/gurl.h" +#if !defined(OS_ANDROID) +#include "content/browser/devtools/devtools_video_consumer.h" +#endif // !defined(OS_ANDROID) + class SkBitmap; namespace base { @@ -54,7 +59,7 @@ class EmulationHandler; class PageHandler : public DevToolsDomainHandler, public Page::Backend, - public NotificationObserver { + public RenderWidgetHostObserver { public: explicit PageHandler(EmulationHandler* handler); ~PageHandler() override; @@ -90,6 +95,7 @@ class PageHandler : public DevToolsDomainHandler, Response Disable() override; Response Crash() override; + Response Close() override; Response Reload(Maybe bypassCache, Maybe script_to_evaluate_on_load) override; void Navigate(const std::string& url, @@ -148,12 +154,17 @@ class PageHandler : public DevToolsDomainHandler, void GetAppManifest( std::unique_ptr callback) override; + Response SetWebLifecycleState(const std::string& state) override; + private: enum EncodingFormat { PNG, JPEG }; WebContentsImpl* GetWebContents(); void NotifyScreencastVisibility(bool visible); void InnerSwapCompositorFrame(); +#if !defined(OS_ANDROID) + void OnFrameFromVideoConsumer(scoped_refptr frame); +#endif // !defined(OS_ANDROID) void ScreencastFrameCaptured( std::unique_ptr metadata, const SkBitmap& bitmap); @@ -174,10 +185,10 @@ class PageHandler : public DevToolsDomainHandler, const GURL& manifest_url, blink::mojom::ManifestDebugInfoPtr debug_info); - // NotificationObserver overrides. - void Observe(int type, - const NotificationSource& source, - const NotificationDetails& details) override; + // RenderWidgetHostObserver overrides. + void RenderWidgetHostVisibilityChanged(RenderWidgetHost* widget_host, + bool became_visible) override; + void RenderWidgetHostDestroyed(RenderWidgetHost* widget_host) override; bool enabled_; @@ -195,10 +206,21 @@ class PageHandler : public DevToolsDomainHandler, int frame_counter_; int frames_in_flight_; +#if !defined(OS_ANDROID) + // |video_consumer_| consumes video frames from FrameSinkVideoCapturerImpl, + // and provides PageHandler with these frames via OnFrameFromVideoConsumer. + // This is only used if Viz is enabled and if OS is not Android. + std::unique_ptr video_consumer_; + + // The last surface size used to determine if frames with new sizes need + // to be requested. This changes due to window resizing. + gfx::Size last_surface_size_; +#endif // !defined(OS_ANDROID) + RenderFrameHostImpl* host_; EmulationHandler* emulation_handler_; std::unique_ptr frontend_; - NotificationRegistrar registrar_; + ScopedObserver observer_; JavaScriptDialogCallback pending_dialog_; scoped_refptr download_manager_delegate_; base::flat_map> diff --git a/chromium/content/browser/devtools/protocol/system_info_handler.cc b/chromium/content/browser/devtools/protocol/system_info_handler.cc index 3a90a35db77..618ad4a9f24 100644 --- a/chromium/content/browser/devtools/protocol/system_info_handler.cc +++ b/chromium/content/browser/devtools/protocol/system_info_handler.cc @@ -184,7 +184,8 @@ class SystemInfoHandlerGpuObserver : public content::GpuDataManagerObserver { // TODO(zmo): CHECK everywhere once https://crbug.com/796386 is fixed. gpu::GpuFeatureInfo gpu_feature_info = gpu::ComputeGpuFeatureInfoWithHardwareAccelerationDisabled(); - GpuDataManagerImpl::GetInstance()->UpdateGpuFeatureInfo(gpu_feature_info); + GpuDataManagerImpl::GetInstance()->UpdateGpuFeatureInfo(gpu_feature_info, + base::nullopt); UnregisterAndSendResponse(); #else CHECK(false) << "Gathering system GPU info took more than 5 seconds."; diff --git a/chromium/content/browser/devtools/protocol/target_auto_attacher.cc b/chromium/content/browser/devtools/protocol/target_auto_attacher.cc index 3d4b128b294..c194d824988 100644 --- a/chromium/content/browser/devtools/protocol/target_auto_attacher.cc +++ b/chromium/content/browser/devtools/protocol/target_auto_attacher.cc @@ -25,11 +25,11 @@ void GetMatchingHostsByScopeMap( const ServiceWorkerDevToolsAgentHost::List& agent_hosts, const base::flat_set& urls, ScopeAgentsMap* scope_agents_map) { - base::flat_set host_name_set; + base::flat_set host_name_set; for (const GURL& url : urls) - host_name_set.insert(url.host_piece()); + host_name_set.insert(url.GetOrigin()); for (const auto& host : agent_hosts) { - if (host_name_set.find(host->scope().host_piece()) == host_name_set.end()) + if (host_name_set.find(host->scope().GetOrigin()) == host_name_set.end()) continue; const auto& it = scope_agents_map->find(host->scope()); if (it == scope_agents_map->end()) { diff --git a/chromium/content/browser/devtools/protocol/target_handler.cc b/chromium/content/browser/devtools/protocol/target_handler.cc index 371456d2fb7..1ebf94ff0ef 100644 --- a/chromium/content/browser/devtools/protocol/target_handler.cc +++ b/chromium/content/browser/devtools/protocol/target_handler.cc @@ -10,6 +10,7 @@ #include "content/browser/devtools/devtools_manager.h" #include "content/browser/devtools/devtools_session.h" #include "content/browser/frame_host/navigation_handle_impl.h" +#include "content/public/browser/browser_context.h" #include "content/public/browser/devtools_agent_host_client.h" #include "content/public/browser/navigation_throttle.h" @@ -32,6 +33,8 @@ std::unique_ptr CreateInfo(DevToolsAgentHost* host) { .Build(); if (!host->GetOpenerId().empty()) target_info->SetOpenerId(host->GetOpenerId()); + if (host->GetBrowserContext()) + target_info->SetBrowserContextId(host->GetBrowserContext()->UniqueId()); return target_info; } @@ -389,8 +392,12 @@ Response TargetHandler::CreateBrowserContext(std::string* out_context_id) { return Response::Error("Not supported"); } -Response TargetHandler::DisposeBrowserContext(const std::string& context_id, - bool* out_success) { +Response TargetHandler::DisposeBrowserContext(const std::string& context_id) { + return Response::Error("Not supported"); +} + +Response TargetHandler::GetBrowserContexts( + std::unique_ptr>* browser_context_ids) { return Response::Error("Not supported"); } diff --git a/chromium/content/browser/devtools/protocol/target_handler.h b/chromium/content/browser/devtools/protocol/target_handler.h index 6bb18ca0d7b..7d75e62ad4c 100644 --- a/chromium/content/browser/devtools/protocol/target_handler.h +++ b/chromium/content/browser/devtools/protocol/target_handler.h @@ -63,8 +63,9 @@ class TargetHandler : public DevToolsDomainHandler, Response CloseTarget(const std::string& target_id, bool* out_success) override; Response CreateBrowserContext(std::string* out_context_id) override; - Response DisposeBrowserContext(const std::string& context_id, - bool* out_success) override; + Response DisposeBrowserContext(const std::string& context_id) override; + Response GetBrowserContexts( + std::unique_ptr>* browser_context_ids) override; Response CreateTarget(const std::string& url, Maybe width, Maybe height, diff --git a/chromium/content/browser/devtools/protocol/tracing_handler.cc b/chromium/content/browser/devtools/protocol/tracing_handler.cc index ea314dff3c0..3435d90b298 100644 --- a/chromium/content/browser/devtools/protocol/tracing_handler.cc +++ b/chromium/content/browser/devtools/protocol/tracing_handler.cc @@ -23,11 +23,12 @@ #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/trace_event_impl.h" #include "base/trace_event/tracing_agent.h" -#include "components/tracing/common/trace_config_file.h" +#include "components/tracing/common/trace_startup_config.h" #include "components/viz/common/features.h" #include "content/browser/devtools/devtools_frame_trace_recorder.h" #include "content/browser/devtools/devtools_io_context.h" #include "content/browser/devtools/devtools_session.h" +#include "content/browser/devtools/devtools_stream_file.h" #include "content/browser/devtools/devtools_traceable_screenshot.h" #include "content/browser/devtools/devtools_video_consumer.h" #include "content/browser/frame_host/frame_tree.h" @@ -125,7 +126,7 @@ class DevToolsStreamEndpoint : public TracingController::TraceDataEndpoint { public: explicit DevToolsStreamEndpoint( base::WeakPtr handler, - const scoped_refptr& stream) + const scoped_refptr& stream) : stream_(stream), tracing_handler_(handler) {} void ReceiveTraceChunk(std::unique_ptr chunk) override { @@ -156,7 +157,7 @@ class DevToolsStreamEndpoint : public TracingController::TraceDataEndpoint { private: ~DevToolsStreamEndpoint() override {} - scoped_refptr stream_; + scoped_refptr stream_; base::WeakPtr tracing_handler_; }; @@ -169,8 +170,7 @@ void SendProcessReadyInBrowserEvent(const base::UnguessableToken& frame_token, auto data = std::make_unique(); data->SetString("frame", frame_token.ToString()); data->SetString("processPseudoId", GetProcessHostHex(host)); - data->SetInteger("processId", - static_cast(base::GetProcId(host->GetHandle()))); + data->SetInteger("processId", static_cast(host->GetProcess().Pid())); TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ProcessReadyInBrowser", TRACE_EVENT_SCOPE_THREAD, "data", std::move(data)); @@ -190,7 +190,7 @@ void FillFrameData(base::trace_event::TracedValue* data, node->parent()->devtools_frame_token().ToString()); if (frame_host) { RenderProcessHost* process_host = frame_host->GetProcess(); - base::ProcessId process_id = base::GetProcId(process_host->GetHandle()); + base::ProcessId process_id = process_host->GetProcess().Pid(); if (process_id == base::kNullProcessId) { data->SetString("processPseudoId", GetProcessHostHex(process_host)); frame_host->GetProcess()->PostTaskWhenProcessIsReady( @@ -423,8 +423,9 @@ void TracingHandler::End(std::unique_ptr callback) { scoped_refptr endpoint; if (return_as_stream_) { endpoint = new DevToolsStreamEndpoint( - weak_factory_.GetWeakPtr(), io_context_->CreateTempFileBackedStream( - gzip_compression_ /* binary */)); + weak_factory_.GetWeakPtr(), + DevToolsStreamFile::Create(io_context_, + gzip_compression_ /* binary */)); if (gzip_compression_) { endpoint = TracingControllerImpl::CreateCompressedStringEndpoint( endpoint, true /* compress_with_background_priority */); @@ -620,7 +621,7 @@ void TracingHandler::FrameDeleted(RenderFrameHostImpl* frame_host) { // static bool TracingHandler::IsStartupTracingActive() { - return ::tracing::TraceConfigFile::GetInstance()->IsEnabled(); + return ::tracing::TraceStartupConfig::GetInstance()->IsEnabled(); } // static diff --git a/chromium/content/browser/devtools/protocol_config.json b/chromium/content/browser/devtools/protocol_config.json index f40789fe6f4..90c595d05dc 100644 --- a/chromium/content/browser/devtools/protocol_config.json +++ b/chromium/content/browser/devtools/protocol_config.json @@ -36,19 +36,20 @@ }, { "domain": "Memory", - "include": ["getBrowserSamplingProfile", "setPressureNotificationsSuppressed", "simulatePressureNotification"] + "include": ["getBrowserSamplingProfile", "setPressureNotificationsSuppressed", "simulatePressureNotification", "prepareForLeakDetection"], + "async": ["prepareForLeakDetection"] }, { "domain": "Network", - "include": ["enable", "disable", "clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "setUserAgentOverride", "setExtraHTTPHeaders", "canEmulateNetworkConditions", "emulateNetworkConditions", "setBypassServiceWorker", "setRequestInterception", "continueInterceptedRequest", "getResponseBodyForInterception", "setCacheDisabled"], - "include_events": ["requestWillBeSent", "responseReceived", "loadingFinished", "loadingFailed", "requestIntercepted"], - "async": ["clearBrowserCookies", "clearBrowserCache", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "continueInterceptedRequest", "getResponseBodyForInterception"] + "include": ["enable", "disable", "clearBrowserCache", "clearBrowserCookies", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "setUserAgentOverride", "setExtraHTTPHeaders", "canEmulateNetworkConditions", "emulateNetworkConditions", "setBypassServiceWorker", "setRequestInterception", "continueInterceptedRequest", "getResponseBodyForInterception", "setCacheDisabled", "takeResponseBodyForInterceptionAsStream"], + "include_events": ["requestWillBeSent", "responseReceived", "loadingFinished", "loadingFailed", "requestIntercepted", "signedExchangeReceived"], + "async": ["clearBrowserCookies", "clearBrowserCache", "getCookies", "getAllCookies", "deleteCookies", "setCookie", "setCookies", "continueInterceptedRequest", "getResponseBodyForInterception", "takeResponseBodyForInterceptionAsStream"] }, { "domain": "Page", "include": ["enable", "disable", "reload", "navigate", "stopLoading", "getNavigationHistory", "navigateToHistoryEntry", "captureScreenshot", "startScreencast", "stopScreencast", "screencastFrameAck", "handleJavaScriptDialog", "setColorPickerEnabled", "requestAppBanner", - "printToPDF", "bringToFront", "setDownloadBehavior", "getAppManifest", "crash"], + "printToPDF", "bringToFront", "setDownloadBehavior", "getAppManifest", "crash", "close", "setWebLifecycleState"], "include_events": ["colorPicked", "interstitialShown", "interstitialHidden", "javascriptDialogOpening", "javascriptDialogClosed", "screencastVisibilityChanged", "screencastFrame"], "async": ["captureScreenshot", "printToPDF", "navigate", "getAppManifest"] }, diff --git a/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc b/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc index f6977596a09..8c663d94e92 100644 --- a/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc @@ -41,6 +41,7 @@ #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/site_instance_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/browser/web_package/signed_exchange_header.h" #include "content/common/view_messages.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" @@ -54,6 +55,7 @@ #include "mojo/public/cpp/bindings/associated_binding.h" #include "net/base/load_flags.h" #include "net/http/http_request_headers.h" +#include "net/ssl/ssl_info.h" #include "services/network/public/cpp/features.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" @@ -215,12 +217,62 @@ void RenderFrameDevToolsAgentHost::OnNavigationResponseReceived( // static void RenderFrameDevToolsAgentHost::OnNavigationRequestFailed( const NavigationRequest& nav_request, - int error_code) { + const network::URLLoaderCompletionStatus& status) { FrameTreeNode* ftn = nav_request.frame_tree_node(); std::string id = nav_request.devtools_navigation_token().ToString(); DispatchToAgents(ftn, &protocol::NetworkHandler::LoadingComplete, id, - protocol::Page::ResourceTypeEnum::Document, - network::URLLoaderCompletionStatus(error_code)); + protocol::Page::ResourceTypeEnum::Document, status); +} + +// static +void RenderFrameDevToolsAgentHost::OnSignedExchangeReceived( + FrameTreeNode* frame_tree_node, + base::Optional devtools_navigation_token, + const GURL& outer_request_url, + const network::ResourceResponseHead& outer_response, + const base::Optional& header, + const base::Optional& ssl_info, + const std::vector& error_messages) { + DispatchToAgents(frame_tree_node, + &protocol::NetworkHandler::OnSignedExchangeReceived, + devtools_navigation_token, outer_request_url, outer_response, + header, ssl_info, error_messages); +} + +// static +void RenderFrameDevToolsAgentHost::OnSignedExchangeCertificateRequestSent( + FrameTreeNode* frame_tree_node, + const base::UnguessableToken& request_id, + const base::UnguessableToken& loader_id, + const network::ResourceRequest& request, + const GURL& signed_exchange_url) { + DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::RequestSent, + request_id.ToString(), loader_id.ToString(), request, + protocol::Network::Initiator::TypeEnum::SignedExchange, + signed_exchange_url); +} + +// static +void RenderFrameDevToolsAgentHost::OnSignedExchangeCertificateResponseReceived( + FrameTreeNode* frame_tree_node, + const base::UnguessableToken& request_id, + const base::UnguessableToken& loader_id, + const GURL& url, + const network::ResourceResponseHead& head) { + DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::ResponseReceived, + request_id.ToString(), loader_id.ToString(), url, + protocol::Page::ResourceTypeEnum::Other, head, + protocol::Maybe()); +} + +// static +void RenderFrameDevToolsAgentHost::OnSignedExchangeCertificateRequestCompleted( + FrameTreeNode* frame_tree_node, + const base::UnguessableToken& request_id, + const network::URLLoaderCompletionStatus& status) { + DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::LoadingComplete, + request_id.ToString(), + protocol::Page::ResourceTypeEnum::Other, status); } // static @@ -296,6 +348,7 @@ void RenderFrameDevToolsAgentHost::ApplyOverrides( bool RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory( RenderFrameHostImpl* rfh, bool is_navigation, + bool is_download, network::mojom::URLLoaderFactoryRequest* target_factory_request) { FrameTreeNode* frame_tree_node = rfh->frame_tree_node(); base::UnguessableToken frame_token = frame_tree_node->devtools_frame_token(); @@ -304,9 +357,10 @@ bool RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory( if (!agent_host) return false; int process_id = is_navigation ? 0 : rfh->GetProcess()->GetID(); + DCHECK(!is_download || is_navigation); for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) { - if (network->MaybeCreateProxyForInterception(frame_token, process_id, - target_factory_request)) { + if (network->MaybeCreateProxyForInterception( + frame_token, process_id, is_download, target_factory_request)) { return true; } } @@ -384,7 +438,8 @@ bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session) { session->AddHandler(base::WrapUnique(new protocol::IOHandler( GetIOContext()))); session->AddHandler(base::WrapUnique(new protocol::MemoryHandler())); - session->AddHandler(base::WrapUnique(new protocol::NetworkHandler(GetId()))); + session->AddHandler( + base::WrapUnique(new protocol::NetworkHandler(GetId(), GetIOContext()))); session->AddHandler(base::WrapUnique(new protocol::SchemaHandler())); session->AddHandler(base::WrapUnique(new protocol::ServiceWorkerHandler())); session->AddHandler(base::WrapUnique(new protocol::StorageHandler())); diff --git a/chromium/content/browser/devtools/render_frame_devtools_agent_host.h b/chromium/content/browser/devtools/render_frame_devtools_agent_host.h index afd61919b40..d4c77584e64 100644 --- a/chromium/content/browser/devtools/render_frame_devtools_agent_host.h +++ b/chromium/content/browser/devtools/render_frame_devtools_agent_host.h @@ -7,11 +7,13 @@ #include #include +#include #include "base/compiler_specific.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/macros.h" +#include "base/optional.h" #include "build/build_config.h" #include "content/browser/devtools/devtools_agent_host_impl.h" #include "content/common/content_export.h" @@ -26,6 +28,10 @@ #include "ui/android/view_android.h" #endif // OS_ANDROID +namespace base { +class UnguessableToken; +} + namespace network { struct ResourceResponse; } @@ -34,6 +40,10 @@ namespace viz { class CompositorFrameMetadata; } +namespace net { +class SSLInfo; +} + namespace content { class BrowserContext; @@ -43,6 +53,7 @@ class NavigationHandleImpl; class NavigationRequest; class NavigationThrottle; class RenderFrameHostImpl; +class SignedExchangeHeader; class CONTENT_EXPORT RenderFrameDevToolsAgentHost : public DevToolsAgentHostImpl, @@ -73,6 +84,7 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost static bool WillCreateURLLoaderFactory( RenderFrameHostImpl* rfh, bool is_navigation, + bool is_download, network::mojom::URLLoaderFactoryRequest* loader_factory_request); static void OnNavigationRequestWillBeSent( @@ -80,8 +92,34 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost static void OnNavigationResponseReceived( const NavigationRequest& nav_request, const network::ResourceResponse& response); - static void OnNavigationRequestFailed(const NavigationRequest& nav_request, - int error_code); + static void OnNavigationRequestFailed( + const NavigationRequest& nav_request, + const network::URLLoaderCompletionStatus& status); + + static void OnSignedExchangeReceived( + FrameTreeNode* frame_tree_node, + base::Optional devtools_navigation_token, + const GURL& outer_request_url, + const network::ResourceResponseHead& outer_response, + const base::Optional& header, + const base::Optional& ssl_info, + const std::vector& error_messages); + static void OnSignedExchangeCertificateRequestSent( + FrameTreeNode* frame_tree_node, + const base::UnguessableToken& request_id, + const base::UnguessableToken& loader_id, + const network::ResourceRequest& request, + const GURL& signed_exchange_url); + static void OnSignedExchangeCertificateResponseReceived( + FrameTreeNode* frame_tree_node, + const base::UnguessableToken& request_id, + const base::UnguessableToken& loader_id, + const GURL& url, + const network::ResourceResponseHead& head); + static void OnSignedExchangeCertificateRequestCompleted( + FrameTreeNode* frame_tree_node, + const base::UnguessableToken& request_id, + const network::URLLoaderCompletionStatus& status); static std::vector> CreateNavigationThrottles(NavigationHandleImpl* navigation_handle); diff --git a/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc b/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc index b4886cc9754..64801cf7cda 100644 --- a/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc @@ -126,7 +126,8 @@ bool ServiceWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) { session->AttachToAgent(agent_ptr_); } session->AddHandler(base::WrapUnique(new protocol::InspectorHandler())); - session->AddHandler(base::WrapUnique(new protocol::NetworkHandler(GetId()))); + session->AddHandler( + base::WrapUnique(new protocol::NetworkHandler(GetId(), GetIOContext()))); session->AddHandler(base::WrapUnique(new protocol::SchemaHandler())); return true; } diff --git a/chromium/content/browser/devtools/service_worker_devtools_agent_host.h b/chromium/content/browser/devtools/service_worker_devtools_agent_host.h index 456e9938e8e..453f52c6da9 100644 --- a/chromium/content/browser/devtools/service_worker_devtools_agent_host.h +++ b/chromium/content/browser/devtools/service_worker_devtools_agent_host.h @@ -72,6 +72,8 @@ class ServiceWorkerDevToolsAgentHost : public DevToolsAgentHostImpl { // Returns the time when the ServiceWorker was doomed. base::Time version_doomed_time() const { return version_doomed_time_; } + int64_t version_id() const { return version_id_; } + bool Matches(const ServiceWorkerContextCore* context, int64_t version_id); private: diff --git a/chromium/content/browser/devtools/service_worker_devtools_manager.cc b/chromium/content/browser/devtools/service_worker_devtools_manager.cc index 3414d0e9e9a..60007e08358 100644 --- a/chromium/content/browser/devtools/service_worker_devtools_manager.cc +++ b/chromium/content/browser/devtools/service_worker_devtools_manager.cc @@ -188,7 +188,8 @@ void ServiceWorkerDevToolsManager::NavigationPreloadRequestSent( for (auto* network : protocol::NetworkHandler::ForAgentHost(it->second.get())) { network->RequestSent(request_id, std::string(), request, - protocol::Network::Initiator::TypeEnum::Preload); + protocol::Network::Initiator::TypeEnum::Preload, + base::nullopt /* initiator_url */); } } diff --git a/chromium/content/browser/devtools/shared_worker_devtools_agent_host.cc b/chromium/content/browser/devtools/shared_worker_devtools_agent_host.cc index 485edd42a67..fd40420eed3 100644 --- a/chromium/content/browser/devtools/shared_worker_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/shared_worker_devtools_agent_host.cc @@ -68,7 +68,8 @@ bool SharedWorkerDevToolsAgentHost::Close() { bool SharedWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) { session->AddHandler(std::make_unique()); - session->AddHandler(std::make_unique(GetId())); + session->AddHandler( + std::make_unique(GetId(), GetIOContext())); session->AddHandler(std::make_unique()); session->SetRenderer(worker_host_ ? worker_host_->process_id() : -1, nullptr); if (state_ == WORKER_READY) diff --git a/chromium/content/browser/dom_storage/OWNERS b/chromium/content/browser/dom_storage/OWNERS index 1e82b965531..19d6324eaf3 100644 --- a/chromium/content/browser/dom_storage/OWNERS +++ b/chromium/content/browser/dom_storage/OWNERS @@ -1,8 +1,5 @@ dmurph@chromium.org mek@chromium.org -# OOO until this comment is removed. -michaeln@chromium.org - # TEAM: storage-dev@chromium.org # COMPONENT: Blink>Storage>DOMStorage 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 8089997ca7b..efa6d8dc10f 100644 --- a/chromium/content/browser/dom_storage/dom_storage_context_wrapper.cc +++ b/chromium/content/browser/dom_storage/dom_storage_context_wrapper.cc @@ -283,6 +283,19 @@ DOMStorageContextWrapper::RecreateSessionStorage( } void DOMStorageContextWrapper::StartScavengingUnusedSessionStorage() { + if (mojo_session_state_) { + // base::Unretained is safe here, because the mojo_session_state_ won't be + // deleted until a ShutdownAndDelete task has been ran on the + // mojo_task_runner_, and as soon as that task is posted, + // mojo_session_state_ is set to null, preventing further tasks from being + // queued. + mojo_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&SessionStorageContextMojo::ScavengeUnusedNamespaces, + base::Unretained(mojo_session_state_), + base::OnceClosure())); + return; + } DCHECK(context_.get()); context_->task_runner()->PostShutdownBlockingTask( FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE, @@ -305,12 +318,6 @@ void DOMStorageContextWrapper::SetForceKeepSessionState() { FROM_HERE, base::BindOnce(&LocalStorageContextMojo::SetForceKeepSessionState, base::Unretained(mojo_state_))); - if (mojo_session_state_) { - mojo_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&SessionStorageContextMojo::SetForceKeepSessionState, - base::Unretained(mojo_session_state_))); - } } void DOMStorageContextWrapper::Shutdown() { diff --git a/chromium/content/browser/dom_storage/dom_storage_database.h b/chromium/content/browser/dom_storage/dom_storage_database.h index 122b6646826..5ea7d79329b 100644 --- a/chromium/content/browser/dom_storage/dom_storage_database.h +++ b/chromium/content/browser/dom_storage/dom_storage_database.h @@ -123,6 +123,8 @@ class CONTENT_EXPORT DOMStorageDatabase { bool failed_to_open_; bool tried_to_recreate_; bool known_to_be_empty_; + + DISALLOW_COPY_AND_ASSIGN(DOMStorageDatabase); }; } // namespace content 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 38e62495ad3..5226a1a7a26 100644 --- a/chromium/content/browser/dom_storage/dom_storage_database_unittest.cc +++ b/chromium/content/browser/dom_storage/dom_storage_database_unittest.cc @@ -310,12 +310,18 @@ TEST(DOMStorageDatabaseTest, TestSimpleRemoveOneValue) { TEST(DOMStorageDatabaseTest, TestCanOpenAndReadWebCoreDatabase) { base::FilePath dir_test_data; - ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &dir_test_data)); - base::FilePath webcore_database = dir_test_data.AppendASCII("dom_storage"); - webcore_database = - webcore_database.AppendASCII("webcore_test_database.localstorage"); + ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &dir_test_data)); + base::FilePath test_data = dir_test_data.AppendASCII("dom_storage"); + test_data = test_data.AppendASCII("webcore_test_database.localstorage"); + ASSERT_TRUE(base::PathExists(test_data)); - ASSERT_TRUE(base::PathExists(webcore_database)); + // Create a temporary copy of the WebCore test database, in case DIR_TEST_DATA + // is read-only. + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath webcore_database = + temp_dir.GetPath().AppendASCII("dom_storage"); + ASSERT_TRUE(base::CopyFile(test_data, webcore_database)); DOMStorageDatabase db(webcore_database); DOMStorageValuesMap values; diff --git a/chromium/content/browser/dom_storage/local_storage_context_mojo.cc b/chromium/content/browser/dom_storage/local_storage_context_mojo.cc index c6975829bbe..369046443da 100644 --- a/chromium/content/browser/dom_storage/local_storage_context_mojo.cc +++ b/chromium/content/browser/dom_storage/local_storage_context_mojo.cc @@ -679,7 +679,7 @@ void LocalStorageContextMojo::InitiateConnection(bool in_memory_only) { // We were not given a subdirectory. Use a memory backed database. connector_->BindInterface(file::mojom::kServiceName, &leveldb_service_); leveldb_service_->OpenInMemory( - memory_dump_id_, MakeRequest(&database_), + memory_dump_id_, "local-storage", MakeRequest(&database_), base::BindOnce(&LocalStorageContextMojo::OnDatabaseOpened, weak_ptr_factory_.GetWeakPtr(), true)); } diff --git a/chromium/content/browser/dom_storage/local_storage_context_mojo.h b/chromium/content/browser/dom_storage/local_storage_context_mojo.h index 2d7799a7920..3670af2a646 100644 --- a/chromium/content/browser/dom_storage/local_storage_context_mojo.h +++ b/chromium/content/browser/dom_storage/local_storage_context_mojo.h @@ -134,7 +134,6 @@ class CONTENT_EXPORT LocalStorageContextMojo void GetStatistics(size_t* total_cache_size, size_t* unused_wrapper_count); void OnCommitResult(leveldb::mojom::DatabaseError error); - void OnReconnectedToDB(); // These values are written to logs. New enum values can be added, but // existing enums must never be renumbered or deleted and reused. diff --git a/chromium/content/browser/dom_storage/local_storage_context_mojo_unittest.cc b/chromium/content/browser/dom_storage/local_storage_context_mojo_unittest.cc index 1603bdb3282..b758d40ec16 100644 --- a/chromium/content/browser/dom_storage/local_storage_context_mojo_unittest.cc +++ b/chromium/content/browser/dom_storage/local_storage_context_mojo_unittest.cc @@ -10,6 +10,7 @@ #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/bind_test_util.h" #include "build/build_config.h" #include "components/services/filesystem/public/interfaces/file_system.mojom.h" #include "components/services/leveldb/public/cpp/util.h" @@ -18,12 +19,16 @@ #include "content/browser/dom_storage/dom_storage_database.h" #include "content/browser/dom_storage/dom_storage_namespace.h" #include "content/browser/dom_storage/dom_storage_task_runner.h" +#include "content/browser/dom_storage/test/fake_leveldb_database_error_on_write.h" +#include "content/browser/dom_storage/test/fake_leveldb_service.h" +#include "content/browser/dom_storage/test/mojo_test_with_file_service.h" #include "content/common/dom_storage/dom_storage_types.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/local_storage_usage_info.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "content/test/fake_leveldb_database.h" +#include "content/test/leveldb_wrapper_test_util.h" #include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding_set.h" @@ -32,7 +37,7 @@ #include "services/file/public/mojom/constants.mojom.h" #include "services/file/user_id_map.h" #include "services/service_manager/public/cpp/service_context.h" -#include "services/service_manager/public/cpp/service_test.h" +#include "services/service_manager/public/cpp/test/test_service_decorator.h" #include "services/service_manager/public/mojom/service_factory.mojom.h" #include "storage/browser/test/mock_special_storage_policy.h" #include "testing/gtest/include/gtest/gtest.h" @@ -45,34 +50,19 @@ using leveldb::Uint8VectorToStdString; namespace content { namespace { +using test::FakeLevelDBService; +using test::FakeLevelDBDatabaseErrorOnWrite; + // An empty namespace is the local storage namespace. constexpr const char kLocalStorageNamespaceId[] = ""; -void SuccessCallback(base::OnceClosure callback, - bool* success_out, - bool success) { - if (success_out) - *success_out = success; - std::move(callback).Run(); -} - -void GetStorageUsageCallback(const base::Closure& callback, +void GetStorageUsageCallback(const base::RepeatingClosure& callback, std::vector* out_result, std::vector result) { *out_result = std::move(result); callback.Run(); } -void GetCallback(const base::Closure& callback, - bool* success_out, - std::vector* value_out, - bool success, - const std::vector& value) { - *success_out = success; - *value_out = value; - callback.Run(); -} - class TestLevelDBObserver : public mojom::LevelDBObserver { public: struct Observation { @@ -123,29 +113,6 @@ class TestLevelDBObserver : public mojom::LevelDBObserver { mojo::AssociatedBinding binding_; }; -class GetAllCallback : public mojom::LevelDBWrapperGetAllCallback { - public: - static mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo CreateAndBind( - base::OnceClosure callback) { - mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo ptr_info; - auto request = mojo::MakeRequest(&ptr_info); - mojo::MakeStrongAssociatedBinding( - base::WrapUnique(new GetAllCallback(std::move(callback))), - std::move(request)); - return ptr_info; - } - - private: - GetAllCallback(base::OnceClosure callback) : callback_(std::move(callback)) {} - - void Complete(bool success) override { - EXPECT_TRUE(success); - std::move(callback_).Run(); - } - - base::OnceClosure callback_; -}; - } // namespace class LocalStorageContextMojoTest : public testing::Test { @@ -223,12 +190,8 @@ class LocalStorageContextMojoTest : public testing::Test { mojom::LevelDBWrapperPtr dummy_wrapper; // To make sure values are cached. context()->OpenLocalStorage(kOrigin, MakeRequest(&wrapper)); context()->OpenLocalStorage(kOrigin, MakeRequest(&dummy_wrapper)); - base::RunLoop run_loop; - bool success = false; std::vector result; - wrapper->Get(key, base::BindOnce(&GetCallback, run_loop.QuitClosure(), - &success, &result)); - run_loop.Run(); + bool success = test::GetSync(wrapper.get(), key, &result); return success ? base::Optional>(result) : base::nullopt; } @@ -668,25 +631,17 @@ TEST_F(LocalStorageContextMojoTest, Migration) { EXPECT_FALSE(mock_data().empty()); { - base::RunLoop run_loop; - bool success = false; std::vector result; - wrapper->Get(LocalStorageContextMojo::MigrateString(key), - base::BindOnce(&GetCallback, run_loop.QuitClosure(), &success, - &result)); - run_loop.Run(); + bool success = test::GetSync( + wrapper.get(), LocalStorageContextMojo::MigrateString(key), &result); EXPECT_TRUE(success); EXPECT_EQ(LocalStorageContextMojo::MigrateString(value), result); } { - base::RunLoop run_loop; - bool success = false; std::vector result; - wrapper->Get(LocalStorageContextMojo::MigrateString(key2), - base::BindOnce(&GetCallback, run_loop.QuitClosure(), &success, - &result)); - run_loop.Run(); + bool success = test::GetSync( + wrapper.get(), LocalStorageContextMojo::MigrateString(key2), &result); EXPECT_TRUE(success); EXPECT_EQ(LocalStorageContextMojo::MigrateString(value), result); } @@ -730,25 +685,18 @@ TEST_F(LocalStorageContextMojoTest, FixUp) { MakeRequest(&dummy_wrapper)); { - base::RunLoop run_loop; - bool success = false; std::vector result; - wrapper->Get(leveldb::StdStringToUint8Vector("\x01key"), - base::BindOnce(&GetCallback, run_loop.QuitClosure(), &success, - &result)); - run_loop.Run(); + bool success = test::GetSync( + wrapper.get(), leveldb::StdStringToUint8Vector("\x01key"), &result); EXPECT_TRUE(success); EXPECT_EQ(leveldb::StdStringToUint8Vector("value1"), result); } { - base::RunLoop run_loop; - bool success = false; std::vector result; - wrapper->Get(leveldb::StdStringToUint8Vector("\x01" - "foo"), - base::BindOnce(&GetCallback, run_loop.QuitClosure(), &success, - &result)); - run_loop.Run(); + bool success = test::GetSync(wrapper.get(), + leveldb::StdStringToUint8Vector("\x01" + "foo"), + &result); EXPECT_TRUE(success); EXPECT_EQ(leveldb::StdStringToUint8Vector("value3"), result); } @@ -798,82 +746,13 @@ TEST_F(LocalStorageContextMojoTest, ShutdownClearsData) { } } -namespace { - -class ServiceTestClient : public service_manager::test::ServiceTestClient, - public service_manager::mojom::ServiceFactory { - public: - explicit ServiceTestClient(service_manager::test::ServiceTest* test) - : service_manager::test::ServiceTestClient(test) { - registry_.AddInterface(base::Bind( - &ServiceTestClient::BindServiceFactoryRequest, base::Unretained(this))); - } - ~ServiceTestClient() override {} - - protected: - void OnBindInterface(const service_manager::BindSourceInfo& source_info, - const std::string& interface_name, - mojo::ScopedMessagePipeHandle interface_pipe) override { - registry_.BindInterface(interface_name, std::move(interface_pipe)); - } - - void CreateService( - service_manager::mojom::ServiceRequest request, - const std::string& name, - service_manager::mojom::PIDReceiverPtr pid_receiver) override { - if (name == file::mojom::kServiceName) { - file_service_context_.reset(new service_manager::ServiceContext( - file::CreateFileService(), std::move(request))); - } - } - - void BindServiceFactoryRequest( - service_manager::mojom::ServiceFactoryRequest request) { - service_factory_bindings_.AddBinding(this, std::move(request)); - } - - private: - service_manager::BinderRegistry registry_; - mojo::BindingSet - service_factory_bindings_; - std::unique_ptr file_service_context_; -}; - -} // namespace - class LocalStorageContextMojoTestWithService - : public service_manager::test::ServiceTest { + : public test::MojoTestWithFileService { public: - LocalStorageContextMojoTestWithService() : ServiceTest("content_unittests") {} + LocalStorageContextMojoTestWithService() {} ~LocalStorageContextMojoTestWithService() override {} protected: - void SetUp() override { - ServiceTest::SetUp(); - ASSERT_TRUE(temp_path_.CreateUniqueTempDir()); - file::AssociateServiceUserIdWithUserDir(test_userid(), - temp_path_.GetPath()); - } - - void TearDown() override { - service_manager::ServiceContext::ClearGlobalBindersForTesting( - file::mojom::kServiceName); - ServiceTest::TearDown(); - } - - std::unique_ptr CreateService() override { - return std::make_unique(this); - } - - const base::FilePath& temp_path() { return temp_path_.GetPath(); } - - base::FilePath FirstEntryInDir() { - base::FileEnumerator enumerator( - temp_path(), false /* recursive */, - base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); - return enumerator.Next(); - } - void DoTestPut(LocalStorageContextMojo* context, const std::vector& key, const std::vector& value) { @@ -882,9 +761,8 @@ class LocalStorageContextMojoTestWithService base::RunLoop run_loop; context->OpenLocalStorage(url::Origin::Create(GURL("http://foobar.com")), MakeRequest(&wrapper)); - wrapper->Put( - key, value, base::nullopt, "source", - base::BindOnce(&SuccessCallback, run_loop.QuitClosure(), &success)); + wrapper->Put(key, value, base::nullopt, "source", + test::MakeSuccessCallback(run_loop.QuitClosure(), &success)); run_loop.Run(); EXPECT_TRUE(success); wrapper.reset(); @@ -900,15 +778,14 @@ class LocalStorageContextMojoTestWithService base::RunLoop run_loop; std::vector data; - auto callback = [](std::vector* data_out, - leveldb::mojom::DatabaseError status, - std::vector data) { - EXPECT_EQ(status, leveldb::mojom::DatabaseError::OK); - data_out->swap(data); - }; - wrapper->GetAll(GetAllCallback::CreateAndBind(run_loop.QuitClosure()), - base::BindOnce(callback, &data)); + leveldb::mojom::DatabaseError status; + bool done = false; + wrapper->GetAll( + test::GetAllCallback::CreateAndBind(&done, run_loop.QuitClosure()), + test::MakeGetAllCallback(&status, &data)); run_loop.Run(); + EXPECT_TRUE(done); + EXPECT_EQ(status, leveldb::mojom::DatabaseError::OK); for (auto& entry : data) { if (key == entry->key) { @@ -920,9 +797,14 @@ class LocalStorageContextMojoTestWithService return false; } - private: - base::ScopedTempDir temp_path_; + base::FilePath FirstEntryInDir() { + base::FileEnumerator enumerator( + temp_path(), false /* recursive */, + base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); + return enumerator.Next(); + } + private: DISALLOW_COPY_AND_ASSIGN(LocalStorageContextMojoTestWithService); }; @@ -1115,99 +997,15 @@ TEST_F(LocalStorageContextMojoTestWithService, CorruptionOnDisk) { context->ShutdownAndDelete(); } -namespace { - -class FakeLevelDBService : public leveldb::mojom::LevelDBService { - public: - void Open(filesystem::mojom::DirectoryPtr, - const std::string& dbname, - const base::Optional& - memory_dump_id, - leveldb::mojom::LevelDBDatabaseAssociatedRequest request, - OpenCallback callback) override { - open_requests_.push_back( - {false, dbname, std::move(request), std::move(callback)}); - if (on_open_callback_) - on_open_callback_.Run(); - } - - void OpenWithOptions( - const leveldb_env::Options& options, - filesystem::mojom::DirectoryPtr, - const std::string& dbname, - const base::Optional& - memory_dump_id, - leveldb::mojom::LevelDBDatabaseAssociatedRequest request, - OpenCallback callback) override { - open_requests_.push_back( - {false, dbname, std::move(request), std::move(callback)}); - if (on_open_callback_) - on_open_callback_.Run(); - } - - void OpenInMemory( - const base::Optional& - memory_dump_id, - leveldb::mojom::LevelDBDatabaseAssociatedRequest request, - OpenCallback callback) override { - open_requests_.push_back( - {true, "", std::move(request), std::move(callback)}); - if (on_open_callback_) - on_open_callback_.Run(); - } - - void Destroy(filesystem::mojom::DirectoryPtr, - const std::string& dbname, - DestroyCallback callback) override { - destroy_requests_.push_back({dbname}); - std::move(callback).Run(leveldb::mojom::DatabaseError::OK); - } - - struct OpenRequest { - bool in_memory; - std::string dbname; - leveldb::mojom::LevelDBDatabaseAssociatedRequest request; - OpenCallback callback; - }; - std::vector open_requests_; - base::Closure on_open_callback_; - - struct DestroyRequest { - std::string dbname; - }; - std::vector destroy_requests_; - - void Bind(const std::string& interface_name, - mojo::ScopedMessagePipeHandle interface_pipe, - const service_manager::BindSourceInfo& source_info) { - bindings_.AddBinding( - this, leveldb::mojom::LevelDBServiceRequest(std::move(interface_pipe))); - } - - private: - mojo::BindingSet bindings_; -}; - -class FakeLevelDBDatabaseErrorOnWrite : public FakeLevelDBDatabase { - public: - explicit FakeLevelDBDatabaseErrorOnWrite( - std::map, std::vector>* mock_data) - : FakeLevelDBDatabase(mock_data) {} - - void Write(std::vector operations, - WriteCallback callback) override { - std::move(callback).Run(leveldb::mojom::DatabaseError::IO_ERROR); - } -}; - -} // namespace - TEST_F(LocalStorageContextMojoTestWithService, RecreateOnCommitFailure) { FakeLevelDBService mock_leveldb_service; - service_manager::ServiceContext::SetGlobalBinderForTesting( - file::mojom::kServiceName, leveldb::mojom::LevelDBService::Name_, - base::Bind(&FakeLevelDBService::Bind, - base::Unretained(&mock_leveldb_service))); + ResetFileServiceAndConnector( + service_manager::TestServiceDecorator::CreateServiceWithUniqueOverride( + file::CreateFileService(), + + leveldb::mojom::LevelDBService::Name_, + base::BindRepeating(&test::FakeLevelDBService::Bind, + base::Unretained(&mock_leveldb_service)))); std::map, std::vector> test_data; @@ -1226,7 +1024,7 @@ TEST_F(LocalStorageContextMojoTestWithService, RecreateOnCommitFailure) { mojom::LevelDBWrapperPtr wrapper3; { base::RunLoop loop; - mock_leveldb_service.on_open_callback_ = loop.QuitClosure(); + mock_leveldb_service.SetOnOpenCallback(loop.QuitClosure()); context->OpenLocalStorage(url::Origin::Create(GURL("http://foobar.com")), MakeRequest(&wrapper1)); context->OpenLocalStorage(url::Origin::Create(GURL("http://foobar.com")), @@ -1244,19 +1042,19 @@ TEST_F(LocalStorageContextMojoTestWithService, RecreateOnCommitFailure) { // Verify one attempt was made to open the database, and connect that request // with a database implementation that always fails on write. - ASSERT_EQ(1u, mock_leveldb_service.open_requests_.size()); - auto& open_request = mock_leveldb_service.open_requests_[0]; + ASSERT_EQ(1u, mock_leveldb_service.open_requests().size()); + auto& open_request = mock_leveldb_service.open_requests()[0]; auto mock_db = mojo::MakeStrongAssociatedBinding( std::make_unique(&test_data), std::move(open_request.request)); std::move(open_request.callback).Run(leveldb::mojom::DatabaseError::OK); - mock_leveldb_service.open_requests_.clear(); + mock_leveldb_service.open_requests().clear(); // Setup a RunLoop so we can wait until LocalStorageContextMojo tries to // reconnect to the database, which should happen after several commit // errors. base::RunLoop reopen_loop; - mock_leveldb_service.on_open_callback_ = reopen_loop.QuitClosure(); + mock_leveldb_service.SetOnOpenCallback(reopen_loop.QuitClosure()); // Start a put operation on the third connection before starting to commit // a lot of data on the first origin. This put operation should result in a @@ -1274,12 +1072,10 @@ TEST_F(LocalStorageContextMojoTestWithService, RecreateOnCommitFailure) { value[0]++; wrapper1.set_connection_error_handler(put_loop.QuitClosure()); wrapper1->Put(key, value, base::nullopt, "source", - base::BindOnce( - [](base::Closure quit_closure, bool success) { - EXPECT_TRUE(success); - quit_closure.Run(); - }, - put_loop.QuitClosure())); + base::BindLambdaForTesting([&](bool success) { + EXPECT_TRUE(success); + put_loop.Quit(); + })); put_loop.RunUntilIdle(); values_written++; // And we need to flush after every change. Otherwise changes get batched up @@ -1300,34 +1096,31 @@ TEST_F(LocalStorageContextMojoTestWithService, RecreateOnCommitFailure) { EXPECT_TRUE(wrapper2.encountered_error()); // And the old database should have been destroyed. - EXPECT_EQ(1u, mock_leveldb_service.destroy_requests_.size()); + EXPECT_EQ(1u, mock_leveldb_service.destroy_requests().size()); // Reconnect wrapper1 to the database, and try to read a value. context->OpenLocalStorage(url::Origin::Create(GURL("http://foobar.com")), MakeRequest(&wrapper1)); base::RunLoop delete_loop; bool success = true; - auto callback = [](bool* success_out, const base::Closure& callback, - bool success) { - *success_out = success; - callback.Run(); - }; TestLevelDBObserver observer3; wrapper1->AddObserver(observer3.Bind()); - wrapper1->Delete( - key, base::nullopt, "source", - base::BindOnce(callback, &success, delete_loop.QuitClosure())); + wrapper1->Delete(key, base::nullopt, "source", + base::BindLambdaForTesting([&](bool success_in) { + success = success_in; + delete_loop.Quit(); + })); // Wait for LocalStorageContextMojo to try to reconnect to the database, and // connect that new request to a properly functioning database. reopen_loop.Run(); - ASSERT_EQ(1u, mock_leveldb_service.open_requests_.size()); - auto& reopen_request = mock_leveldb_service.open_requests_[0]; + ASSERT_EQ(1u, mock_leveldb_service.open_requests().size()); + auto& reopen_request = mock_leveldb_service.open_requests()[0]; mock_db = mojo::MakeStrongAssociatedBinding( std::make_unique(&test_data), std::move(reopen_request.request)); std::move(reopen_request.callback).Run(leveldb::mojom::DatabaseError::OK); - mock_leveldb_service.open_requests_.clear(); + mock_leveldb_service.open_requests().clear(); // And deleting the value from the new wrapper should have failed (as the // database is empty). @@ -1358,10 +1151,11 @@ TEST_F(LocalStorageContextMojoTestWithService, RecreateOnCommitFailure) { TEST_F(LocalStorageContextMojoTestWithService, DontRecreateOnRepeatedCommitFailure) { FakeLevelDBService mock_leveldb_service; - service_manager::ServiceContext::SetGlobalBinderForTesting( - file::mojom::kServiceName, leveldb::mojom::LevelDBService::Name_, - base::Bind(&FakeLevelDBService::Bind, - base::Unretained(&mock_leveldb_service))); + ResetFileServiceAndConnector( + service_manager::TestServiceDecorator::CreateServiceWithUniqueOverride( + file::CreateFileService(), leveldb::mojom::LevelDBService::Name_, + base::BindRepeating(&test::FakeLevelDBService::Bind, + base::Unretained(&mock_leveldb_service)))); std::map, std::vector> test_data; @@ -1377,7 +1171,7 @@ TEST_F(LocalStorageContextMojoTestWithService, mojom::LevelDBWrapperPtr wrapper; { base::RunLoop loop; - mock_leveldb_service.on_open_callback_ = loop.QuitClosure(); + mock_leveldb_service.SetOnOpenCallback(loop.QuitClosure()); context->OpenLocalStorage(url::Origin::Create(GURL("http://foobar.com")), MakeRequest(&wrapper)); loop.Run(); @@ -1385,19 +1179,19 @@ TEST_F(LocalStorageContextMojoTestWithService, // Verify one attempt was made to open the database, and connect that request // with a database implementation that always fails on write. - ASSERT_EQ(1u, mock_leveldb_service.open_requests_.size()); - auto& open_request = mock_leveldb_service.open_requests_[0]; + ASSERT_EQ(1u, mock_leveldb_service.open_requests().size()); + auto& open_request = mock_leveldb_service.open_requests()[0]; auto mock_db = mojo::MakeStrongAssociatedBinding( std::make_unique(&test_data), std::move(open_request.request)); std::move(open_request.callback).Run(leveldb::mojom::DatabaseError::OK); - mock_leveldb_service.open_requests_.clear(); + mock_leveldb_service.open_requests().clear(); // Setup a RunLoop so we can wait until LocalStorageContextMojo tries to // reconnect to the database, which should happen after several commit // errors. base::RunLoop reopen_loop; - mock_leveldb_service.on_open_callback_ = reopen_loop.QuitClosure(); + mock_leveldb_service.SetOnOpenCallback(reopen_loop.QuitClosure()); // Repeatedly write data to the database, to trigger enough commit errors. base::Optional> old_value; @@ -1408,12 +1202,10 @@ TEST_F(LocalStorageContextMojoTestWithService, value[0]++; wrapper.set_connection_error_handler(put_loop.QuitClosure()); wrapper->Put(key, value, old_value, "source", - base::BindOnce( - [](base::Closure quit_closure, bool success) { - EXPECT_TRUE(success); - quit_closure.Run(); - }, - put_loop.QuitClosure())); + base::BindLambdaForTesting([&](bool success) { + EXPECT_TRUE(success); + put_loop.Quit(); + })); old_value = std::vector(value); put_loop.RunUntilIdle(); // And we need to flush after every change. Otherwise changes get batched up @@ -1434,16 +1226,16 @@ TEST_F(LocalStorageContextMojoTestWithService, // connect that new request with a database implementation that always fails // on write. reopen_loop.Run(); - ASSERT_EQ(1u, mock_leveldb_service.open_requests_.size()); - auto& reopen_request = mock_leveldb_service.open_requests_[0]; + ASSERT_EQ(1u, mock_leveldb_service.open_requests().size()); + auto& reopen_request = mock_leveldb_service.open_requests()[0]; mock_db = mojo::MakeStrongAssociatedBinding( std::make_unique(&test_data), std::move(reopen_request.request)); std::move(reopen_request.callback).Run(leveldb::mojom::DatabaseError::OK); - mock_leveldb_service.open_requests_.clear(); + mock_leveldb_service.open_requests().clear(); // The old database should also have been destroyed. - EXPECT_EQ(1u, mock_leveldb_service.destroy_requests_.size()); + EXPECT_EQ(1u, mock_leveldb_service.destroy_requests().size()); // Reconnect a wrapper to the database, and repeatedly write data to it again. // This time all should just keep getting written, and commit errors are @@ -1458,12 +1250,10 @@ TEST_F(LocalStorageContextMojoTestWithService, value[0]++; wrapper.set_connection_error_handler(put_loop.QuitClosure()); wrapper->Put(key, value, old_value, "source", - base::BindOnce( - [](base::Closure quit_closure, bool success) { - EXPECT_TRUE(success); - quit_closure.Run(); - }, - put_loop.QuitClosure())); + base::BindLambdaForTesting([&](bool success) { + EXPECT_TRUE(success); + put_loop.Quit(); + })); put_loop.RunUntilIdle(); old_value = value; // And we need to flush after every change. Otherwise changes get batched up diff --git a/chromium/content/browser/dom_storage/session_storage_context_mojo.cc b/chromium/content/browser/dom_storage/session_storage_context_mojo.cc index 1297a78569c..0f4bf4aed6c 100644 --- a/chromium/content/browser/dom_storage/session_storage_context_mojo.cc +++ b/chromium/content/browser/dom_storage/session_storage_context_mojo.cc @@ -4,80 +4,869 @@ #include "content/browser/dom_storage/session_storage_context_mojo.h" +#include +#include // for std::isalnum +#include +#include + +#include "base/barrier_closure.h" +#include "base/bind.h" +#include "base/debug/stack_trace.h" +#include "base/memory/ptr_util.h" +#include "base/metrics/histogram_functions.h" +#include "base/metrics/histogram_macros.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/sys_info.h" +#include "base/trace_event/memory_dump_manager.h" +#include "build/build_config.h" +#include "components/services/leveldb/public/cpp/util.h" +#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "content/browser/dom_storage/session_storage_leveldb_wrapper.h" +#include "content/browser/dom_storage/session_storage_namespace_impl_mojo.h" #include "content/browser/leveldb_wrapper_impl.h" #include "content/common/dom_storage/dom_storage_types.h" +#include "content/public/browser/session_storage_usage_info.h" #include "content/public/common/content_features.h" +#include "services/file/public/mojom/constants.mojom.h" #include "services/service_manager/public/cpp/connector.h" +#include "third_party/leveldatabase/env_chromium.h" +#include "third_party/leveldatabase/leveldb_chrome.h" +#include "url/gurl.h" namespace content { +namespace { +// After this many consecutive commit errors we'll throw away the entire +// database. +const int kSessionStorageCommitErrorThreshold = 8; + +// Limits on the cache size and number of areas in memory, over which the areas +// are purged. +#if defined(OS_ANDROID) +const unsigned kMaxSessionStorageAreaCount = 10; +const size_t kMaxSessionStorageCacheSize = 2 * 1024 * 1024; +#else +const unsigned kMaxSessionStorageAreaCount = 50; +const size_t kMaxSessionStorageCacheSize = 20 * 1024 * 1024; +#endif + +enum class SessionStorageCachePurgeReason { + kNotNeeded, + kSizeLimitExceeded, + kAreaCountLimitExceeded, + kInactiveOnLowEndDevice, + kAggressivePurgeTriggered +}; + +void RecordSessionStorageCachePurgedHistogram( + SessionStorageCachePurgeReason reason, + size_t purged_size_kib) { + UMA_HISTOGRAM_COUNTS_100000("SessionStorageContext.CachePurgedInKB", + purged_size_kib); + switch (reason) { + case SessionStorageCachePurgeReason::kSizeLimitExceeded: + UMA_HISTOGRAM_COUNTS_100000( + "SessionStorageContext.CachePurgedInKB.SizeLimitExceeded", + purged_size_kib); + break; + case SessionStorageCachePurgeReason::kAreaCountLimitExceeded: + UMA_HISTOGRAM_COUNTS_100000( + "SessionStorageContext.CachePurgedInKB.AreaCountLimitExceeded", + purged_size_kib); + break; + case SessionStorageCachePurgeReason::kInactiveOnLowEndDevice: + UMA_HISTOGRAM_COUNTS_100000( + "SessionStorageContext.CachePurgedInKB.InactiveOnLowEndDevice", + purged_size_kib); + break; + case SessionStorageCachePurgeReason::kAggressivePurgeTriggered: + UMA_HISTOGRAM_COUNTS_100000( + "SessionStorageContext.CachePurgedInKB.AggressivePurgeTriggered", + purged_size_kib); + break; + case SessionStorageCachePurgeReason::kNotNeeded: + NOTREACHED(); + break; + } +} +} // namespace SessionStorageContextMojo::SessionStorageContextMojo( - scoped_refptr task_runner, + scoped_refptr memory_dump_task_runner, service_manager::Connector* connector, - base::Optional partition_directory, + base::Optional local_partition_directory, std::string leveldb_name) : connector_(connector ? connector->Clone() : nullptr), - partition_directory_path_(std::move(partition_directory)), + partition_directory_path_(std::move(local_partition_directory)), leveldb_name_(std::move(leveldb_name)), + memory_dump_id_(base::StringPrintf("SessionStorage/0x%" PRIXPTR, + reinterpret_cast(this))), + is_low_end_device_(base::SysInfo::IsLowEndDevice()), weak_ptr_factory_(this) { DCHECK(base::FeatureList::IsEnabled(features::kMojoSessionStorage)); + base::trace_event::MemoryDumpManager::GetInstance() + ->RegisterDumpProviderWithSequencedTaskRunner( + this, "SessionStorage", std::move(memory_dump_task_runner), + base::trace_event::MemoryDumpProvider::Options()); +} + +SessionStorageContextMojo::~SessionStorageContextMojo() { + DCHECK_EQ(connection_state_, CONNECTION_SHUTDOWN); + base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( + this); } -SessionStorageContextMojo::~SessionStorageContextMojo() {} void SessionStorageContextMojo::OpenSessionStorage( int process_id, const std::string& namespace_id, mojom::SessionStorageNamespaceRequest request) { - // TODO(dmurph): Check the process ID against the origin like so: - // if (!ChildProcessSecurityPolicy::GetInstance()->CanAccessDataForOrigin( - // process_id, origin.GetURL())) { - // bindings_.ReportBadMessage("Access denied for sessionStorage request"); - // return; - // } - NOTREACHED(); + if (connection_state_ != CONNECTION_FINISHED) { + RunWhenConnected( + base::BindOnce(&SessionStorageContextMojo::OpenSessionStorage, + weak_ptr_factory_.GetWeakPtr(), process_id, namespace_id, + std::move(request))); + return; + } + auto found = namespaces_.find(namespace_id); + DCHECK(found != namespaces_.end()) << namespace_id; + + if (!found->second->IsPopulated() && + !found->second->waiting_on_clone_population()) { + found->second->PopulateFromMetadata( + database_.get(), metadata_.GetOrCreateNamespaceEntry(namespace_id), + data_maps_); + } + + PurgeUnusedWrappersIfNeeded(); + found->second->Bind(std::move(request), process_id); + + size_t total_cache_size, unused_wrapper_count; + GetStatistics(&total_cache_size, &unused_wrapper_count); + // Track the total sessionStorage cache size. + UMA_HISTOGRAM_COUNTS_100000("SessionStorageContext.CacheSizeInKB", + total_cache_size / 1024); } void SessionStorageContextMojo::CreateSessionNamespace( const std::string& namespace_id) { - NOTREACHED(); + if (namespaces_.find(namespace_id) != namespaces_.end()) + return; + + namespaces_.emplace(std::make_pair( + namespace_id, CreateSessionStorageNamespaceImplMojo(namespace_id))); } void SessionStorageContextMojo::CloneSessionNamespace( const std::string& namespace_id_to_clone, const std::string& clone_namespace_id) { - NOTREACHED(); + if (namespaces_.find(clone_namespace_id) != namespaces_.end()) + return; + + std::unique_ptr namespace_impl = + CreateSessionStorageNamespaceImplMojo(clone_namespace_id); + namespace_impl->SetWaitingForClonePopulation(); + namespaces_.emplace( + std::make_pair(clone_namespace_id, std::move(namespace_impl))); } void SessionStorageContextMojo::DeleteSessionNamespace( const std::string& namespace_id, bool should_persist) { - NOTREACHED(); + // The object hierarchy uses iterators bound to the metadata object, so make + // sure to delete the object hierarchy first. + namespaces_.erase(namespace_id); + + if (!has_scavenged_ && should_persist) + protected_namespaces_from_scavenge_.insert(namespace_id); + + if (!should_persist) { + RunWhenConnected( + base::BindOnce(&SessionStorageContextMojo::DoDatabaseDelete, + weak_ptr_factory_.GetWeakPtr(), namespace_id)); + } } void SessionStorageContextMojo::Flush() { - NOTREACHED(); + if (connection_state_ != CONNECTION_FINISHED) { + RunWhenConnected(base::BindOnce(&SessionStorageContextMojo::Flush, + weak_ptr_factory_.GetWeakPtr())); + return; + } + for (const auto& it : data_maps_) + it.second->level_db_wrapper()->ScheduleImmediateCommit(); } void SessionStorageContextMojo::GetStorageUsage( GetStorageUsageCallback callback) { - NOTREACHED(); + if (connection_state_ != CONNECTION_FINISHED) { + RunWhenConnected(base::BindOnce(&SessionStorageContextMojo::GetStorageUsage, + weak_ptr_factory_.GetWeakPtr(), + std::move(callback))); + return; + } + + const SessionStorageMetadata::NamespaceOriginMap& all_namespaces = + metadata_.namespace_origin_map(); + + std::vector result; + result.reserve(all_namespaces.size()); + for (const auto& pair : all_namespaces) { + for (const auto& origin_map_pair : pair.second) { + SessionStorageUsageInfo info = {origin_map_pair.first.GetURL(), + pair.first}; + result.push_back(std::move(info)); + } + } + std::move(callback).Run(std::move(result)); } -void SessionStorageContextMojo::DeleteStorage( - const url::Origin& origin, - const std::string& persistent_namespace_id) { - NOTREACHED(); + +void SessionStorageContextMojo::DeleteStorage(const url::Origin& origin, + const std::string& namespace_id) { + if (connection_state_ != CONNECTION_FINISHED) { + RunWhenConnected(base::BindOnce(&SessionStorageContextMojo::DeleteStorage, + weak_ptr_factory_.GetWeakPtr(), origin, + namespace_id)); + return; + } + auto found = namespaces_.find(namespace_id); + if (found != namespaces_.end()) { + found->second->RemoveOriginData(origin); + } else { + // If we don't have the namespace loaded, then we can delete it all + // using the metadata. + std::vector delete_operations; + metadata_.DeleteArea(namespace_id, origin, &delete_operations); + database_->Write(std::move(delete_operations), + base::BindOnce(&SessionStorageContextMojo::OnCommitResult, + base::Unretained(this))); + } } void SessionStorageContextMojo::ShutdownAndDelete() { - delete this; + DCHECK_NE(connection_state_, CONNECTION_SHUTDOWN); + + // Nothing to do if no connection to the database was ever finished. + if (connection_state_ != CONNECTION_FINISHED) { + connection_state_ = CONNECTION_SHUTDOWN; + OnShutdownComplete(leveldb::mojom::DatabaseError::OK); + return; + } + connection_state_ = CONNECTION_SHUTDOWN; + + // Flush any uncommitted data. + for (const auto& it : data_maps_) { + auto* wrapper = it.second->level_db_wrapper(); + LOCAL_HISTOGRAM_BOOLEAN( + "SessionStorageContext.ShutdownAndDelete.MaybeDroppedChanges", + wrapper->has_pending_load_tasks()); + wrapper->ScheduleImmediateCommit(); + // TODO(dmurph): Monitor the above histogram, and if dropping changes is + // common then handle that here. + wrapper->CancelAllPendingRequests(); + } + + OnShutdownComplete(leveldb::mojom::DatabaseError::OK); } void SessionStorageContextMojo::PurgeMemory() { - NOTREACHED(); + size_t total_cache_size, unused_wrapper_count; + GetStatistics(&total_cache_size, &unused_wrapper_count); + + // Purge all wrappers that don't have bindings. + for (auto it = namespaces_.begin(); it != namespaces_.end();) { + if (!it->second->IsBound()) { + it = namespaces_.erase(it); + continue; + } + it->second->PurgeUnboundWrappers(); + } + + // Track the size of cache purged. + size_t final_total_cache_size; + GetStatistics(&final_total_cache_size, &unused_wrapper_count); + size_t purged_size_kib = (total_cache_size - final_total_cache_size) / 1024; + RecordSessionStorageCachePurgedHistogram( + SessionStorageCachePurgeReason::kAggressivePurgeTriggered, + purged_size_kib); } void SessionStorageContextMojo::PurgeUnusedWrappersIfNeeded() { - NOTREACHED(); + size_t total_cache_size, unused_wrapper_count; + GetStatistics(&total_cache_size, &unused_wrapper_count); + + // Nothing to purge. + if (!unused_wrapper_count) + return; + + SessionStorageCachePurgeReason purge_reason = + SessionStorageCachePurgeReason::kNotNeeded; + + if (total_cache_size > kMaxSessionStorageCacheSize) + purge_reason = SessionStorageCachePurgeReason::kSizeLimitExceeded; + else if (data_maps_.size() > kMaxSessionStorageAreaCount) + purge_reason = SessionStorageCachePurgeReason::kAreaCountLimitExceeded; + else if (is_low_end_device_) + purge_reason = SessionStorageCachePurgeReason::kInactiveOnLowEndDevice; + + if (purge_reason == SessionStorageCachePurgeReason::kNotNeeded) + return; + + // Purge all wrappers that don't have bindings. + for (auto it = namespaces_.begin(); it != namespaces_.end();) { + if (!it->second->IsBound()) + it = namespaces_.erase(it); + } + + size_t final_total_cache_size; + GetStatistics(&final_total_cache_size, &unused_wrapper_count); + size_t purged_size_kib = (total_cache_size - final_total_cache_size) / 1024; + RecordSessionStorageCachePurgedHistogram( + SessionStorageCachePurgeReason::kAggressivePurgeTriggered, + purged_size_kib); +} + +void SessionStorageContextMojo::ScavengeUnusedNamespaces( + base::OnceClosure done) { + if (has_scavenged_) + return; + if (connection_state_ != CONNECTION_FINISHED) { + RunWhenConnected( + base::BindOnce(&SessionStorageContextMojo::ScavengeUnusedNamespaces, + weak_ptr_factory_.GetWeakPtr(), std::move(done))); + return; + } + has_scavenged_ = true; + std::vector namespaces_to_delete; + for (const auto& metadata_namespace : metadata_.namespace_origin_map()) { + const std::string& namespace_id = metadata_namespace.first; + if (namespaces_.find(namespace_id) != namespaces_.end() || + protected_namespaces_from_scavenge_.find(namespace_id) != + protected_namespaces_from_scavenge_.end()) { + continue; + } + namespaces_to_delete.push_back(namespace_id); + } + std::vector delete_operations; + for (const auto& namespace_id : namespaces_to_delete) { + metadata_.DeleteNamespace(namespace_id, &delete_operations); + } + + if (!delete_operations.empty()) { + database_->Write(std::move(delete_operations), + base::BindOnce(&SessionStorageContextMojo::OnCommitResult, + base::Unretained(this))); + } + protected_namespaces_from_scavenge_.clear(); + if (done) + std::move(done).Run(); +} + +bool SessionStorageContextMojo::OnMemoryDump( + const base::trace_event::MemoryDumpArgs& args, + base::trace_event::ProcessMemoryDump* pmd) { + if (connection_state_ != CONNECTION_FINISHED) + return true; + + std::string context_name = + base::StringPrintf("site_storage/sessionstorage/0x%" PRIXPTR, + reinterpret_cast(this)); + + // Account for leveldb memory usage, which actually lives in the file service. + auto* global_dump = pmd->CreateSharedGlobalAllocatorDump(memory_dump_id_); + // The size of the leveldb dump will be added by the leveldb service. + auto* leveldb_mad = pmd->CreateAllocatorDump(context_name + "/leveldb"); + // Specifies that the current context is responsible for keeping memory alive. + int kImportance = 2; + pmd->AddOwnershipEdge(leveldb_mad->guid(), global_dump->guid(), kImportance); + + if (args.level_of_detail == + base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) { + size_t total_cache_size, unused_wrapper_count; + GetStatistics(&total_cache_size, &unused_wrapper_count); + auto* mad = pmd->CreateAllocatorDump(context_name + "/cache_size"); + mad->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, + base::trace_event::MemoryAllocatorDump::kUnitsBytes, + total_cache_size); + mad->AddScalar("total_areas", + base::trace_event::MemoryAllocatorDump::kUnitsObjects, + data_maps_.size()); + return true; + } + for (const auto& it : data_maps_) { + // Limit the url length to 50 and strip special characters. + const auto& origin = it.second->map_data()->origin(); + std::string url = origin.Serialize().substr(0, 50); + for (size_t index = 0; index < url.size(); ++index) { + if (!std::isalnum(url[index])) + url[index] = '_'; + } + std::string wrapper_dump_name = base::StringPrintf( + "%s/%s/0x%" PRIXPTR, context_name.c_str(), url.c_str(), + reinterpret_cast(it.second->level_db_wrapper())); + it.second->level_db_wrapper()->OnMemoryDump(wrapper_dump_name, pmd); + } + return true; +} + +void SessionStorageContextMojo::OnDataMapCreation( + const std::vector& map_prefix, + SessionStorageDataMap* map) { + DCHECK(data_maps_.find(map_prefix) == data_maps_.end()); + data_maps_.emplace(std::piecewise_construct, + std::forward_as_tuple(map_prefix), + std::forward_as_tuple(map)); +} + +void SessionStorageContextMojo::OnDataMapDestruction( + const std::vector& map_prefix) { + data_maps_.erase(map_prefix); +} + +void SessionStorageContextMojo::OnCommitResult( + leveldb::mojom::DatabaseError error) { + DCHECK_EQ(connection_state_, CONNECTION_FINISHED); + UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.CommitResult", + leveldb::GetLevelDBStatusUMAValue(error), + leveldb_env::LEVELDB_STATUS_MAX); + if (error == leveldb::mojom::DatabaseError::OK) { + commit_error_count_ = 0; + return; + } + commit_error_count_++; + if (commit_error_count_ > kSessionStorageCommitErrorThreshold) { + if (tried_to_recover_from_commit_errors_) { + // We already tried to recover from a high commit error rate before, but + // are still having problems: there isn't really anything left to try, so + // just ignore errors. + return; + } + tried_to_recover_from_commit_errors_ = true; + + // Deleting LevelDBWrappers in here could cause more commits (and commit + // errors), but those commits won't reach OnCommitResult because the wrapper + // will have been deleted before the commit finishes. + DeleteAndRecreateDatabase( + "SessionStorageContext.OpenResultAfterCommitErrors"); + } +} + +void SessionStorageContextMojo::SetDatabaseForTesting( + leveldb::mojom::LevelDBDatabaseAssociatedPtr database) { + DCHECK_EQ(connection_state_, NO_CONNECTION); + connection_state_ = CONNECTION_IN_PROGRESS; + database_ = std::move(database); + OnDatabaseOpened(true, leveldb::mojom::DatabaseError::OK); +} + +void SessionStorageContextMojo::FlushAreaForTesting( + const std::string& namespace_id, + const url::Origin& origin) { + if (connection_state_ != CONNECTION_FINISHED) + return; + const auto& it = namespaces_.find(namespace_id); + if (it == namespaces_.end()) + return; + it->second->FlushOriginForTesting(origin); +} + +scoped_refptr +SessionStorageContextMojo::RegisterNewAreaMap( + SessionStorageMetadata::NamespaceEntry namespace_entry, + const url::Origin& origin) { + std::vector save_operations; + scoped_refptr map_entry = + metadata_.RegisterNewMap(namespace_entry, origin, &save_operations); + + database_->Write(std::move(save_operations), + base::BindOnce(&SessionStorageContextMojo::OnCommitResult, + base::Unretained(this))); + return map_entry; +} + +void SessionStorageContextMojo::RegisterShallowClonedNamespace( + SessionStorageMetadata::NamespaceEntry source_namespace_entry, + const std::string& new_namespace_id, + const SessionStorageNamespaceImplMojo::OriginAreas& clone_from_areas) { + std::vector save_operations; + SessionStorageMetadata::NamespaceEntry namespace_entry = + metadata_.GetOrCreateNamespaceEntry(new_namespace_id); + metadata_.RegisterShallowClonedNamespace(source_namespace_entry, + namespace_entry, &save_operations); + database_->Write(std::move(save_operations), + base::BindOnce(&SessionStorageContextMojo::OnCommitResult, + base::Unretained(this))); + + auto it = namespaces_.find(new_namespace_id); + if (it != namespaces_.end()) { + it->second->PopulateAsClone(database_.get(), namespace_entry, + clone_from_areas); + return; + } + + auto namespace_impl = CreateSessionStorageNamespaceImplMojo(new_namespace_id); + namespace_impl->PopulateAsClone(database_.get(), namespace_entry, + clone_from_areas); + namespaces_.emplace( + std::make_pair(new_namespace_id, std::move(namespace_impl))); +} + +std::unique_ptr +SessionStorageContextMojo::CreateSessionStorageNamespaceImplMojo( + std::string namespace_id) { + SessionStorageNamespaceImplMojo::RegisterShallowClonedNamespace + add_namespace_callback = base::BindRepeating( + &SessionStorageContextMojo::RegisterShallowClonedNamespace, + base::Unretained(this)); + SessionStorageLevelDBWrapper::RegisterNewAreaMap map_id_callback = + base::BindRepeating(&SessionStorageContextMojo::RegisterNewAreaMap, + base::Unretained(this)); + + return std::make_unique( + std::move(namespace_id), this, std::move(add_namespace_callback), + std::move(map_id_callback)); +} + +void SessionStorageContextMojo::DoDatabaseDelete( + const std::string& namespace_id) { + DCHECK_EQ(connection_state_, CONNECTION_FINISHED); + std::vector delete_operations; + metadata_.DeleteNamespace(namespace_id, &delete_operations); + database_->Write(std::move(delete_operations), + base::BindOnce(&SessionStorageContextMojo::OnCommitResult, + base::Unretained(this))); +} + +void SessionStorageContextMojo::RunWhenConnected(base::OnceClosure callback) { + DCHECK_NE(connection_state_, CONNECTION_SHUTDOWN); + + // If we don't have a filesystem_connection_, we'll need to establish one. + if (connection_state_ == NO_CONNECTION) { + connection_state_ = CONNECTION_IN_PROGRESS; + InitiateConnection(); + } + + if (connection_state_ == CONNECTION_IN_PROGRESS) { + // Queue this OpenSessionStorage call for when we have a level db pointer. + on_database_opened_callbacks_.push_back(std::move(callback)); + return; + } + + std::move(callback).Run(); +} + +void SessionStorageContextMojo::InitiateConnection(bool in_memory_only) { + DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); + // Unit tests might not always have a Connector, use in-memory only if that + // happens. + if (!connector_) { + OnDatabaseOpened(false, leveldb::mojom::DatabaseError::OK); + return; + } + + if (partition_directory_path_ && !in_memory_only) { + // We were given a subdirectory to write to. Get it and use a disk backed + // database. + connector_->BindInterface(file::mojom::kServiceName, &file_system_); + file_system_->GetSubDirectory( + partition_directory_path_.value().AsUTF8Unsafe(), + MakeRequest(&partition_directory_), + base::BindOnce(&SessionStorageContextMojo::OnDirectoryOpened, + weak_ptr_factory_.GetWeakPtr())); + } else { + // We were not given a subdirectory. Use a memory backed database. + connector_->BindInterface(file::mojom::kServiceName, &leveldb_service_); + leveldb_service_->OpenInMemory( + memory_dump_id_, "SessionStorageDatabase", MakeRequest(&database_), + base::BindOnce(&SessionStorageContextMojo::OnDatabaseOpened, + weak_ptr_factory_.GetWeakPtr(), true)); + } +} + +void SessionStorageContextMojo::OnDirectoryOpened(base::File::Error err) { + if (err != base::File::FILE_OK) { + // We failed to open the directory; continue with startup so that we create + // the |level_db_wrappers_|. + UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.DirectoryOpenError", -err, + -base::File::FILE_ERROR_MAX); + LogDatabaseOpenResult(OpenResult::kDirectoryOpenFailed); + OnDatabaseOpened(false, leveldb::mojom::DatabaseError::OK); + return; + } + + // Now that we have a directory, connect to the LevelDB service and get our + // database. + connector_->BindInterface(file::mojom::kServiceName, &leveldb_service_); + + // We might still need to use the directory, so create a clone. + filesystem::mojom::DirectoryPtr partition_directory_clone; + partition_directory_->Clone(MakeRequest(&partition_directory_clone)); + + leveldb_env::Options options; + options.create_if_missing = true; + options.max_open_files = 0; // use minimum + // Default write_buffer_size is 4 MB but that might leave a 3.999 + // memory allocation in RAM from a log file recovery. + options.write_buffer_size = 64 * 1024; + options.block_cache = leveldb_chrome::GetSharedWebBlockCache(); + leveldb_service_->OpenWithOptions( + std::move(options), std::move(partition_directory_clone), leveldb_name_, + memory_dump_id_, MakeRequest(&database_), + base::BindOnce(&SessionStorageContextMojo::OnDatabaseOpened, + weak_ptr_factory_.GetWeakPtr(), false)); +} + +void SessionStorageContextMojo::OnDatabaseOpened( + bool in_memory, + leveldb::mojom::DatabaseError status) { + if (status != leveldb::mojom::DatabaseError::OK) { + UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.DatabaseOpenError", + leveldb::GetLevelDBStatusUMAValue(status), + leveldb_env::LEVELDB_STATUS_MAX); + if (in_memory) { + UMA_HISTOGRAM_ENUMERATION( + "SessionStorageContext.DatabaseOpenError.Memory", + leveldb::GetLevelDBStatusUMAValue(status), + leveldb_env::LEVELDB_STATUS_MAX); + } else { + UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.DatabaseOpenError.Disk", + leveldb::GetLevelDBStatusUMAValue(status), + leveldb_env::LEVELDB_STATUS_MAX); + } + LogDatabaseOpenResult(OpenResult::kDatabaseOpenFailed); + // If we failed to open the database, try to delete and recreate the + // database, or ultimately fallback to an in-memory database. + DeleteAndRecreateDatabase( + "SessionStorageContext.OpenResultAfterOpenFailed"); + return; + } + + // Verify DB schema version. + if (database_) { + database_->Get( + std::vector( + SessionStorageMetadata::kDatabaseVersionBytes, + std::end(SessionStorageMetadata::kDatabaseVersionBytes)), + base::BindOnce(&SessionStorageContextMojo::OnGotDatabaseVersion, + weak_ptr_factory_.GetWeakPtr())); + return; + } + + OnConnectionFinished(); +} + +void SessionStorageContextMojo::OnGotDatabaseVersion( + leveldb::mojom::DatabaseError status, + const std::vector& value) { + std::vector migration_operations; + if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { + // New database, or schema v0. We must treat this as a schema v0 database. + metadata_.ParseDatabaseVersion(base::nullopt, &migration_operations); + } else if (status == leveldb::mojom::DatabaseError::OK) { + if (!metadata_.ParseDatabaseVersion(value, &migration_operations)) { + LogDatabaseOpenResult(OpenResult::kInvalidVersion); + DeleteAndRecreateDatabase( + "SessionStorageContext.OpenResultAfterInvalidVersion"); + return; + } + database_initialized_ = true; + } else { + // Other read error. Possibly database corruption. + UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.ReadVersionError", + leveldb::GetLevelDBStatusUMAValue(status), + leveldb_env::LEVELDB_STATUS_MAX); + LogDatabaseOpenResult(OpenResult::kVersionReadError); + DeleteAndRecreateDatabase( + "SessionStorageContext.OpenResultAfterReadVersionError"); + return; + } + connection_state_ = FETCHING_METADATA; + + base::RepeatingClosure barrier = base::BarrierClosure( + 2, base::BindOnce(&SessionStorageContextMojo::OnConnectionFinished, + weak_ptr_factory_.GetWeakPtr())); + + std::vector namespace_prefix( + SessionStorageMetadata::kNamespacePrefixBytes, + std::end(SessionStorageMetadata::kNamespacePrefixBytes)); + std::vector next_map_id_key( + SessionStorageMetadata::kNextMapIdKeyBytes, + std::end(SessionStorageMetadata::kNextMapIdKeyBytes)); + database_->GetPrefixed( + namespace_prefix, + base::BindOnce(&SessionStorageContextMojo::OnGotNamespaces, + weak_ptr_factory_.GetWeakPtr(), barrier, + std::move(migration_operations))); + database_->Get(next_map_id_key, + base::BindOnce(&SessionStorageContextMojo::OnGotNextMapId, + weak_ptr_factory_.GetWeakPtr(), barrier)); +} + +void SessionStorageContextMojo::OnGotNamespaces( + base::OnceClosure done, + std::vector migration_operations, + leveldb::mojom::DatabaseError status, + std::vector values) { + DCHECK(connection_state_ == FETCHING_METADATA); + bool parsing_failure = + status == leveldb::mojom::DatabaseError::OK && + !metadata_.ParseNamespaces(std::move(values), &migration_operations); + if (status != leveldb::mojom::DatabaseError::OK || parsing_failure) { + UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.ReadNamespacesError", + leveldb::GetLevelDBStatusUMAValue(status), + leveldb_env::LEVELDB_STATUS_MAX); + LogDatabaseOpenResult(OpenResult::kNamespacesReadError); + DeleteAndRecreateDatabase( + "SessionStorageContext.OpenResultAfterReadNamespacesError"); + return; + } + + // Write all of our migration operations if we have any. + if (!migration_operations.empty()) { + database_->Write(std::move(migration_operations), + base::BindOnce(&SessionStorageContextMojo::OnCommitResult, + base::Unretained(this))); + } + std::move(done).Run(); +} + +void SessionStorageContextMojo::OnGotNextMapId( + base::OnceClosure done, + leveldb::mojom::DatabaseError status, + const std::vector& map_id) { + DCHECK(connection_state_ == FETCHING_METADATA); + if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { + std::move(done).Run(); + return; + } + if (status == leveldb::mojom::DatabaseError::OK) { + metadata_.ParseNextMapId(map_id); + std::move(done).Run(); + return; + } + + // Other read error. Possibly database corruption. + UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.ReadNextMapIdError", + leveldb::GetLevelDBStatusUMAValue(status), + leveldb_env::LEVELDB_STATUS_MAX); + LogDatabaseOpenResult(OpenResult::kNamespacesReadError); + DeleteAndRecreateDatabase( + "SessionStorageContext.OpenResultAfterReadNextMapIdError"); +} + +void SessionStorageContextMojo::OnConnectionFinished() { + DCHECK(!database_ || connection_state_ == FETCHING_METADATA); + if (!database_) { + partition_directory_.reset(); + file_system_.reset(); + leveldb_service_.reset(); + } + + // If connection was opened successfully, reset tried_to_recreate_during_open_ + // to enable recreating the database on future errors. + if (database_) + tried_to_recreate_during_open_ = false; + + open_result_histogram_ = nullptr; + + // |database_| should be known to either be valid or invalid by now. Run our + // delayed bindings. + connection_state_ = CONNECTION_FINISHED; + std::vector callbacks; + std::swap(callbacks, on_database_opened_callbacks_); + for (size_t i = 0; i < callbacks.size(); ++i) + std::move(callbacks[i]).Run(); +} + +void SessionStorageContextMojo::DeleteAndRecreateDatabase( + const char* histogram_name) { + // We're about to set database_ to null, so delete the LevelDBWrappers + // that might still be using the old database. + for (const auto& it : data_maps_) + it.second->level_db_wrapper()->CancelAllPendingRequests(); + + for (const auto& namespace_pair : namespaces_) { + namespace_pair.second->Reset(); + } + DCHECK(data_maps_.empty()); + + // Reset state to be in process of connecting. This will cause requests for + // LevelDBWrappers to be queued until the connection is complete. + connection_state_ = CONNECTION_IN_PROGRESS; + commit_error_count_ = 0; + database_ = nullptr; + open_result_histogram_ = histogram_name; + + bool recreate_in_memory = false; + + // If tried to recreate database on disk already, try again but this time + // in memory. + if (tried_to_recreate_during_open_ && !!partition_directory_path_) { + recreate_in_memory = true; + } else if (tried_to_recreate_during_open_) { + // Give up completely, run without any database. + OnConnectionFinished(); + return; + } + + tried_to_recreate_during_open_ = true; + + // Unit tests might not have a bound file_service_, in which case there is + // nothing to retry. + if (!file_system_.is_bound()) { + OnConnectionFinished(); + return; + } + + protected_namespaces_from_scavenge_.clear(); + + // Destroy database, and try again. + if (partition_directory_.is_bound()) { + leveldb_service_->Destroy( + std::move(partition_directory_), leveldb_name_, + base::BindOnce(&SessionStorageContextMojo::OnDBDestroyed, + weak_ptr_factory_.GetWeakPtr(), recreate_in_memory)); + } else { + // No directory, so nothing to destroy. Retrying to recreate will probably + // fail, but try anyway. + InitiateConnection(recreate_in_memory); + } +} + +void SessionStorageContextMojo::OnDBDestroyed( + bool recreate_in_memory, + leveldb::mojom::DatabaseError status) { + UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.DestroyDBResult", + leveldb::GetLevelDBStatusUMAValue(status), + leveldb_env::LEVELDB_STATUS_MAX); + // We're essentially ignoring the status here. Even if destroying failed we + // still want to go ahead and try to recreate. + InitiateConnection(recreate_in_memory); +} + +void SessionStorageContextMojo::OnShutdownComplete( + leveldb::mojom::DatabaseError error) { + delete this; +} + +void SessionStorageContextMojo::GetStatistics(size_t* total_cache_size, + size_t* unused_wrapper_count) { + *total_cache_size = 0; + *unused_wrapper_count = 0; + for (const auto& it : data_maps_) { + *total_cache_size += it.second->level_db_wrapper()->memory_used(); + if (it.second->binding_count() == 0) + (*unused_wrapper_count)++; + } +} + +void SessionStorageContextMojo::LogDatabaseOpenResult(OpenResult result) { + if (result != OpenResult::kSuccess) { + LOG(ERROR) << "Got error when openning: " << static_cast(result); + UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.OpenError", result); + } + if (open_result_histogram_) { + base::UmaHistogramEnumeration(open_result_histogram_, result); + } } } // namespace content diff --git a/chromium/content/browser/dom_storage/session_storage_context_mojo.h b/chromium/content/browser/dom_storage/session_storage_context_mojo.h index 919c1356d22..6361d3a2bfd 100644 --- a/chromium/content/browser/dom_storage/session_storage_context_mojo.h +++ b/chromium/content/browser/dom_storage/session_storage_context_mojo.h @@ -6,18 +6,25 @@ #define CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_CONTEXT_MOJO_H_ #include +#include #include #include #include #include "base/callback_forward.h" #include "base/files/file_path.h" +#include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" +#include "base/trace_event/memory_dump_provider.h" +#include "content/browser/dom_storage/session_storage_data_map.h" +#include "content/browser/dom_storage/session_storage_metadata.h" +#include "content/browser/dom_storage/session_storage_namespace_impl_mojo.h" #include "content/common/content_export.h" #include "content/common/leveldb_wrapper.mojom.h" #include "content/common/storage_partition_service.mojom.h" #include "content/public/browser/session_storage_usage_info.h" +#include "services/file/public/mojom/file_system.mojom.h" #include "url/origin.h" namespace base { @@ -29,9 +36,16 @@ class Connector; } // namespace service_manager namespace content { +struct SessionStorageUsageInfo; // Used for mojo-based SessionStorage implementation. -class CONTENT_EXPORT SessionStorageContextMojo { +// Created on the UI thread, but all further methods are called on the task +// runner passed to the constructor. Furthermore since destruction of this class +// can involve asynchronous steps, it can only be deleted by calling +// ShutdownAndDelete (on the correct task runner). +class CONTENT_EXPORT SessionStorageContextMojo + : public base::trace_event::MemoryDumpProvider, + public SessionStorageDataMap::Listener { public: using GetStorageUsageCallback = base::OnceCallback)>; @@ -39,9 +53,8 @@ class CONTENT_EXPORT SessionStorageContextMojo { SessionStorageContextMojo( scoped_refptr task_runner, service_manager::Connector* connector, - base::Optional partition_directory, + base::Optional local_partition_directory, std::string leveldb_name); - ~SessionStorageContextMojo(); void OpenSessionStorage(int process_id, const std::string& namespace_id, @@ -50,24 +63,21 @@ class CONTENT_EXPORT SessionStorageContextMojo { void CreateSessionNamespace(const std::string& namespace_id); void CloneSessionNamespace(const std::string& namespace_id_to_clone, const std::string& clone_namespace_id); + void DeleteSessionNamespace(const std::string& namespace_id, bool should_persist); - void Flush(); - // 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 - // the implementation for details. - void SetForceKeepSessionState() { force_keep_session_state_ = true; } + void Flush(); void GetStorageUsage(GetStorageUsageCallback callback); + void DeleteStorage(const url::Origin& origin, - const std::string& persistent_namespace_id); + const std::string& namespace_id); - // Called when the owning BrowserContext is ending. - // Schedules the commit of any unsaved changes and will delete - // and keep data on disk per the content settings and special storage - // policies. + // Called when the owning BrowserContext is ending. Schedules the commit of + // any unsaved changes then deletes this object. All data on disk (where there + // was no call to |DeleteSessionNamespace| will stay on disk for later + // restoring. void ShutdownAndDelete(); // Clears any caches, to free up as much memory as possible. Next access to @@ -77,16 +87,148 @@ class CONTENT_EXPORT SessionStorageContextMojo { // Clears unused leveldb wrappers, when thresholds are reached. void PurgeUnusedWrappersIfNeeded(); - base::WeakPtr AsWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); + // Any namespaces that have been loaded from disk and have not had a + // corresponding CreateSessionNamespace() call will be deleted. Called after + // startup. The calback is used for unittests, and is called after the + // scavenging has finished (but not necessarily saved to disk). A null + // callback is ok. + void ScavengeUnusedNamespaces(base::OnceClosure done); + + // base::trace_event::MemoryDumpProvider implementation: + bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, + base::trace_event::ProcessMemoryDump* pmd) override; + + // SessionStorageLevelDBWrapper::Listener implementation: + void OnDataMapCreation(const std::vector& map_prefix, + SessionStorageDataMap* map) override; + void OnDataMapDestruction(const std::vector& map_prefix) override; + void OnCommitResult(leveldb::mojom::DatabaseError error) override; + + // Sets the database for testing. + void SetDatabaseForTesting( + leveldb::mojom::LevelDBDatabaseAssociatedPtr database); + + leveldb::mojom::LevelDBDatabase* DatabaseForTesting() { + return database_.get(); } + void FlushAreaForTesting(const std::string& namespace_id, + const url::Origin& origin); + private: + // Object deletion is done through |ShutdownAndDelete()|. + ~SessionStorageContextMojo() override; + + scoped_refptr RegisterNewAreaMap( + SessionStorageMetadata::NamespaceEntry namespace_entry, + const url::Origin& origin); + + void RegisterShallowClonedNamespace( + SessionStorageMetadata::NamespaceEntry source_namespace_entry, + const std::string& new_namespace_id, + const SessionStorageNamespaceImplMojo::OriginAreas& clone_from_areas); + + std::unique_ptr + CreateSessionStorageNamespaceImplMojo(std::string namespace_id); + + void DoDatabaseDelete(const std::string& namespace_id); + + // Runs |callback| immediately if already connected to a database, otherwise + // delays running |callback| untill after a connection has been established. + // Initiates connecting to the database if no connection is in progress yet. + void RunWhenConnected(base::OnceClosure callback); + + // Part of our asynchronous directory opening called from RunWhenConnected(). + void InitiateConnection(bool in_memory_only = false); + void OnDirectoryOpened(base::File::Error err); + void OnDatabaseOpened(bool in_memory, leveldb::mojom::DatabaseError status); + void OnGotDatabaseVersion(leveldb::mojom::DatabaseError status, + const std::vector& value); + void OnGotNamespaces( + base::OnceClosure done, + std::vector migration_operations, + leveldb::mojom::DatabaseError status, + std::vector values); + void OnGotNextMapId(base::OnceClosure done, + leveldb::mojom::DatabaseError status, + const std::vector& map_id); + void OnConnectionFinished(); + void DeleteAndRecreateDatabase(const char* histogram_name); + void OnDBDestroyed(bool recreate_in_memory, + leveldb::mojom::DatabaseError status); + + void OnGotMetaData(GetStorageUsageCallback callback, + leveldb::mojom::DatabaseError status, + std::vector data); + + void OnShutdownComplete(leveldb::mojom::DatabaseError error); + + void GetStatistics(size_t* total_cache_size, size_t* unused_wrapper_count); + + // These values are written to logs. New enum values can be added, but + // existing enums must never be renumbered or deleted and reused. + enum class OpenResult { + kDirectoryOpenFailed = 0, + kDatabaseOpenFailed = 1, + kInvalidVersion = 2, + kVersionReadError = 3, + kNamespacesReadError = 4, + kSuccess = 6, + kMaxValue = kSuccess + }; + + void LogDatabaseOpenResult(OpenResult result); + + // Since the session storage object hierarchy references iterators owned by + // the metadata, make sure it is destroyed last on destruction. + SessionStorageMetadata metadata_; + std::unique_ptr connector_; const base::Optional partition_directory_path_; std::string leveldb_name_; - bool force_keep_session_state_ = false; + enum ConnectionState { + NO_CONNECTION, + FETCHING_METADATA, + CONNECTION_IN_PROGRESS, + CONNECTION_FINISHED, + CONNECTION_SHUTDOWN + } connection_state_ = NO_CONNECTION; + bool database_initialized_ = false; + + file::mojom::FileSystemPtr file_system_; + filesystem::mojom::DirectoryPtr partition_directory_; + + base::trace_event::MemoryAllocatorDumpGuid memory_dump_id_; + + leveldb::mojom::LevelDBServicePtr leveldb_service_; + leveldb::mojom::LevelDBDatabaseAssociatedPtr database_; + bool tried_to_recreate_during_open_ = false; + + std::vector on_database_opened_callbacks_; + + // The removal of items from this map is managed by the refcounting in + // SessionStorageDataMap. + // Populated after the database is connected. + std::map, SessionStorageDataMap*> data_maps_; + // Populated on OpenSessionStorage calls. + std::map> + namespaces_; + + // Scavenging only happens once. + bool has_scavenged_ = false; + // When namespaces are destroyed but marked as persistent, a scavenge should + // not delete them. Cleared after ScavengeUnusedNamespaces is called. + std::set protected_namespaces_from_scavenge_; + + bool is_low_end_device_; + // Counts consecutive commit errors. If this number reaches a threshold, the + // whole database is thrown away. + int commit_error_count_ = 0; + bool tried_to_recover_from_commit_errors_ = false; + + // Name of an extra histogram to log open results to, if not null. + const char* open_result_histogram_ = nullptr; base::WeakPtrFactory weak_ptr_factory_; }; diff --git a/chromium/content/browser/dom_storage/session_storage_context_mojo_unittest.cc b/chromium/content/browser/dom_storage/session_storage_context_mojo_unittest.cc new file mode 100644 index 00000000000..1b9ee547d47 --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_context_mojo_unittest.cc @@ -0,0 +1,762 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/session_storage_context_mojo.h" + +#include +#include + +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/guid.h" +#include "base/memory/ref_counted.h" +#include "base/sequenced_task_runner.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/bind_test_util.h" +#include "base/test/scoped_feature_list.h" +#include "components/services/leveldb/public/cpp/util.h" +#include "content/browser/dom_storage/session_storage_database.h" +#include "content/browser/dom_storage/test/fake_leveldb_database_error_on_write.h" +#include "content/browser/dom_storage/test/fake_leveldb_service.h" +#include "content/browser/dom_storage/test/mojo_test_with_file_service.h" +#include "content/common/dom_storage/dom_storage_types.h" +#include "content/public/browser/session_storage_usage_info.h" +#include "content/public/common/content_features.h" +#include "content/public/test/test_utils.h" +#include "content/test/leveldb_wrapper_test_util.h" +#include "mojo/public/cpp/bindings/strong_associated_binding.h" +#include "services/file/file_service.h" +#include "services/file/public/mojom/constants.mojom.h" +#include "services/service_manager/public/cpp/service_context.h" +#include "services/service_manager/public/cpp/test/test_service_decorator.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { +namespace { +using leveldb::StdStringToUint8Vector; +using leveldb::String16ToUint8Vector; +using leveldb::Uint8VectorToStdString; +using leveldb::mojom::DatabaseError; +using leveldb::mojom::KeyValuePtr; + +static const char kSessionStorageDirectory[] = "Session Storage"; +static const int kTestProcessId = 0; + +void GetStorageUsageCallback(base::OnceClosure callback, + std::vector* out_result, + std::vector result) { + *out_result = std::move(result); + std::move(callback).Run(); +} + +class SessionStorageContextMojoTest : public test::MojoTestWithFileService { + public: + SessionStorageContextMojoTest() {} + + ~SessionStorageContextMojoTest() override { + if (context_) + ShutdownContext(); + } + + void SetUp() override { + features_.InitAndEnableFeature(features::kMojoSessionStorage); + } + + SessionStorageContextMojo* context() { + if (!context_) { + context_ = new SessionStorageContextMojo( + base::SequencedTaskRunnerHandle::Get(), connector(), base::FilePath(), + kSessionStorageDirectory); + } + return context_; + } + + void ShutdownContext() { + context_->ShutdownAndDelete(); + context_ = nullptr; + base::RunLoop().RunUntilIdle(); + } + + std::vector GetStorageUsageSync() { + base::RunLoop run_loop; + std::vector result; + context()->GetStorageUsage(base::BindOnce(&GetStorageUsageCallback, + run_loop.QuitClosure(), &result)); + run_loop.Run(); + return result; + } + + void DoTestPut(const std::string& namespace_id, + const url::Origin& origin, + base::StringPiece key, + base::StringPiece value, + const std::string& source) { + context()->CreateSessionNamespace(namespace_id); + mojom::SessionStorageNamespacePtr ss_namespace; + context()->OpenSessionStorage(kTestProcessId, namespace_id, + mojo::MakeRequest(&ss_namespace)); + mojom::LevelDBWrapperAssociatedPtr leveldb; + ss_namespace->OpenArea(origin, mojo::MakeRequest(&leveldb)); + EXPECT_TRUE(test::PutSync( + leveldb.get(), leveldb::StringPieceToUint8Vector(key), + leveldb::StringPieceToUint8Vector(value), base::nullopt, source)); + context()->DeleteSessionNamespace(namespace_id, true); + } + + base::Optional> DoTestGet( + const std::string& namespace_id, + const url::Origin& origin, + base::StringPiece key) { + context()->CreateSessionNamespace(namespace_id); + mojom::SessionStorageNamespacePtr ss_namespace; + context()->OpenSessionStorage(kTestProcessId, namespace_id, + mojo::MakeRequest(&ss_namespace)); + mojom::LevelDBWrapperAssociatedPtr leveldb; + ss_namespace->OpenArea(origin, mojo::MakeRequest(&leveldb)); + + // Use the GetAll interface because Gets are being removed. + std::vector data; + DatabaseError status = test::GetAllSync(leveldb.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + context()->DeleteSessionNamespace(namespace_id, true); + + std::vector key_as_bytes = leveldb::StringPieceToUint8Vector(key); + for (const auto& key_value : data) { + if (key_value->key == key_as_bytes) { + return key_value->value; + } + } + return base::nullopt; + } + + private: + base::test::ScopedFeatureList features_; + SessionStorageContextMojo* context_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(SessionStorageContextMojoTest); +}; + +TEST_F(SessionStorageContextMojoTest, MigrationV0ToV1) { + std::string namespace_id1 = base::GenerateGUID(); + std::string namespace_id2 = base::GenerateGUID(); + url::Origin origin1 = url::Origin::Create(GURL("http://foobar.com")); + url::Origin origin2 = url::Origin::Create(GURL("http://example.com")); + base::string16 key = base::ASCIIToUTF16("key"); + base::string16 value = base::ASCIIToUTF16("value"); + base::string16 key2 = base::ASCIIToUTF16("key2"); + key2.push_back(0xd83d); + key2.push_back(0xde00); + + base::FilePath old_db_path = + temp_path().AppendASCII(kSessionStorageDirectory); + { + scoped_refptr db = + base::MakeRefCounted( + old_db_path, base::ThreadTaskRunnerHandle::Get().get()); + DOMStorageValuesMap data; + data[key] = base::NullableString16(value, false); + data[key2] = base::NullableString16(value, false); + EXPECT_TRUE(db->CommitAreaChanges(namespace_id1, origin1, false, data)); + EXPECT_TRUE(db->CloneNamespace(namespace_id1, namespace_id2)); + } + EXPECT_TRUE(base::PathExists(old_db_path)); + + // The first call to context() here constructs it. + context()->CreateSessionNamespace(namespace_id1); + context()->CreateSessionNamespace(namespace_id2); + + mojom::SessionStorageNamespacePtr ss_namespace1; + context()->OpenSessionStorage(kTestProcessId, namespace_id1, + mojo::MakeRequest(&ss_namespace1)); + mojom::SessionStorageNamespacePtr ss_namespace2; + context()->OpenSessionStorage(kTestProcessId, namespace_id2, + mojo::MakeRequest(&ss_namespace2)); + + mojom::LevelDBWrapperAssociatedPtr leveldb_n2_o1; + mojom::LevelDBWrapperAssociatedPtr leveldb_n2_o2; + ss_namespace2->OpenArea(origin1, mojo::MakeRequest(&leveldb_n2_o1)); + ss_namespace2->OpenArea(origin2, mojo::MakeRequest(&leveldb_n2_o2)); + + std::vector data; + DatabaseError status = test::GetAllSync(leveldb_n2_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + // There should have been a migration to get rid of the "map-0-" refcount + // field. + EXPECT_EQ(2ul, data.size()); + std::vector key_as_vector = + StdStringToUint8Vector(base::UTF16ToUTF8(key)); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(key_as_vector, String16ToUint8Vector(value)))); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(key_as_vector, String16ToUint8Vector(value)))); +} + +TEST_F(SessionStorageContextMojoTest, StartupShutdownSave) { + std::string namespace_id1 = base::GenerateGUID(); + url::Origin origin1 = url::Origin::Create(GURL("http://foobar.com")); + context()->CreateSessionNamespace(namespace_id1); + + mojom::SessionStorageNamespacePtr ss_namespace1; + context()->OpenSessionStorage(kTestProcessId, namespace_id1, + mojo::MakeRequest(&ss_namespace1)); + + mojom::LevelDBWrapperAssociatedPtr leveldb_n1_o1; + ss_namespace1->OpenArea(origin1, mojo::MakeRequest(&leveldb_n1_o1)); + + // Verify no data. + std::vector data; + DatabaseError status = test::GetAllSync(leveldb_n1_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(0ul, data.size()); + + // Put some data. + EXPECT_TRUE(test::PutSync( + leveldb_n1_o1.get(), leveldb::StringPieceToUint8Vector("key1"), + leveldb::StringPieceToUint8Vector("value1"), base::nullopt, "source1")); + + // Verify data is there. + status = test::GetAllSync(leveldb_n1_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(1ul, data.size()); + + // Delete the namespace and shutdown the context, BUT persist the namespace so + // it can be loaded again. + context()->DeleteSessionNamespace(namespace_id1, true); + ShutdownContext(); + + // This will re-open the context, and load the persisted namespace. + context()->CreateSessionNamespace(namespace_id1); + context()->OpenSessionStorage(kTestProcessId, namespace_id1, + mojo::MakeRequest(&ss_namespace1)); + ss_namespace1->OpenArea(origin1, mojo::MakeRequest(&leveldb_n1_o1)); + + // The data from before should be here. + status = test::GetAllSync(leveldb_n1_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(1ul, data.size()); + + // Delete the namespace and shutdown the context and do not persist the data. + context()->DeleteSessionNamespace(namespace_id1, false); + ShutdownContext(); + + // This will re-open the context, and the namespace should be empty. + context()->CreateSessionNamespace(namespace_id1); + context()->OpenSessionStorage(kTestProcessId, namespace_id1, + mojo::MakeRequest(&ss_namespace1)); + ss_namespace1->OpenArea(origin1, mojo::MakeRequest(&leveldb_n1_o1)); + + // The data from before should be here. + status = test::GetAllSync(leveldb_n1_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(0ul, data.size()); +} + +TEST_F(SessionStorageContextMojoTest, Cloning) { + std::string namespace_id1 = base::GenerateGUID(); + std::string namespace_id2 = base::GenerateGUID(); + url::Origin origin1 = url::Origin::Create(GURL("http://foobar.com")); + context()->CreateSessionNamespace(namespace_id1); + mojom::SessionStorageNamespacePtr ss_namespace1; + context()->OpenSessionStorage(kTestProcessId, namespace_id1, + mojo::MakeRequest(&ss_namespace1)); + mojom::LevelDBWrapperAssociatedPtr leveldb_n1_o1; + ss_namespace1->OpenArea(origin1, mojo::MakeRequest(&leveldb_n1_o1)); + + // Context-triggered clone before the put. The clone doesn't actually count + // until a clone comes from the namespace. + context()->CloneSessionNamespace(namespace_id1, namespace_id2); + + // Put some data. + EXPECT_TRUE(test::PutSync( + leveldb_n1_o1.get(), leveldb::StringPieceToUint8Vector("key1"), + leveldb::StringPieceToUint8Vector("value1"), base::nullopt, "source1")); + + ss_namespace1->Clone(namespace_id2); + leveldb_n1_o1.FlushForTesting(); + + // Open the second namespace. + mojom::SessionStorageNamespacePtr ss_namespace2; + context()->OpenSessionStorage(kTestProcessId, namespace_id2, + mojo::MakeRequest(&ss_namespace2)); + mojom::LevelDBWrapperAssociatedPtr leveldb_n2_o1; + ss_namespace2->OpenArea(origin1, mojo::MakeRequest(&leveldb_n2_o1)); + + // Delete the namespace and shutdown the context, BUT persist the namespace so + // it can be loaded again. This tests the case where our cloning works even + // though the namespace is deleted (but persisted on disk). + context()->DeleteSessionNamespace(namespace_id1, true); + + // The data from before should be in namespace 2. + std::vector data; + DatabaseError status = test::GetAllSync(leveldb_n2_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(1ul, data.size()); + + // Put some data in namespace 2. + EXPECT_TRUE(test::PutSync( + leveldb_n2_o1.get(), leveldb::StringPieceToUint8Vector("key2"), + leveldb::StringPieceToUint8Vector("value2"), base::nullopt, "source1")); + status = test::GetAllSync(leveldb_n2_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(2ul, data.size()); + + // Re-open namespace 1, check that we don't have the extra data. + context()->CreateSessionNamespace(namespace_id1); + context()->OpenSessionStorage(kTestProcessId, namespace_id1, + mojo::MakeRequest(&ss_namespace1)); + ss_namespace1->OpenArea(origin1, mojo::MakeRequest(&leveldb_n1_o1)); + + // We should only have the first value. + status = test::GetAllSync(leveldb_n1_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(1ul, data.size()); +} + +TEST_F(SessionStorageContextMojoTest, Scavenging) { + // Create our namespace, destroy our context and leave that namespace on disk, + // and verify that it is scavenged if we re-create the context without calling + // CreateSessionNamespace. + + // Create, verify we have no data. + std::string namespace_id1 = base::GenerateGUID(); + url::Origin origin1 = url::Origin::Create(GURL("http://foobar.com")); + context()->CreateSessionNamespace(namespace_id1); + + // This scavenge call should NOT delete the namespace, as we just created it. + { + base::RunLoop loop; + context()->ScavengeUnusedNamespaces(loop.QuitClosure()); + loop.Run(); + } + // Restart context. + ShutdownContext(); + context()->CreateSessionNamespace(namespace_id1); + + mojom::SessionStorageNamespacePtr ss_namespace1; + context()->OpenSessionStorage(kTestProcessId, namespace_id1, + mojo::MakeRequest(&ss_namespace1)); + mojom::LevelDBWrapperAssociatedPtr leveldb_n1_o1; + ss_namespace1->OpenArea(origin1, mojo::MakeRequest(&leveldb_n1_o1)); + EXPECT_TRUE(test::PutSync( + leveldb_n1_o1.get(), leveldb::StringPieceToUint8Vector("key1"), + leveldb::StringPieceToUint8Vector("value1"), base::nullopt, "source1")); + + // This scavenge call should NOT delete the namespace, as we never called + // delete. + context()->ScavengeUnusedNamespaces(base::OnceClosure()); + + // Restart context. + ShutdownContext(); + context()->CreateSessionNamespace(namespace_id1); + + // Delete the namespace and shutdown the context, BUT persist the namespace so + // it can be loaded again. + context()->DeleteSessionNamespace(namespace_id1, true); + + // This scavenge call should NOT delete the namespace, as we explicity + // persisted the namespace. + { + base::RunLoop loop; + context()->ScavengeUnusedNamespaces(loop.QuitClosure()); + loop.Run(); + } + + ShutdownContext(); + + // Re-open the context, load the persisted namespace, and verify we still have + // data. + context()->CreateSessionNamespace(namespace_id1); + context()->OpenSessionStorage(kTestProcessId, namespace_id1, + mojo::MakeRequest(&ss_namespace1)); + ss_namespace1->OpenArea(origin1, mojo::MakeRequest(&leveldb_n1_o1)); + std::vector data; + DatabaseError status = test::GetAllSync(leveldb_n1_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(1ul, data.size()); + + // Shutting down the context without an explicit DeleteSessionNamespace should + // leave the data on disk. + ShutdownContext(); + + // Re-open the context, and scavenge should now remove the namespace as there + // has been no call to CreateSessionNamespace. Check the data is empty. + { + base::RunLoop loop; + context()->ScavengeUnusedNamespaces(loop.QuitClosure()); + loop.Run(); + } + context()->CreateSessionNamespace(namespace_id1); + context()->OpenSessionStorage(kTestProcessId, namespace_id1, + mojo::MakeRequest(&ss_namespace1)); + ss_namespace1->OpenArea(origin1, mojo::MakeRequest(&leveldb_n1_o1)); + status = test::GetAllSync(leveldb_n1_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(0ul, data.size()); +} + +TEST_F(SessionStorageContextMojoTest, InvalidVersionOnDisk) { + std::string namespace_id = base::GenerateGUID(); + url::Origin origin = url::Origin::Create(GURL("http://foobar.com")); + + // Create context and add some data to it (and check it's there). + DoTestPut(namespace_id, origin, "key", "value", "source"); + base::Optional> opt_value = + DoTestGet(namespace_id, origin, "key"); + ASSERT_TRUE(opt_value); + EXPECT_EQ(leveldb::StringPieceToUint8Vector("value"), opt_value.value()); + + ShutdownContext(); + { + // Mess up version number in database. + leveldb_env::ChromiumEnv env; + std::unique_ptr db; + leveldb_env::Options options; + options.env = &env; + base::FilePath db_path = + temp_path().Append(FILE_PATH_LITERAL("Session Storage")); + ASSERT_TRUE(leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db).ok()); + ASSERT_TRUE(db->Put(leveldb::WriteOptions(), "version", "argh").ok()); + } + + opt_value = DoTestGet(namespace_id, origin, "key"); + EXPECT_FALSE(opt_value); + + // Write data again. + DoTestPut(namespace_id, origin, "key", "value", "source"); + + ShutdownContext(); + + // Data should have been preserved now. + opt_value = DoTestGet(namespace_id, origin, "key"); + ASSERT_TRUE(opt_value); + EXPECT_EQ(leveldb::StringPieceToUint8Vector("value"), opt_value.value()); + ShutdownContext(); +} + +TEST_F(SessionStorageContextMojoTest, CorruptionOnDisk) { + std::string namespace_id = base::GenerateGUID(); + url::Origin origin = url::Origin::Create(GURL("http://foobar.com")); + + // Create context and add some data to it (and check it's there). + DoTestPut(namespace_id, origin, "key", "value", "source"); + base::Optional> opt_value = + DoTestGet(namespace_id, origin, "key"); + ASSERT_TRUE(opt_value); + EXPECT_EQ(leveldb::StringPieceToUint8Vector("value"), opt_value.value()); + + ShutdownContext(); + // Also flush Task Scheduler tasks to make sure the leveldb is fully closed. + content::RunAllTasksUntilIdle(); + + // Delete manifest files to mess up opening DB. + base::FilePath db_path = + temp_path().Append(FILE_PATH_LITERAL("Session Storage")); + base::FileEnumerator file_enum(db_path, true, base::FileEnumerator::FILES, + FILE_PATH_LITERAL("MANIFEST*")); + for (base::FilePath name = file_enum.Next(); !name.empty(); + name = file_enum.Next()) { + base::DeleteFile(name, false); + } + opt_value = DoTestGet(namespace_id, origin, "key"); + EXPECT_FALSE(opt_value); + + // Write data again. + DoTestPut(namespace_id, origin, "key", "value", "source"); + + ShutdownContext(); + + // Data should have been preserved now. + opt_value = DoTestGet(namespace_id, origin, "key"); + ASSERT_TRUE(opt_value); + EXPECT_EQ(leveldb::StringPieceToUint8Vector("value"), opt_value.value()); + ShutdownContext(); +} + +TEST_F(SessionStorageContextMojoTest, RecreateOnCommitFailure) { + std::string namespace_id = base::GenerateGUID(); + url::Origin origin1 = url::Origin::Create(GURL("http://foobar.com")); + url::Origin origin2 = url::Origin::Create(GURL("http://asf.com")); + url::Origin origin3 = url::Origin::Create(GURL("http://example.com")); + + test::FakeLevelDBService fake_leveldb_service; + ResetFileServiceAndConnector( + service_manager::TestServiceDecorator::CreateServiceWithUniqueOverride( + file::CreateFileService(), leveldb::mojom::LevelDBService::Name_, + base::BindRepeating(&test::FakeLevelDBService::Bind, + base::Unretained(&fake_leveldb_service)))); + + // Open three connections to the database. + mojom::LevelDBWrapperAssociatedPtr wrapper1; + mojom::LevelDBWrapperAssociatedPtr wrapper2; + mojom::LevelDBWrapperAssociatedPtr wrapper3; + mojom::SessionStorageNamespacePtr ss_namespace; + context()->CreateSessionNamespace(namespace_id); + { + base::RunLoop loop; + fake_leveldb_service.SetOnOpenCallback(loop.QuitClosure()); + context()->OpenSessionStorage(kTestProcessId, namespace_id, + mojo::MakeRequest(&ss_namespace)); + ss_namespace->OpenArea(origin1, mojo::MakeRequest(&wrapper1)); + ss_namespace->OpenArea(origin2, mojo::MakeRequest(&wrapper2)); + ss_namespace->OpenArea(origin3, mojo::MakeRequest(&wrapper3)); + loop.Run(); + } + + // Add observers to the first two connections. + testing::StrictMock observer1; + wrapper1->AddObserver(observer1.Bind()); + EXPECT_FALSE(wrapper1.encountered_error()); + testing::StrictMock observer3; + wrapper3->AddObserver(observer3.Bind()); + + // Verify one attempt was made to open the database, and connect that request + // with a database implementation that always fails on write. + ASSERT_EQ(1u, fake_leveldb_service.open_requests().size()); + auto& open_request = fake_leveldb_service.open_requests()[0]; + std::map, std::vector> test_data; + auto mock_db = mojo::MakeStrongAssociatedBinding( + std::make_unique(&test_data), + std::move(open_request.request)); + std::move(open_request.callback).Run(leveldb::mojom::DatabaseError::OK); + fake_leveldb_service.open_requests().clear(); + + // Setup a RunLoop so we can wait until LocalStorageContextMojo tries to + // reconnect to the database, which should happen after several commit + // errors. + base::RunLoop reopen_loop; + fake_leveldb_service.SetOnOpenCallback(reopen_loop.QuitClosure()); + + // Start a put operation on the third connection before starting to commit + // a lot of data on the first origin. This put operation should result in a + // pending commit that will get cancelled when the database connection is + // closed. + auto value = leveldb::StringPieceToUint8Vector("avalue"); + EXPECT_CALL(observer3, KeyAdded(leveldb::StringPieceToUint8Vector("w3key"), + value, "source")) + .Times(1); + wrapper3->Put(leveldb::StringPieceToUint8Vector("w3key"), value, + base::nullopt, "source", + base::BindOnce([](bool success) { EXPECT_TRUE(success); })); + + // Repeatedly write data to the database, to trigger enough commit errors. + int i = 0; + while (!wrapper1.encountered_error()) { + ++i; + base::RunLoop put_loop; + // Every write needs to be different to make sure there actually is a + // change to commit. + std::vector old_value = value; + value[0]++; + wrapper1.set_connection_error_handler(put_loop.QuitClosure()); + + if (i == 1) { + EXPECT_CALL(observer1, ShouldSendOldValueOnMutations(false)).Times(1); + EXPECT_CALL(observer1, KeyAdded(leveldb::StringPieceToUint8Vector("key"), + value, "source")) + .Times(1); + } else { + EXPECT_CALL(observer1, + KeyChanged(leveldb::StringPieceToUint8Vector("key"), value, + old_value, "source")) + .Times(1); + } + wrapper1->Put(leveldb::StringPieceToUint8Vector("key"), value, + base::nullopt, "source", + base::BindLambdaForTesting([&](bool success) { + EXPECT_TRUE(success); + put_loop.Quit(); + })); + wrapper1.FlushForTesting(); + put_loop.RunUntilIdle(); + // And we need to flush after every change. Otherwise changes get batched up + // and only one commit is done some time later. + context()->FlushAreaForTesting(namespace_id, origin1); + } + // Make sure all messages to the DB have been processed (Flush above merely + // schedules a commit, but there is no guarantee about those having been + // processed yet). + if (mock_db) + mock_db->FlushForTesting(); + // At this point enough commit failures should have happened to cause the + // connection to the database to have been severed. + EXPECT_FALSE(mock_db); + + // The connection to the second wrapper should have closed as well. + EXPECT_TRUE(wrapper2.encountered_error()); + EXPECT_TRUE(ss_namespace.encountered_error()); + + // And the old database should have been destroyed. + EXPECT_EQ(1u, fake_leveldb_service.destroy_requests().size()); + + // Reconnect wrapper1 to the database, and try to read a value. + context()->OpenSessionStorage(kTestProcessId, namespace_id, + mojo::MakeRequest(&ss_namespace)); + ss_namespace->OpenArea(origin1, mojo::MakeRequest(&wrapper1)); + + base::RunLoop delete_loop; + bool success = true; + test::MockLevelDBObserver observer4; + wrapper1->AddObserver(observer4.Bind()); + wrapper1->Delete(leveldb::StringPieceToUint8Vector("key"), base::nullopt, + "source", base::BindLambdaForTesting([&](bool success_in) { + success = success_in; + delete_loop.Quit(); + })); + + // Wait for LocalStorageContextMojo to try to reconnect to the database, and + // connect that new request to a properly functioning database. + reopen_loop.Run(); + ASSERT_EQ(1u, fake_leveldb_service.open_requests().size()); + auto& reopen_request = fake_leveldb_service.open_requests()[0]; + mock_db = mojo::MakeStrongAssociatedBinding( + std::make_unique(&test_data), + std::move(reopen_request.request)); + std::move(reopen_request.callback).Run(leveldb::mojom::DatabaseError::OK); + fake_leveldb_service.open_requests().clear(); + + // And deleting the value from the new wrapper should have failed (as the + // database is empty). + delete_loop.Run(); + wrapper1 = nullptr; + ss_namespace.reset(); + context()->DeleteSessionNamespace(namespace_id, true); + + { + // Committing data should now work. + DoTestPut(namespace_id, origin1, "key", "value", "source"); + base::Optional> opt_value = + DoTestGet(namespace_id, origin1, "key"); + ASSERT_TRUE(opt_value); + EXPECT_EQ(leveldb::StringPieceToUint8Vector("value"), opt_value.value()); + } +} + +TEST_F(SessionStorageContextMojoTest, DontRecreateOnRepeatedCommitFailure) { + std::string namespace_id = base::GenerateGUID(); + url::Origin origin1 = url::Origin::Create(GURL("http://foobar.com")); + + test::FakeLevelDBService fake_leveldb_service; + ResetFileServiceAndConnector( + service_manager::TestServiceDecorator::CreateServiceWithUniqueOverride( + file::CreateFileService(), leveldb::mojom::LevelDBService::Name_, + base::BindRepeating(&test::FakeLevelDBService::Bind, + base::Unretained(&fake_leveldb_service)))); + + std::map, std::vector> test_data; + + // Open three connections to the database. + mojom::LevelDBWrapperAssociatedPtr wrapper; + mojom::SessionStorageNamespacePtr ss_namespace; + context()->CreateSessionNamespace(namespace_id); + { + base::RunLoop loop; + fake_leveldb_service.SetOnOpenCallback(loop.QuitClosure()); + context()->OpenSessionStorage(kTestProcessId, namespace_id, + mojo::MakeRequest(&ss_namespace)); + ss_namespace->OpenArea(origin1, mojo::MakeRequest(&wrapper)); + loop.Run(); + } + + // Verify one attempt was made to open the database, and connect that request + // with a database implementation that always fails on write. + ASSERT_EQ(1u, fake_leveldb_service.open_requests().size()); + auto& open_request = fake_leveldb_service.open_requests()[0]; + auto mock_db = mojo::MakeStrongAssociatedBinding( + std::make_unique(&test_data), + std::move(open_request.request)); + std::move(open_request.callback).Run(leveldb::mojom::DatabaseError::OK); + fake_leveldb_service.open_requests().clear(); + + // Setup a RunLoop so we can wait until LocalStorageContextMojo tries to + // reconnect to the database, which should happen after several commit + // errors. + base::RunLoop reopen_loop; + fake_leveldb_service.SetOnOpenCallback(reopen_loop.QuitClosure()); + + // Repeatedly write data to the database, to trigger enough commit errors. + auto value = leveldb::StringPieceToUint8Vector("avalue"); + base::Optional> old_value = base::nullopt; + while (!wrapper.encountered_error()) { + base::RunLoop put_loop; + // Every write needs to be different to make sure there actually is a + // change to commit. + wrapper.set_connection_error_handler(put_loop.QuitClosure()); + wrapper->Put(leveldb::StringPieceToUint8Vector("key"), value, old_value, + "source", base::BindLambdaForTesting([&](bool success) { + EXPECT_TRUE(success); + put_loop.Quit(); + })); + wrapper.FlushForTesting(); + put_loop.RunUntilIdle(); + // And we need to flush after every change. Otherwise changes get batched up + // and only one commit is done some time later. + context()->FlushAreaForTesting(namespace_id, origin1); + + old_value = value; + value[0]++; + } + // Make sure all messages to the DB have been processed (Flush above merely + // schedules a commit, but there is no guarantee about those having been + // processed yet). + if (mock_db) + mock_db->FlushForTesting(); + // At this point enough commit failures should have happened to cause the + // connection to the database to have been severed. + EXPECT_FALSE(mock_db); + + // Wait for LocalStorageContextMojo to try to reconnect to the database, and + // connect that new request with a database implementation that always fails + // on write. + reopen_loop.Run(); + ASSERT_EQ(1u, fake_leveldb_service.open_requests().size()); + auto& reopen_request = fake_leveldb_service.open_requests()[0]; + mock_db = mojo::MakeStrongAssociatedBinding( + std::make_unique(&test_data), + std::move(reopen_request.request)); + std::move(reopen_request.callback).Run(leveldb::mojom::DatabaseError::OK); + fake_leveldb_service.open_requests().clear(); + + // The old database should also have been destroyed. + EXPECT_EQ(1u, fake_leveldb_service.destroy_requests().size()); + + // Reconnect a wrapper to the database, and repeatedly write data to it again. + // This time all should just keep getting written, and commit errors are + // getting ignored. + context()->OpenSessionStorage(kTestProcessId, namespace_id, + mojo::MakeRequest(&ss_namespace)); + ss_namespace->OpenArea(origin1, mojo::MakeRequest(&wrapper)); + old_value = base::nullopt; + for (int i = 0; i < 64; ++i) { + base::RunLoop put_loop; + // Every write needs to be different to make sure there actually is a + // change to commit. + wrapper.set_connection_error_handler(put_loop.QuitClosure()); + wrapper->Put(leveldb::StringPieceToUint8Vector("key"), value, old_value, + "source", base::BindLambdaForTesting([&](bool success) { + EXPECT_TRUE(success); + put_loop.Quit(); + })); + wrapper.FlushForTesting(); + put_loop.RunUntilIdle(); + // And we need to flush after every change. Otherwise changes get batched up + // and only one commit is done some time later. + context()->FlushAreaForTesting(namespace_id, origin1); + + old_value = value; + value[0]++; + } + // Make sure all messages to the DB have been processed (Flush above merely + // schedules a commit, but there is no guarantee about those having been + // processed yet). + if (mock_db) + mock_db->FlushForTesting(); + EXPECT_TRUE(mock_db); + EXPECT_FALSE(wrapper.encountered_error()); + + context()->DeleteSessionNamespace(namespace_id, false); +} + +} // namespace +} // namespace content diff --git a/chromium/content/browser/dom_storage/session_storage_data_map.cc b/chromium/content/browser/dom_storage/session_storage_data_map.cc new file mode 100644 index 00000000000..4580e195381 --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_data_map.cc @@ -0,0 +1,105 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/session_storage_data_map.h" + +#include "base/sys_info.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "content/common/dom_storage/dom_storage_types.h" + +namespace content { + +// static +scoped_refptr SessionStorageDataMap::Create( + Listener* listener, + scoped_refptr map_data, + leveldb::mojom::LevelDBDatabase* database) { + return base::WrapRefCounted( + new SessionStorageDataMap(listener, std::move(map_data), database)); +} + +// static +scoped_refptr SessionStorageDataMap::CreateClone( + Listener* listener, + scoped_refptr map_data, + LevelDBWrapperImpl* clone_from) { + return base::WrapRefCounted( + new SessionStorageDataMap(listener, std::move(map_data), clone_from)); +} + +std::vector +SessionStorageDataMap::PrepareToCommit() { + return std::vector(); +} + +void SessionStorageDataMap::DidCommit(leveldb::mojom::DatabaseError error) { + listener_->OnCommitResult(error); +} + +SessionStorageDataMap::SessionStorageDataMap( + Listener* listener, + scoped_refptr map_data, + leveldb::mojom::LevelDBDatabase* database) + : listener_(listener), + map_data_(std::move(map_data)), + wrapper_impl_(std::make_unique(database, + map_data_->KeyPrefix(), + this, + GetOptions())), + level_db_wrapper_ptr_(wrapper_impl_.get()) { + DCHECK(listener_); + DCHECK(map_data_); + listener_->OnDataMapCreation(map_data_->MapNumberAsBytes(), this); +} + +SessionStorageDataMap::SessionStorageDataMap( + Listener* listener, + scoped_refptr map_data, + LevelDBWrapperImpl* forking_from) + : listener_(listener), + map_data_(std::move(map_data)), + wrapper_impl_(forking_from->ForkToNewPrefix(map_data_->KeyPrefix(), + this, + GetOptions())), + level_db_wrapper_ptr_(wrapper_impl_.get()) { + DCHECK(listener_); + DCHECK(map_data_); + listener_->OnDataMapCreation(map_data_->MapNumberAsBytes(), this); +} + +SessionStorageDataMap::~SessionStorageDataMap() { + listener_->OnDataMapDestruction(map_data_->MapNumberAsBytes()); +} + +void SessionStorageDataMap::RemoveBindingReference() { + DCHECK_GT(binding_count_, 0); + --binding_count_; + if (binding_count_ > 0) + return; + // Don't delete ourselves, but do schedule an immediate commit. Possible + // deletion will happen under memory pressure or when another sessionstorage + // area is opened. + level_db_wrapper()->ScheduleImmediateCommit(); +} + +// static +LevelDBWrapperImpl::Options SessionStorageDataMap::GetOptions() { + // Delay for a moment after a value is set in anticipation + // of other values being set, so changes are batched. + constexpr const base::TimeDelta kCommitDefaultDelaySecs = + base::TimeDelta::FromSeconds(5); + + // To avoid excessive IO we apply limits to the amount of data being + // written and the frequency of writes. + LevelDBWrapperImpl::Options options; + options.max_size = kPerStorageAreaQuota + kPerStorageAreaOverQuotaAllowance; + options.default_commit_delay = kCommitDefaultDelaySecs; + options.max_bytes_per_hour = kPerStorageAreaQuota; + options.max_commits_per_hour = 60; + options.cache_mode = LevelDBWrapperImpl::CacheMode::KEYS_ONLY_WHEN_POSSIBLE; + return options; +} + +} // namespace content diff --git a/chromium/content/browser/dom_storage/session_storage_data_map.h b/chromium/content/browser/dom_storage/session_storage_data_map.h new file mode 100644 index 00000000000..b4f60811a9a --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_data_map.h @@ -0,0 +1,107 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_DATA_MAP_H_ +#define CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_DATA_MAP_H_ + +#include +#include + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/optional.h" +#include "content/browser/dom_storage/session_storage_metadata.h" +#include "content/browser/leveldb_wrapper_impl.h" +#include "content/common/content_export.h" +#include "content/common/leveldb_wrapper.mojom.h" +#include "mojo/public/cpp/bindings/associated_binding.h" + +namespace content { + +// Holds the LevelDBWrapper for a session storage data map. Every +// namespace-origin area has a data map. To support shallow copying of the data +// (copy-on-write), a single data map can be shared between multiple namespaces. +// Thus this class is refcounted. This class has a one-to-one relationship with +// the SessionStorageMetadata::MapData object, accessible from |map_data()|. +// +// Neither this data map nor the inner LevelDBWrapper is bound to, as it needs +// to be shared between multiple connections if it is shallow-copied. However, +// it does allow it's user to keep track of the number of binding using +// |binding_count()|, |AddBindingReference()|, and |RemoveBindingReference()|. +class CONTENT_EXPORT SessionStorageDataMap final + : public LevelDBWrapperImpl::Delegate, + public base::RefCounted { + public: + class CONTENT_EXPORT Listener { + public: + virtual ~Listener() {} + virtual void OnDataMapCreation(const std::vector& map_id, + SessionStorageDataMap* map) = 0; + virtual void OnDataMapDestruction(const std::vector& map_id) = 0; + virtual void OnCommitResult(leveldb::mojom::DatabaseError error) = 0; + }; + + static scoped_refptr Create( + Listener* listener, + scoped_refptr map_data, + leveldb::mojom::LevelDBDatabase* database); + + static scoped_refptr CreateClone( + Listener* listener, + scoped_refptr map_data, + LevelDBWrapperImpl* clone_from); + + Listener* listener() const { return listener_; } + + LevelDBWrapperImpl* level_db_wrapper() { return level_db_wrapper_ptr_; } + + scoped_refptr map_data() { + return map_data_.get(); + } + + int binding_count() { return binding_count_; } + void AddBindingReference() { ++binding_count_; } + // When the binding count reaches 0, we schedule an immediate commit on our + // wrapper, but we don't close the connection. + void RemoveBindingReference(); + + // Note: this is irrelevant, as the parent wrapper is handling binding. + void OnNoBindings() override {} + + std::vector PrepareToCommit() override; + + void DidCommit(leveldb::mojom::DatabaseError error) override; + + private: + friend class base::RefCounted; + + SessionStorageDataMap( + Listener* listener, + scoped_refptr map_entry, + leveldb::mojom::LevelDBDatabase* database); + SessionStorageDataMap( + Listener* listener, + scoped_refptr map_entry, + LevelDBWrapperImpl* forking_from); + ~SessionStorageDataMap() override; + + static LevelDBWrapperImpl::Options GetOptions(); + + Listener* listener_; + int binding_count_ = 0; + scoped_refptr map_data_; + std::unique_ptr wrapper_impl_; + // Holds the same value as |wrapper_impl_|. The reason for this is that + // during destruction of the LevelDBWrapperImpl instance we might still get + // called and need access to the LevelDBWrapperImpl instance. The + // unique_ptr could already be null, but this field should still be valid. + // TODO(dmurph): Change delegate ownership so this doesn't have to be done. + LevelDBWrapperImpl* level_db_wrapper_ptr_; + + DISALLOW_COPY_AND_ASSIGN(SessionStorageDataMap); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_DATA_MAP_H_ diff --git a/chromium/content/browser/dom_storage/session_storage_data_map_unittest.cc b/chromium/content/browser/dom_storage/session_storage_data_map_unittest.cc new file mode 100644 index 00000000000..97af433c270 --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_data_map_unittest.cc @@ -0,0 +1,186 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/session_storage_data_map.h" + +#include +#include + +#include "base/memory/ptr_util.h" +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "components/services/leveldb/public/cpp/util.h" +#include "content/test/fake_leveldb_database.h" +#include "mojo/public/cpp/bindings/strong_associated_binding.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace content { +namespace { +using leveldb::StdStringToUint8Vector; +using leveldb::Uint8VectorToStdString; + +class MockListener : public SessionStorageDataMap::Listener { + public: + MockListener() {} + ~MockListener() override {} + MOCK_METHOD2(OnDataMapCreation, + void(const std::vector& map_id, + SessionStorageDataMap* map)); + MOCK_METHOD1(OnDataMapDestruction, void(const std::vector& map_id)); + MOCK_METHOD1(OnCommitResult, void(leveldb::mojom::DatabaseError error)); +}; + +void GetAllDataCallback(leveldb::mojom::DatabaseError* status_out, + std::vector* data_out, + leveldb::mojom::DatabaseError status, + std::vector data) { + *status_out = status; + *data_out = std::move(data); +} + +base::OnceCallback data)> +MakeGetAllCallback(leveldb::mojom::DatabaseError* status_out, + std::vector* data_out) { + return base::BindOnce(&GetAllDataCallback, status_out, data_out); +} + +class GetAllCallback : public mojom::LevelDBWrapperGetAllCallback { + public: + static mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo CreateAndBind( + bool* result, + base::OnceClosure callback) { + mojom::LevelDBWrapperGetAllCallbackAssociatedPtr ptr; + auto request = mojo::MakeRequestAssociatedWithDedicatedPipe(&ptr); + mojo::MakeStrongAssociatedBinding( + base::WrapUnique(new GetAllCallback(result, std::move(callback))), + std::move(request)); + return ptr.PassInterface(); + } + + private: + GetAllCallback(bool* result, base::OnceClosure callback) + : result_(result), callback_(std::move(callback)) {} + void Complete(bool success) override { + *result_ = success; + if (callback_) + std::move(callback_).Run(); + } + + bool* result_; + base::OnceClosure callback_; +}; + +class SessionStorageDataMapTest : public testing::Test { + public: + SessionStorageDataMapTest() + : test_origin_(url::Origin::Create(GURL("http://host1.com:1"))), + database_(&mock_data_) { + // Should show up in first map. + mock_data_[StdStringToUint8Vector("map-1-key1")] = + StdStringToUint8Vector("data1"); + // Dummy data to verify we don't delete everything. + mock_data_[StdStringToUint8Vector("map-3-key1")] = + StdStringToUint8Vector("data3"); + } + ~SessionStorageDataMapTest() override {} + + protected: + base::test::ScopedTaskEnvironment task_environment_; + testing::StrictMock listener_; + url::Origin test_origin_; + std::map, std::vector> mock_data_; + FakeLevelDBDatabase database_; +}; + +} // namespace + +TEST_F(SessionStorageDataMapTest, BasicEmptyCreation) { + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("1"), testing::_)) + .Times(1); + + scoped_refptr map = SessionStorageDataMap::Create( + &listener_, + base::MakeRefCounted(1, test_origin_), + &database_); + + leveldb::mojom::DatabaseError status; + std::vector data; + bool done = false; + base::RunLoop loop; + map->level_db_wrapper()->GetAll( + GetAllCallback::CreateAndBind(&done, loop.QuitClosure()), + MakeGetAllCallback(&status, &data)); + loop.Run(); + + EXPECT_TRUE(done); + ASSERT_EQ(1u, data.size()); + EXPECT_EQ(StdStringToUint8Vector("key1"), data[0]->key); + EXPECT_EQ(StdStringToUint8Vector("data1"), data[0]->value); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1"))) + .Times(1); + + // Test data is not cleared on deletion. + map = nullptr; + EXPECT_EQ(2u, mock_data_.size()); +} + +TEST_F(SessionStorageDataMapTest, Clone) { + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("1"), testing::_)) + .Times(1); + + scoped_refptr map1 = SessionStorageDataMap::Create( + &listener_, + base::MakeRefCounted(1, test_origin_), + &database_); + + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("2"), testing::_)) + .Times(1); + // One call on fork. + EXPECT_CALL(listener_, OnCommitResult(leveldb::mojom::DatabaseError::OK)) + .Times(1); + + scoped_refptr map2 = + SessionStorageDataMap::CreateClone( + &listener_, + base::MakeRefCounted(2, + test_origin_), + map1->level_db_wrapper()); + + leveldb::mojom::DatabaseError status; + std::vector data; + bool done = false; + base::RunLoop loop; + map2->level_db_wrapper()->GetAll( + GetAllCallback::CreateAndBind(&done, loop.QuitClosure()), + MakeGetAllCallback(&status, &data)); + loop.Run(); + + EXPECT_TRUE(done); + ASSERT_EQ(1u, data.size()); + EXPECT_EQ(StdStringToUint8Vector("key1"), data[0]->key); + EXPECT_EQ(StdStringToUint8Vector("data1"), data[0]->value); + + // Test that the data was copied. + EXPECT_EQ(StdStringToUint8Vector("data1"), + mock_data_[StdStringToUint8Vector("map-2-key1")]); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1"))) + .Times(1); + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("2"))) + .Times(1); + + // Test data is not cleared on deletion. + map1 = nullptr; + map2 = nullptr; + EXPECT_EQ(3u, mock_data_.size()); +} + +} // namespace content diff --git a/chromium/content/browser/dom_storage/session_storage_database.cc b/chromium/content/browser/dom_storage/session_storage_database.cc index ef35996152a..8af5497c82c 100644 --- a/chromium/content/browser/dom_storage/session_storage_database.cc +++ b/chromium/content/browser/dom_storage/session_storage_database.cc @@ -191,6 +191,7 @@ bool SessionStorageDatabase::CommitAreaChanges( if (!GetMapForArea(namespace_id, origin.GetURL().spec(), leveldb::ReadOptions(), &exists, &map_id)) return false; + if (exists) { int64_t ref_count; if (!GetMapRefCount(map_id, &ref_count)) @@ -398,6 +399,12 @@ void SessionStorageDatabase::OnMemoryDump( tracker_dump->GetSizeInternal()); } +void SessionStorageDatabase::SetDatabaseForTesting( + std::unique_ptr db) { + CHECK(!db_); + db_ = std::move(db); +} + bool SessionStorageDatabase::LazyOpen(bool create_if_needed) { base::AutoLock auto_lock(db_lock_); if (db_error_ || is_inconsistent_) { diff --git a/chromium/content/browser/dom_storage/session_storage_database.h b/chromium/content/browser/dom_storage/session_storage_database.h index 842213cd608..e1ec30e83d3 100644 --- a/chromium/content/browser/dom_storage/session_storage_database.h +++ b/chromium/content/browser/dom_storage/session_storage_database.h @@ -99,6 +99,12 @@ class CONTENT_EXPORT SessionStorageDatabase // Adds memory statistics to |pmd| for chrome://tracing. void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd); + // Used in testing to set an easier to handle in-memory database. Should + // happen before any database operations. + void SetDatabaseForTesting(std::unique_ptr db); + + leveldb::DB* db() const { return db_.get(); } + private: class DBOperation; friend class SessionStorageDatabase::DBOperation; diff --git a/chromium/content/browser/dom_storage/session_storage_leveldb_wrapper.cc b/chromium/content/browser/dom_storage/session_storage_leveldb_wrapper.cc new file mode 100644 index 00000000000..2e20a45d2c3 --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_leveldb_wrapper.cc @@ -0,0 +1,165 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/session_storage_leveldb_wrapper.h" + +#include "base/bind.h" +#include "base/metrics/histogram_macros.h" +#include "content/browser/dom_storage/session_storage_data_map.h" +#include "content/common/dom_storage/dom_storage_types.h" +#include "third_party/leveldatabase/env_chromium.h" + +namespace content { + +SessionStorageLevelDBWrapper::SessionStorageLevelDBWrapper( + SessionStorageMetadata::NamespaceEntry namespace_entry, + url::Origin origin, + scoped_refptr data_map, + RegisterNewAreaMap register_new_map_callback) + : namespace_entry_(namespace_entry), + origin_(std::move(origin)), + shared_data_map_(std::move(data_map)), + register_new_map_callback_(std::move(register_new_map_callback)), + binding_(this) {} + +SessionStorageLevelDBWrapper::~SessionStorageLevelDBWrapper() { + if (binding_.is_bound()) + shared_data_map_->RemoveBindingReference(); +} + +void SessionStorageLevelDBWrapper::Bind( + mojom::LevelDBWrapperAssociatedRequest request) { + DCHECK(!IsBound()); + shared_data_map_->AddBindingReference(); + binding_.Bind(std::move(request)); + binding_.set_connection_error_handler( + base::BindOnce(&SessionStorageLevelDBWrapper::OnConnectionError, + base::Unretained(this))); +} + +std::unique_ptr +SessionStorageLevelDBWrapper::Clone( + SessionStorageMetadata::NamespaceEntry namespace_entry) { + DCHECK(namespace_entry_ != namespace_entry); + return base::WrapUnique(new SessionStorageLevelDBWrapper( + namespace_entry, origin_, shared_data_map_, register_new_map_callback_)); +} + +// LevelDBWrapper: +void SessionStorageLevelDBWrapper::AddObserver( + mojom::LevelDBObserverAssociatedPtrInfo observer) { + mojom::LevelDBObserverAssociatedPtr observer_ptr; + observer_ptr.Bind(std::move(observer)); + mojo::InterfacePtrSetElementId ptr_id = + shared_data_map_->level_db_wrapper()->AddObserver( + std::move(observer_ptr)); + observer_ptrs_.push_back(ptr_id); +} + +void SessionStorageLevelDBWrapper::Put( + const std::vector& key, + const std::vector& value, + const base::Optional>& client_old_value, + const std::string& source, + PutCallback callback) { + DCHECK(IsBound()); + DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount()); + if (shared_data_map_->map_data()->ReferenceCount() > 1) + CreateNewMap(NewMapType::FORKED, base::nullopt); + shared_data_map_->level_db_wrapper()->Put(key, value, client_old_value, + source, std::move(callback)); +} + +void SessionStorageLevelDBWrapper::Delete( + const std::vector& key, + const base::Optional>& client_old_value, + const std::string& source, + DeleteCallback callback) { + DCHECK(IsBound()); + DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount()); + if (shared_data_map_->map_data()->ReferenceCount() > 1) + CreateNewMap(NewMapType::FORKED, base::nullopt); + shared_data_map_->level_db_wrapper()->Delete(key, client_old_value, source, + std::move(callback)); +} + +void SessionStorageLevelDBWrapper::DeleteAll(const std::string& source, + DeleteAllCallback callback) { + DCHECK(IsBound()); + DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount()); + if (shared_data_map_->map_data()->ReferenceCount() > 1) { + CreateNewMap(NewMapType::EMPTY_FROM_DELETE_ALL, source); + std::move(callback).Run(true); + return; + } + shared_data_map_->level_db_wrapper()->DeleteAll(source, std::move(callback)); +} + +void SessionStorageLevelDBWrapper::Get(const std::vector& key, + GetCallback callback) { + DCHECK(IsBound()); + DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount()); + shared_data_map_->level_db_wrapper()->Get(key, std::move(callback)); +} + +void SessionStorageLevelDBWrapper::GetAll( + mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo complete_callback, + GetAllCallback callback) { + DCHECK(IsBound()); + DCHECK_NE(0, shared_data_map_->map_data()->ReferenceCount()); + shared_data_map_->level_db_wrapper()->GetAll(std::move(complete_callback), + std::move(callback)); +} + +// Note: this can be called after invalidation of the |namespace_entry_|. +void SessionStorageLevelDBWrapper::OnConnectionError() { + shared_data_map_->RemoveBindingReference(); + // Make sure we totally unbind the binding - this doesn't seem to happen + // automatically on connection error. The bound status is used in the + // destructor to know if |RemoveBindingReference| was already called. + if (binding_.is_bound()) + binding_.Unbind(); +} + +void SessionStorageLevelDBWrapper::CreateNewMap( + NewMapType map_type, + const base::Optional& delete_all_source) { + std::vector ptrs_to_move; + for (const mojo::InterfacePtrSetElementId& ptr_id : observer_ptrs_) { + DCHECK(shared_data_map_->level_db_wrapper()->HasObserver(ptr_id)); + ptrs_to_move.push_back( + shared_data_map_->level_db_wrapper()->RemoveObserver(ptr_id)); + } + observer_ptrs_.clear(); + shared_data_map_->RemoveBindingReference(); + switch (map_type) { + case NewMapType::FORKED: + shared_data_map_ = SessionStorageDataMap::CreateClone( + shared_data_map_->listener(), + register_new_map_callback_.Run(namespace_entry_, origin_), + shared_data_map_->level_db_wrapper()); + break; + case NewMapType::EMPTY_FROM_DELETE_ALL: { + // The code optimizes the 'delete all' for shared maps by just creating + // a new map instead of forking. However, we still need the observers to + // be correctly called. To do that, we manually call them here. + shared_data_map_ = SessionStorageDataMap::Create( + shared_data_map_->listener(), + register_new_map_callback_.Run(namespace_entry_, origin_), + shared_data_map_->level_db_wrapper()->database()); + for (auto& ptr : ptrs_to_move) { + ptr->AllDeleted(delete_all_source.value_or("\n")); + } + break; + } + } + shared_data_map_->AddBindingReference(); + + for (auto& observer_ptr : ptrs_to_move) { + observer_ptrs_.push_back(shared_data_map_->level_db_wrapper()->AddObserver( + std::move(observer_ptr))); + } +} + +} // namespace content diff --git a/chromium/content/browser/dom_storage/session_storage_leveldb_wrapper.h b/chromium/content/browser/dom_storage/session_storage_leveldb_wrapper.h new file mode 100644 index 00000000000..0a856c8b05a --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_leveldb_wrapper.h @@ -0,0 +1,106 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_LEVELDB_WRAPPER_H_ +#define CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_LEVELDB_WRAPPER_H_ + +#include + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/optional.h" +#include "content/browser/dom_storage/session_storage_metadata.h" +#include "content/browser/leveldb_wrapper_impl.h" +#include "content/common/content_export.h" +#include "content/common/leveldb_wrapper.mojom.h" +#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" +#include "url/origin.h" + +namespace content { +class SessionStorageDataMap; + +// This class provides session storage access to the renderer by binding to the +// LevelDBWrapper mojom interface. It represents the data stored for a +// namespace-origin area. +// +// This class delegates calls to SessionStorageDataMap objects, and can share +// them with other SessionStorageLevelDBImpl instances to support shallow +// cloning (copy-on-write). This should be done through the |Clone()| method and +// not manually. +// +// During forking, this class is responsible for dealing with moving its +// observers from the SessionStorageDataMap's LevelDBWrapper to the new forked +// SessionStorageDataMap's LevelDBWrapper. +class CONTENT_EXPORT SessionStorageLevelDBWrapper + : public mojom::LevelDBWrapper { + public: + using RegisterNewAreaMap = + base::RepeatingCallback( + SessionStorageMetadata::NamespaceEntry namespace_entry, + const url::Origin& origin)>; + + // Creates a wrapper for the given |namespace_entry|-|origin| data area. All + // LevelDBWrapper calls are delegated to the |data_map|. The + // |register_new_map_callback| is called when a shared |data_map| needs to be + // forked for the copy-on-write behavior and a new map needs to be registered. + SessionStorageLevelDBWrapper( + SessionStorageMetadata::NamespaceEntry namespace_entry, + url::Origin origin, + scoped_refptr data_map, + RegisterNewAreaMap register_new_map_callback); + ~SessionStorageLevelDBWrapper() override; + + // Creates a shallow copy clone for the new namespace entry. + // This doesn't change the refcount of the underlying map - that operation + // must be done using SessionStorageMetadata::RegisterShallowClonedNamespace. + std::unique_ptr Clone( + SessionStorageMetadata::NamespaceEntry namespace_entry); + + void Bind(mojom::LevelDBWrapperAssociatedRequest request); + + bool IsBound() const { return binding_.is_bound(); } + + SessionStorageDataMap* data_map() { return shared_data_map_.get(); } + + // LevelDBWrapper: + void AddObserver(mojom::LevelDBObserverAssociatedPtrInfo observer) override; + void Put(const std::vector& key, + const std::vector& value, + const base::Optional>& client_old_value, + const std::string& source, + PutCallback callback) override; + void Delete(const std::vector& key, + const base::Optional>& client_old_value, + const std::string& source, + DeleteCallback callback) override; + void DeleteAll(const std::string& source, + DeleteAllCallback callback) override; + void Get(const std::vector& key, GetCallback callback) override; + void GetAll( + mojom::LevelDBWrapperGetAllCallbackAssociatedPtrInfo complete_callback, + GetAllCallback callback) override; + + private: + void OnConnectionError(); + + enum class NewMapType { FORKED, EMPTY_FROM_DELETE_ALL }; + + void CreateNewMap(NewMapType map_type, + const base::Optional& delete_all_source); + + SessionStorageMetadata::NamespaceEntry namespace_entry_; + url::Origin origin_; + scoped_refptr shared_data_map_; + RegisterNewAreaMap register_new_map_callback_; + + std::vector observer_ptrs_; + mojo::AssociatedBinding binding_; + + DISALLOW_COPY_AND_ASSIGN(SessionStorageLevelDBWrapper); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_LEVELDB_WRAPPER_H_ diff --git a/chromium/content/browser/dom_storage/session_storage_leveldb_wrapper_unittest.cc b/chromium/content/browser/dom_storage/session_storage_leveldb_wrapper_unittest.cc new file mode 100644 index 00000000000..6f768e0a050 --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_leveldb_wrapper_unittest.cc @@ -0,0 +1,376 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/session_storage_leveldb_wrapper.h" + +#include "base/barrier_closure.h" +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/guid.h" +#include "base/memory/ptr_util.h" +#include "base/run_loop.h" +#include "base/sequenced_task_runner.h" +#include "base/task_scheduler/post_task.h" +#include "base/test/bind_test_util.h" +#include "base/test/scoped_task_environment.h" +#include "base/threading/thread.h" +#include "components/services/leveldb/leveldb_service_impl.h" +#include "components/services/leveldb/public/cpp/util.h" +#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "content/browser/dom_storage/session_storage_data_map.h" +#include "content/browser/dom_storage/session_storage_metadata.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/test/fake_leveldb_database.h" +#include "content/test/gmock_util.h" +#include "content/test/leveldb_wrapper_test_util.h" +#include "mojo/public/cpp/bindings/strong_associated_binding.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/strong_binding_set.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace content { +namespace { +using leveldb::StdStringToUint8Vector; +using leveldb::Uint8VectorToStdString; +using leveldb::mojom::DatabaseError; + +template +void CreateStrongBindingOnTaskRunner( + scoped_refptr runner, + mojo::InterfacePtr* interface_ptr, + std::unique_ptr interface) { + runner->PostTask( + FROM_HERE, + base::BindOnce( + base::IgnoreResult(&mojo::MakeStrongBinding), + std::move(interface), mojo::MakeRequest(interface_ptr))); +} + +class MockListener : public SessionStorageDataMap::Listener { + public: + MockListener() = default; + ~MockListener() override = default; + MOCK_METHOD2(OnDataMapCreation, + void(const std::vector& map_id, + SessionStorageDataMap* map)); + MOCK_METHOD1(OnDataMapDestruction, void(const std::vector& map_id)); + MOCK_METHOD1(OnCommitResult, void(DatabaseError error)); +}; + +class SessionStorageLevelDBWrapperTest : public testing::Test { + public: + SessionStorageLevelDBWrapperTest() + : test_namespace_id1_(base::GenerateGUID()), + test_namespace_id2_(base::GenerateGUID()), + test_origin1_(url::Origin::Create(GURL("https://host1.com:1/"))), + test_origin2_(url::Origin::Create(GURL("https://host2.com:2/"))) { + auto file_runner = + base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}); + CreateStrongBindingOnTaskRunner( + base::CreateSequencedTaskRunnerWithTraits({}), &leveldb_service_, + std::make_unique(std::move(file_runner))); + + leveldb_service_->OpenInMemory( + base::nullopt, "SessionStorageLevelDBWrapperTestDatabase", + mojo::MakeRequest(&leveldb_database_), base::DoNothing()); + + leveldb_database_->Put(StdStringToUint8Vector("map-0-key1"), + StdStringToUint8Vector("data1"), base::DoNothing()); + + std::vector save_operations = + metadata_.SetupNewDatabase(); + auto map_id = metadata_.RegisterNewMap( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), test_origin1_, + &save_operations); + DCHECK(map_id->KeyPrefix() == StdStringToUint8Vector("map-0-")); + leveldb_database_->Write(std::move(save_operations), base::DoNothing()); + } + ~SessionStorageLevelDBWrapperTest() override = default; + + scoped_refptr RegisterNewAreaMap( + SessionStorageMetadata::NamespaceEntry namespace_entry, + const url::Origin& origin) { + std::vector save_operations; + auto map_data = + metadata_.RegisterNewMap(namespace_entry, origin, &save_operations); + leveldb_database_->Write(std::move(save_operations), base::DoNothing()); + return map_data; + } + + SessionStorageLevelDBWrapper::RegisterNewAreaMap + GetRegisterNewAreaMapCallback() { + return base::BindRepeating( + &SessionStorageLevelDBWrapperTest::RegisterNewAreaMap, + base::Unretained(this)); + } + + protected: + base::test::ScopedTaskEnvironment environment_; + const std::string test_namespace_id1_; + const std::string test_namespace_id2_; + const url::Origin test_origin1_; + const url::Origin test_origin2_; + leveldb::mojom::LevelDBServicePtr leveldb_service_; + leveldb::mojom::LevelDBDatabaseAssociatedPtr leveldb_database_; + SessionStorageMetadata metadata_; + + testing::StrictMock listener_; +}; +} // namespace + +TEST_F(SessionStorageLevelDBWrapperTest, BasicUsage) { + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + auto ss_leveldb_impl = std::make_unique( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), test_origin1_, + SessionStorageDataMap::Create( + &listener_, + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_) + ->second[test_origin1_], + leveldb_database_.get()), + GetRegisterNewAreaMapCallback()); + + mojom::LevelDBWrapperAssociatedPtr ss_leveldb; + ss_leveldb_impl->Bind( + mojo::MakeRequestAssociatedWithDedicatedPipe(&ss_leveldb)); + + std::vector data; + DatabaseError status = test::GetAllSync(ss_leveldb.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + ASSERT_EQ(1ul, data.size()); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key1"), + StdStringToUint8Vector("data1")))); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); +} + +TEST_F(SessionStorageLevelDBWrapperTest, Cloning) { + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + auto ss_leveldb_impl1 = std::make_unique( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), test_origin1_, + SessionStorageDataMap::Create( + &listener_, + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_) + ->second[test_origin1_], + leveldb_database_.get()), + GetRegisterNewAreaMapCallback()); + + // Perform a shallow clone. + std::vector save_operations; + metadata_.RegisterShallowClonedNamespace( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), + metadata_.GetOrCreateNamespaceEntry(test_namespace_id2_), + &save_operations); + leveldb_database_->Write(std::move(save_operations), base::DoNothing()); + auto ss_leveldb_impl2 = ss_leveldb_impl1->Clone( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id2_)); + + mojom::LevelDBWrapperAssociatedPtr ss_leveldb1; + ss_leveldb_impl1->Bind( + mojo::MakeRequestAssociatedWithDedicatedPipe(&ss_leveldb1)); + mojom::LevelDBWrapperAssociatedPtr ss_leveldb2; + ss_leveldb_impl2->Bind( + mojo::MakeRequestAssociatedWithDedicatedPipe(&ss_leveldb2)); + + // Same maps are used. + EXPECT_EQ(ss_leveldb_impl1->data_map(), ss_leveldb_impl2->data_map()); + + // The |Put| call will fork the maps. + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("1"), testing::_)) + .Times(1); + EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)) + .Times(testing::AnyNumber()); + EXPECT_TRUE(test::PutSync(ss_leveldb2.get(), StdStringToUint8Vector("key2"), + StdStringToUint8Vector("data2"), base::nullopt, + "")); + + // The maps were forked on the above put. + EXPECT_NE(ss_leveldb_impl1->data_map(), ss_leveldb_impl2->data_map()); + + // Check map 1 data. + std::vector data; + DatabaseError status = test::GetAllSync(ss_leveldb1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + ASSERT_EQ(1ul, data.size()); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key1"), + StdStringToUint8Vector("data1")))); + + // Check map 2 data. + data.clear(); + status = test::GetAllSync(ss_leveldb2.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + ASSERT_EQ(2ul, data.size()); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key1"), + StdStringToUint8Vector("data1")))); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key2"), + StdStringToUint8Vector("data2")))); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1"))) + .Times(1); + + ss_leveldb_impl1 = nullptr; + ss_leveldb_impl2 = nullptr; +} + +TEST_F(SessionStorageLevelDBWrapperTest, ObserverTransfer) { + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + auto ss_leveldb_impl1 = std::make_unique( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), test_origin1_, + SessionStorageDataMap::Create( + &listener_, + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_) + ->second[test_origin1_], + leveldb_database_.get()), + GetRegisterNewAreaMapCallback()); + + // Create the mojo binding. + mojom::LevelDBWrapperAssociatedPtr ss_leveldb1; + ss_leveldb_impl1->Bind( + mojo::MakeRequestAssociatedWithDedicatedPipe(&ss_leveldb1)); + + // Create the observer, and attach it to the mojo bound implementation. + testing::StrictMock mock_observer; + mojo::AssociatedBinding observer_binding( + &mock_observer); + mojom::LevelDBObserverAssociatedPtrInfo observer_ptr_info; + observer_binding.Bind(mojo::MakeRequest(&observer_ptr_info)); + ss_leveldb1->AddObserver(std::move(observer_ptr_info)); + + // Perform a shallow clone. + std::vector save_operations; + metadata_.RegisterShallowClonedNamespace( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), + metadata_.GetOrCreateNamespaceEntry(test_namespace_id2_), + &save_operations); + leveldb_database_->Write(std::move(save_operations), base::DoNothing()); + auto ss_leveldb_impl2 = ss_leveldb_impl1->Clone( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id2_)); + mojom::LevelDBWrapperAssociatedPtr ss_leveldb2; + ss_leveldb_impl2->Bind( + mojo::MakeRequestAssociatedWithDedicatedPipe(&ss_leveldb2)); + + // Wait for our future commits to finish. + base::RunLoop commit_waiters; + auto barrier = base::BarrierClosure(3, commit_waiters.QuitClosure()); + EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)) + .Times(3) + .WillRepeatedly(base::test::RunClosure(barrier)); + + // Do a change on the new interface. There should be no observation. + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("1"), testing::_)) + .Times(1); + EXPECT_TRUE(test::PutSync(ss_leveldb2.get(), StdStringToUint8Vector("key2"), + StdStringToUint8Vector("data2"), base::nullopt, + "")); + ss_leveldb_impl2->data_map()->level_db_wrapper()->ScheduleImmediateCommit(); + + // Do a change on the old interface. + EXPECT_CALL(mock_observer, + KeyChanged(StdStringToUint8Vector("key1"), + StdStringToUint8Vector("data2"), + StdStringToUint8Vector("data1"), "ss_leveldb1")) + .Times(1); + EXPECT_TRUE(test::PutSync(ss_leveldb1.get(), StdStringToUint8Vector("key1"), + StdStringToUint8Vector("data2"), + StdStringToUint8Vector("data1"), "ss_leveldb1")); + ss_leveldb_impl1->data_map()->level_db_wrapper()->ScheduleImmediateCommit(); + + // Wait for the commits. + commit_waiters.Run(); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1"))) + .Times(1); + + ss_leveldb_impl1 = nullptr; + ss_leveldb_impl2 = nullptr; +} + +TEST_F(SessionStorageLevelDBWrapperTest, DeleteAllOnShared) { + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + auto ss_leveldb_impl1 = std::make_unique( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), test_origin1_, + SessionStorageDataMap::Create( + &listener_, + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_) + ->second[test_origin1_], + leveldb_database_.get()), + GetRegisterNewAreaMapCallback()); + + // Perform a shallow clone. + std::vector save_operations; + metadata_.RegisterShallowClonedNamespace( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), + metadata_.GetOrCreateNamespaceEntry(test_namespace_id2_), + &save_operations); + leveldb_database_->Write(std::move(save_operations), base::DoNothing()); + auto ss_leveldb_impl2 = ss_leveldb_impl1->Clone( + metadata_.GetOrCreateNamespaceEntry(test_namespace_id2_)); + + mojom::LevelDBWrapperAssociatedPtr ss_leveldb1; + ss_leveldb_impl1->Bind( + mojo::MakeRequestAssociatedWithDedicatedPipe(&ss_leveldb1)); + mojom::LevelDBWrapperAssociatedPtr ss_leveldb2; + ss_leveldb_impl2->Bind( + mojo::MakeRequestAssociatedWithDedicatedPipe(&ss_leveldb2)); + + // Same maps are used. + EXPECT_EQ(ss_leveldb_impl1->data_map(), ss_leveldb_impl2->data_map()); + + // Create the observer, attach to the first namespace. + testing::StrictMock mock_observer; + mojo::AssociatedBinding observer_binding( + &mock_observer); + mojom::LevelDBObserverAssociatedPtrInfo observer_ptr_info; + observer_binding.Bind(mojo::MakeRequest(&observer_ptr_info)); + ss_leveldb1->AddObserver(std::move(observer_ptr_info)); + + // The |DeleteAll| call will fork the maps, and the observer should see a + // DeleteAll. + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("1"), testing::_)) + .Times(1); + // There should be no commits, as we don't actually have to change any data. + // |ss_leveldb_impl1| should just switch to a new, empty map. + EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(0); + EXPECT_CALL(mock_observer, AllDeleted("source")).Times(1); + EXPECT_TRUE(test::DeleteAllSync(ss_leveldb1.get(), "source")); + + // The maps were forked on the above call. + EXPECT_NE(ss_leveldb_impl1->data_map(), ss_leveldb_impl2->data_map()); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1"))) + .Times(1); + + ss_leveldb_impl1 = nullptr; + ss_leveldb_impl2 = nullptr; +} + +} // namespace content diff --git a/chromium/content/browser/dom_storage/session_storage_metadata.cc b/chromium/content/browser/dom_storage/session_storage_metadata.cc new file mode 100644 index 00000000000..d129f92e127 --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_metadata.cc @@ -0,0 +1,427 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/session_storage_metadata.h" + +#include "base/macros.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "components/services/leveldb/public/cpp/util.h" +#include "content/common/dom_storage/dom_storage_namespace_ids.h" +#include "url/gurl.h" + +namespace content { +namespace { +using leveldb::mojom::BatchedOperation; +using leveldb::mojom::BatchOperationType; +using leveldb::mojom::BatchedOperationPtr; + +// Example layout of the database: +// | key | value | +// |----------------------------------------|--------------------| +// | map-1-a | b (a = b in map 1) | +// | ... | | +// | namespace-<36 char guid 1>-origin1 | 1 (mapid) | +// | namespace-<36 char guid 1>-origin2 | 2 | +// | namespace-<36 char guid 2>-origin1 | 1 (shallow copy) | +// | namespace-<36 char guid 2>-origin2 | 2 (shallow copy) | +// | namespace-<36 char guid 3>-origin1 | 3 (deep copy) | +// | namespace-<36 char guid 3>-origin2 | 2 (shallow copy) | +// | next-map-id | 4 | +// | version | 1 | +// Example area key: namespace-dabc53e1_8291_4de5_824f_dab8aa69c846-origin2 +// +// All number values (map numbers and the version) are string conversions of +// numbers. Map keys are converted to UTF-8 and the values stay as UTF-16. + +// This is "map-" (without the quotes). +constexpr const uint8_t kMapIdPrefixBytes[] = {'m', 'a', 'p', '-'}; + +constexpr const size_t kNamespacePrefixLength = + arraysize(SessionStorageMetadata::kNamespacePrefixBytes); +constexpr const uint8_t kNamespaceOriginSeperatorByte = '-'; +constexpr const size_t kNamespaceOriginSeperatorLength = 1; +constexpr const size_t kPrefixBeforeOriginLength = + kNamespacePrefixLength + kSessionStorageNamespaceIdLength + + kNamespaceOriginSeperatorLength; + +bool ValueToNumber(const std::vector& value, int64_t* out) { + return base::StringToInt64(leveldb::Uint8VectorToStringPiece(value), out); +} + +std::vector NumberToValue(int64_t map_number) { + return leveldb::StdStringToUint8Vector(base::NumberToString(map_number)); +} +} // namespace + +constexpr const int64_t SessionStorageMetadata::kMinSessionStorageSchemaVersion; +constexpr const int64_t + SessionStorageMetadata::kLatestSessionStorageSchemaVersion; +constexpr const int64_t SessionStorageMetadata::kInvalidDatabaseVersion; +constexpr const int64_t SessionStorageMetadata::kInvalidMapId; +constexpr const uint8_t SessionStorageMetadata::kDatabaseVersionBytes[]; +constexpr const uint8_t SessionStorageMetadata::kNamespacePrefixBytes[]; +constexpr const uint8_t SessionStorageMetadata::kNextMapIdKeyBytes[]; + +SessionStorageMetadata::MapData::MapData(int64_t map_number, url::Origin origin) + : number_as_bytes_(NumberToValue(map_number)), + key_prefix_(SessionStorageMetadata::GetMapPrefix(number_as_bytes_)), + origin_(std::move(origin)) {} +SessionStorageMetadata::MapData::~MapData() = default; + +SessionStorageMetadata::SessionStorageMetadata() {} + +SessionStorageMetadata::~SessionStorageMetadata() {} + +std::vector +SessionStorageMetadata::SetupNewDatabase() { + next_map_id_ = 0; + next_map_id_from_namespaces_ = 0; + namespace_origin_map_.clear(); + + std::vector operations; + operations.reserve(2); + operations.push_back(BatchedOperation::New( + BatchOperationType::PUT_KEY, + std::vector(std::begin(kDatabaseVersionBytes), + std::end(kDatabaseVersionBytes)), + LatestDatabaseVersionAsVector())); + operations.push_back( + BatchedOperation::New(BatchOperationType::PUT_KEY, + std::vector(std::begin(kNextMapIdKeyBytes), + std::end(kNextMapIdKeyBytes)), + NumberToValue(next_map_id_))); + return operations; +} + +bool SessionStorageMetadata::ParseDatabaseVersion( + base::Optional> value, + std::vector* upgrade_operations) { + if (!value) { + initial_database_version_from_disk_ = 0; + } else { + if (!ValueToNumber(value.value(), &initial_database_version_from_disk_)) { + initial_database_version_from_disk_ = kInvalidDatabaseVersion; + return false; + } + if (initial_database_version_from_disk_ == + kLatestSessionStorageSchemaVersion) + return true; + } + if (initial_database_version_from_disk_ < kMinSessionStorageSchemaVersion) + return false; + upgrade_operations->push_back(BatchedOperation::New( + BatchOperationType::PUT_KEY, + std::vector(std::begin(kDatabaseVersionBytes), + std::end(kDatabaseVersionBytes)), + LatestDatabaseVersionAsVector())); + return true; +} + +bool SessionStorageMetadata::ParseNamespaces( + std::vector values, + std::vector* upgrade_operations) { + namespace_origin_map_.clear(); + next_map_id_from_namespaces_ = 0; + // Since the data is ordered, all namespace data is in one spot. This keeps a + // reference to the last namespace data map to be more efficient. + std::string last_namespace_id; + std::map>* last_namespace = nullptr; + std::map> maps; + bool error = false; + for (const leveldb::mojom::KeyValuePtr& key_value : values) { + size_t key_size = key_value->key.size(); + + base::StringPiece key_as_string = + leveldb::Uint8VectorToStringPiece(key_value->key); + + if (key_size < kNamespacePrefixLength) { + LOG(ERROR) << "Key size is less than prefix length: " << key_as_string; + error = true; + break; + } + + // The key must start with 'namespace-'. + if (!key_as_string.starts_with(base::StringPiece( + reinterpret_cast(kNamespacePrefixBytes), + kNamespacePrefixLength))) { + LOG(ERROR) << "Key must start with 'namespace-': " << key_as_string; + error = true; + break; + } + + // Old databases have a dummy 'namespace-' entry. + if (key_size == kNamespacePrefixLength) + continue; + + // Check that the prefix is 'namespace-- + if (key_size < kPrefixBeforeOriginLength || + key_as_string[kPrefixBeforeOriginLength - 1] != + static_cast(kNamespaceOriginSeperatorByte)) { + LOG(ERROR) << "Prefix is not 'namespace--': " << key_as_string; + error = true; + break; + } + + // Old databases have a dummy 'namespace--' entry. + if (key_size == kPrefixBeforeOriginLength) + continue; + + base::StringPiece namespace_id = key_as_string.substr( + kNamespacePrefixLength, kSessionStorageNamespaceIdLength); + + base::StringPiece origin_str = + key_as_string.substr(kPrefixBeforeOriginLength); + + int64_t map_number; + if (!ValueToNumber(key_value->value, &map_number)) { + error = true; + LOG(ERROR) << "Could not parse map number " + << leveldb::Uint8VectorToStringPiece(key_value->value); + break; + } + + if (map_number >= next_map_id_from_namespaces_) + next_map_id_from_namespaces_ = map_number + 1; + + auto origin_gurl = GURL(origin_str); + if (!origin_gurl.is_valid()) { + LOG(ERROR) << "Invalid origin " << origin_str; + error = true; + break; + } + + auto origin = url::Origin::Create(origin_gurl); + if (namespace_id != last_namespace_id) { + last_namespace_id = namespace_id.as_string(); + DCHECK(namespace_origin_map_.find(last_namespace_id) == + namespace_origin_map_.end()); + last_namespace = &(namespace_origin_map_[last_namespace_id]); + } + auto map_it = maps.find(map_number); + if (map_it == maps.end()) { + map_it = + maps.emplace(std::piecewise_construct, + std::forward_as_tuple(map_number), + std::forward_as_tuple(new MapData(map_number, origin))) + .first; + } + map_it->second->IncReferenceCount(); + + last_namespace->emplace(std::make_pair(std::move(origin), map_it->second)); + } + if (error) { + namespace_origin_map_.clear(); + next_map_id_from_namespaces_ = 0; + return false; + } + if (next_map_id_ == 0 || next_map_id_ < next_map_id_from_namespaces_) + next_map_id_ = next_map_id_from_namespaces_; + + // Namespace metadata migration. + DCHECK_NE(kInvalidDatabaseVersion, initial_database_version_from_disk_); + if (initial_database_version_from_disk_ == 0) { + // Remove the dummy 'namespaces-' entry. + upgrade_operations->push_back(BatchedOperation::New( + BatchOperationType::DELETE_KEY, + std::vector(std::begin(kNamespacePrefixBytes), + std::end(kNamespacePrefixBytes)), + base::nullopt)); + // Remove all the refcount storage. + for (const auto& map_pair : maps) { + upgrade_operations->push_back( + BatchedOperation::New(BatchOperationType::DELETE_KEY, + map_pair.second->KeyPrefix(), base::nullopt)); + } + } + + return true; +} + +void SessionStorageMetadata::ParseNextMapId( + const std::vector& map_id) { + if (!ValueToNumber(map_id, &next_map_id_)) + next_map_id_ = next_map_id_from_namespaces_; + if (next_map_id_ < next_map_id_from_namespaces_) + next_map_id_ = next_map_id_from_namespaces_; +} + +// static +std::vector SessionStorageMetadata::LatestDatabaseVersionAsVector() { + return NumberToValue(kLatestSessionStorageSchemaVersion); +} + +scoped_refptr +SessionStorageMetadata::RegisterNewMap( + NamespaceEntry namespace_entry, + const url::Origin& origin, + std::vector* save_operations) { + auto new_map_data = base::MakeRefCounted(next_map_id_, origin); + ++next_map_id_; + + save_operations->push_back(BatchedOperation::New( + BatchOperationType::PUT_KEY, + std::vector( + SessionStorageMetadata::kNextMapIdKeyBytes, + std::end(SessionStorageMetadata::kNextMapIdKeyBytes)), + NumberToValue(next_map_id_))); + + std::map>& namespace_origins = + namespace_entry->second; + auto namespace_it = namespace_origins.find(origin); + if (namespace_it != namespace_origins.end()) { + // Check the old map doesn't have the same number as the new map. + DCHECK(namespace_it->second->MapNumberAsBytes() != + new_map_data->MapNumberAsBytes()); + DCHECK_GT(namespace_it->second->ReferenceCount(), 1) + << "A new map should never be registered for an area that has a " + "single-refcount map."; + // There was already an area key here, so decrement that map reference. + namespace_it->second->DecReferenceCount(); + namespace_it->second = new_map_data; + } else { + namespace_origins.emplace(std::make_pair(origin, new_map_data)); + } + new_map_data->IncReferenceCount(); + + save_operations->push_back(BatchedOperation::New( + BatchOperationType::PUT_KEY, GetAreaKey(namespace_entry->first, origin), + new_map_data->MapNumberAsBytes())); + + return new_map_data; +} + +void SessionStorageMetadata::RegisterShallowClonedNamespace( + NamespaceEntry source_namespace, + NamespaceEntry destination_namespace, + std::vector* save_operations) { + std::map>& source_origins = + source_namespace->second; + std::map>& destination_origins = + destination_namespace->second; + DCHECK_EQ(0ul, destination_origins.size()); + + save_operations->reserve(save_operations->size() + source_origins.size()); + for (const auto& origin_map_pair : source_origins) { + destination_origins.emplace(std::piecewise_construct, + std::forward_as_tuple(origin_map_pair.first), + std::forward_as_tuple(origin_map_pair.second)); + origin_map_pair.second->IncReferenceCount(); + + save_operations->push_back(BatchedOperation::New( + BatchOperationType::PUT_KEY, + GetAreaKey(destination_namespace->first, origin_map_pair.first), + origin_map_pair.second->MapNumberAsBytes())); + } +} + +void SessionStorageMetadata::DeleteNamespace( + const std::string& namespace_id, + std::vector* delete_operations) { + auto it = namespace_origin_map_.find(namespace_id); + if (it == namespace_origin_map_.end()) + return; + + delete_operations->push_back( + BatchedOperation::New(BatchOperationType::DELETE_PREFIXED_KEY, + GetNamespacePrefix(namespace_id), base::nullopt)); + + const std::map>& origins = it->second; + for (const auto& origin_map_pair : origins) { + MapData* map_data = origin_map_pair.second.get(); + DCHECK_GT(map_data->ReferenceCount(), 0); + map_data->DecReferenceCount(); + if (map_data->ReferenceCount() == 0) { + delete_operations->push_back( + BatchedOperation::New(BatchOperationType::DELETE_PREFIXED_KEY, + map_data->KeyPrefix(), base::nullopt)); + } + } + + namespace_origin_map_.erase(it); +} + +void SessionStorageMetadata::DeleteArea( + const std::string& namespace_id, + const url::Origin& origin, + std::vector* delete_operations) { + NamespaceEntry ns_entry = namespace_origin_map_.find(namespace_id); + if (ns_entry == namespace_origin_map_.end()) + return; + + auto origin_map_it = ns_entry->second.find(origin); + if (origin_map_it == ns_entry->second.end()) + return; + + MapData* map_data = origin_map_it->second.get(); + + delete_operations->push_back( + BatchedOperation::New(BatchOperationType::DELETE_KEY, + GetAreaKey(namespace_id, origin), base::nullopt)); + + DCHECK_GT(map_data->ReferenceCount(), 0); + map_data->DecReferenceCount(); + if (map_data->ReferenceCount() == 0) { + delete_operations->push_back( + BatchedOperation::New(BatchOperationType::DELETE_PREFIXED_KEY, + map_data->KeyPrefix(), base::nullopt)); + } + ns_entry->second.erase(origin_map_it); +} + +SessionStorageMetadata::NamespaceEntry +SessionStorageMetadata::GetOrCreateNamespaceEntry( + const std::string& namespace_id) { + // Note: if the entry exists, emplace will return the existing entry and NOT + // insert a new entry. + return namespace_origin_map_ + .emplace(std::piecewise_construct, std::forward_as_tuple(namespace_id), + std::forward_as_tuple()) + .first; +} + +// static +std::vector SessionStorageMetadata::GetNamespacePrefix( + const std::string& namespace_id) { + std::vector namespace_prefix( + SessionStorageMetadata::kNamespacePrefixBytes, + std::end(SessionStorageMetadata::kNamespacePrefixBytes)); + namespace_prefix.insert(namespace_prefix.end(), namespace_id.data(), + namespace_id.data() + namespace_id.size()); + namespace_prefix.push_back(kNamespaceOriginSeperatorByte); + return namespace_prefix; +} + +// static +std::vector SessionStorageMetadata::GetAreaKey( + const std::string& namespace_id, + const url::Origin& origin) { + std::vector area_key( + SessionStorageMetadata::kNamespacePrefixBytes, + std::end(SessionStorageMetadata::kNamespacePrefixBytes)); + area_key.insert(area_key.end(), namespace_id.begin(), namespace_id.end()); + area_key.push_back(kNamespaceOriginSeperatorByte); + std::string origin_str = origin.GetURL().spec(); + area_key.insert(area_key.end(), origin_str.data(), + origin_str.data() + origin_str.size()); + return area_key; +} + +// static +std::vector SessionStorageMetadata::GetMapPrefix(int64_t map_number) { + return GetMapPrefix(NumberToValue(map_number)); +} + +// static +std::vector SessionStorageMetadata::GetMapPrefix( + const std::vector& map_number_as_bytes) { + std::vector map_prefix(kMapIdPrefixBytes, + std::end(kMapIdPrefixBytes)); + map_prefix.insert(map_prefix.end(), map_number_as_bytes.begin(), + map_number_as_bytes.end()); + map_prefix.push_back(kNamespaceOriginSeperatorByte); + return map_prefix; +} + +} // namespace content diff --git a/chromium/content/browser/dom_storage/session_storage_metadata.h b/chromium/content/browser/dom_storage/session_storage_metadata.h new file mode 100644 index 00000000000..c1a6b861979 --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_metadata.h @@ -0,0 +1,182 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_METADATA_H_ +#define CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_METADATA_H_ + +#include +#include +#include +#include + +#include "base/memory/ref_counted.h" +#include "components/services/leveldb/public/interfaces/leveldb.mojom.h" +#include "content/common/content_export.h" +#include "url/origin.h" + +namespace content { + +// Holds the metadata information for a session storage database. This includes +// logic for parsing and saving database content. +class CONTENT_EXPORT SessionStorageMetadata { + public: + // Version 0 represents the old SessionStorageDatabase where we never stored a + // version. This class stores '0' as the version in this case. Version 1 + // removes 'namespaces-' dummy entry, and 'map-_-' refcount entries that are + // present in version 0. + static constexpr const int64_t kMinSessionStorageSchemaVersion = 0; + static constexpr const int64_t kLatestSessionStorageSchemaVersion = 1; + + static constexpr const int64_t kInvalidDatabaseVersion = -1; + static constexpr const int64_t kInvalidMapId = -1; + + static constexpr const uint8_t kDatabaseVersionBytes[] = {'v', 'e', 'r', 's', + 'i', 'o', 'n'}; + + static constexpr const uint8_t kNamespacePrefixBytes[] = { + 'n', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '-'}; + + // This is "next-map-id" (without the quotes). + static constexpr const uint8_t kNextMapIdKeyBytes[] = { + 'n', 'e', 'x', 't', '-', 'm', 'a', 'p', '-', 'i', 'd'}; + + // Represents a map which can be shared by multiple areas. + // The |DeleteNamespace| and |DeleteArea| methods can destroy any MapData + // objects who are no longer referenced by another namespace. + // Maps (and thus MapData objects) can only be shared for the same origin. + class CONTENT_EXPORT MapData : public base::RefCounted { + public: + explicit MapData(int64_t map_number, url::Origin origin); + + const url::Origin& origin() const { return origin_; } + + // The number of namespaces that reference this map. + int ReferenceCount() const { return reference_count_; } + + // The key prefix for the map data (e.g. "map-2-"). + const std::vector& KeyPrefix() const { return key_prefix_; } + + // The number of the map as bytes (e.g. "2"). + const std::vector& MapNumberAsBytes() const { + return number_as_bytes_; + } + + private: + friend class base::RefCounted; + friend class SessionStorageMetadata; + ~MapData(); + + void IncReferenceCount() { ++reference_count_; } + void DecReferenceCount() { --reference_count_; } + + // The map number as bytes (e.g. "2"). These bytes are the string + // representation of the map number. + std::vector number_as_bytes_; + std::vector key_prefix_; + url::Origin origin_; + int reference_count_ = 0; + }; + + using NamespaceOriginMap = + std::map>>; + using NamespaceEntry = NamespaceOriginMap::iterator; + + SessionStorageMetadata(); + ~SessionStorageMetadata(); + + // For a new database, this saves the database version, clears the metadata, + // and returns the operations to save to disk. + std::vector SetupNewDatabase(); + + // This parses the database version from the bytes that were stored on + // disk, or if there was no version saved then passes a base::nullopt. This + // call is not necessary on new databases. The |upgrade_operations| are + // populated with any operations needed to upgrade the databases versioning + // metadata. Note this is different than the namespaces metadata, which will + // be upgraded in ParseNamespaces. + // Returns if the parsing is correct and we support the version read. + bool ParseDatabaseVersion( + base::Optional> value, + std::vector* upgrade_operations); + + // Parses all namespaces and maps, and stores all metadata locally. This + // invalidates all NamespaceEntry and MapData objects. If there is a parsing + // error, the namespaces will be cleared.If the version given to + // |ParseDatabaseVersion| is an older version, any namespace metadata upgrades + // will be populated in |upgrade_operations|. This call is not necessary on + // new databases. + bool ParseNamespaces( + std::vector values, + std::vector* upgrade_operations); + + // Parses the next map id from the given bytes. If that fails, then it uses + // the next available id from parsing the namespaces. This call is not + // necessary on new databases. + void ParseNextMapId(const std::vector& map_id); + + // Creates new map data for the given namespace-origin area. If the area + // entry exists, then it will decrement the refcount of the old map. The + // |save_operations| save the new or modified area entry, as well as saving + // the next available map id. + // Note: It is invalid to call this method for an area that has a map with + // only one reference. + scoped_refptr RegisterNewMap( + NamespaceEntry namespace_entry, + const url::Origin& origin, + std::vector* save_operations); + + // Registers an origin-map in the |destination_namespace| from every + // origin-map in the |source_namespace|. The |destination_namespace| must have + // no origin-maps. All maps in the destination namespace are the same maps as + // the source namespace. All database operations to save the namespace origin + // metadata are put in |save_operations|. + void RegisterShallowClonedNamespace( + NamespaceEntry source_namespace, + NamespaceEntry destination_namespace, + std::vector* save_operations); + + // Deletes the given namespace any any maps that no longer have any + // references. This will invalidate all NamespaceEntry objects for the + // |namespace_id|, and can invalidate any MapData objects whose reference + // count hits zero. + void DeleteNamespace( + const std::string& namespace_id, + std::vector* delete_operations); + + // This removes the metadata entry for this namespace-origin area. If the map + // at this entry isn't reference by any other area (refcount hits 0), then + // this will delete that map on disk and invalidate that MapData. + void DeleteArea( + const std::string& namespace_id, + const url::Origin& origin, + std::vector* delete_operations); + + NamespaceEntry GetOrCreateNamespaceEntry(const std::string& namespace_id); + + const NamespaceOriginMap& namespace_origin_map() const { + return namespace_origin_map_; + } + + int64_t NextMapId() const { return next_map_id_; } + + private: + static std::vector LatestDatabaseVersionAsVector(); + + static std::vector GetNamespacePrefix( + const std::string& namespace_id); + static std::vector GetAreaKey(const std::string& namespace_id, + const url::Origin& origin); + static std::vector GetMapPrefix(int64_t map_number); + static std::vector GetMapPrefix( + const std::vector& map_number_as_bytes); + + int64_t initial_database_version_from_disk_ = kInvalidDatabaseVersion; + int64_t next_map_id_ = kInvalidMapId; + int64_t next_map_id_from_namespaces_ = 0; + + NamespaceOriginMap namespace_origin_map_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_METADATA_H_ diff --git a/chromium/content/browser/dom_storage/session_storage_metadata_unittest.cc b/chromium/content/browser/dom_storage/session_storage_metadata_unittest.cc new file mode 100644 index 00000000000..e6e58fb07cb --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_metadata_unittest.cc @@ -0,0 +1,480 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/session_storage_metadata.h" + +#include "base/bind.h" +#include "base/files/scoped_temp_dir.h" +#include "base/guid.h" +#include "base/memory/ptr_util.h" +#include "base/run_loop.h" +#include "base/stl_util.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_task_environment.h" +#include "components/services/leveldb/public/cpp/util.h" +#include "content/browser/dom_storage/session_storage_database.h" +#include "content/browser/indexed_db/leveldb/leveldb_env.h" +#include "content/common/dom_storage/dom_storage_types.h" +#include "content/test/fake_leveldb_database.h" +#include "mojo/public/cpp/bindings/strong_associated_binding.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/leveldatabase/env_chromium.h" +#include "third_party/leveldatabase/leveldb_chrome.h" +#include "third_party/leveldatabase/src/include/leveldb/db.h" +#include "third_party/leveldatabase/src/include/leveldb/options.h" +#include "url/gurl.h" +#include "url/origin.h" + +namespace content { +namespace { +using leveldb::StdStringToUint8Vector; +using leveldb::Uint8VectorToStdString; +using leveldb::mojom::DatabaseError; + +void GetCallback(std::vector* value_out, + DatabaseError error, + const std::vector& value) { + *value_out = value; +} + +void ErrorCallback(DatabaseError* error_out, DatabaseError error) { + *error_out = error; +} + +void GetAllCallback(std::vector* values_out, + DatabaseError error, + std::vector values) { + *values_out = std::move(values); +} + +class SessionStorageMetadataTest : public testing::Test { + public: + SessionStorageMetadataTest() + : test_namespace1_id_(base::GenerateGUID()), + test_namespace2_id_(base::GenerateGUID()), + test_namespace3_id_(base::GenerateGUID()), + test_origin1_(url::Origin::Create(GURL("http://host1:1/"))), + test_origin2_(url::Origin::Create(GURL("http://host2:2/"))), + database_(&mock_data_) { + next_map_id_key_ = std::vector( + std::begin(SessionStorageMetadata::kNextMapIdKeyBytes), + std::end(SessionStorageMetadata::kNextMapIdKeyBytes)); + database_version_key_ = std::vector( + std::begin(SessionStorageMetadata::kDatabaseVersionBytes), + std::end(SessionStorageMetadata::kDatabaseVersionBytes)); + namespaces_prefix_key_ = std::vector( + std::begin(SessionStorageMetadata::kNamespacePrefixBytes), + std::end(SessionStorageMetadata::kNamespacePrefixBytes)); + } + ~SessionStorageMetadataTest() override {} + + void ReadMetadataFromDatabase(SessionStorageMetadata* metadata) { + std::vector value; + database_.Get(database_version_key_, base::BindOnce(&GetCallback, &value)); + std::vector migration_operations; + EXPECT_TRUE(metadata->ParseDatabaseVersion(value, &migration_operations)); + EXPECT_TRUE(migration_operations.empty()); + database_.Get(next_map_id_key_, base::BindOnce(&GetCallback, &value)); + metadata->ParseNextMapId(value); + std::vector values; + database_.GetPrefixed(namespaces_prefix_key_, + base::BindOnce(&GetAllCallback, &values)); + EXPECT_TRUE( + metadata->ParseNamespaces(std::move(values), &migration_operations)); + EXPECT_TRUE(migration_operations.empty()); + } + + void SetupTestData() { + // | key | value | + // |----------------------------------------|--------------------| + // | map-1-key1 | data1 | + // | map-3-key1 | data3 | + // | map-4-key1 | data4 | + // | namespace--http://host1:1/ | 1 | + // | namespace--http://host2:2/ | 3 | + // | namespace--http://host1:1/ | 1 | + // | namespace--http://host2:2/ | 4 | + // | next-map-id | 5 | + // | version | 1 | + mock_data_[StdStringToUint8Vector( + std::string("namespace-") + test_namespace1_id_ + "-" + + test_origin1_.GetURL().spec())] = StdStringToUint8Vector("1"); + mock_data_[StdStringToUint8Vector( + std::string("namespace-") + test_namespace1_id_ + "-" + + test_origin2_.GetURL().spec())] = StdStringToUint8Vector("3"); + mock_data_[StdStringToUint8Vector( + std::string("namespace-") + test_namespace2_id_ + "-" + + test_origin1_.GetURL().spec())] = StdStringToUint8Vector("1"); + mock_data_[StdStringToUint8Vector( + std::string("namespace-") + test_namespace2_id_ + "-" + + test_origin2_.GetURL().spec())] = StdStringToUint8Vector("4"); + + mock_data_[next_map_id_key_] = StdStringToUint8Vector("5"); + + mock_data_[StdStringToUint8Vector("map-1-key1")] = + StdStringToUint8Vector("data1"); + mock_data_[StdStringToUint8Vector("map-3-key1")] = + StdStringToUint8Vector("data3"); + mock_data_[StdStringToUint8Vector("map-4-key1")] = + StdStringToUint8Vector("data4"); + + mock_data_[database_version_key_] = StdStringToUint8Vector("1"); + } + + protected: + base::test::ScopedTaskEnvironment scoped_task_environment_; + std::string test_namespace1_id_; + std::string test_namespace2_id_; + std::string test_namespace3_id_; + url::Origin test_origin1_; + url::Origin test_origin2_; + std::map, std::vector> mock_data_; + FakeLevelDBDatabase database_; + + std::vector database_version_key_; + std::vector next_map_id_key_; + std::vector namespaces_prefix_key_; +}; + +TEST_F(SessionStorageMetadataTest, SaveNewMetadata) { + SessionStorageMetadata metadata; + std::vector operations = + metadata.SetupNewDatabase(); + + DatabaseError error; + database_.Write(std::move(operations), + base::BindOnce(&ErrorCallback, &error)); + EXPECT_EQ(DatabaseError::OK, error); + + EXPECT_EQ(StdStringToUint8Vector("1"), mock_data_[database_version_key_]); + EXPECT_EQ(StdStringToUint8Vector("0"), mock_data_[next_map_id_key_]); +} + +TEST_F(SessionStorageMetadataTest, LoadingData) { + SetupTestData(); + SessionStorageMetadata metadata; + ReadMetadataFromDatabase(&metadata); + + EXPECT_EQ(5, metadata.NextMapId()); + EXPECT_EQ(2ul, metadata.namespace_origin_map().size()); + + // Namespace 1 should have 2 origins, referencing map 1 and 3. Map 1 is shared + // between namespace 1 and namespace 2. + auto entry = metadata.GetOrCreateNamespaceEntry(test_namespace1_id_); + EXPECT_EQ(test_namespace1_id_, entry->first); + EXPECT_EQ(2ul, entry->second.size()); + EXPECT_EQ(StdStringToUint8Vector("map-1-"), + entry->second[test_origin1_]->KeyPrefix()); + EXPECT_EQ(2, entry->second[test_origin1_]->ReferenceCount()); + EXPECT_EQ(StdStringToUint8Vector("map-3-"), + entry->second[test_origin2_]->KeyPrefix()); + EXPECT_EQ(1, entry->second[test_origin2_]->ReferenceCount()); + + // Namespace 2 is the same, except the second origin references map 4. + entry = metadata.GetOrCreateNamespaceEntry(test_namespace2_id_); + EXPECT_EQ(test_namespace2_id_, entry->first); + EXPECT_EQ(2ul, entry->second.size()); + EXPECT_EQ(StdStringToUint8Vector("map-1-"), + entry->second[test_origin1_]->KeyPrefix()); + EXPECT_EQ(2, entry->second[test_origin1_]->ReferenceCount()); + EXPECT_EQ(StdStringToUint8Vector("map-4-"), + entry->second[test_origin2_]->KeyPrefix()); + EXPECT_EQ(1, entry->second[test_origin2_]->ReferenceCount()); +} + +TEST_F(SessionStorageMetadataTest, SaveNewMap) { + SetupTestData(); + SessionStorageMetadata metadata; + ReadMetadataFromDatabase(&metadata); + + std::vector operations; + auto ns1_entry = metadata.GetOrCreateNamespaceEntry(test_namespace1_id_); + auto map_data = + metadata.RegisterNewMap(ns1_entry, test_origin1_, &operations); + ASSERT_TRUE(map_data); + + // Verify in-memory metadata is correct. + EXPECT_EQ(StdStringToUint8Vector("map-5-"), + ns1_entry->second[test_origin1_]->KeyPrefix()); + EXPECT_EQ(1, ns1_entry->second[test_origin1_]->ReferenceCount()); + EXPECT_EQ(1, metadata.GetOrCreateNamespaceEntry(test_namespace2_id_) + ->second[test_origin1_] + ->ReferenceCount()); + + DatabaseError error; + database_.Write(std::move(operations), + base::BindOnce(&ErrorCallback, &error)); + EXPECT_EQ(DatabaseError::OK, error); + + // Verify metadata was written to disk. + EXPECT_EQ(StdStringToUint8Vector("6"), mock_data_[next_map_id_key_]); + EXPECT_EQ(StdStringToUint8Vector("5"), + mock_data_[StdStringToUint8Vector(std::string("namespace-") + + test_namespace1_id_ + "-" + + test_origin1_.GetURL().spec())]); +} + +TEST_F(SessionStorageMetadataTest, ShallowCopies) { + SetupTestData(); + SessionStorageMetadata metadata; + ReadMetadataFromDatabase(&metadata); + + auto ns1_entry = metadata.GetOrCreateNamespaceEntry(test_namespace1_id_); + auto ns3_entry = metadata.GetOrCreateNamespaceEntry(test_namespace3_id_); + + std::vector operations; + metadata.RegisterShallowClonedNamespace(ns1_entry, ns3_entry, &operations); + + DatabaseError error; + database_.Write(std::move(operations), + base::BindOnce(&ErrorCallback, &error)); + EXPECT_EQ(DatabaseError::OK, error); + + // Verify in-memory metadata is correct. + EXPECT_EQ(StdStringToUint8Vector("map-1-"), + ns3_entry->second[test_origin1_]->KeyPrefix()); + EXPECT_EQ(StdStringToUint8Vector("map-3-"), + ns3_entry->second[test_origin2_]->KeyPrefix()); + EXPECT_EQ(ns1_entry->second[test_origin1_].get(), + ns3_entry->second[test_origin1_].get()); + EXPECT_EQ(ns1_entry->second[test_origin2_].get(), + ns3_entry->second[test_origin2_].get()); + EXPECT_EQ(3, ns3_entry->second[test_origin1_]->ReferenceCount()); + EXPECT_EQ(2, ns3_entry->second[test_origin2_]->ReferenceCount()); + + // Verify metadata was written to disk. + EXPECT_EQ(StdStringToUint8Vector("1"), + mock_data_[StdStringToUint8Vector(std::string("namespace-") + + test_namespace3_id_ + "-" + + test_origin1_.GetURL().spec())]); + EXPECT_EQ(StdStringToUint8Vector("3"), + mock_data_[StdStringToUint8Vector(std::string("namespace-") + + test_namespace3_id_ + "-" + + test_origin2_.GetURL().spec())]); +} + +TEST_F(SessionStorageMetadataTest, DeleteNamespace) { + SetupTestData(); + SessionStorageMetadata metadata; + ReadMetadataFromDatabase(&metadata); + + std::vector operations; + metadata.DeleteNamespace(test_namespace1_id_, &operations); + DatabaseError error; + database_.Write(std::move(operations), + base::BindOnce(&ErrorCallback, &error)); + EXPECT_EQ(DatabaseError::OK, error); + + EXPECT_FALSE( + base::ContainsKey(metadata.namespace_origin_map(), test_namespace1_id_)); + + // Verify in-memory metadata is correct. + auto ns2_entry = metadata.GetOrCreateNamespaceEntry(test_namespace2_id_); + EXPECT_EQ(1, ns2_entry->second[test_origin1_]->ReferenceCount()); + EXPECT_EQ(1, ns2_entry->second[test_origin2_]->ReferenceCount()); + + // Verify metadata and data was deleted from disk. + EXPECT_FALSE(base::ContainsKey( + mock_data_, + StdStringToUint8Vector(std::string("namespace-") + test_namespace1_id_ + + "-" + test_origin1_.GetURL().spec()))); + EXPECT_FALSE(base::ContainsKey( + mock_data_, + StdStringToUint8Vector(std::string("namespace-") + test_namespace1_id_ + + "-" + test_origin2_.GetURL().spec()))); + EXPECT_FALSE( + base::ContainsKey(mock_data_, StdStringToUint8Vector("map-3-key1"))); + EXPECT_TRUE( + base::ContainsKey(mock_data_, StdStringToUint8Vector("map-1-key1"))); +} + +TEST_F(SessionStorageMetadataTest, DeleteArea) { + SetupTestData(); + SessionStorageMetadata metadata; + ReadMetadataFromDatabase(&metadata); + + // First delete an area with a shared map. + std::vector operations; + metadata.DeleteArea(test_namespace1_id_, test_origin1_, &operations); + DatabaseError error; + database_.Write(std::move(operations), + base::BindOnce(&ErrorCallback, &error)); + EXPECT_EQ(DatabaseError::OK, error); + + // Verify in-memory metadata is correct. + auto ns1_entry = metadata.GetOrCreateNamespaceEntry(test_namespace1_id_); + auto ns2_entry = metadata.GetOrCreateNamespaceEntry(test_namespace2_id_); + EXPECT_FALSE(base::ContainsKey(ns1_entry->second, test_origin1_)); + EXPECT_EQ(1, ns1_entry->second[test_origin2_]->ReferenceCount()); + EXPECT_EQ(1, ns2_entry->second[test_origin1_]->ReferenceCount()); + EXPECT_EQ(1, ns2_entry->second[test_origin2_]->ReferenceCount()); + + // Verify only the applicable data was deleted. + EXPECT_FALSE(base::ContainsKey( + mock_data_, + StdStringToUint8Vector(std::string("namespace-") + test_namespace1_id_ + + "-" + test_origin1_.GetURL().spec()))); + EXPECT_TRUE(base::ContainsKey( + mock_data_, + StdStringToUint8Vector(std::string("namespace-") + test_namespace1_id_ + + "-" + test_origin2_.GetURL().spec()))); + EXPECT_TRUE( + base::ContainsKey(mock_data_, StdStringToUint8Vector("map-1-key1"))); + EXPECT_TRUE( + base::ContainsKey(mock_data_, StdStringToUint8Vector("map-4-key1"))); + + // Now delete an area with a unique map. + operations.clear(); + metadata.DeleteArea(test_namespace2_id_, test_origin2_, &operations); + database_.Write(std::move(operations), + base::BindOnce(&ErrorCallback, &error)); + EXPECT_EQ(DatabaseError::OK, error); + + // Verify in-memory metadata is correct. + EXPECT_FALSE(base::ContainsKey(ns1_entry->second, test_origin1_)); + EXPECT_EQ(1, ns1_entry->second[test_origin2_]->ReferenceCount()); + EXPECT_EQ(1, ns2_entry->second[test_origin1_]->ReferenceCount()); + EXPECT_FALSE(base::ContainsKey(ns2_entry->second, test_origin2_)); + + // Verify only the applicable data was deleted. + EXPECT_TRUE(base::ContainsKey( + mock_data_, + StdStringToUint8Vector(std::string("namespace-") + test_namespace2_id_ + + "-" + test_origin1_.GetURL().spec()))); + EXPECT_FALSE(base::ContainsKey( + mock_data_, + StdStringToUint8Vector(std::string("namespace-") + test_namespace2_id_ + + "-" + test_origin2_.GetURL().spec()))); + EXPECT_TRUE( + base::ContainsKey(mock_data_, StdStringToUint8Vector("map-1-key1"))); + EXPECT_TRUE( + base::ContainsKey(mock_data_, StdStringToUint8Vector("map-3-key1"))); + EXPECT_FALSE( + base::ContainsKey(mock_data_, StdStringToUint8Vector("map-4-key1"))); +} + +class SessionStorageMetadataMigrationTest : public testing::Test { + public: + SessionStorageMetadataMigrationTest() + : test_namespace1_id_(base::GenerateGUID()), + test_namespace2_id_(base::GenerateGUID()), + test_origin1_(url::Origin::Create(GURL("http://host1:1/"))) { + next_map_id_key_ = std::vector( + std::begin(SessionStorageMetadata::kNextMapIdKeyBytes), + std::end(SessionStorageMetadata::kNextMapIdKeyBytes)); + database_version_key_ = std::vector( + std::begin(SessionStorageMetadata::kDatabaseVersionBytes), + std::end(SessionStorageMetadata::kDatabaseVersionBytes)); + namespaces_prefix_key_ = std::vector( + std::begin(SessionStorageMetadata::kNamespacePrefixBytes), + std::end(SessionStorageMetadata::kNamespacePrefixBytes)); + } + ~SessionStorageMetadataMigrationTest() override = default; + + void SetUp() override { + ASSERT_TRUE(temp_path_.CreateUniqueTempDir()); + in_memory_env_ = + leveldb_chrome::NewMemEnv("SessionStorage", LevelDBEnv::Get()); + leveldb_env::Options options; + options.create_if_missing = true; + options.env = in_memory_env_.get(); + std::unique_ptr db; + leveldb::Status s = + leveldb_env::OpenDB(options, temp_path_.GetPath().AsUTF8Unsafe(), &db); + ASSERT_TRUE(s.ok()) << s.ToString(); + old_ss_database_ = base::MakeRefCounted( + temp_path_.GetPath(), base::ThreadTaskRunnerHandle::Get().get()); + old_ss_database_->SetDatabaseForTesting(std::move(db)); + } + + leveldb::DB* db() { return old_ss_database_->db(); } + + protected: + base::test::ScopedTaskEnvironment scoped_task_environment_; + base::ScopedTempDir temp_path_; + std::string test_namespace1_id_; + std::string test_namespace2_id_; + url::Origin test_origin1_; + std::unique_ptr in_memory_env_; + scoped_refptr old_ss_database_; + + std::vector database_version_key_; + std::vector next_map_id_key_; + std::vector namespaces_prefix_key_; +}; + +TEST_F(SessionStorageMetadataMigrationTest, MigrateV0ToV1) { + base::string16 key = base::ASCIIToUTF16("key"); + base::string16 value = base::ASCIIToUTF16("value"); + base::string16 key2 = base::ASCIIToUTF16("key2"); + key2.push_back(0xd83d); + key2.push_back(0xde00); + DOMStorageValuesMap data; + data[key] = base::NullableString16(value, false); + data[key2] = base::NullableString16(value, false); + EXPECT_TRUE(old_ss_database_->CommitAreaChanges(test_namespace1_id_, + test_origin1_, false, data)); + EXPECT_TRUE(old_ss_database_->CloneNamespace(test_namespace1_id_, + test_namespace2_id_)); + + SessionStorageMetadata metadata; + // Read non-existant version, give new version to save. + leveldb::ReadOptions options; + std::string db_value; + leveldb::Status s = db()->Get(options, leveldb::Slice("version"), &db_value); + EXPECT_TRUE(s.IsNotFound()); + std::vector migration_operations; + EXPECT_TRUE( + metadata.ParseDatabaseVersion(base::nullopt, &migration_operations)); + EXPECT_FALSE(migration_operations.empty()); + EXPECT_EQ(1ul, migration_operations.size()); + + // Grab the next map id, verify it doesn't crash. + s = db()->Get(options, leveldb::Slice("next-map-id"), &db_value); + EXPECT_TRUE(s.ok()); + metadata.ParseNextMapId(leveldb::StdStringToUint8Vector(db_value)); + + // Get all keys-value pairs with the given key prefix + std::vector values; + { + std::unique_ptr it(db()->NewIterator(options)); + it->Seek(leveldb::Slice("namespace-")); + for (; it->Valid(); it->Next()) { + if (!it->key().starts_with(leveldb::Slice("namespace-"))) + break; + leveldb::mojom::KeyValuePtr kv = leveldb::mojom::KeyValue::New(); + kv->key = leveldb::GetVectorFor(it->key()); + kv->value = leveldb::GetVectorFor(it->value()); + values.push_back(std::move(kv)); + } + EXPECT_TRUE(it->status().ok()); + } + + EXPECT_TRUE( + metadata.ParseNamespaces(std::move(values), &migration_operations)); + EXPECT_FALSE(migration_operations.empty()); + EXPECT_EQ(3ul, migration_operations.size()); + + EXPECT_TRUE(base::ContainsValue( + migration_operations, + leveldb::mojom::BatchedOperation::New( + leveldb::mojom::BatchOperationType::PUT_KEY, + StdStringToUint8Vector("version"), StdStringToUint8Vector("1")))); + EXPECT_TRUE(base::ContainsValue( + migration_operations, + leveldb::mojom::BatchedOperation::New( + leveldb::mojom::BatchOperationType::DELETE_KEY, + StdStringToUint8Vector("map-0-"), base::nullopt))); + EXPECT_TRUE(base::ContainsValue( + migration_operations, + leveldb::mojom::BatchedOperation::New( + leveldb::mojom::BatchOperationType::DELETE_KEY, + StdStringToUint8Vector("namespace-"), base::nullopt))); +} + +} // namespace + +} // namespace content diff --git a/chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo.cc b/chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo.cc new file mode 100644 index 00000000000..d02473e3d23 --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo.cc @@ -0,0 +1,178 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/session_storage_namespace_impl_mojo.h" + +#include +#include +#include + +#include "components/services/leveldb/public/cpp/util.h" +#include "content/public/browser/child_process_security_policy.h" + +namespace content { + +SessionStorageNamespaceImplMojo::SessionStorageNamespaceImplMojo( + std::string namespace_id, + SessionStorageDataMap::Listener* data_map_listener, + RegisterShallowClonedNamespace add_namespace_callback, + SessionStorageLevelDBWrapper::RegisterNewAreaMap register_new_map_callback) + : namespace_id_(std::move(namespace_id)), + data_map_listener_(data_map_listener), + add_namespace_callback_(std::move(add_namespace_callback)), + register_new_map_callback_(std::move(register_new_map_callback)) {} + +SessionStorageNamespaceImplMojo::~SessionStorageNamespaceImplMojo() = default; + +bool SessionStorageNamespaceImplMojo::HasAreaForOrigin( + const url::Origin& origin) const { + return origin_areas_.find(origin) != origin_areas_.end(); +} + +void SessionStorageNamespaceImplMojo::PopulateFromMetadata( + leveldb::mojom::LevelDBDatabase* database, + SessionStorageMetadata::NamespaceEntry namespace_metadata, + const std::map, SessionStorageDataMap*>& + current_data_maps) { + DCHECK(!IsPopulated()); + DCHECK(!waiting_on_clone_population()); + database_ = database; + populated_ = true; + namespace_entry_ = namespace_metadata; + for (const auto& pair : namespace_entry_->second) { + scoped_refptr data_map; + auto map_it = current_data_maps.find(pair.second->MapNumberAsBytes()); + if (map_it == current_data_maps.end()) { + data_map = SessionStorageDataMap::Create(data_map_listener_, pair.second, + database_); + } else { + data_map = base::WrapRefCounted(map_it->second); + } + origin_areas_[pair.first] = std::make_unique( + namespace_entry_, pair.first, std::move(data_map), + register_new_map_callback_); + } +} + +void SessionStorageNamespaceImplMojo::PopulateAsClone( + leveldb::mojom::LevelDBDatabase* database, + SessionStorageMetadata::NamespaceEntry namespace_metadata, + const OriginAreas& areas_to_clone) { + DCHECK(!IsPopulated()); + database_ = database; + populated_ = true; + waiting_on_clone_population_ = false; + namespace_entry_ = namespace_metadata; + std::transform(areas_to_clone.begin(), areas_to_clone.end(), + std::inserter(origin_areas_, origin_areas_.begin()), + [namespace_metadata](const auto& source) { + return std::make_pair( + source.first, source.second->Clone(namespace_metadata)); + }); + if (!run_after_clone_population_.empty()) { + for (base::OnceClosure& callback : run_after_clone_population_) + std::move(callback).Run(); + run_after_clone_population_.clear(); + } +} + +void SessionStorageNamespaceImplMojo::Reset() { + namespace_entry_ = SessionStorageMetadata::NamespaceEntry(); + process_id_ = ChildProcessHost::kInvalidUniqueID; + database_ = nullptr; + waiting_on_clone_population_ = false; + bind_waiting_on_clone_population_ = false; + run_after_clone_population_.clear(); + populated_ = false; + origin_areas_.clear(); + bindings_.CloseAllBindings(); +} + +void SessionStorageNamespaceImplMojo::Bind( + mojom::SessionStorageNamespaceRequest request, + int process_id) { + if (waiting_on_clone_population_) { + bind_waiting_on_clone_population_ = true; + run_after_clone_population_.push_back( + base::BindOnce(&SessionStorageNamespaceImplMojo::Bind, + base::Unretained(this), std::move(request), process_id)); + return; + } + DCHECK(IsPopulated()); + process_id_ = process_id; + bindings_.AddBinding(this, std::move(request)); + bind_waiting_on_clone_population_ = false; +} + +void SessionStorageNamespaceImplMojo::PurgeUnboundWrappers() { + auto it = origin_areas_.begin(); + while (it != origin_areas_.end()) { + if (!it->second->IsBound()) + it = origin_areas_.erase(it); + } +} + +void SessionStorageNamespaceImplMojo::RemoveOriginData( + const url::Origin& origin) { + if (waiting_on_clone_population_) { + run_after_clone_population_.push_back( + base::BindOnce(&SessionStorageNamespaceImplMojo::RemoveOriginData, + base::Unretained(this), origin)); + return; + } + DCHECK(IsPopulated()); + auto it = origin_areas_.find(origin); + if (it == origin_areas_.end()) + return; + // Renderer process expects |source| to always be two newline separated + // strings. + it->second->DeleteAll("\n", base::DoNothing()); + it->second->data_map()->level_db_wrapper()->ScheduleImmediateCommit(); +} + +void SessionStorageNamespaceImplMojo::OpenArea( + const url::Origin& origin, + mojom::LevelDBWrapperAssociatedRequest database) { + DCHECK(IsPopulated()); + DCHECK(!bindings_.empty()); + DCHECK_NE(process_id_, ChildProcessHost::kInvalidUniqueID); + if (!ChildProcessSecurityPolicy::GetInstance()->CanAccessDataForOrigin( + process_id_, origin.GetURL())) { + bindings_.ReportBadMessage("Access denied for sessionStorage request"); + return; + } + auto it = origin_areas_.find(origin); + if (it == origin_areas_.end()) { + it = origin_areas_ + .emplace(std::make_pair( + origin, std::make_unique( + namespace_entry_, origin, + SessionStorageDataMap::Create( + data_map_listener_, + register_new_map_callback_.Run( + namespace_entry_, origin), + database_), + register_new_map_callback_))) + .first; + } + it->second->Bind(std::move(database)); +} + +void SessionStorageNamespaceImplMojo::Clone( + const std::string& clone_to_namespace) { + add_namespace_callback_.Run(namespace_entry_, clone_to_namespace, + origin_areas_); +} + +void SessionStorageNamespaceImplMojo::FlushOriginForTesting( + const url::Origin& origin) { + if (!IsPopulated()) + return; + auto it = origin_areas_.find(origin); + if (it == origin_areas_.end()) + return; + it->second->data_map()->level_db_wrapper()->ScheduleImmediateCommit(); +} + +} // namespace content diff --git a/chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo.h b/chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo.h new file mode 100644 index 00000000000..5936c469fc7 --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo.h @@ -0,0 +1,158 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_NAMESPACE_IMPL_MOJO_H_ +#define CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_NAMESPACE_IMPL_MOJO_H_ + +#include + +#include "base/callback.h" +#include "base/containers/flat_map.h" +#include "base/memory/ref_counted.h" +#include "content/browser/dom_storage/session_storage_data_map.h" +#include "content/browser/dom_storage/session_storage_leveldb_wrapper.h" +#include "content/browser/dom_storage/session_storage_metadata.h" +#include "content/common/leveldb_wrapper.mojom.h" +#include "content/common/storage_partition_service.mojom.h" +#include "content/public/common/child_process_host.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" +#include "url/origin.h" + +namespace content { + +// Implements the mojo interface SessionStorageNamespace. Stores data maps per +// origin, which are accessible using the LevelDBWrapper interface with the +// |OpenArea| call. Supports cloning (shallow cloning with copy-on-write +// behavior) from another SessionStorageNamespaceImplMojo. +// +// This class is populated & bound in the following patterns: +// 1. The namespace is new or being populated from data on disk, and +// |PopulateFromMetadata| is called. Afterwards |Bind| can be called. +// 2. The namespace is being created as a clone from a |Clone| call on another +// SessionStorageNamespaceImplMojo. PopulateFromMetadata is called with the +// data from the other namespace, and then |Bind| can be called afterwards. +// 3. The namespace is being created as a clone, but the |Clone| call from the +// source namespace hasn't been called yet. |SetWaitingForClonePopulation| is +// called first, after which |Bind| can be called. The actually binding +// doesn't happen until |PopulateAsClone| is finally called with the source +// namespace data. +// Note: The reason for cases 2 and 3 is because there are two ways the Session +// Storage system knows about clones. First, it gets the |Clone| call on the +// source namespace, coming from the renderer doing the navigation, and in the +// correct order with any session storage modifications from that source +// renderer. Second, the RenderViewHostImpl of the navigated-to-frame will +// create the cloned namespace and expect to manage it's lifetime that way, and +// this can happen before the first case, as they are on different task runners. +class CONTENT_EXPORT SessionStorageNamespaceImplMojo final + : public mojom::SessionStorageNamespace { + public: + using OriginAreas = + std::map>; + using RegisterShallowClonedNamespace = base::RepeatingCallback; + + // Constructs a namespace with the given |namespace_id|, expecting to be + // populated and bound later (see class comment). The |database| and + // |data_map_listener| are given to any data maps constructed for this + // namespace. The |add_namespace_callback| is called when the |Clone| method + // is called by mojo. The |register_new_map_callback| is given to the the + // SessionStorageLevelDBWrapper's, used per-origin, that are bound to in + // OpenArea. + SessionStorageNamespaceImplMojo( + std::string namespace_id, + SessionStorageDataMap::Listener* data_map_listener, + RegisterShallowClonedNamespace add_namespace_callback, + SessionStorageLevelDBWrapper::RegisterNewAreaMap + register_new_map_callback); + + ~SessionStorageNamespaceImplMojo() override; + + // Returns if a storage area exists for the given origin in this map. + bool HasAreaForOrigin(const url::Origin& origin) const; + + void SetWaitingForClonePopulation() { waiting_on_clone_population_ = true; } + + bool waiting_on_clone_population() { return waiting_on_clone_population_; } + + // Called when this is a new namespace, or when the namespace was loaded from + // disk. Should be called before |Bind|. + void PopulateFromMetadata( + leveldb::mojom::LevelDBDatabase* database, + SessionStorageMetadata::NamespaceEntry namespace_metadata, + const std::map, SessionStorageDataMap*>& + current_data_maps); + + // Can either be called before |Bind|, or if the source namespace isn't + // available yet, |SetWaitingForClonePopulation| can be called. Then |Bind| + // will work, and hold onto the request until after this method is called. + void PopulateAsClone( + leveldb::mojom::LevelDBDatabase* database, + SessionStorageMetadata::NamespaceEntry namespace_metadata, + const OriginAreas& areas_to_clone); + + // Resets to a pre-populated and pre-bound state. Used when the owner needs to + // delete & recreate the database. + void Reset(); + + SessionStorageMetadata::NamespaceEntry namespace_entry() { + return namespace_entry_; + } + + bool IsPopulated() const { return populated_; } + + // Must be preceded by a call to |PopulateFromMetadata|, |PopulateAsClone|, or + // |SetWaitingForClonePopulation|. For the later case, |PopulateAsClone| must + // eventually be called before the SessionStorageNamespaceRequest can be + // bound. + void Bind(mojom::SessionStorageNamespaceRequest request, int process_id); + + bool IsBound() const { + return !bindings_.empty() || bind_waiting_on_clone_population_; + } + + // Removes any LevelDBWrappers bound in |OpenArea| that are no longer bound. + void PurgeUnboundWrappers(); + + // Removes data for the given origin from this namespace. If there is no data + // map for that given origin, this does nothing. + void RemoveOriginData(const url::Origin& origin); + + // SessionStorageNamespace: + // Connects the given database mojo request to the data map for the given + // origin. Before connection, it checks to make sure the |process_id| given to + // the |Bind| method can access the given origin. + void OpenArea(const url::Origin& origin, + mojom::LevelDBWrapperAssociatedRequest database) override; + + // Simply calls the |add_namespace_callback_| callback with this namespace's + // data. + void Clone(const std::string& clone_to_namespace) override; + + void FlushOriginForTesting(const url::Origin& origin); + + private: + const std::string namespace_id_; + SessionStorageMetadata::NamespaceEntry namespace_entry_; + int process_id_ = ChildProcessHost::kInvalidUniqueID; + leveldb::mojom::LevelDBDatabase* database_; + + SessionStorageDataMap::Listener* data_map_listener_; + RegisterShallowClonedNamespace add_namespace_callback_; + SessionStorageLevelDBWrapper::RegisterNewAreaMap register_new_map_callback_; + + bool waiting_on_clone_population_ = false; + bool bind_waiting_on_clone_population_ = false; + std::vector run_after_clone_population_; + + bool populated_ = false; + OriginAreas origin_areas_; + mojo::BindingSet bindings_; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_NAMESPACE_IMPL_MOJO_H_ diff --git a/chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc b/chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc new file mode 100644 index 00000000000..7ee00233e60 --- /dev/null +++ b/chromium/content/browser/dom_storage/session_storage_namespace_impl_mojo_unittest.cc @@ -0,0 +1,458 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/dom_storage/session_storage_namespace_impl_mojo.h" + +#include "base/guid.h" +#include "base/memory/ptr_util.h" +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "components/services/leveldb/public/cpp/util.h" +#include "content/browser/child_process_security_policy_impl.h" +#include "content/browser/dom_storage/session_storage_data_map.h" +#include "content/browser/dom_storage/session_storage_metadata.h" +#include "content/test/fake_leveldb_database.h" +#include "content/test/leveldb_wrapper_test_util.h" +#include "mojo/edk/embedder/embedder.h" +#include "mojo/public/cpp/bindings/strong_associated_binding.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace content { +namespace { +using leveldb::StdStringToUint8Vector; +using leveldb::mojom::DatabaseError; +using NamespaceEntry = SessionStorageMetadata::NamespaceEntry; + +constexpr const int kTestProcessIdOrigin1 = 11; +constexpr const int kTestProcessIdAllOrigins = 12; +constexpr const int kTestProcessIdOrigin3 = 13; + +class MockListener : public SessionStorageDataMap::Listener { + public: + MockListener() {} + ~MockListener() override {} + MOCK_METHOD2(OnDataMapCreation, + void(const std::vector& map_id, + SessionStorageDataMap* map)); + MOCK_METHOD1(OnDataMapDestruction, void(const std::vector& map_id)); + MOCK_METHOD1(OnCommitResult, void(leveldb::mojom::DatabaseError error)); +}; + +class SessionStorageNamespaceImplMojoTest : public testing::Test { + public: + SessionStorageNamespaceImplMojoTest() + : test_namespace_id1_(base::GenerateGUID()), + test_namespace_id2_(base::GenerateGUID()), + test_origin1_(url::Origin::Create(GURL("https://host1.com:1/"))), + test_origin2_(url::Origin::Create(GURL("https://host2.com:2/"))), + test_origin3_(url::Origin::Create(GURL("https://host3.com:3/"))), + database_(&mock_data_) {} + ~SessionStorageNamespaceImplMojoTest() override = default; + + void SetUp() override { + // Create a database that already has a namespace saved. + metadata_.SetupNewDatabase(); + std::vector save_operations; + NamespaceEntry entry = + metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_); + auto map_id = + metadata_.RegisterNewMap(entry, test_origin1_, &save_operations); + DCHECK(map_id->KeyPrefix() == StdStringToUint8Vector("map-0-")); + database_.Write(std::move(save_operations), base::DoNothing()); + // Put some data in one of the maps. + mock_data_[StdStringToUint8Vector("map-0-key1")] = + StdStringToUint8Vector("data1"); + + auto* security_policy = ChildProcessSecurityPolicyImpl::GetInstance(); + security_policy->Add(kTestProcessIdOrigin1); + security_policy->Add(kTestProcessIdAllOrigins); + security_policy->Add(kTestProcessIdOrigin3); + security_policy->AddIsolatedOrigins( + {test_origin1_, test_origin2_, test_origin3_}); + security_policy->LockToOrigin(kTestProcessIdOrigin1, + test_origin1_.GetURL()); + security_policy->LockToOrigin(kTestProcessIdOrigin3, + test_origin3_.GetURL()); + + mojo::edk::SetDefaultProcessErrorCallback( + base::BindRepeating(&SessionStorageNamespaceImplMojoTest::OnBadMessage, + base::Unretained(this))); + } + + void OnBadMessage(const std::string& reason) { bad_message_called_ = true; } + + void TearDown() override { + auto* security_policy = ChildProcessSecurityPolicyImpl::GetInstance(); + security_policy->Remove(kTestProcessIdOrigin1); + security_policy->Remove(kTestProcessIdAllOrigins); + security_policy->Remove(kTestProcessIdOrigin3); + + mojo::edk::SetDefaultProcessErrorCallback( + mojo::edk::ProcessErrorCallback()); + } + + // Creates a SessionStorageNamespaceImplMojo, saves it in the namespaces_ map, + // and returns a pointer to the object. + SessionStorageNamespaceImplMojo* CreateSessionStorageNamespaceImplMojo( + const std::string& namespace_id) { + DCHECK(namespaces_.find(namespace_id) == namespaces_.end()); + SessionStorageNamespaceImplMojo::RegisterShallowClonedNamespace + add_namespace_callback = + base::BindRepeating(&SessionStorageNamespaceImplMojoTest:: + RegisterShallowClonedNamespace, + base::Unretained(this)); + SessionStorageLevelDBWrapper::RegisterNewAreaMap map_id_callback = + base::BindRepeating( + &SessionStorageNamespaceImplMojoTest::RegisterNewAreaMap, + base::Unretained(this)); + + auto namespace_impl = std::make_unique( + namespace_id, &listener_, std::move(add_namespace_callback), + std::move(map_id_callback)); + auto* namespace_impl_ptr = namespace_impl.get(); + namespaces_[namespace_id] = std::move(namespace_impl); + return namespace_impl_ptr; + } + + scoped_refptr RegisterNewAreaMap( + NamespaceEntry namespace_entry, + const url::Origin& origin) { + std::vector save_operations; + auto map_data = + metadata_.RegisterNewMap(namespace_entry, origin, &save_operations); + database_.Write(std::move(save_operations), base::DoNothing()); + return map_data; + } + + void RegisterShallowClonedNamespace( + NamespaceEntry source_namespace, + const std::string& destination_namespace, + const SessionStorageNamespaceImplMojo::OriginAreas& areas_to_clone) { + std::vector save_operations; + NamespaceEntry namespace_entry = + metadata_.GetOrCreateNamespaceEntry(destination_namespace); + metadata_.RegisterShallowClonedNamespace(source_namespace, namespace_entry, + &save_operations); + database_.Write(std::move(save_operations), base::DoNothing()); + + auto it = namespaces_.find(destination_namespace); + if (it == namespaces_.end()) { + auto* namespace_impl = + CreateSessionStorageNamespaceImplMojo(destination_namespace); + namespace_impl->PopulateAsClone(&database_, namespace_entry, + areas_to_clone); + return; + } + it->second->PopulateAsClone(&database_, namespace_entry, areas_to_clone); + } + + protected: + base::test::ScopedTaskEnvironment task_environment_; + const std::string test_namespace_id1_; + const std::string test_namespace_id2_; + const url::Origin test_origin1_; + const url::Origin test_origin2_; + const url::Origin test_origin3_; + SessionStorageMetadata metadata_; + bool bad_message_called_ = false; + + std::map> + namespaces_; + + testing::StrictMock listener_; + std::map, std::vector> mock_data_; + FakeLevelDBDatabase database_; +}; + +TEST_F(SessionStorageNamespaceImplMojoTest, MetadataLoad) { + // Exercises creation, population, binding, and getting all data. + SessionStorageNamespaceImplMojo* namespace_impl = + CreateSessionStorageNamespaceImplMojo(test_namespace_id1_); + + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + namespace_impl->PopulateFromMetadata( + &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), + std::map, SessionStorageDataMap*>()); + + mojom::SessionStorageNamespacePtr ss_namespace; + namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1); + + mojom::LevelDBWrapperAssociatedPtr leveldb_1; + ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1)); + + std::vector data; + DatabaseError status = test::GetAllSync(leveldb_1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(1ul, data.size()); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key1"), + StdStringToUint8Vector("data1")))); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); + namespaces_.clear(); +} + +TEST_F(SessionStorageNamespaceImplMojoTest, MetadataLoadWithMapOperations) { + // Exercises creation, population, binding, and a map operation, and then + // getting all the data. + SessionStorageNamespaceImplMojo* namespace_impl = + CreateSessionStorageNamespaceImplMojo(test_namespace_id1_); + + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + namespace_impl->PopulateFromMetadata( + &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), + std::map, SessionStorageDataMap*>()); + + mojom::SessionStorageNamespacePtr ss_namespace; + namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1); + + mojom::LevelDBWrapperAssociatedPtr leveldb_1; + ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1)); + + EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(1); + test::PutSync(leveldb_1.get(), StdStringToUint8Vector("key2"), + StdStringToUint8Vector("data2"), base::nullopt, ""); + + std::vector data; + DatabaseError status = test::GetAllSync(leveldb_1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(2ul, data.size()); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key1"), + StdStringToUint8Vector("data1")))); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key2"), + StdStringToUint8Vector("data2")))); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); + namespaces_.clear(); +} + +TEST_F(SessionStorageNamespaceImplMojoTest, CloneBeforeBind) { + // Exercises cloning the namespace before we bind to the new cloned namespace. + SessionStorageNamespaceImplMojo* namespace_impl1 = + CreateSessionStorageNamespaceImplMojo(test_namespace_id1_); + SessionStorageNamespaceImplMojo* namespace_impl2 = + CreateSessionStorageNamespaceImplMojo(test_namespace_id2_); + + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + namespace_impl1->PopulateFromMetadata( + &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), + std::map, SessionStorageDataMap*>()); + + mojom::SessionStorageNamespacePtr ss_namespace1; + namespace_impl1->Bind(mojo::MakeRequest(&ss_namespace1), + kTestProcessIdOrigin1); + ss_namespace1->Clone(test_namespace_id2_); + ss_namespace1.FlushForTesting(); + + ASSERT_TRUE(namespace_impl2->IsPopulated()); + + mojom::SessionStorageNamespacePtr ss_namespace2; + namespace_impl2->Bind(mojo::MakeRequest(&ss_namespace2), + kTestProcessIdOrigin1); + mojom::LevelDBWrapperAssociatedPtr leveldb_2; + ss_namespace2->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_2)); + + // Do a put in the cloned namespace. + EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(2); + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("1"), testing::_)) + .Times(1); + test::PutSync(leveldb_2.get(), StdStringToUint8Vector("key2"), + StdStringToUint8Vector("data2"), base::nullopt, ""); + + std::vector data; + DatabaseError status = test::GetAllSync(leveldb_2.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(2ul, data.size()); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key1"), + StdStringToUint8Vector("data1")))); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key2"), + StdStringToUint8Vector("data2")))); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1"))) + .Times(1); + namespaces_.clear(); +} + +TEST_F(SessionStorageNamespaceImplMojoTest, CloneAfterBind) { + // Exercises cloning the namespace before we bind to the new cloned namespace. + // Unlike the test above, we create a new area for the test_origin2_ in the + // new namespace. + SessionStorageNamespaceImplMojo* namespace_impl1 = + CreateSessionStorageNamespaceImplMojo(test_namespace_id1_); + SessionStorageNamespaceImplMojo* namespace_impl2 = + CreateSessionStorageNamespaceImplMojo(test_namespace_id2_); + + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + namespace_impl1->PopulateFromMetadata( + &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), + std::map, SessionStorageDataMap*>()); + + mojom::SessionStorageNamespacePtr ss_namespace1; + namespace_impl1->Bind(mojo::MakeRequest(&ss_namespace1), + kTestProcessIdOrigin1); + + // Set that we are waiting for clone, so binding is possible. + namespace_impl2->SetWaitingForClonePopulation(); + + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("1"), testing::_)) + .Times(1); + // Get a new area. + mojom::SessionStorageNamespacePtr ss_namespace2; + namespace_impl2->Bind(mojo::MakeRequest(&ss_namespace2), + kTestProcessIdAllOrigins); + mojom::LevelDBWrapperAssociatedPtr leveldb_n2_o1; + mojom::LevelDBWrapperAssociatedPtr leveldb_n2_o2; + ss_namespace2->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_n2_o1)); + ss_namespace2->OpenArea(test_origin2_, mojo::MakeRequest(&leveldb_n2_o2)); + + // Finally do the clone. + ss_namespace1->Clone(test_namespace_id2_); + ss_namespace1.FlushForTesting(); + EXPECT_FALSE(bad_message_called_); + ASSERT_TRUE(namespace_impl2->IsPopulated()); + + // Do a put in the cloned namespace. + EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(1); + test::PutSync(leveldb_n2_o2.get(), StdStringToUint8Vector("key2"), + StdStringToUint8Vector("data2"), base::nullopt, ""); + + std::vector data; + DatabaseError status = test::GetAllSync(leveldb_n2_o1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(1ul, data.size()); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key1"), + StdStringToUint8Vector("data1")))); + + data.clear(); + status = test::GetAllSync(leveldb_n2_o2.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(1ul, data.size()); + EXPECT_TRUE(base::ContainsValue( + data, mojom::KeyValue::New(StdStringToUint8Vector("key2"), + StdStringToUint8Vector("data2")))); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("1"))) + .Times(1); + namespaces_.clear(); +} + +TEST_F(SessionStorageNamespaceImplMojoTest, RemoveOriginData) { + SessionStorageNamespaceImplMojo* namespace_impl = + CreateSessionStorageNamespaceImplMojo(test_namespace_id1_); + + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + namespace_impl->PopulateFromMetadata( + &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), + std::map, SessionStorageDataMap*>()); + + mojom::SessionStorageNamespacePtr ss_namespace; + namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1); + + mojom::LevelDBWrapperAssociatedPtr leveldb_1; + ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1)); + ss_namespace.FlushForTesting(); + + EXPECT_CALL(listener_, OnCommitResult(DatabaseError::OK)).Times(1); + namespace_impl->RemoveOriginData(test_origin1_); + + std::vector data; + DatabaseError status = test::GetAllSync(leveldb_1.get(), &data); + EXPECT_EQ(DatabaseError::OK, status); + EXPECT_EQ(0ul, data.size()); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); + namespaces_.clear(); +} + +TEST_F(SessionStorageNamespaceImplMojoTest, ProcessLockedToOtherOrigin) { + // Tries to open an area with a process that is locked to a different origin + // and verifies the bad message callback. + SessionStorageNamespaceImplMojo* namespace_impl = + CreateSessionStorageNamespaceImplMojo(test_namespace_id1_); + + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + namespace_impl->PopulateFromMetadata( + &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), + std::map, SessionStorageDataMap*>()); + + mojom::SessionStorageNamespacePtr ss_namespace; + namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1); + mojom::LevelDBWrapperAssociatedPtr leveldb_1; + ss_namespace->OpenArea(test_origin3_, mojo::MakeRequest(&leveldb_1)); + ss_namespace.FlushForTesting(); + EXPECT_TRUE(bad_message_called_); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); + namespaces_.clear(); +} + +TEST_F(SessionStorageNamespaceImplMojoTest, PurgeUnused) { + // Verifies that wrappers are kept alive after the area is unbound, and they + // are removed when PurgeUnboundWrappers() is called. + SessionStorageNamespaceImplMojo* namespace_impl = + CreateSessionStorageNamespaceImplMojo(test_namespace_id1_); + + EXPECT_CALL(listener_, + OnDataMapCreation(StdStringToUint8Vector("0"), testing::_)) + .Times(1); + + namespace_impl->PopulateFromMetadata( + &database_, metadata_.GetOrCreateNamespaceEntry(test_namespace_id1_), + std::map, SessionStorageDataMap*>()); + + mojom::SessionStorageNamespacePtr ss_namespace; + namespace_impl->Bind(mojo::MakeRequest(&ss_namespace), kTestProcessIdOrigin1); + + mojom::LevelDBWrapperAssociatedPtr leveldb_1; + ss_namespace->OpenArea(test_origin1_, mojo::MakeRequest(&leveldb_1)); + EXPECT_TRUE(namespace_impl->HasAreaForOrigin(test_origin1_)); + + EXPECT_CALL(listener_, OnDataMapDestruction(StdStringToUint8Vector("0"))) + .Times(1); + leveldb_1.reset(); + EXPECT_TRUE(namespace_impl->HasAreaForOrigin(test_origin1_)); + + namespace_impl->PurgeUnboundWrappers(); + EXPECT_FALSE(namespace_impl->HasAreaForOrigin(test_origin1_)); + + namespaces_.clear(); +} + +} // namespace +} // namespace content diff --git a/chromium/content/browser/download/blob_download_url_loader_factory_getter.cc b/chromium/content/browser/download/blob_download_url_loader_factory_getter.cc index 93027498724..d5d418186a1 100644 --- a/chromium/content/browser/download/blob_download_url_loader_factory_getter.cc +++ b/chromium/content/browser/download/blob_download_url_loader_factory_getter.cc @@ -6,7 +6,7 @@ #include "components/download/public/common/download_task_runner.h" #include "content/browser/url_loader_factory_getter.h" -#include "content/common/wrapper_shared_url_loader_factory.h" +#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "storage/browser/blob/blob_data_handle.h" #include "storage/browser/blob/blob_url_loader_factory.h" @@ -31,7 +31,7 @@ BlobDownloadURLLoaderFactoryGetter::GetURLLoaderFactory() { storage::BlobURLLoaderFactory::Create( std::move(blob_data_handle_), url_, mojo::MakeRequest(&url_loader_factory_ptr_info)); - return base::MakeRefCounted( + return base::MakeRefCounted( std::move(url_loader_factory_ptr_info)); } diff --git a/chromium/content/browser/download/byte_stream_input_stream.cc b/chromium/content/browser/download/byte_stream_input_stream.cc index 24782195796..d62aefaa703 100644 --- a/chromium/content/browser/download/byte_stream_input_stream.cc +++ b/chromium/content/browser/download/byte_stream_input_stream.cc @@ -4,13 +4,16 @@ #include "content/browser/download/byte_stream_input_stream.h" +#include "components/download/public/common/download_task_runner.h" #include "content/browser/byte_stream.h" namespace content { ByteStreamInputStream::ByteStreamInputStream( std::unique_ptr stream_reader) - : stream_reader_(std::move(stream_reader)), + : stream_reader_( + stream_reader.release(), + base::OnTaskRunnerDeleter(download::GetDownloadTaskRunner())), completion_status_(download::DOWNLOAD_INTERRUPT_REASON_NONE) {} ByteStreamInputStream::~ByteStreamInputStream() = default; diff --git a/chromium/content/browser/download/byte_stream_input_stream.h b/chromium/content/browser/download/byte_stream_input_stream.h index 1deaeb4b5a7..3ec4cbca9ff 100644 --- a/chromium/content/browser/download/byte_stream_input_stream.h +++ b/chromium/content/browser/download/byte_stream_input_stream.h @@ -30,7 +30,7 @@ class CONTENT_EXPORT ByteStreamInputStream : public download::InputStream { private: // ByteStreamReader to read from. - std::unique_ptr stream_reader_; + std::unique_ptr stream_reader_; // Status when the response completes. download::DownloadInterruptReason completion_status_; diff --git a/chromium/content/browser/download/download_browsertest.cc b/chromium/content/browser/download/download_browsertest.cc index e642ee09851..1cae12047b7 100644 --- a/chromium/content/browser/download/download_browsertest.cc +++ b/chromium/content/browser/download/download_browsertest.cc @@ -18,7 +18,6 @@ #include "base/format_macros.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" #include "base/metrics/field_trial.h" #include "base/metrics/field_trial_params.h" #include "base/path_service.h" @@ -125,7 +124,7 @@ class DownloadTestContentBrowserClient : public TestContentBrowserClient { class MockDownloadItemObserver : public download::DownloadItem::Observer { public: MockDownloadItemObserver() {} - virtual ~MockDownloadItemObserver() {} + ~MockDownloadItemObserver() override {} MOCK_METHOD1(OnDownloadUpdated, void(download::DownloadItem*)); MOCK_METHOD1(OnDownloadOpened, void(download::DownloadItem*)); @@ -139,7 +138,7 @@ class MockDownloadManagerObserver : public DownloadManager::Observer { manager_ = manager; manager->AddObserver(this); } - virtual ~MockDownloadManagerObserver() { + ~MockDownloadManagerObserver() override { if (manager_) manager_->RemoveObserver(this); } @@ -147,7 +146,7 @@ class MockDownloadManagerObserver : public DownloadManager::Observer { MOCK_METHOD2(OnDownloadCreated, void(DownloadManager*, download::DownloadItem*)); MOCK_METHOD1(ModelChanged, void(DownloadManager*)); - void ManagerGoingDown(DownloadManager* manager) { + void ManagerGoingDown(DownloadManager* manager) override { DCHECK_EQ(manager_, manager); MockManagerGoingDown(manager); @@ -370,7 +369,7 @@ class CountingDownloadFile : public download::DownloadFileImpl { download::GetDownloadTaskRunner()->PostTaskAndReply( FROM_HERE, base::BindOnce(&CountingDownloadFile::GetNumberActiveFiles, &result), - base::MessageLoop::current()->QuitWhenIdleClosure()); + base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); base::RunLoop().Run(); DCHECK_NE(-1, result); return result; @@ -576,6 +575,46 @@ class DownloadCreateObserver : DownloadManager::Observer { base::Closure completion_closure_; }; +class ErrorStreamCountingObserver : download::DownloadItem::Observer { + public: + ErrorStreamCountingObserver() : item_(nullptr), count_(0){}; + + ~ErrorStreamCountingObserver() override { + if (item_) + item_->RemoveObserver(this); + } + + void OnDownloadUpdated(download::DownloadItem* download) override { + std::unique_ptr samples = + histogram_tester_.GetHistogramSamplesSinceCreation( + "Download.ParallelDownloadAddStreamSuccess"); + if (samples->GetCount(0 /* failure */) == count_ && + !completion_closure_.is_null()) + base::ResetAndReturn(&completion_closure_).Run(); + } + + void OnDownloadDestroyed(download::DownloadItem* download) override { + item_ = nullptr; + } + + void WaitForFinished(download::DownloadItem* item, int count) { + item_ = item; + count_ = count; + if (item_) { + item_->AddObserver(this); + base::RunLoop run_loop; + completion_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + } + } + + private: + base::HistogramTester histogram_tester_; + download::DownloadItem* item_; + int count_; + base::Closure completion_closure_; +}; + bool IsDownloadInState(download::DownloadItem::DownloadState state, download::DownloadItem* item) { return item->GetState() == state; @@ -723,7 +762,7 @@ class DownloadContentTest : public ContentBrowserTest { test_delegate_->SetDownloadManager(manager); base::FilePath test_data_dir; - ASSERT_TRUE(PathService::Get(content::DIR_TEST_DATA, &test_data_dir)); + ASSERT_TRUE(base::PathService::Get(content::DIR_TEST_DATA, &test_data_dir)); embedded_test_server()->ServeFilesFromDirectory(test_data_dir); embedded_test_server()->RegisterRequestHandler( base::Bind(&SlowDownloadHttpResponse::HandleSlowDownloadRequest)); @@ -974,7 +1013,8 @@ class ParallelDownloadTest : public DownloadContentTest { void RunResumptionTest( const download::DownloadItem::ReceivedSlices& received_slices, int64_t total_length, - size_t expected_request_count) { + size_t expected_request_count, + bool support_partial_response) { EXPECT_TRUE( base::FeatureList::IsEnabled(download::features::kParallelDownloading)); GURL url = TestDownloadHttpResponse::GetNextURLForDownload(); @@ -983,6 +1023,7 @@ class ParallelDownloadTest : public DownloadContentTest { parameters.etag = "ABC"; parameters.size = total_length; parameters.last_modified = std::string(); + parameters.support_partial_response = support_partial_response; // Needed to specify HTTP connection type to create parallel download. parameters.connection_type = net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1; TestDownloadHttpResponse::StartServing(parameters, server_url); @@ -999,12 +1040,17 @@ class ParallelDownloadTest : public DownloadContentTest { // Resume the parallel download with sparse file and received slices data. download->Resume(); WaitForCompletion(download); - test_response_handler()->WaitUntilCompletion(expected_request_count); - - // Verify number of requests sent to the server. - const TestDownloadResponseHandler::CompletedRequests& completed_requests = - test_response_handler()->completed_requests(); - EXPECT_EQ(expected_request_count, completed_requests.size()); + // TODO(qinmin): count the failed partial responses in DownloadJob when + // support_partial_response is false. EmbeddedTestServer doesn't know + // whether completing or canceling the response will come first. + if (support_partial_response) { + test_response_handler()->WaitUntilCompletion(expected_request_count); + + // Verify number of requests sent to the server. + const TestDownloadResponseHandler::CompletedRequests& completed_requests = + test_response_handler()->completed_requests(); + EXPECT_EQ(expected_request_count, completed_requests.size()); + } // Verify download content on disk. ReadAndVerifyFileContents(parameters.pattern_generator_seed, @@ -1013,6 +1059,7 @@ class ParallelDownloadTest : public DownloadContentTest { // Verifies parallel download completion. void RunCompletionTest(TestDownloadHttpResponse::Parameters& parameters) { + ErrorStreamCountingObserver observer; EXPECT_TRUE( base::FeatureList::IsEnabled(download::features::kParallelDownloading)); @@ -1026,22 +1073,28 @@ class ParallelDownloadTest : public DownloadContentTest { parameters.connection_type = net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1; TestRequestPauseHandler request_pause_handler; parameters.on_pause_handler = request_pause_handler.GetOnPauseHandler(); + // Send some data for the first request and pause it so download won't + // complete before other parallel requests are created. parameters.pause_offset = DownloadRequestCore::kDownloadByteStreamSize; TestDownloadHttpResponse::StartServing(parameters, server_url); download::DownloadItem* download = StartDownloadAndReturnItem(shell(), server_url); - // Send some data for the first request and pause it so download won't - // complete before other parallel requests are created. - test_response_handler()->WaitUntilCompletion(2u); + + if (parameters.support_partial_response) + test_response_handler()->WaitUntilCompletion(2u); + else + observer.WaitForFinished(download, 2); // Now resume the first request. request_pause_handler.Resume(); WaitForCompletion(download); - test_response_handler()->WaitUntilCompletion(3u); - const TestDownloadResponseHandler::CompletedRequests& completed_requests = - test_response_handler()->completed_requests(); - EXPECT_EQ(kTestRequestCount, static_cast(completed_requests.size())); + if (parameters.support_partial_response) { + test_response_handler()->WaitUntilCompletion(3u); + const TestDownloadResponseHandler::CompletedRequests& completed_requests = + test_response_handler()->completed_requests(); + EXPECT_EQ(3u, completed_requests.size()); + } ReadAndVerifyFileContents(parameters.pattern_generator_seed, parameters.size, download->GetTargetFilePath()); } @@ -2141,7 +2194,14 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelResumingDownload) { EXPECT_TRUE(EnsureNoPendingDownloads()); } -IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveResumedDownload) { +// Flaky on ASAN. crbug.com/838403 +#if defined(ADDRESS_SANITIZER) +#define MAYBE_RemoveResumedDownload DISABLED_RemoveResumedDownload +#else +#define MAYBE_RemoveResumedDownload RemoveResumedDownload +#endif // defined(ADDRESS_SANITIZER) + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, MAYBE_RemoveResumedDownload) { SetupErrorInjectionDownloads(); TestDownloadHttpResponse::Parameters parameters = TestDownloadHttpResponse::Parameters::WithSingleInterruption( @@ -2180,7 +2240,13 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveResumedDownload) { test_response_handler()->WaitUntilCompletion(2u); } -IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelResumedDownload) { +// TODO(qinmin): Flaky crashes on ASAN Linux. https://crbug.com/836689 +#if defined(OS_LINUX) && defined(ADDRESS_SANITIZER) +#define MAYBE_CancelResumedDownload DISABLED_CancelResumedDownload +#else +#define MAYBE_CancelResumedDownload CancelResumedDownload +#endif +IN_PROC_BROWSER_TEST_F(DownloadContentTest, MAYBE_CancelResumedDownload) { SetupErrorInjectionDownloads(); TestDownloadHttpResponse::Parameters parameters = TestDownloadHttpResponse::Parameters::WithSingleInterruption( @@ -2919,7 +2985,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeServerError) { } // A cross-origin request that fails before it gets a response from the server -// should result in the old page staying current. +// should result in a network error page. IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeNetworkError) { SetupErrorInjectionDownloads(); GURL url = TestDownloadHttpResponse::GetNextURLForDownload(); @@ -3169,6 +3235,17 @@ IN_PROC_BROWSER_TEST_F(ParallelDownloadTest, OnlyFirstRequestValid) { RunCompletionTest(parameters); } +// The server will send Accept-Ranges header without partial response. +IN_PROC_BROWSER_TEST_F(ParallelDownloadTest, NoPartialResponse) { + TestDownloadHttpResponse::Parameters parameters; + parameters.etag = "ABC"; + parameters.size = 5097152; + parameters.support_byte_ranges = true; + parameters.support_partial_response = false; + + RunCompletionTest(parameters); +} + // Verify parallel download resumption. IN_PROC_BROWSER_TEST_F(ParallelDownloadTest, Resumption) { // Create the received slices data, the last request is not finished and the @@ -3179,7 +3256,8 @@ IN_PROC_BROWSER_TEST_F(ParallelDownloadTest, Resumption) { download::DownloadItem::ReceivedSlice(2000000, 1000, false /* finished */)}; - RunResumptionTest(received_slices, 3000000, kTestRequestCount); + RunResumptionTest(received_slices, 3000000, kTestRequestCount, + true /* support_partial_response */); } // Verifies that if the last slice is finished, parallel download resumption @@ -3194,7 +3272,8 @@ IN_PROC_BROWSER_TEST_F(ParallelDownloadTest, ResumptionLastSliceFinished) { // The server shouldn't receive an additional request, since the last slice // is marked as finished. - RunResumptionTest(received_slices, 3000000, kTestRequestCount - 1); + RunResumptionTest(received_slices, 3000000, kTestRequestCount - 1, + true /* support_partial_response */); } // Verifies that if the last slice is finished, but the database record is not @@ -3210,7 +3289,23 @@ IN_PROC_BROWSER_TEST_F(ParallelDownloadTest, ResumptionLastSliceUnfinished) { // Client will send an out of range request where server will send back HTTP // range not satisfied, and download can complete. - RunResumptionTest(received_slices, 3000000, kTestRequestCount); + RunResumptionTest(received_slices, 3000000, kTestRequestCount, + true /* support_partial_response */); +} + +// Verify that if server doesn't support partial response, resuming a parallel +// download should complete the download. +IN_PROC_BROWSER_TEST_F(ParallelDownloadTest, ResumptionNoPartialResponse) { + // Create the received slices data, the last request is not finished and the + // server will send more data to finish the last slice. + std::vector received_slices = { + download::DownloadItem::ReceivedSlice(0, 1000), + download::DownloadItem::ReceivedSlice(1000000, 1000), + download::DownloadItem::ReceivedSlice(2000000, 1000, + false /* finished */)}; + + RunResumptionTest(received_slices, 3000000, kTestRequestCount, + false /* support_partial_response */); } // Test to verify that the browser-side enforcement of X-Frame-Options does @@ -3375,7 +3470,8 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, FetchErrorResponseBodyResumption) { std::string("header_value")); } -IN_PROC_BROWSER_TEST_F(DownloadContentTest, ForceDownloadMultipartRelatedPage) { +IN_PROC_BROWSER_TEST_F(DownloadContentTest, + DISABLED_ForceDownloadMultipartRelatedPage) { // Force downloading the MHTML. DownloadTestContentBrowserClient new_client; new_client.set_allowed_rendering_mhtml_over_http(false); diff --git a/chromium/content/browser/download/download_manager_impl.cc b/chromium/content/browser/download/download_manager_impl.cc index 316ddd4982c..081c7a2c2f1 100644 --- a/chromium/content/browser/download/download_manager_impl.cc +++ b/chromium/content/browser/download/download_manager_impl.cc @@ -14,9 +14,7 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" -#include "base/rand_util.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" @@ -27,7 +25,6 @@ #include "components/download/downloader/in_progress/in_progress_cache_impl.h" #include "components/download/public/common/download_create_info.h" #include "components/download/public/common/download_file.h" -#include "components/download/public/common/download_file_factory.h" #include "components/download/public/common/download_interrupt_reasons.h" #include "components/download/public/common/download_item_factory.h" #include "components/download/public/common/download_item_impl.h" @@ -37,13 +34,14 @@ #include "components/download/public/common/download_url_loader_factory_getter.h" #include "components/download/public/common/download_url_parameters.h" #include "components/download/public/common/download_utils.h" -#include "components/download/public/common/resource_downloader.h" #include "components/download/public/common/url_download_handler_factory.h" #include "content/browser/byte_stream.h" #include "content/browser/child_process_security_policy_impl.h" +#include "content/browser/devtools/render_frame_devtools_agent_host.h" #include "content/browser/download/blob_download_url_loader_factory_getter.h" #include "content/browser/download/byte_stream_input_stream.h" #include "content/browser/download/download_resource_handler.h" +#include "content/browser/download/download_url_loader_factory_getter_impl.h" #include "content/browser/download/download_utils.h" #include "content/browser/download/network_download_url_loader_factory_getter.h" #include "content/browser/download/url_downloader.h" @@ -54,7 +52,6 @@ #include "content/browser/storage_partition_impl.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/throttling_url_loader.h" -#include "content/common/wrapper_shared_url_loader_factory.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/download_item_utils.h" @@ -104,7 +101,7 @@ StoragePartitionImpl* GetStoragePartition(BrowserContext* context, } bool CanRequestURLFromRenderer(int render_process_id, GURL url) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Check if the renderer is permitted to request the requested URL. if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( render_process_id, url)) { @@ -115,11 +112,12 @@ bool CanRequestURLFromRenderer(int render_process_id, GURL url) { return true; } +// Creates an interrupted download and calls StartDownload. Can be called on +// any thread. void CreateInterruptedDownload( - download::DownloadUrlParameters* params, + std::unique_ptr params, download::DownloadInterruptReason reason, base::WeakPtr download_manager) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); std::unique_ptr failed_created_info( new download::DownloadCreateInfo( base::Time::Now(), base::WrapUnique(new download::DownloadSaveInfo))); @@ -135,29 +133,6 @@ void CreateInterruptedDownload( nullptr, params->callback())); } -// Helper functions for DownloadItem -> DownloadEntry for InProgressCache. - -uint64_t GetUniqueDownloadId() { - // Get a new UKM download_id that is not 0. - uint64_t download_id = 0; - do { - download_id = base::RandUint64(); - } while (download_id == 0); - return download_id; -} - -download::DownloadEntry CreateDownloadEntryFromItem( - const download::DownloadItem& item, - const std::string& request_origin, - download::DownloadSource download_source, - bool fetch_error_body, - const download::DownloadUrlParameters::RequestHeadersType& - request_headers) { - return download::DownloadEntry(item.GetGuid(), request_origin, - download_source, fetch_error_body, - request_headers, GetUniqueDownloadId()); -} - void BeginDownload(std::unique_ptr params, std::unique_ptr blob_data_handle, content::ResourceContext* resource_context, @@ -189,8 +164,10 @@ void BeginDownload(std::unique_ptr params, // If the download was accepted, the DownloadResourceHandler is now // responsible for driving the request to completion. // Otherwise, create an interrupted download. - if (reason != download::DOWNLOAD_INTERRUPT_REASON_NONE) - CreateInterruptedDownload(params.get(), reason, download_manager); + if (reason != download::DOWNLOAD_INTERRUPT_REASON_NONE) { + CreateInterruptedDownload(std::move(params), reason, download_manager); + return; + } } else { downloader.reset(UrlDownloader::BeginDownload(download_manager, std::move(url_request), @@ -204,48 +181,6 @@ void BeginDownload(std::unique_ptr params, download_manager, std::move(downloader))); } -void BeginResourceDownload( - std::unique_ptr params, - std::unique_ptr request, - scoped_refptr - url_loader_factory_getter, - uint32_t download_id, - base::WeakPtr download_manager, - const GURL& site_url, - const GURL& tab_url, - const GURL& tab_referrer_url, - const scoped_refptr& task_runner) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr downloader( - nullptr, base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get())); - - // Check if the renderer is permitted to request the requested URL. - if (params->render_process_host_id() >= 0 && - !CanRequestURLFromRenderer(params->render_process_host_id(), - params->url())) { - CreateInterruptedDownload( - params.get(), - download::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, - download_manager); - } else { - // TODO(qinmin): Check the storage permission before creating the URLLoader. - // This is already done for context menu download, but it is missing for - // download service and download resumption. - downloader.reset( - download::ResourceDownloader::BeginDownload( - download_manager, std::move(params), std::move(request), - url_loader_factory_getter->GetURLLoaderFactory(), site_url, tab_url, - tab_referrer_url, download_id, false, task_runner) - .release()); - } - task_runner->PostTask( - FROM_HERE, - base::BindOnce( - &download::UrlDownloadHandler::Delegate::OnUrlDownloadHandlerCreated, - download_manager, std::move(downloader))); -} - class DownloadItemFactoryImpl : public download::DownloadItemFactory { public: DownloadItemFactoryImpl() {} @@ -314,83 +249,32 @@ base::FilePath GetTemporaryDownloadDirectory() { } #endif -} // namespace - -// Responsible for persisting the in-progress metadata associated with a -// download. -class InProgressDownloadObserver : public download::DownloadItem::Observer { - public: - explicit InProgressDownloadObserver( - download::InProgressCache* in_progress_cache); - ~InProgressDownloadObserver() override; - - private: - // download::DownloadItem::Observer - void OnDownloadUpdated(download::DownloadItem* download) override; - void OnDownloadRemoved(download::DownloadItem* download) override; - - // The persistent cache to store in-progress metadata. - download::InProgressCache* in_progress_cache_; - - DISALLOW_COPY_AND_ASSIGN(InProgressDownloadObserver); -}; - -InProgressDownloadObserver::InProgressDownloadObserver( - download::InProgressCache* in_progress_cache) - : in_progress_cache_(in_progress_cache) {} - -InProgressDownloadObserver::~InProgressDownloadObserver() = default; - -void InProgressDownloadObserver::OnDownloadUpdated( - download::DownloadItem* download) { - // TODO(crbug.com/778425): Properly handle fail/resume/retry for downloads - // that are in the INTERRUPTED state for a long time. - if (!in_progress_cache_) - return; - - switch (download->GetState()) { - case download::DownloadItem::DownloadState::COMPLETE: - // Intentional fallthrough. - case download::DownloadItem::DownloadState::CANCELLED: - if (in_progress_cache_) - in_progress_cache_->RemoveEntry(download->GetGuid()); - break; - - case download::DownloadItem::DownloadState::INTERRUPTED: - // Intentional fallthrough. - case download::DownloadItem::DownloadState::IN_PROGRESS: { - // Make sure the entry exists in the cache. - base::Optional entry_opt = - in_progress_cache_->RetrieveEntry(download->GetGuid()); - download::DownloadEntry entry; - if (!entry_opt.has_value()) { - entry = CreateDownloadEntryFromItem( - *download, std::string(), /* request_origin */ - download::DownloadSource::UNKNOWN, false, /* fetch_error_body */ - download::DownloadUrlParameters::RequestHeadersType()); - in_progress_cache_->AddOrReplaceEntry(entry); - break; - } - entry = entry_opt.value(); - break; +scoped_refptr +CreateDownloadURLLoaderFactoryGetter(StoragePartitionImpl* storage_partition, + RenderFrameHost* rfh, + bool is_download) { + network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info; + network::mojom::URLLoaderFactoryRequest proxy_factory_request; + if (rfh) { + network::mojom::URLLoaderFactoryPtrInfo devtools_factory_ptr_info; + network::mojom::URLLoaderFactoryRequest devtools_factory_request = + MakeRequest(&devtools_factory_ptr_info); + if (RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory( + static_cast(rfh), true, is_download, + &devtools_factory_request)) { + proxy_factory_ptr_info = std::move(devtools_factory_ptr_info); + proxy_factory_request = std::move(devtools_factory_request); } - - default: - break; } + return base::MakeRefCounted( + storage_partition->url_loader_factory_getter(), + std::move(proxy_factory_ptr_info), std::move(proxy_factory_request)); } -void InProgressDownloadObserver::OnDownloadRemoved( - download::DownloadItem* download) { - if (!in_progress_cache_) - return; - - in_progress_cache_->RemoveEntry(download->GetGuid()); -} +} // namespace DownloadManagerImpl::DownloadManagerImpl(BrowserContext* browser_context) : item_factory_(new DownloadItemFactoryImpl()), - file_factory_(new download::DownloadFileFactory()), shutdown_needed_(true), initialized_(false), history_db_initialized_(false), @@ -403,6 +287,13 @@ DownloadManagerImpl::DownloadManagerImpl(BrowserContext* browser_context) BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)); if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) download::UrlDownloadHandlerFactory::Install(new UrlDownloaderFactory()); + in_progress_manager_ = std::make_unique( + this, base::BindRepeating(&IsOriginSecure)); + in_progress_manager_->Initialize( + IsOffTheRecord() ? base::FilePath() : browser_context_->GetPath(), + base::BindOnce(&DownloadManagerImpl::PostInitialization, + weak_factory_.GetWeakPtr(), + DOWNLOAD_INITIALIZATION_DEPENDENCY_IN_PROGRESS_CACHE)); } DownloadManagerImpl::~DownloadManagerImpl() { @@ -485,28 +376,6 @@ bool DownloadManagerImpl::ShouldOpenDownload( void DownloadManagerImpl::SetDelegate(DownloadManagerDelegate* delegate) { delegate_ = delegate; - - // If initialization has not occurred and there's a delegate and cache, - // initialize and indicate initialization is complete. Else, just indicate it - // is ok to continue. - if (initialized_ || in_progress_cache_initialized_) - return; - - base::RepeatingClosure post_init_callback = base::BindRepeating( - &DownloadManagerImpl::PostInitialization, weak_factory_.GetWeakPtr(), - DOWNLOAD_INITIALIZATION_DEPENDENCY_IN_PROGRESS_CACHE); - - if (delegate_) { - download::InProgressCache* in_progress_cache = - delegate_->GetInProgressCache(); - if (in_progress_cache) { - in_progress_cache->Initialize(std::move(post_init_callback)); - return; - } - } - - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - std::move(post_init_callback)); } DownloadManagerDelegate* DownloadManagerImpl::GetDelegate() const { @@ -539,6 +408,8 @@ void DownloadManagerImpl::Shutdown() { // We'll have nothing more to report to the observers after this point. observers_.Clear(); + in_progress_manager_->ShutDown(); + if (delegate_) delegate_->Shutdown(); delegate_ = nullptr; @@ -558,86 +429,7 @@ bool DownloadManagerImpl::InterceptDownload( return true; } -void DownloadManagerImpl::StartDownload( - std::unique_ptr info, - std::unique_ptr stream, - scoped_refptr shared_url_loader_factory, - const download::DownloadUrlParameters::OnStartedCallback& on_started) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(info); - - uint32_t download_id = info->download_id; - bool new_download = (download_id == download::DownloadItem::kInvalidId); - if (new_download && - info->result == download::DOWNLOAD_INTERRUPT_REASON_NONE && - InterceptDownload(*info)) { - download::GetDownloadTaskRunner()->DeleteSoon(FROM_HERE, stream.release()); - return; - } - - // |stream| is only non-nil if the download request was successful. - DCHECK((info->result == download::DOWNLOAD_INTERRUPT_REASON_NONE && - !stream->IsEmpty()) || - (info->result != download::DOWNLOAD_INTERRUPT_REASON_NONE && - stream->IsEmpty())); - DVLOG(20) << __func__ << "() result=" - << download::DownloadInterruptReasonToString(info->result); - if (new_download) { - download::RecordDownloadConnectionSecurity(info->url(), info->url_chain); - download::RecordDownloadContentTypeSecurity( - info->url(), info->url_chain, info->mime_type, - base::BindRepeating(&IsOriginSecure)); - } - base::Callback got_id(base::Bind( - &DownloadManagerImpl::StartDownloadWithId, weak_factory_.GetWeakPtr(), - base::Passed(&info), base::Passed(&stream), - std::move(shared_url_loader_factory), on_started, new_download)); - if (new_download) { - GetNextId(std::move(got_id)); - } else { - std::move(got_id).Run(download_id); - } -} - -void DownloadManagerImpl::StartDownloadWithId( - std::unique_ptr info, - std::unique_ptr stream, - scoped_refptr shared_url_loader_factory, - const download::DownloadUrlParameters::OnStartedCallback& on_started, - bool new_download, - uint32_t id) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK_NE(download::DownloadItem::kInvalidId, id); - download::DownloadItemImpl* download = nullptr; - if (new_download) { - download = CreateActiveItem(id, *info); - } else { - auto item_iterator = downloads_.find(id); - // Trying to resume an interrupted download. - if (item_iterator == downloads_.end() || - (item_iterator->second->GetState() == - download::DownloadItem::CANCELLED)) { - // If the download is no longer known to the DownloadManager, then it was - // removed after it was resumed. Ignore. If the download is cancelled - // while resuming, then also ignore the request. - if (info->request_handle) - info->request_handle->CancelRequest(true); - if (!on_started.is_null()) - on_started.Run(nullptr, - download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); - // The ByteStreamReader lives and dies on the download sequence. - if (info->result == download::DOWNLOAD_INTERRUPT_REASON_NONE) - download::GetDownloadTaskRunner()->DeleteSoon(FROM_HERE, - stream.release()); - return; - } - download = item_iterator->second.get(); - } - DownloadItemUtils::AttachInfo( - download, GetBrowserContext(), - WebContentsImpl::FromRenderFrameHostID(info->render_process_id, - info->render_frame_id)); - +base::FilePath DownloadManagerImpl::GetDefaultDownloadDirectory() { base::FilePath default_download_directory; #if defined(USE_X11) // TODO(thomasanderson,crbug.com/784010): Remove this when all Linux @@ -660,60 +452,61 @@ void DownloadManagerImpl::StartDownloadWithId( GetContentClient()->browser()->GetDefaultDownloadDirectory(); } - if (delegate_) { - download::InProgressCache* in_progress_cache = - delegate_->GetInProgressCache(); - if (in_progress_cache) { - base::Optional entry_opt = - in_progress_cache->RetrieveEntry(download->GetGuid()); - if (!entry_opt.has_value()) { - in_progress_cache->AddOrReplaceEntry(CreateDownloadEntryFromItem( - *download, info->request_origin, info->download_source, - info->fetch_error_body, info->request_headers)); - } - } + return default_download_directory; +} - if (!in_progress_download_observer_) { - in_progress_download_observer_.reset( - new InProgressDownloadObserver(delegate_->GetInProgressCache())); - } - // May already observe this item, remove observer first. - download->RemoveObserver(in_progress_download_observer_.get()); - download->AddObserver(in_progress_download_observer_.get()); - } +void DownloadManagerImpl::OnNewDownloadStarted( + download::DownloadItem* download) { + for (auto& observer : observers_) + observer.OnDownloadCreated(this, download); +} - std::unique_ptr download_file; +download::DownloadItemImpl* DownloadManagerImpl::GetDownloadItem( + uint32_t id, + bool new_download, + const download::DownloadCreateInfo& info) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK_NE(download::DownloadItem::kInvalidId, id); - if (info->result == download::DOWNLOAD_INTERRUPT_REASON_NONE) { - DCHECK(stream.get()); - download_file.reset(file_factory_->CreateFile( - std::move(info->save_info), default_download_directory, - std::move(stream), id, download->DestinationObserverAsWeakPtr())); + download::DownloadItemImpl* download = nullptr; + if (new_download) { + download = CreateActiveItem(id, info); + } else { + auto item_iterator = downloads_.find(id); + // Trying to resume an interrupted download. + if (item_iterator == downloads_.end() || + (item_iterator->second->GetState() == + download::DownloadItem::CANCELLED)) { + return nullptr; + } + download = item_iterator->second.get(); } - // It is important to leave info->save_info intact in the case of an interrupt - // so that the download::DownloadItem can salvage what it can out of a failed - // resumption attempt. + DownloadItemUtils::AttachInfo( + download, GetBrowserContext(), + WebContentsImpl::FromRenderFrameHostID(info.render_process_id, + info.render_frame_id)); + return download; +} +net::URLRequestContextGetter* DownloadManagerImpl::GetURLRequestContextGetter( + const download::DownloadCreateInfo& info) { StoragePartition* storage_partition = GetStoragePartition( - browser_context_, info->render_process_id, info->render_frame_id); - - download->Start( - std::move(download_file), std::move(info->request_handle), *info, - std::move(shared_url_loader_factory), - storage_partition ? storage_partition->GetURLRequestContext() : nullptr); - - // For interrupted downloads, Start() will transition the state to - // IN_PROGRESS and consumers will be notified via OnDownloadUpdated(). - // For new downloads, we notify here, rather than earlier, so that - // the download_file is bound to download and all the usual - // setters (e.g. Cancel) work. - if (new_download) { - for (auto& observer : observers_) - observer.OnDownloadCreated(this, download); - } + browser_context_, info.render_process_id, info.render_frame_id); + return storage_partition ? storage_partition->GetURLRequestContext() + : nullptr; +} - if (!on_started.is_null()) - on_started.Run(download, download::DOWNLOAD_INTERRUPT_REASON_NONE); +void DownloadManagerImpl::StartDownload( + std::unique_ptr info, + std::unique_ptr stream, + scoped_refptr + url_loader_factory_getter, + const download::DownloadUrlParameters::OnStartedCallback& on_started) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(info); + in_progress_manager_->StartDownload(std::move(info), std::move(stream), + std::move(url_loader_factory_getter), + on_started); } void DownloadManagerImpl::CheckForHistoryFilesRemoval() { @@ -762,14 +555,13 @@ void DownloadManagerImpl::CreateSavePackageDownloadItem( int render_process_id, int render_frame_id, std::unique_ptr request_handle, - const ukm::SourceId ukm_source_id, const DownloadItemImplCreated& item_created) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - GetNextId(base::Bind( - &DownloadManagerImpl::CreateSavePackageDownloadItemWithId, - weak_factory_.GetWeakPtr(), main_file_path, page_url, mime_type, - render_process_id, render_frame_id, - base::Passed(std::move(request_handle)), ukm_source_id, item_created)); + GetNextId( + base::Bind(&DownloadManagerImpl::CreateSavePackageDownloadItemWithId, + weak_factory_.GetWeakPtr(), main_file_path, page_url, + mime_type, render_process_id, render_frame_id, + base::Passed(std::move(request_handle)), item_created)); } void DownloadManagerImpl::CreateSavePackageDownloadItemWithId( @@ -779,7 +571,6 @@ void DownloadManagerImpl::CreateSavePackageDownloadItemWithId( int render_process_id, int render_frame_id, std::unique_ptr request_handle, - const ukm::SourceId ukm_source_id, const DownloadItemImplCreated& item_created, uint32_t id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -797,23 +588,6 @@ void DownloadManagerImpl::CreateSavePackageDownloadItemWithId( observer.OnDownloadCreated(this, download_item); if (!item_created.is_null()) item_created.Run(download_item); - - // Add download_id and source_id for UKM. - auto* delegate = GetDelegate(); - if (delegate) { - download::InProgressCache* in_progress_cache = - delegate_->GetInProgressCache(); - if (in_progress_cache) { - base::Optional entry_opt = - in_progress_cache->RetrieveEntry(download_item->GetGuid()); - if (!entry_opt.has_value()) { - in_progress_cache->AddOrReplaceEntry(CreateDownloadEntryFromItem( - *download_item, std::string(), /* request_origin */ - download::DownloadSource::UNKNOWN, false, /* fetch_error_body */ - download::DownloadUrlParameters::RequestHeadersType())); - } - } - } } // Resume a download of a specific URL. We send the request to the @@ -823,11 +597,8 @@ void DownloadManagerImpl::ResumeInterruptedDownload( std::unique_ptr params, uint32_t id, const GURL& site_url) { - StoragePartitionImpl* storage_partition = static_cast( - BrowserContext::GetStoragePartitionForSite(browser_context_, site_url)); - params->set_url_request_context_getter( - storage_partition->GetURLRequestContext()); - BeginDownloadInternal(std::move(params), nullptr, id, storage_partition); + BeginDownloadInternal(std::move(params), nullptr /* blob_data_handle */, + nullptr /* blob_url_loader_factory */, id, site_url); } void DownloadManagerImpl::SetDownloadItemFactoryForTesting( @@ -837,12 +608,12 @@ void DownloadManagerImpl::SetDownloadItemFactoryForTesting( void DownloadManagerImpl::SetDownloadFileFactoryForTesting( std::unique_ptr file_factory) { - file_factory_ = std::move(file_factory); + in_progress_manager_->set_file_factory(std::move(file_factory)); } download::DownloadFileFactory* DownloadManagerImpl::GetDownloadFileFactoryForTesting() { - return file_factory_.get(); + return in_progress_manager_->file_factory(); } void DownloadManagerImpl::DownloadRemoved( @@ -865,14 +636,7 @@ void DownloadManagerImpl::DownloadInterrupted( base::Optional DownloadManagerImpl::GetInProgressEntry( download::DownloadItemImpl* download) { - if (!download || !delegate_) - return base::Optional(); - - download::InProgressCache* in_progress_cache = - delegate_->GetInProgressCache(); - if (in_progress_cache) - return in_progress_cache->RetrieveEntry(download->GetGuid()); - return base::Optional(); + return in_progress_manager_->GetInProgressEntry(download); } bool DownloadManagerImpl::IsOffTheRecord() const { @@ -881,16 +645,7 @@ bool DownloadManagerImpl::IsOffTheRecord() const { void DownloadManagerImpl::ReportBytesWasted( download::DownloadItemImpl* download) { - if (!delegate_) - return; - auto entry_opt = GetInProgressEntry(download); - if (entry_opt.has_value()) { - download::DownloadEntry entry = entry_opt.value(); - entry.bytes_wasted = download->GetBytesWasted(); - download::InProgressCache* in_progress_cache = - delegate_->GetInProgressCache(); - in_progress_cache->AddOrReplaceEntry(entry); - } + in_progress_manager_->ReportBytesWasted(download); } void DownloadManagerImpl::OnUrlDownloadHandlerCreated( @@ -930,10 +685,6 @@ download::DownloadInterruptReason DownloadManagerImpl::BeginDownloadRequest( const GURL& url = url_request->original_url(); - // Check if the renderer is permitted to request the requested URL. - if (!CanRequestURLFromRenderer(params->render_process_host_id(), url)) - return download::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST; - const net::URLRequestContext* request_context = url_request->context(); if (!request_context->job_factory()->IsHandledProtocol(url.scheme())) { DVLOG(1) << "Download request for unsupported protocol: " @@ -960,15 +711,13 @@ download::DownloadInterruptReason DownloadManagerImpl::BeginDownloadRequest( void DownloadManagerImpl::InterceptNavigation( std::unique_ptr resource_request, std::vector url_chain, - const base::Optional& suggested_filename, scoped_refptr response, network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, net::CertStatus cert_status, int frame_tree_node_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!delegate_) { - for (auto& observer : observers_) - observer.OnDownloadDropped(this); + DropDownload(); return; } @@ -982,7 +731,7 @@ void DownloadManagerImpl::InterceptNavigation( on_download_checks_done = base::BindOnce( &DownloadManagerImpl::InterceptNavigationOnChecksComplete, weak_factory_.GetWeakPtr(), web_contents_getter, - std::move(resource_request), std::move(url_chain), suggested_filename, + std::move(resource_request), std::move(url_chain), std::move(response), cert_status, std::move(url_loader_client_endpoints)); @@ -1015,12 +764,14 @@ int DownloadManagerImpl::RemoveDownloadsByURLAndTime( void DownloadManagerImpl::DownloadUrl( std::unique_ptr params) { - DownloadUrl(std::move(params), nullptr); + DownloadUrl(std::move(params), nullptr /* blob_data_handle */, + nullptr /* blob_url_loader_factory */); } void DownloadManagerImpl::DownloadUrl( std::unique_ptr params, - std::unique_ptr blob_data_handle) { + std::unique_ptr blob_data_handle, + scoped_refptr blob_url_loader_factory) { if (params->post_id() >= 0) { // Check this here so that the traceback is more useful. DCHECK(params->prefer_cache()); @@ -1030,11 +781,12 @@ void DownloadManagerImpl::DownloadUrl( download::RecordDownloadCountWithSource( download::DownloadCountTypes::DOWNLOAD_TRIGGERED_COUNT, params->download_source()); - StoragePartitionImpl* storage_partition = - GetStoragePartition(browser_context_, params->render_process_host_id(), - params->render_frame_host_routing_id()); + auto* rfh = RenderFrameHost::FromID(params->render_process_host_id(), + params->render_frame_host_routing_id()); BeginDownloadInternal(std::move(params), std::move(blob_data_handle), - download::DownloadItem::kInvalidId, storage_partition); + std::move(blob_url_loader_factory), + download::DownloadItem::kInvalidId, + rfh ? rfh->GetSiteInstance()->GetSiteURL() : GURL()); } void DownloadManagerImpl::AddObserver(Observer* observer) { @@ -1166,10 +918,11 @@ download::DownloadItem* DownloadManagerImpl::GetDownloadByGuid( void DownloadManagerImpl::OnUrlDownloadStarted( std::unique_ptr download_create_info, std::unique_ptr stream, - scoped_refptr shared_url_loader_factory, + scoped_refptr + url_loader_factory_getter, const download::DownloadUrlParameters::OnStartedCallback& callback) { StartDownload(std::move(download_create_info), std::move(stream), - std::move(shared_url_loader_factory), callback); + std::move(url_loader_factory_getter), callback); } void DownloadManagerImpl::OnUrlDownloadStopped( @@ -1215,31 +968,35 @@ void DownloadManagerImpl::ShowDownloadInShell( delegate_->ShowDownloadInShell(download); } +void DownloadManagerImpl::DropDownload() { + download::RecordDownloadCount(download::DOWNLOAD_DROPPED_COUNT); + for (auto& observer : observers_) + observer.OnDownloadDropped(this); +} + void DownloadManagerImpl::InterceptNavigationOnChecksComplete( ResourceRequestInfo::WebContentsGetter web_contents_getter, std::unique_ptr resource_request, std::vector url_chain, - const base::Optional& suggested_filename, scoped_refptr response, net::CertStatus cert_status, network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, bool is_download_allowed) { if (!is_download_allowed) { - for (auto& observer : observers_) - observer.OnDownloadDropped(this); + DropDownload(); return; } int render_process_id = -1; int render_frame_id = -1; GURL site_url, tab_url, tab_referrer_url; + RenderFrameHost* render_frame_host = nullptr; WebContents* web_contents = std::move(web_contents_getter).Run(); if (web_contents) { - RenderFrameHost* render_frame_host = web_contents->GetMainFrame(); + render_frame_host = web_contents->GetMainFrame(); if (render_frame_host) { render_process_id = render_frame_host->GetProcess()->GetID(); render_frame_id = render_frame_host->GetRoutingID(); - site_url = render_frame_host->GetSiteInstance()->GetSiteURL(); } NavigationEntry* entry = web_contents->GetController().GetVisibleEntry(); if (entry) { @@ -1249,92 +1006,115 @@ void DownloadManagerImpl::InterceptNavigationOnChecksComplete( } StoragePartitionImpl* storage_partition = GetStoragePartition(browser_context_, render_process_id, render_frame_id); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&DownloadManagerImpl::CreateDownloadHandlerForNavigation, - weak_factory_.GetWeakPtr(), std::move(resource_request), - render_process_id, render_frame_id, site_url, tab_url, - tab_referrer_url, std::move(url_chain), suggested_filename, - std::move(response), std::move(cert_status), - std::move(url_loader_client_endpoints), - storage_partition->url_loader_factory_getter(), - base::MessageLoop::current()->task_runner())); + in_progress_manager_->InterceptDownloadFromNavigation( + std::move(resource_request), render_process_id, render_frame_id, site_url, + tab_url, tab_referrer_url, std::move(url_chain), std::move(response), + std::move(cert_status), std::move(url_loader_client_endpoints), + CreateDownloadURLLoaderFactoryGetter(storage_partition, render_frame_host, + false)); } -// static -void DownloadManagerImpl::CreateDownloadHandlerForNavigation( - base::WeakPtr download_manager, - std::unique_ptr resource_request, - int render_process_id, - int render_frame_id, +void DownloadManagerImpl::BeginResourceDownloadOnChecksComplete( + std::unique_ptr params, + std::unique_ptr blob_data_handle, + scoped_refptr blob_url_loader_factory, + uint32_t id, const GURL& site_url, - const GURL& tab_url, - const GURL& tab_referrer_url, - std::vector url_chain, - const base::Optional& suggested_filename, - scoped_refptr response, - net::CertStatus cert_status, - network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, - scoped_refptr url_loader_factory_getter, - const scoped_refptr& task_runner) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); + bool is_download_allowed) { + if (!is_download_allowed) { + DropDownload(); + return; + } - download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr resource_downloader( - download::ResourceDownloader::InterceptNavigationResponse( - download_manager, std::move(resource_request), render_process_id, - render_frame_id, site_url, tab_url, tab_referrer_url, - std::move(url_chain), suggested_filename, std::move(response), - std::move(cert_status), std::move(url_loader_client_endpoints), - url_loader_factory_getter->GetNetworkFactory(), task_runner) - .release(), - base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get())); + GURL tab_url, tab_referrer_url; + auto* rfh = RenderFrameHost::FromID(params->render_process_host_id(), + params->render_frame_host_routing_id()); + if (rfh) { + auto* web_contents = WebContents::FromRenderFrameHost(rfh); + NavigationEntry* entry = web_contents->GetController().GetVisibleEntry(); + if (entry) { + tab_url = entry->GetURL(); + tab_referrer_url = entry->GetReferrer().url; + } + } - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::BindOnce(&DownloadManagerImpl::OnUrlDownloadHandlerCreated, - download_manager, std::move(resource_downloader))); + scoped_refptr + url_loader_factory_getter; + if (blob_url_loader_factory) { + DCHECK(params->url().SchemeIsBlob()); + url_loader_factory_getter = + base::MakeRefCounted( + blob_url_loader_factory->Clone()); + } else if (params->url().SchemeIsBlob()) { + url_loader_factory_getter = + base::MakeRefCounted( + params->url(), std::move(blob_data_handle)); + } else { + StoragePartitionImpl* storage_partition = + static_cast( + BrowserContext::GetStoragePartitionForSite(browser_context_, + site_url)); + url_loader_factory_getter = + CreateDownloadURLLoaderFactoryGetter(storage_partition, rfh, true); + } + + in_progress_manager_->BeginDownload(std::move(params), + std::move(url_loader_factory_getter), id, + site_url, tab_url, tab_referrer_url); } void DownloadManagerImpl::BeginDownloadInternal( std::unique_ptr params, std::unique_ptr blob_data_handle, + scoped_refptr blob_url_loader_factory, uint32_t id, - StoragePartitionImpl* storage_partition) { + const GURL& site_url) { + // Check if the renderer is permitted to request the requested URL. + if (params->render_process_host_id() >= 0 && + !CanRequestURLFromRenderer(params->render_process_host_id(), + params->url())) { + CreateInterruptedDownload( + std::move(params), + download::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, + weak_factory_.GetWeakPtr()); + return; + } + if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { - std::unique_ptr request = - download::CreateResourceRequest(params.get()); - GURL site_url, tab_url, tab_referrer_url; auto* rfh = RenderFrameHost::FromID(params->render_process_host_id(), params->render_frame_host_routing_id()); - if (rfh) { - site_url = rfh->GetSiteInstance()->GetSiteURL(); - auto* web_contents = WebContents::FromRenderFrameHost(rfh); - NavigationEntry* entry = web_contents->GetController().GetVisibleEntry(); - if (entry) { - tab_url = entry->GetURL(); - tab_referrer_url = entry->GetReferrer().url; + bool content_initiated = params->content_initiated(); + // If it's from the web, we don't trust it, so we push the throttle on. + if (rfh && content_initiated) { + ResourceRequestInfo::WebContentsGetter web_contents_getter = + base::BindRepeating(WebContents::FromFrameTreeNodeId, + rfh->GetFrameTreeNodeId()); + const GURL& url = params->url(); + const std::string& method = params->method(); + + base::OnceCallback + on_can_download_checks_done = base::BindOnce( + &DownloadManagerImpl::BeginResourceDownloadOnChecksComplete, + weak_factory_.GetWeakPtr(), std::move(params), + std::move(blob_data_handle), std::move(blob_url_loader_factory), + id, site_url); + if (delegate_) { + delegate_->CheckDownloadAllowed(std::move(web_contents_getter), url, + method, + std::move(on_can_download_checks_done)); + return; } } - scoped_refptr - url_loader_factory_getter; - if (params->url().SchemeIs(url::kBlobScheme)) { - url_loader_factory_getter = - base::MakeRefCounted( - params->url(), std::move(blob_data_handle)); - } else { - url_loader_factory_getter = - base::MakeRefCounted( - storage_partition->url_loader_factory_getter()); - } - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&BeginResourceDownload, std::move(params), - std::move(request), std::move(url_loader_factory_getter), - id, weak_factory_.GetWeakPtr(), site_url, tab_url, - tab_referrer_url, - base::MessageLoop::current()->task_runner())); + BeginResourceDownloadOnChecksComplete( + std::move(params), std::move(blob_data_handle), + std::move(blob_url_loader_factory), id, site_url, + rfh ? !content_initiated : true); } else { + StoragePartition* storage_partition = + BrowserContext::GetStoragePartitionForSite(browser_context_, site_url); + params->set_url_request_context_getter( + storage_partition->GetURLRequestContext()); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce(&BeginDownload, std::move(params), diff --git a/chromium/content/browser/download/download_manager_impl.h b/chromium/content/browser/download/download_manager_impl.h index 13ae60ba113..041492c4347 100644 --- a/chromium/content/browser/download/download_manager_impl.h +++ b/chromium/content/browser/download/download_manager_impl.h @@ -22,6 +22,7 @@ #include "base/synchronization/lock.h" #include "components/download/public/common/download_item_impl_delegate.h" #include "components/download/public/common/download_url_parameters.h" +#include "components/download/public/common/in_progress_download_manager.h" #include "components/download/public/common/url_download_handler.h" #include "content/browser/loader/navigation_url_loader.h" #include "content/common/content_export.h" @@ -41,12 +42,11 @@ class DownloadRequestHandleInterface; namespace content { class ResourceContext; -class StoragePartitionImpl; -class URLLoaderFactoryGetter; class CONTENT_EXPORT DownloadManagerImpl : public DownloadManager, public download::UrlDownloadHandler::Delegate, + public download::InProgressDownloadManager::Delegate, private download::DownloadItemImplDelegate { public: using DownloadItemImplCreated = @@ -69,7 +69,6 @@ class CONTENT_EXPORT DownloadManagerImpl int render_process_id, int render_frame_id, std::unique_ptr request_handle, - const ukm::SourceId ukm_source_id, const DownloadItemImplCreated& item_created); // DownloadManager functions. @@ -77,12 +76,12 @@ class CONTENT_EXPORT DownloadManagerImpl DownloadManagerDelegate* GetDelegate() const override; void Shutdown() override; void GetAllDownloads(DownloadVector* result) override; - void StartDownload( - std::unique_ptr info, - std::unique_ptr stream, - scoped_refptr shared_url_loader_factory, - const download::DownloadUrlParameters::OnStartedCallback& on_started) - override; + void StartDownload(std::unique_ptr info, + std::unique_ptr stream, + scoped_refptr + url_loader_factory_getter, + const download::DownloadUrlParameters::OnStartedCallback& + on_started) override; int RemoveDownloadsByURLAndTime( const base::Callback& url_filter, @@ -90,9 +89,10 @@ class CONTENT_EXPORT DownloadManagerImpl base::Time remove_end) override; void DownloadUrl( std::unique_ptr parameters) override; - void DownloadUrl( - std::unique_ptr params, - std::unique_ptr blob_data_handle) override; + void DownloadUrl(std::unique_ptr params, + std::unique_ptr blob_data_handle, + scoped_refptr + blob_url_loader_factory) override; void AddObserver(Observer* observer) override; void RemoveObserver(Observer* observer) override; download::DownloadItem* CreateDownloadItem( @@ -135,7 +135,8 @@ class CONTENT_EXPORT DownloadManagerImpl void OnUrlDownloadStarted( std::unique_ptr download_create_info, std::unique_ptr stream, - scoped_refptr shared_url_loader_factory, + scoped_refptr + url_loader_factory_getter, const download::DownloadUrlParameters::OnStartedCallback& callback) override; void OnUrlDownloadStopped(download::UrlDownloadHandler* downloader) override; @@ -165,7 +166,6 @@ class CONTENT_EXPORT DownloadManagerImpl void InterceptNavigation( std::unique_ptr resource_request, std::vector url_chain, - const base::Optional& suggested_filename, scoped_refptr response, network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, net::CertStatus cert_status, @@ -181,14 +181,6 @@ class CONTENT_EXPORT DownloadManagerImpl friend class DownloadManagerTest; friend class DownloadTest; - void StartDownloadWithId( - std::unique_ptr info, - std::unique_ptr stream, - scoped_refptr shared_url_loader_factory, - const download::DownloadUrlParameters::OnStartedCallback& on_started, - bool new_download, - uint32_t id); - void CreateSavePackageDownloadItemWithId( const base::FilePath& main_file_path, const GURL& page_url, @@ -196,13 +188,20 @@ class CONTENT_EXPORT DownloadManagerImpl int render_process_id, int render_frame_id, std::unique_ptr request_handle, - const ukm::SourceId ukm_source_id, const DownloadItemImplCreated& on_started, uint32_t id); - // Intercepts the download to another system if applicable. Returns true if - // the download was intercepted. - bool InterceptDownload(const download::DownloadCreateInfo& info); + // InProgressDownloadManager::Delegate implementations. + bool InterceptDownload(const download::DownloadCreateInfo& info) override; + base::FilePath GetDefaultDownloadDirectory() override; + download::DownloadItemImpl* GetDownloadItem( + uint32_t id, + bool new_download, + const download::DownloadCreateInfo& info) override; + net::URLRequestContextGetter* GetURLRequestContextGetter( + const download::DownloadCreateInfo& info) override; + void OnNewDownloadStarted(download::DownloadItem* download) override; + void GetNextId(const DownloadIdCallback& callback) override; // Create a new active item based on the info. Separate from // StartDownload() for testing. @@ -210,9 +209,6 @@ class CONTENT_EXPORT DownloadManagerImpl uint32_t id, const download::DownloadCreateInfo& info); - // Get next download id. |callback| is called on the UI thread and may - // be called synchronously. - void GetNextId(const DownloadIdCallback& callback); // Called with the result of DownloadManagerDelegate::CheckForFileExistence. // Updates the state of the file and then notifies this update to the file's @@ -220,7 +216,6 @@ class CONTENT_EXPORT DownloadManagerImpl void OnFileExistenceChecked(uint32_t download_id, bool result); // Overridden from DownloadItemImplDelegate - // (Note that |GetBrowserContext| are present in both interfaces.) void DetermineDownloadTarget(download::DownloadItemImpl* item, const DownloadTargetCallback& callback) override; bool ShouldCompleteDownload(download::DownloadItemImpl* item, @@ -245,48 +240,36 @@ class CONTENT_EXPORT DownloadManagerImpl bool IsOffTheRecord() const override; void ReportBytesWasted(download::DownloadItemImpl* download) override; + // Drops a download before it is created. + void DropDownload(); + // Helper method to start or resume a download. void BeginDownloadInternal( std::unique_ptr params, std::unique_ptr blob_data_handle, + scoped_refptr blob_url_loader_factory, uint32_t id, - StoragePartitionImpl* storage_partition); + const GURL& site_url); void InterceptNavigationOnChecksComplete( ResourceRequestInfo::WebContentsGetter web_contents_getter, std::unique_ptr resource_request, std::vector url_chain, - const base::Optional& suggested_filename, scoped_refptr response, net::CertStatus cert_status, network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, bool is_download_allowed); - - // Called when a navigation turns to be a download. Create a new - // DownloadHandler. It will be used to continue the loading instead of the - // regular document loader. Must be called on the IO thread. - static void CreateDownloadHandlerForNavigation( - base::WeakPtr download_manager, - std::unique_ptr resource_request, - int render_process_id, - int render_frame_id, + void BeginResourceDownloadOnChecksComplete( + std::unique_ptr params, + std::unique_ptr blob_data_handle, + scoped_refptr blob_url_loader_factory, + uint32_t id, const GURL& site_url, - const GURL& tab_url, - const GURL& tab_referrer_url, - std::vector url_chain, - const base::Optional& suggested_filename, - scoped_refptr response, - net::CertStatus cert_status, - network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, - scoped_refptr url_loader_factory_getter, - const scoped_refptr& task_runner); + bool is_download_allowed); // Factory for creation of downloads items. std::unique_ptr item_factory_; - // Factory for the creation of download files. - std::unique_ptr file_factory_; - // |downloads_| is the owning set for all downloads known to the // DownloadManager. This includes downloads started by the user in // this session, downloads initialized from the history system, and @@ -325,9 +308,12 @@ class CONTENT_EXPORT DownloadManagerImpl // Allows an embedder to control behavior. Guaranteed to outlive this object. DownloadManagerDelegate* delegate_; + // TODO(qinmin): remove this once network service is enabled by default. std::vector url_download_handlers_; + std::unique_ptr in_progress_manager_; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(DownloadManagerImpl); diff --git a/chromium/content/browser/download/download_manager_impl_unittest.cc b/chromium/content/browser/download/download_manager_impl_unittest.cc index ccbc17a8954..9ccc49d3df7 100644 --- a/chromium/content/browser/download/download_manager_impl_unittest.cc +++ b/chromium/content/browser/download/download_manager_impl_unittest.cc @@ -72,7 +72,7 @@ namespace { class MockDownloadManagerDelegate : public DownloadManagerDelegate { public: MockDownloadManagerDelegate(); - virtual ~MockDownloadManagerDelegate(); + ~MockDownloadManagerDelegate() override; MOCK_METHOD0(Shutdown, void()); MOCK_METHOD1(GetNextId, void(const DownloadIdCallback&)); @@ -119,10 +119,6 @@ class MockDownloadItemFactory // we don't keep dangling pointers. void RemoveItem(int id); - // Sets |has_observer_calls_| to reflect whether we expect to add/remove - // observers during the CreateActiveItem. - void SetHasObserverCalls(bool observer_calls); - // Overridden methods from DownloadItemFactory. download::DownloadItemImpl* CreatePersistedItem( download::DownloadItemImplDelegate* delegate, @@ -165,16 +161,20 @@ class MockDownloadItemFactory std::unique_ptr request_handle) override; + void set_is_download_started(bool is_download_started) { + is_download_started_ = is_download_started; + } + private: std::map items_; download::DownloadItemImplDelegate item_delegate_; - bool has_observer_calls_; + bool is_download_started_; DISALLOW_COPY_AND_ASSIGN(MockDownloadItemFactory); }; MockDownloadItemFactory::MockDownloadItemFactory() - : has_observer_calls_(false) {} + : is_download_started_(false) {} MockDownloadItemFactory::~MockDownloadItemFactory() {} @@ -200,10 +200,6 @@ void MockDownloadItemFactory::RemoveItem(int id) { items_.erase(id); } -void MockDownloadItemFactory::SetHasObserverCalls(bool has_observer_calls) { - has_observer_calls_ = has_observer_calls; -} - download::DownloadItemImpl* MockDownloadItemFactory::CreatePersistedItem( download::DownloadItemImplDelegate* delegate, const std::string& guid, @@ -253,18 +249,16 @@ download::DownloadItemImpl* MockDownloadItemFactory::CreateActiveItem( .WillRepeatedly(Return(download_id)); EXPECT_CALL(*result, GetGuid()) .WillRepeatedly(ReturnRefOfCopy(base::GenerateGUID())); + if (is_download_started_) { + EXPECT_CALL(*result, RemoveObserver(_)); + EXPECT_CALL(*result, AddObserver(_)); + } items_[download_id] = result; // Active items are created and then immediately are called to start // the download. EXPECT_CALL(*result, MockStart(_, _)); - // In the StartDownload case, expect the remove/add observer calls. - if (has_observer_calls_) { - EXPECT_CALL(*result, RemoveObserver(_)); - EXPECT_CALL(*result, AddObserver(_)); - } - return result; } @@ -291,7 +285,7 @@ class MockDownloadFileFactory public base::SupportsWeakPtr { public: MockDownloadFileFactory() {} - virtual ~MockDownloadFileFactory() {} + ~MockDownloadFileFactory() override {} // Overridden method from DownloadFileFactory MOCK_METHOD2(MockCreateFile, @@ -311,7 +305,7 @@ class MockDownloadFileFactory class MockDownloadManagerObserver : public DownloadManager::Observer { public: MockDownloadManagerObserver() {} - ~MockDownloadManagerObserver() {} + ~MockDownloadManagerObserver() override {} MOCK_METHOD2(OnDownloadCreated, void(DownloadManager*, download::DownloadItem*)); MOCK_METHOD1(ManagerGoingDown, void(DownloadManager*)); @@ -320,7 +314,7 @@ class MockDownloadManagerObserver : public DownloadManager::Observer { class MockByteStreamReader : public ByteStreamReader { public: - virtual ~MockByteStreamReader() {} + ~MockByteStreamReader() override {} MOCK_METHOD2(Read, StreamState(scoped_refptr*, size_t*)); MOCK_CONST_METHOD0(GetStatus, int()); MOCK_METHOD1(RegisterCallback, void(const base::Closure&)); @@ -456,14 +450,11 @@ class DownloadManagerTest : public testing::Test { base::Unretained(this))); } - void SetHasObserverCalls(bool has_observer_calls) { - mock_download_item_factory_->SetHasObserverCalls(has_observer_calls); - } - protected: // Key test variable; we'll keep it available to sub-classes. std::unique_ptr download_manager_; base::WeakPtr mock_download_file_factory_; + base::WeakPtr mock_download_item_factory_; // Target detetermined callback. bool callback_called_; @@ -477,7 +468,6 @@ class DownloadManagerTest : public testing::Test { private: TestBrowserThreadBundle thread_bundle_; - base::WeakPtr mock_download_item_factory_; std::unique_ptr mock_download_manager_delegate_; std::unique_ptr observer_; std::unique_ptr browser_context_; @@ -488,8 +478,6 @@ class DownloadManagerTest : public testing::Test { // Confirm the appropriate invocations occur when you start a download. TEST_F(DownloadManagerTest, StartDownload) { - SetHasObserverCalls(true); - std::unique_ptr info( new download::DownloadCreateInfo); std::unique_ptr stream(new MockByteStreamReader); @@ -517,12 +505,11 @@ TEST_F(DownloadManagerTest, StartDownload) { MockCreateFile(Ref(*info->save_info.get()), input_stream.get())) .WillOnce(Return(mock_file)); + mock_download_item_factory_->set_is_download_started(true); download_manager_->StartDownload( std::move(info), std::move(input_stream), nullptr, download::DownloadUrlParameters::OnStartedCallback()); EXPECT_TRUE(download_manager_->GetDownload(local_id)); - - SetHasObserverCalls(false); } // Confirm that calling DetermineDownloadTarget behaves properly if the delegate diff --git a/chromium/content/browser/download/download_request_core.cc b/chromium/content/browser/download/download_request_core.cc index b7eb780ae47..3b2045e18ee 100644 --- a/chromium/content/browser/download/download_request_core.cc +++ b/chromium/content/browser/download/download_request_core.cc @@ -203,12 +203,6 @@ DownloadRequestCore::DownloadRequestCore( is_partial_request_ = save_info_->offset > 0; } else { save_info_.reset(new download::DownloadSaveInfo); - ResourceRequestInfoImpl* request_info = - ResourceRequestInfoImpl::ForRequest(request_); - if (request_info && request_info->suggested_filename().has_value()) { - save_info_->suggested_name = - base::UTF8ToUTF16(*request_info->suggested_filename()); - } } } @@ -216,7 +210,7 @@ DownloadRequestCore::~DownloadRequestCore() { DCHECK_CURRENTLY_ON(BrowserThread::IO); // Remove output stream callback if a stream exists. if (stream_writer_) - stream_writer_->RegisterCallback(base::Closure()); + stream_writer_->RegisterCallback(base::RepeatingClosure()); } std::unique_ptr @@ -286,7 +280,7 @@ bool DownloadRequestCore::OnResponseStarted( download::GetDownloadTaskRunner(), kDownloadByteStreamSize, &stream_writer_, &stream_reader); stream_writer_->RegisterCallback( - base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr())); + base::BindRepeating(&DownloadRequestCore::ResumeRequest, AsWeakPtr())); if (!override_mime_type.empty()) create_info->mime_type = override_mime_type; diff --git a/chromium/content/browser/download/download_url_loader_factory_getter_impl.cc b/chromium/content/browser/download/download_url_loader_factory_getter_impl.cc new file mode 100644 index 00000000000..e26386c8bb6 --- /dev/null +++ b/chromium/content/browser/download/download_url_loader_factory_getter_impl.cc @@ -0,0 +1,25 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/download/download_url_loader_factory_getter_impl.h" + +namespace content { + +DownloadURLLoaderFactoryGetterImpl::DownloadURLLoaderFactoryGetterImpl( + std::unique_ptr url_loader_factory) + : url_loader_factory_info_(std::move(url_loader_factory)) {} + +DownloadURLLoaderFactoryGetterImpl::~DownloadURLLoaderFactoryGetterImpl() = + default; + +scoped_refptr +DownloadURLLoaderFactoryGetterImpl::GetURLLoaderFactory() { + if (!url_loader_factory_) { + url_loader_factory_ = network::SharedURLLoaderFactory::Create( + std::move(url_loader_factory_info_)); + } + return url_loader_factory_; +} + +} // namespace content diff --git a/chromium/content/browser/download/download_url_loader_factory_getter_impl.h b/chromium/content/browser/download/download_url_loader_factory_getter_impl.h new file mode 100644 index 00000000000..d002dbb1e14 --- /dev/null +++ b/chromium/content/browser/download/download_url_loader_factory_getter_impl.h @@ -0,0 +1,39 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_URL_LOADER_FACTORY_GETTER_IMPL_H_ +#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_URL_LOADER_FACTORY_GETTER_IMPL_H_ + +#include "components/download/public/common/download_url_loader_factory_getter.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" + +namespace content { + +// Class for retrieving a fixed SharedURLLoaderFactory. +class DownloadURLLoaderFactoryGetterImpl + : public download::DownloadURLLoaderFactoryGetter { + public: + explicit DownloadURLLoaderFactoryGetterImpl( + std::unique_ptr url_loader_factory); + + // download::DownloadURLLoaderFactoryGetter implementation. + scoped_refptr GetURLLoaderFactory() override; + + protected: + ~DownloadURLLoaderFactoryGetterImpl() override; + + private: + // Only one of the following two members is ever set. Initially that would be + // |url_loader_factory_info_|, but after GetURLLoaderFactory is called for the + // first time instead |url_loader_factory_| will be set. This is safe because + // GetURLLoaderFactory is always called from the same thread. + std::unique_ptr url_loader_factory_info_; + scoped_refptr url_loader_factory_; + + DISALLOW_COPY_AND_ASSIGN(DownloadURLLoaderFactoryGetterImpl); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_URL_LOADER_FACTORY_GETTER_IMPL_H_ diff --git a/chromium/content/browser/download/download_utils.cc b/chromium/content/browser/download/download_utils.cc index c9e72c38329..ab0e360a306 100644 --- a/chromium/content/browser/download/download_utils.cc +++ b/chromium/content/browser/download/download_utils.cc @@ -6,6 +6,7 @@ #include "base/format_macros.h" #include "base/process/process_handle.h" +#include "base/single_thread_task_runner.h" #include "base/strings/stringprintf.h" #include "base/task_scheduler/post_task.h" #include "components/download/downloader/in_progress/download_entry.h" diff --git a/chromium/content/browser/download/drag_download_file_browsertest.cc b/chromium/content/browser/download/drag_download_file_browsertest.cc index f080504c23c..c267b435449 100644 --- a/chromium/content/browser/download/drag_download_file_browsertest.cc +++ b/chromium/content/browser/download/drag_download_file_browsertest.cc @@ -6,8 +6,8 @@ #include "base/files/scoped_temp_dir.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" #include "base/path_service.h" +#include "base/run_loop.h" #include "content/browser/download/download_manager_impl.h" #include "content/browser/download/drag_download_file.h" #include "content/browser/download/drag_download_util.h" @@ -41,7 +41,7 @@ class MockDownloadFileObserver : public ui::DownloadFileObserver { MOCK_METHOD0(OnDownloadAborted, void()); private: - virtual ~MockDownloadFileObserver() {} + ~MockDownloadFileObserver() override {} DISALLOW_COPY_AND_ASSIGN(MockDownloadFileObserver); }; @@ -54,7 +54,7 @@ class DragDownloadFileTest : public ContentBrowserTest { void Succeed() { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::MessageLoopForUI::current()->QuitWhenIdleClosure()); + base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); } void FailFast() { @@ -70,7 +70,7 @@ class DragDownloadFileTest : public ContentBrowserTest { ->GetDownloadManagerDelegate()); delegate->SetDownloadBehaviorForTesting(downloads_directory()); base::FilePath test_data_dir; - ASSERT_TRUE(PathService::Get(content::DIR_TEST_DATA, &test_data_dir)); + ASSERT_TRUE(base::PathService::Get(content::DIR_TEST_DATA, &test_data_dir)); embedded_test_server()->ServeFilesFromDirectory(test_data_dir); ASSERT_TRUE(embedded_test_server()->Start()); } diff --git a/chromium/content/browser/download/mhtml_generation_manager.cc b/chromium/content/browser/download/mhtml_generation_manager.cc index de16bb4ffc9..7d67b75473e 100644 --- a/chromium/content/browser/download/mhtml_generation_manager.cc +++ b/chromium/content/browser/download/mhtml_generation_manager.cc @@ -94,8 +94,7 @@ class MHTMLGenerationManager::Job : public RenderProcessHostObserver { // RenderProcessHostObserver: void RenderProcessExited(RenderProcessHost* host, - base::TerminationStatus status, - int exit_code) override; + const ChildProcessTerminationInfo& info) override; void RenderProcessHostDestroyed(RenderProcessHost* host) override; void MarkAsFinished(); @@ -262,8 +261,7 @@ MhtmlSaveStatus MHTMLGenerationManager::Job::SendToNextRenderFrame() { void MHTMLGenerationManager::Job::RenderProcessExited( RenderProcessHost* host, - base::TerminationStatus status, - int exit_code) { + const ChildProcessTerminationInfo& info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); MHTMLGenerationManager::GetInstance()->RenderProcessExited(this); } diff --git a/chromium/content/browser/download/network_download_url_loader_factory_getter.cc b/chromium/content/browser/download/network_download_url_loader_factory_getter.cc index df96621f93b..fe132fb4489 100644 --- a/chromium/content/browser/download/network_download_url_loader_factory_getter.cc +++ b/chromium/content/browser/download/network_download_url_loader_factory_getter.cc @@ -7,12 +7,17 @@ #include "components/download/public/common/download_task_runner.h" #include "content/browser/url_loader_factory_getter.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" namespace content { NetworkDownloadURLLoaderFactoryGetter::NetworkDownloadURLLoaderFactoryGetter( - scoped_refptr url_loader_factory_getter) - : url_loader_factory_getter_(url_loader_factory_getter) {} + scoped_refptr url_loader_factory_getter, + network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info, + network::mojom::URLLoaderFactoryRequest proxy_factory_request) + : url_loader_factory_getter_(url_loader_factory_getter), + proxy_factory_ptr_info_(std::move(proxy_factory_ptr_info)), + proxy_factory_request_(std::move(proxy_factory_request)) {} NetworkDownloadURLLoaderFactoryGetter:: ~NetworkDownloadURLLoaderFactoryGetter() = default; @@ -21,7 +26,18 @@ scoped_refptr NetworkDownloadURLLoaderFactoryGetter::GetURLLoaderFactory() { DCHECK(download::GetIOTaskRunner()); DCHECK(download::GetIOTaskRunner()->BelongsToCurrentThread()); - return url_loader_factory_getter_->GetNetworkFactory(); + if (lazy_factory_) + return lazy_factory_; + if (proxy_factory_request_.is_pending()) { + url_loader_factory_getter_->CloneNetworkFactory( + std::move(proxy_factory_request_)); + lazy_factory_ = + base::MakeRefCounted( + std::move(proxy_factory_ptr_info_)); + } else { + lazy_factory_ = url_loader_factory_getter_->GetNetworkFactory(); + } + return lazy_factory_; } } // namespace content diff --git a/chromium/content/browser/download/network_download_url_loader_factory_getter.h b/chromium/content/browser/download/network_download_url_loader_factory_getter.h index cf6eaa10182..95eb89382e4 100644 --- a/chromium/content/browser/download/network_download_url_loader_factory_getter.h +++ b/chromium/content/browser/download/network_download_url_loader_factory_getter.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_DOWNLOAD_NETWORK_DOWNLOAD_URL_LOADER_FACTORY_GETTER_H_ #include "components/download/public/common/download_url_loader_factory_getter.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" namespace content { @@ -15,8 +16,10 @@ class URLLoaderFactoryGetter; class NetworkDownloadURLLoaderFactoryGetter : public download::DownloadURLLoaderFactoryGetter { public: - explicit NetworkDownloadURLLoaderFactoryGetter( - scoped_refptr url_loader_factory_getter); + NetworkDownloadURLLoaderFactoryGetter( + scoped_refptr url_loader_factory_getter, + network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info, + network::mojom::URLLoaderFactoryRequest proxy_factory_request); // download::DownloadURLLoaderFactoryGetter implementation. scoped_refptr GetURLLoaderFactory() override; @@ -26,6 +29,9 @@ class NetworkDownloadURLLoaderFactoryGetter private: scoped_refptr url_loader_factory_getter_; + scoped_refptr lazy_factory_; + network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info_; + network::mojom::URLLoaderFactoryRequest proxy_factory_request_; DISALLOW_COPY_AND_ASSIGN(NetworkDownloadURLLoaderFactoryGetter); }; diff --git a/chromium/content/browser/download/save_package.cc b/chromium/content/browser/download/save_package.cc index ce081e96056..7035f3e49d4 100644 --- a/chromium/content/browser/download/save_package.cc +++ b/chromium/content/browser/download/save_package.cc @@ -14,7 +14,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" +#include "base/rand_util.h" #include "base/stl_util.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" @@ -29,6 +29,7 @@ #include "components/download/public/common/download_stats.h" #include "components/download/public/common/download_task_runner.h" #include "components/download/public/common/download_ukm_helper.h" +#include "components/download/public/common/download_utils.h" #include "components/filename_generation/filename_generation.h" #include "components/url_formatter/url_formatter.h" #include "content/browser/bad_message.h" @@ -85,7 +86,7 @@ const int32_t kMaxFileOrdinalNumber = 9999; // is less than MAX_PATH #if defined(OS_WIN) const uint32_t kMaxFilePathLength = MAX_PATH - 1; -#elif defined(OS_POSIX) +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) const uint32_t kMaxFilePathLength = PATH_MAX - 1; #endif @@ -255,6 +256,12 @@ void SavePackage::InternalInit() { DCHECK(download_manager_); download::RecordSavePackageEvent(download::SAVE_PACKAGE_STARTED); + + ukm_source_id_ = ukm::UkmRecorder::GetNewSourceID(); + ukm_download_id_ = download::GetUniqueDownloadId(); + download::DownloadUkmHelper::RecordDownloadStarted( + ukm_download_id_, ukm_source_id_, download::DownloadContent::TEXT, + download::DownloadSource::UNKNOWN); } bool SavePackage::Init( @@ -277,11 +284,8 @@ bool SavePackage::Init( std::unique_ptr request_handle( new SavePackageRequestHandle(AsWeakPtr())); - // The download manager keeps ownership but adds us as an observer. - ukm::SourceId ukm_source_id = ukm::UkmRecorder::GetNewSourceID(); - download::DownloadUkmHelper::UpdateSourceURL( - ukm::UkmRecorder::Get(), ukm_source_id, + ukm::UkmRecorder::Get(), ukm_source_id_, web_contents()->GetLastCommittedURL()); RenderFrameHost* frame_host = web_contents()->GetMainFrame(); download_manager_->CreateSavePackageDownloadItem( @@ -289,7 +293,7 @@ bool SavePackage::Init( ((save_type_ == SAVE_PAGE_TYPE_AS_MHTML) ? "multipart/related" : "text/html"), frame_host->GetProcess()->GetID(), frame_host->GetRoutingID(), - std::move(request_handle), std::move(ukm_source_id), + std::move(request_handle), base::Bind(&SavePackage::InitWithDownloadItem, AsWeakPtr(), download_created_callback)); return true; @@ -356,12 +360,12 @@ void SavePackage::OnMHTMLGenerated(int64_t size) { // '/path/to/save_dir' + '/' + NAME_MAX. uint32_t SavePackage::GetMaxPathLengthForDirectory( const base::FilePath& base_dir) { -#if defined(OS_POSIX) +#if defined(OS_WIN) + return kMaxFilePathLength; +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) return std::min( kMaxFilePathLength, static_cast(base_dir.value().length()) + NAME_MAX + 1); -#else - return kMaxFilePathLength; #endif } @@ -693,6 +697,10 @@ void SavePackage::Finish() { // Record finish. download::RecordSavePackageEvent(download::SAVE_PACKAGE_FINISHED); + // TODO(qinmin): report the actual file size and duration for the download. + download::DownloadUkmHelper::RecordDownloadCompleted(ukm_download_id_, 1, + base::TimeDelta(), 0); + // Record any errors that occurred. if (wrote_to_completed_file_) download::RecordSavePackageEvent(download::SAVE_PACKAGE_WRITE_TO_COMPLETED); diff --git a/chromium/content/browser/download/save_package.h b/chromium/content/browser/download/save_package.h index d4ad79aec82..c3385f8f3da 100644 --- a/chromium/content/browser/download/save_package.h +++ b/chromium/content/browser/download/save_package.h @@ -30,6 +30,7 @@ #include "content/public/browser/web_contents_observer.h" #include "content/public/common/referrer.h" #include "net/base/net_errors.h" +#include "services/metrics/public/cpp/ukm_source_id.h" #include "url/gurl.h" class GURL; @@ -418,6 +419,10 @@ class CONTENT_EXPORT SavePackage // Unique ID for this SavePackage. const SavePackageId unique_id_; + // UKM IDs for reporting. + ukm::SourceId ukm_source_id_; + uint64_t ukm_download_id_; + DISALLOW_COPY_AND_ASSIGN(SavePackage); }; diff --git a/chromium/content/browser/download/save_package_unittest.cc b/chromium/content/browser/download/save_package_unittest.cc index 6aa71fcf441..4494255a467 100644 --- a/chromium/content/browser/download/save_package_unittest.cc +++ b/chromium/content/browser/download/save_package_unittest.cc @@ -30,7 +30,7 @@ namespace content { #define HTML_EXTENSION ".html" #if defined(OS_WIN) #define FPL_HTML_EXTENSION L".html" -#else +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) #define FPL_HTML_EXTENSION ".html" #endif @@ -40,7 +40,7 @@ namespace { #if defined(OS_WIN) const uint32_t kMaxFilePathLength = MAX_PATH - 1; const uint32_t kMaxFileNameLength = MAX_PATH - 1; -#elif defined(OS_POSIX) +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) const uint32_t kMaxFilePathLength = PATH_MAX - 1; const uint32_t kMaxFileNameLength = NAME_MAX; #endif diff --git a/chromium/content/browser/download/url_downloader_factory.cc b/chromium/content/browser/download/url_downloader_factory.cc index 342a24ab99d..752cea70a5d 100644 --- a/chromium/content/browser/download/url_downloader_factory.cc +++ b/chromium/content/browser/download/url_downloader_factory.cc @@ -5,6 +5,7 @@ #include "content/browser/download/url_downloader_factory.h" #include "components/download/public/common/download_item.h" +#include "components/download/public/common/download_url_loader_factory_getter.h" #include "content/browser/download/download_request_core.h" #include "content/browser/download/url_downloader.h" @@ -18,7 +19,8 @@ download::UrlDownloadHandler::UniqueUrlDownloadHandlerPtr UrlDownloaderFactory::CreateUrlDownloadHandler( std::unique_ptr params, base::WeakPtr delegate, - scoped_refptr shared_url_loader_factory, + scoped_refptr + url_loader_factory_getter, const scoped_refptr& task_runner) { std::unique_ptr url_request = DownloadRequestCore::CreateRequestOnIOThread( diff --git a/chromium/content/browser/download/url_downloader_factory.h b/chromium/content/browser/download/url_downloader_factory.h index cd187b55971..ae908ecaeca 100644 --- a/chromium/content/browser/download/url_downloader_factory.h +++ b/chromium/content/browser/download/url_downloader_factory.h @@ -7,6 +7,10 @@ #include "components/download/public/common/url_download_handler_factory.h" +namespace download { +class DownloadURLLoaderFactoryGetter; +}; + namespace content { // Class for creating UrlDownloader object. @@ -21,7 +25,8 @@ class UrlDownloaderFactory : public download::UrlDownloadHandlerFactory { CreateUrlDownloadHandler( std::unique_ptr params, base::WeakPtr delegate, - scoped_refptr shared_url_loader_factory, + scoped_refptr + shared_url_loader_factory, const scoped_refptr& task_runner) override; }; diff --git a/chromium/content/browser/file_url_loader_factory.cc b/chromium/content/browser/file_url_loader_factory.cc index 8bf1bcedd08..f260e71a625 100644 --- a/chromium/content/browser/file_url_loader_factory.cc +++ b/chromium/content/browser/file_url_loader_factory.cc @@ -107,7 +107,8 @@ class FileURLDirectoryLoader } // network::mojom::URLLoader: - void FollowRedirect() override {} + void FollowRedirect(const base::Optional& + modified_request_headers) override {} void ProceedWithResponse() override { NOTREACHED(); } void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override {} @@ -188,7 +189,7 @@ class FileURLDirectoryLoader #if defined(OS_WIN) const base::string16& title = path_.value(); -#elif defined(OS_POSIX) +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) const base::string16& title = base::WideToUTF16(base::SysNativeMBToWide(path_.value())); #endif @@ -210,7 +211,7 @@ class FileURLDirectoryLoader filename.value() != base::FilePath::kParentDirectory) { #if defined(OS_WIN) std::string raw_bytes; // Empty on Windows means UTF-8 encoded name. -#elif defined(OS_POSIX) +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) const std::string& raw_bytes = filename.value(); #endif pending_data_.append(net::GetDirectoryListingEntry( @@ -315,7 +316,8 @@ class FileURLLoader : public network::mojom::URLLoader { } // network::mojom::URLLoader: - void FollowRedirect() override {} + void FollowRedirect(const base::Optional& + modified_request_headers) override {} void ProceedWithResponse() override {} void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override {} @@ -454,12 +456,12 @@ class FileURLLoader : public network::mojom::URLLoader { observer->OnStart(); base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); - net::Error net_error = net::FileErrorToNetError(file.error_details()); - if (observer) - observer->OnOpenComplete(net_error); if (!file.IsValid()) { - if (observer) + if (observer) { + observer->OnBytesRead(nullptr, 0u, file.error_details()); observer->OnDoneReading(); + } + net::Error net_error = net::FileErrorToNetError(file.error_details()); client->OnComplete(network::URLLoaderCompletionStatus(net_error)); MaybeDeleteSelf(); return; @@ -471,10 +473,13 @@ class FileURLLoader : public network::mojom::URLLoader { base::File::Error read_error = base::File::GetLastFileError(); DCHECK_NE(base::File::FILE_OK, read_error); if (observer) { + // This can happen when the file is unreadable (which can happen during + // corruption). We need to be sure to inform + // the observer that we've finished reading so that it can proceed. observer->OnBytesRead(nullptr, 0u, read_error); observer->OnDoneReading(); } - net_error = net::FileErrorToNetError(read_error); + net::Error net_error = net::FileErrorToNetError(read_error); client->OnComplete(network::URLLoaderCompletionStatus(net_error)); return; } else if (observer) { diff --git a/chromium/content/browser/fileapi/OWNERS b/chromium/content/browser/fileapi/OWNERS index c72d620620e..b295e075046 100644 --- a/chromium/content/browser/fileapi/OWNERS +++ b/chromium/content/browser/fileapi/OWNERS @@ -1,5 +1,5 @@ jsbell@chromium.org -michaeln@chromium.org +mek@chromium.org pwnall@chromium.org jianli@chromium.org tzik@chromium.org diff --git a/chromium/content/browser/fileapi/browser_file_system_helper.cc b/chromium/content/browser/fileapi/browser_file_system_helper.cc index 4d8ecc2bab9..aef88c1d686 100644 --- a/chromium/content/browser/fileapi/browser_file_system_helper.cc +++ b/chromium/content/browser/fileapi/browser_file_system_helper.cc @@ -5,6 +5,7 @@ #include "content/browser/fileapi/browser_file_system_helper.h" #include +#include #include #include #include @@ -62,11 +63,8 @@ FileSystemOptions CreateBrowserFileSystemOptions(bool is_incognito) { switches::kAllowFileAccessFromFiles)) { additional_allowed_schemes.push_back(url::kFileScheme); } - leveldb::Env* env_override = nullptr; - if (is_incognito) - env_override = leveldb_chrome::NewMemEnv(leveldb::Env::Default()); - return FileSystemOptions(profile_mode, additional_allowed_schemes, - env_override); + return FileSystemOptions(profile_mode, is_incognito, + additional_allowed_schemes); } } // namespace @@ -98,13 +96,11 @@ scoped_refptr CreateFileSystemContext( std::move(additional_backends), url_request_auto_mount_handlers, profile_path, CreateBrowserFileSystemOptions(is_incognito)); - std::vector types; - file_system_context->GetFileSystemTypes(&types); - for (size_t i = 0; i < types.size(); ++i) { + for (const storage::FileSystemType& type : + file_system_context->GetFileSystemTypes()) { ChildProcessSecurityPolicyImpl::GetInstance() ->RegisterFileSystemPermissionPolicy( - types[i], - storage::FileSystemContext::GetPermissionPolicy(types[i])); + type, storage::FileSystemContext::GetPermissionPolicy(type)); } return file_system_context; diff --git a/chromium/content/browser/fileapi/browser_file_system_helper_unittest.cc b/chromium/content/browser/fileapi/browser_file_system_helper_unittest.cc index c1c84c39825..1b0cff4dbf9 100644 --- a/chromium/content/browser/fileapi/browser_file_system_helper_unittest.cc +++ b/chromium/content/browser/fileapi/browser_file_system_helper_unittest.cc @@ -62,7 +62,7 @@ TEST(BrowserFileSystemHelperTest, new base::NullTaskRunner); storage::FileSystemOptions file_system_options( storage::FileSystemOptions::PROFILE_MODE_NORMAL, - std::vector(), nullptr); + false /* force_in_memory */, std::vector()); scoped_refptr test_file_system_context( new storage::FileSystemContext( io_task_runner.get(), file_task_runner.get(), diff --git a/chromium/content/browser/fileapi/file_system_url_loader_factory.cc b/chromium/content/browser/fileapi/file_system_url_loader_factory.cc new file mode 100644 index 00000000000..0b426204681 --- /dev/null +++ b/chromium/content/browser/fileapi/file_system_url_loader_factory.cc @@ -0,0 +1,644 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/fileapi/file_system_url_loader_factory.h" + +#include +#include +#include +#include +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner.h" +#include "base/strings/stringprintf.h" +#include "base/task_scheduler/post_task.h" +#include "base/task_scheduler/task_traits.h" +#include "build/build_config.h" +#include "components/services/filesystem/public/interfaces/types.mojom.h" +#include "content/browser/child_process_security_policy_impl.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/common/child_process_host.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/system/string_data_pipe_producer.h" +#include "net/base/directory_listing.h" +#include "net/base/io_buffer.h" +#include "net/base/mime_sniffer.h" +#include "net/base/mime_util.h" +#include "net/http/http_byte_range.h" +#include "net/http/http_util.h" +#include "storage/browser/fileapi/file_stream_reader.h" +#include "storage/browser/fileapi/file_system_context.h" +#include "storage/browser/fileapi/file_system_operation_runner.h" +#include "storage/browser/fileapi/file_system_url.h" +#include "storage/common/fileapi/file_system_util.h" + +using filesystem::mojom::DirectoryEntry; +using storage::FileStreamReader; +using storage::FileSystemContext; +using storage::FileSystemOperation; +using storage::FileSystemRequestInfo; +using storage::FileSystemURL; +using storage::VirtualPath; + +namespace content { +namespace { + +struct FactoryParams { + int render_process_host_id; + int frame_tree_node_id; + scoped_refptr file_system_context; + std::string storage_domain; +}; + +constexpr size_t kDefaultFileSystemUrlPipeSize = 65536; + +// Implementation sniffs the first file chunk to determine the mime-type. +static_assert(kDefaultFileSystemUrlPipeSize >= net::kMaxBytesToSniff, + "Default file data pipe size must be at least as large as a " + "MIME-type sniffing buffer."); + +scoped_refptr CreateHttpResponseHeaders( + int response_code) { + std::string raw_headers; + raw_headers.append(base::StringPrintf("HTTP/1.1 %d OK", response_code)); + + // Tell WebKit never to cache this content. + raw_headers.append(1, '\0'); + raw_headers.append(net::HttpRequestHeaders::kCacheControl); + raw_headers.append(": no-cache"); + + raw_headers.append(2, '\0'); + return base::MakeRefCounted(raw_headers); +} + +bool GetMimeType(const FileSystemURL& url, std::string* mime_type) { + DCHECK(url.is_valid()); + base::FilePath::StringType extension = url.path().Extension(); + if (!extension.empty()) + extension = extension.substr(1); + return net::GetWellKnownMimeTypeFromExtension(extension, mime_type); +} + +// Common implementation shared between the file and directory URLLoaders. +class FileSystemEntryURLLoader + : public network::mojom::URLLoader, + public base::SupportsWeakPtr { + public: + explicit FileSystemEntryURLLoader(FactoryParams params) + : binding_(this), params_(std::move(params)) {} + + // network::mojom::URLLoader: + void FollowRedirect(const base::Optional& + modified_request_headers) override {} + void ProceedWithResponse() override {} + void SetPriority(net::RequestPriority priority, + int32_t intra_priority_value) override {} + void PauseReadingBodyFromNet() override {} + void ResumeReadingBodyFromNet() override {} + + protected: + virtual void FileSystemIsMounted() = 0; + + void Start(const network::ResourceRequest& request, + network::mojom::URLLoaderRequest loader, + network::mojom::URLLoaderClientPtrInfo client_info, + scoped_refptr io_task_runner) { + io_task_runner->PostTask( + FROM_HERE, + base::BindOnce(&FileSystemEntryURLLoader::StartOnIOThread, AsWeakPtr(), + request, std::move(loader), std::move(client_info))); + } + + void MaybeDeleteSelf() { + if (!binding_.is_bound() && !client_.is_bound()) + delete this; + } + + void OnClientComplete(network::URLLoaderCompletionStatus status) { + client_->OnComplete(status); + client_.reset(); + MaybeDeleteSelf(); + } + + void OnClientComplete(base::File::Error file_error) { + OnClientComplete(net::FileErrorToNetError(file_error)); + } + + void OnClientComplete(net::Error net_error) { + OnClientComplete(network::URLLoaderCompletionStatus(net_error)); + } + + mojo::Binding binding_; + network::mojom::URLLoaderClientPtr client_; + FactoryParams params_; + std::unique_ptr data_producer_; + net::HttpByteRange byte_range_; + FileSystemURL url_; + + private: + void StartOnIOThread(const network::ResourceRequest& request, + network::mojom::URLLoaderRequest loader, + network::mojom::URLLoaderClientPtrInfo client_info) { + binding_.Bind(std::move(loader)); + binding_.set_connection_error_handler(base::BindOnce( + &FileSystemEntryURLLoader::OnConnectionError, base::Unretained(this))); + + client_.Bind(std::move(client_info)); + + if (!request.url.is_valid()) { + OnClientComplete(net::ERR_INVALID_URL); + return; + } + + if (params_.render_process_host_id != ChildProcessHost::kInvalidUniqueID && + !ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( + params_.render_process_host_id, request.url)) { + DVLOG(1) << "Denied unauthorized request for " + << request.url.possibly_invalid_spec(); + OnClientComplete(net::ERR_INVALID_URL); + return; + } + + std::string range_header; + if (request.headers.GetHeader(net::HttpRequestHeaders::kRange, + &range_header)) { + std::vector ranges; + if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) { + if (ranges.size() == 1) { + byte_range_ = ranges[0]; + } else { + // We don't support multiple range requests in one single URL request. + // TODO(adamk): decide whether we want to support multiple range + // requests. + OnClientComplete(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); + return; + } + } + } + + url_ = params_.file_system_context->CrackURL(request.url); + if (!url_.is_valid()) { + const FileSystemRequestInfo request_info = {request.url, nullptr, + params_.storage_domain, + params_.frame_tree_node_id}; + params_.file_system_context->AttemptAutoMountForURLRequest( + request_info, + base::BindOnce(&FileSystemEntryURLLoader::DidAttemptAutoMount, + AsWeakPtr(), request)); + return; + } + FileSystemIsMounted(); + } + + void DidAttemptAutoMount(const network::ResourceRequest& request, + base::File::Error result) { + if (result != base::File::Error::FILE_OK) { + OnClientComplete(result); + return; + } + url_ = params_.file_system_context->CrackURL(request.url); + if (!url_.is_valid()) { + OnClientComplete(net::ERR_FILE_NOT_FOUND); + return; + } + FileSystemIsMounted(); + } + + void OnConnectionError() { + binding_.Close(); + MaybeDeleteSelf(); + } + + DISALLOW_COPY_AND_ASSIGN(FileSystemEntryURLLoader); +}; + +class FileSystemDirectoryURLLoader : public FileSystemEntryURLLoader { + public: + static void CreateAndStart( + const network::ResourceRequest& request, + network::mojom::URLLoaderRequest loader, + network::mojom::URLLoaderClientPtrInfo client_info, + FactoryParams params, + scoped_refptr io_task_runner) { + // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr + // bindings are alive - essentially until either the client gives up or all + // file directory has been sent to it. + auto* filesystem_loader = + new FileSystemDirectoryURLLoader(std::move(params)); + filesystem_loader->Start(request, std::move(loader), std::move(client_info), + io_task_runner); + } + + private: + explicit FileSystemDirectoryURLLoader(FactoryParams params) + : FileSystemEntryURLLoader(params) {} + + void FileSystemIsMounted() override { + DCHECK(url_.is_valid()); + if (!params_.file_system_context->CanServeURLRequest(url_)) { + // In incognito mode the API is not usable and there should be no data. + if (VirtualPath::IsRootPath(url_.virtual_path())) { + // Return an empty directory if the filesystem root is queried. + DidReadDirectory(base::File::FILE_OK, std::vector(), + /*has_more=*/false); + return; + } + // In incognito mode the API is not usable and there should be no data. + OnClientComplete(net::ERR_FILE_NOT_FOUND); + return; + } + params_.file_system_context->operation_runner()->ReadDirectory( + url_, + base::BindRepeating(&FileSystemDirectoryURLLoader::DidReadDirectory, + base::AsWeakPtr(this))); + } + + void DidReadDirectory(base::File::Error result, + std::vector entries, + bool has_more) { + if (result != base::File::FILE_OK) { + net::Error rv = net::ERR_FILE_NOT_FOUND; + if (result == base::File::FILE_ERROR_INVALID_URL) + rv = net::ERR_INVALID_URL; + OnClientComplete(rv); + return; + } + + if (data_.empty()) { + base::FilePath relative_path = url_.path(); +#if defined(OS_POSIX) + relative_path = + base::FilePath(FILE_PATH_LITERAL("/") + relative_path.value()); +#endif + const base::string16& title = relative_path.LossyDisplayName(); + data_.append(net::GetDirectoryListingHeader(title)); + } + + entries_.insert(entries_.end(), entries.begin(), entries.end()); + + if (!has_more) { + if (entries_.size()) + GetMetadata(/*index=*/0); + else + WriteDirectoryData(); + } + } + + void GetMetadata(size_t index) { + const DirectoryEntry& entry = entries_[index]; + const FileSystemURL entry_url = + params_.file_system_context->CreateCrackedFileSystemURL( + url_.origin(), url_.type(), + url_.path().Append(base::FilePath(entry.name))); + DCHECK(entry_url.is_valid()); + params_.file_system_context->operation_runner()->GetMetadata( + entry_url, + FileSystemOperation::GET_METADATA_FIELD_SIZE | + FileSystemOperation::GET_METADATA_FIELD_LAST_MODIFIED, + base::BindRepeating(&FileSystemDirectoryURLLoader::DidGetMetadata, + base::AsWeakPtr(this), index)); + } + + void DidGetMetadata(size_t index, + base::File::Error result, + const base::File::Info& file_info) { + if (result != base::File::FILE_OK) { + OnClientComplete(result); + return; + } + + const DirectoryEntry& entry = entries_[index]; + const base::string16& name = base::FilePath(entry.name).LossyDisplayName(); + data_.append(net::GetDirectoryListingEntry( + name, std::string(), + entry.type == filesystem::mojom::FsFileType::DIRECTORY, file_info.size, + file_info.last_modified)); + + if (index < entries_.size() - 1) + GetMetadata(index + 1); + else + WriteDirectoryData(); + } + + void WriteDirectoryData() { + mojo::DataPipe pipe(std::max(data_.size(), kDefaultFileSystemUrlPipeSize)); + if (!pipe.consumer_handle.is_valid()) { + OnClientComplete(net::ERR_FAILED); + return; + } + + network::ResourceResponseHead head; + head.mime_type = "text/plain"; + head.charset = "utf-8"; + head.content_length = data_.size(); + head.headers = CreateHttpResponseHeaders(200); + + client_->OnReceiveResponse(head, /*downloaded_file=*/nullptr); + client_->OnStartLoadingResponseBody(std::move(pipe.consumer_handle)); + + data_producer_ = std::make_unique( + std::move(pipe.producer_handle)); + + data_producer_->Write( + base::StringPiece(data_), + mojo::StringDataPipeProducer::AsyncWritingMode:: + STRING_STAYS_VALID_UNTIL_COMPLETION, + base::BindOnce(&FileSystemDirectoryURLLoader::OnDirectoryWritten, + base::Unretained(this))); + } + + void OnDirectoryWritten(MojoResult result) { + // All the data has been written now. Close the data pipe. The consumer will + // be notified that there will be no more data to read from now. + data_producer_.reset(); + directory_data_ = nullptr; + entries_.clear(); + data_.clear(); + + OnClientComplete(result == MOJO_RESULT_OK ? net::OK : net::ERR_FAILED); + } + + std::string data_; + std::vector entries_; + scoped_refptr directory_data_; + + DISALLOW_COPY_AND_ASSIGN(FileSystemDirectoryURLLoader); +}; + +class FileSystemFileURLLoader : public FileSystemEntryURLLoader { + public: + static void CreateAndStart( + const network::ResourceRequest& request, + network::mojom::URLLoaderRequest loader, + network::mojom::URLLoaderClientPtrInfo client_info, + FactoryParams params, + scoped_refptr io_task_runner) { + // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr + // bindings are alive - essentially until either the client gives up or all + // file data has been sent to it. + auto* filesystem_loader = + new FileSystemFileURLLoader(std::move(params), request, io_task_runner); + + filesystem_loader->Start(request, std::move(loader), std::move(client_info), + io_task_runner); + } + + private: + FileSystemFileURLLoader( + FactoryParams params, + const network::ResourceRequest& request, + scoped_refptr io_task_runner) + : FileSystemEntryURLLoader(std::move(params)), + original_request_(request), + io_task_runner_(io_task_runner) {} + + void FileSystemIsMounted() override { + DCHECK(url_.is_valid()); + if (!params_.file_system_context->CanServeURLRequest(url_)) { + // In incognito mode the API is not usable and there should be no data. + OnClientComplete(net::ERR_FILE_NOT_FOUND); + return; + } + params_.file_system_context->operation_runner()->GetMetadata( + url_, + FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY | + FileSystemOperation::GET_METADATA_FIELD_SIZE, + base::AdaptCallbackForRepeating(base::BindOnce( + &FileSystemFileURLLoader::DidGetMetadata, base::AsWeakPtr(this)))); + } + + void DidGetMetadata(base::File::Error error_code, + const base::File::Info& file_info) { + if (error_code != base::File::FILE_OK) { + OnClientComplete(error_code == base::File::FILE_ERROR_INVALID_URL + ? net::ERR_INVALID_URL + : net::ERR_FILE_NOT_FOUND); + return; + } + + if (!byte_range_.ComputeBounds(file_info.size)) { + OnClientComplete(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); + return; + } + + if (file_info.is_directory) { + // Redirect to the directory URLLoader. + GURL::Replacements replacements; + std::string new_path = original_request_.url.path(); + new_path.push_back('/'); + replacements.SetPathStr(new_path); + const GURL directory_url = + original_request_.url.ReplaceComponents(replacements); + + net::RedirectInfo redirect_info; + redirect_info.new_method = "GET"; + redirect_info.status_code = 301; + head_.headers = CreateHttpResponseHeaders(redirect_info.status_code); + redirect_info.new_url = + original_request_.url.ReplaceComponents(replacements); + head_.encoded_data_length = 0; + client_->OnReceiveRedirect(redirect_info, head_); + + // Restart the request with a directory loader. + network::ResourceRequest new_request = original_request_; + new_request.url = redirect_info.new_url; + FileSystemDirectoryURLLoader::CreateAndStart( + new_request, binding_.Unbind(), client_.PassInterface(), + std::move(params_), io_task_runner_); + MaybeDeleteSelf(); + return; + } + + remaining_bytes_ = byte_range_.last_byte_position() - + byte_range_.first_byte_position() + 1; + DCHECK_GE(remaining_bytes_, 0); + + DCHECK(!reader_.get()); + reader_ = params_.file_system_context->CreateFileStreamReader( + url_, byte_range_.first_byte_position(), remaining_bytes_, + base::Time()); + + mojo::DataPipe pipe(remaining_bytes_); + if (!pipe.consumer_handle.is_valid()) { + OnClientComplete(net::ERR_FAILED); + return; + } + consumer_handle_ = std::move(pipe.consumer_handle); + + head_.mime_type = "text/html"; // Will sniff file and possibly override. + head_.charset = "utf-8"; + head_.content_length = remaining_bytes_; + head_.headers = CreateHttpResponseHeaders(200); + + data_producer_ = std::make_unique( + std::move(pipe.producer_handle)); + + file_data_ = new net::IOBuffer(kDefaultFileSystemUrlPipeSize); + ReadMoreFileData(); + } + + void ReadMoreFileData() { + int64_t bytes_to_read = std::min( + static_cast(kDefaultFileSystemUrlPipeSize), remaining_bytes_); + if (!bytes_to_read) { + OnFileWritten(MOJO_RESULT_OK); + return; + } + net::CompletionCallback read_callback = base::BindRepeating( + &FileSystemFileURLLoader::DidReadMoreFileData, base::AsWeakPtr(this)); + const int rv = + reader_->Read(file_data_.get(), bytes_to_read, read_callback); + if (rv == net::ERR_IO_PENDING) { + // async callback will be called. + return; + } + std::move(read_callback).Run(rv); + } + + void DidReadMoreFileData(int result) { + if (result <= 0) { + OnFileWritten(result); + return; + } + + if (consumer_handle_.is_valid()) { + if (byte_range_.first_byte_position() == 0) { + // Only sniff for mime-type in the first block of the file. + std::string type_hint; + GetMimeType(url_, &type_hint); + SniffMimeType(file_data_->data(), result, url_.ToGURL(), type_hint, + net::ForceSniffFileUrlsForHtml::kDisabled, + &head_.mime_type); + } + + client_->OnReceiveResponse(head_, /*downloaded_file=*/nullptr); + client_->OnStartLoadingResponseBody(std::move(consumer_handle_)); + } + remaining_bytes_ -= result; + DCHECK_GE(remaining_bytes_, 0); + + WriteFileData(result); + } + + void WriteFileData(int bytes_read) { + data_producer_->Write( + base::StringPiece(file_data_->data(), bytes_read), + mojo::StringDataPipeProducer::AsyncWritingMode:: + STRING_STAYS_VALID_UNTIL_COMPLETION, + base::BindOnce(&FileSystemFileURLLoader::OnFileDataWritten, + base::AsWeakPtr(this))); + } + + void OnFileDataWritten(MojoResult result) { + if (result != MOJO_RESULT_OK || remaining_bytes_ == 0) { + OnFileWritten(result); + return; + } + ReadMoreFileData(); + } + + void OnFileWritten(MojoResult result) { + // All the data has been written now. Close the data pipe. The consumer will + // be notified that there will be no more data to read from now. + data_producer_.reset(); + file_data_ = nullptr; + + OnClientComplete(result == MOJO_RESULT_OK ? net::OK : net::ERR_FAILED); + } + + int64_t remaining_bytes_ = 0; + mojo::ScopedDataPipeConsumerHandle consumer_handle_; + std::unique_ptr reader_; + scoped_refptr file_data_; + network::ResourceResponseHead head_; + const network::ResourceRequest original_request_; + scoped_refptr io_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(FileSystemFileURLLoader); +}; + +// A URLLoaderFactory used for the filesystem:// scheme used when the Network +// Service is enabled. +class FileSystemURLLoaderFactory : public network::mojom::URLLoaderFactory { + public: + FileSystemURLLoaderFactory( + FactoryParams params, + scoped_refptr io_task_runner) + : params_(std::move(params)), io_task_runner_(io_task_runner) {} + + ~FileSystemURLLoaderFactory() override = default; + + network::mojom::URLLoaderFactoryPtr CreateBinding() { + network::mojom::URLLoaderFactoryPtr factory; + bindings_.AddBinding(this, mojo::MakeRequest(&factory)); + return factory; + } + + private: + void CreateLoaderAndStart(network::mojom::URLLoaderRequest loader, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + network::mojom::URLLoaderClientPtr client, + const net::MutableNetworkTrafficAnnotationTag& + traffic_annotation) override { + DVLOG(1) << "CreateLoaderAndStart: " << request.url; + + const std::string path = request.url.path(); + + // If the path ends with a /, we know it's a directory. If the path refers + // to a directory and gets dispatched to FileSystemFileURLLoader, that class + // will redirect to FileSystemDirectoryURLLoader. + if (!path.empty() && path.back() == '/') { + FileSystemDirectoryURLLoader::CreateAndStart(request, std::move(loader), + client.PassInterface(), + params_, io_task_runner_); + return; + } + + FileSystemFileURLLoader::CreateAndStart(request, std::move(loader), + client.PassInterface(), params_, + io_task_runner_); + } + + void Clone(network::mojom::URLLoaderFactoryRequest loader) override { + bindings_.AddBinding(this, std::move(loader)); + } + + const FactoryParams params_; + mojo::BindingSet bindings_; + scoped_refptr io_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(FileSystemURLLoaderFactory); +}; + +} // anonymous namespace + +std::unique_ptr +CreateFileSystemURLLoaderFactory( + RenderFrameHost* render_frame_host, + bool is_navigation, + scoped_refptr file_system_context, + const std::string& storage_domain) { + // Get the RPH ID for security checks for non-navigation resource requests. + int render_process_host_id = is_navigation + ? ChildProcessHost::kInvalidUniqueID + : render_frame_host->GetProcess()->GetID(); + + FactoryParams params = {render_process_host_id, + render_frame_host->GetFrameTreeNodeId(), + file_system_context, storage_domain}; + + return std::make_unique( + std::move(params), + BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)); +} + +} // namespace content diff --git a/chromium/content/browser/fileapi/file_system_url_loader_factory.h b/chromium/content/browser/fileapi/file_system_url_loader_factory.h new file mode 100644 index 00000000000..2cfda1e18ec --- /dev/null +++ b/chromium/content/browser/fileapi/file_system_url_loader_factory.h @@ -0,0 +1,31 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_FILEAPI_FILE_SYSTEM_URL_LOADER_FACTORY_H_ +#define CONTENT_BROWSER_FILEAPI_FILE_SYSTEM_URL_LOADER_FACTORY_H_ + +#include +#include + +#include "base/memory/ref_counted.h" +#include "content/common/content_export.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "storage/browser/fileapi/file_system_context.h" + +namespace content { + +class RenderFrameHost; + +// Create a URLLoaderFactory to serve filesystem: requests from the given +// |file_system_context| and |storage_domain|. +CONTENT_EXPORT std::unique_ptr +CreateFileSystemURLLoaderFactory( + RenderFrameHost* render_frame_host, + bool is_navigation, + scoped_refptr file_system_context, + const std::string& storage_domain); + +} // namespace content + +#endif // CONTENT_BROWSER_FILEAPI_FILE_SYSTEM_URL_LOADER_FACTORY_H_ diff --git a/chromium/content/browser/fileapi/file_system_url_loader_factory_browsertest.cc b/chromium/content/browser/fileapi/file_system_url_loader_factory_browsertest.cc new file mode 100644 index 00000000000..79fd580333d --- /dev/null +++ b/chromium/content/browser/fileapi/file_system_url_loader_factory_browsertest.cc @@ -0,0 +1,817 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/i18n/unicodestring.h" +#include "base/rand_util.h" +#include "base/test/scoped_feature_list.h" +#include "build/build_config.h" +#include "content/browser/fileapi/file_system_url_loader_factory.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/test/content_browser_test.h" +#include "content/shell/browser/shell.h" +#include "net/base/mime_util.h" +#include "net/http/http_util.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "services/network/test/test_url_loader_client.h" +#include "storage/browser/fileapi/external_mount_points.h" +#include "storage/browser/fileapi/file_system_context.h" +#include "storage/browser/fileapi/file_system_file_util.h" +#include "storage/browser/fileapi/file_system_operation_context.h" +#include "storage/browser/fileapi/file_system_url.h" +#include "storage/browser/test/async_file_test_helper.h" +#include "storage/browser/test/mock_special_storage_policy.h" +#include "storage/browser/test/test_file_system_backend.h" +#include "storage/browser/test/test_file_system_context.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/icu/source/i18n/unicode/datefmt.h" +#include "third_party/icu/source/i18n/unicode/regex.h" + +using content::AsyncFileTestHelper; +using network::mojom::URLLoaderFactory; +using storage::FileSystemContext; +using storage::FileSystemOperationContext; +using storage::FileSystemURL; + +namespace content { +namespace { + +// We always use the TEMPORARY FileSystem in these tests. +const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/"; + +const char kValidExternalMountPoint[] = "mnt_name"; + +const char kTestFileData[] = "0123456789"; + +void FillBuffer(char* buffer, size_t len) { + base::RandBytes(buffer, len); +} + +// 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 storage::FileSystemRequestInfo& request_info, + const storage::FileSystemURL& filesystem_url, + base::OnceCallback callback) { + if (request_info.storage_domain != "automount") + return false; + + std::vector components; + filesystem_url.path().GetComponents(&components); + std::string mount_point = base::FilePath(components[0]).AsUTF8Unsafe(); + + if (mount_point == kValidExternalMountPoint) { + storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( + kValidExternalMountPoint, storage::kFileSystemTypeTest, + storage::FileSystemMountOption(), base::FilePath()); + std::move(callback).Run(base::File::FILE_OK); + } else { + std::move(callback).Run(base::File::FILE_ERROR_NOT_FOUND); + } + return true; +} + +void ReadDataPipeInternal(mojo::DataPipeConsumerHandle handle, + std::string* result, + base::OnceClosure quit_closure) { + while (true) { + uint32_t num_bytes; + const void* buffer = nullptr; + MojoResult rv = + handle.BeginReadData(&buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE); + switch (rv) { + case MOJO_RESULT_BUSY: + case MOJO_RESULT_INVALID_ARGUMENT: + NOTREACHED(); + return; + case MOJO_RESULT_FAILED_PRECONDITION: + std::move(quit_closure).Run(); + return; + case MOJO_RESULT_SHOULD_WAIT: + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&ReadDataPipeInternal, handle, result, + std::move(quit_closure))); + return; + case MOJO_RESULT_OK: + EXPECT_NE(nullptr, buffer); + EXPECT_GT(num_bytes, 0u); + uint32_t before_size = result->size(); + result->append(static_cast(buffer), num_bytes); + uint32_t read_size = result->size() - before_size; + EXPECT_EQ(num_bytes, read_size); + rv = handle.EndReadData(read_size); + EXPECT_EQ(MOJO_RESULT_OK, rv); + break; + } + } + NOTREACHED(); + return; +} + +std::string ReadDataPipe(mojo::ScopedDataPipeConsumerHandle handle) { + EXPECT_TRUE(handle.is_valid()); + if (!handle.is_valid()) + return ""; + std::string result; + base::RunLoop loop; + ReadDataPipeInternal(handle.get(), &result, loop.QuitClosure()); + loop.Run(); + return result; +} + +// Directory listings can have a HTML header in the file to format the response. +// This function determines if a single line in the response is for a directory +// entry. +bool IsDirectoryListingLine(const std::string& line) { + return line.find(""); +#undef NUMBER +#undef STR + icu::UnicodeString input(entry_line.c_str()); + + UErrorCode status = U_ZERO_ERROR; + icu::RegexMatcher match(pattern, input, 0, status); + + EXPECT_TRUE(match.find()); + EXPECT_EQ(7, match.groupCount()); + EXPECT_EQ(icu::UnicodeString(name.c_str()), match.group(1, status)); + EXPECT_EQ(icu::UnicodeString(url.c_str()), match.group(2, status)); + EXPECT_EQ(icu::UnicodeString(is_directory ? "1" : "0"), + match.group(3, status)); + if (size >= 0) { + icu::UnicodeString size_string( + base::FormatBytesUnlocalized(size).c_str()); + EXPECT_EQ(size_string, match.group(5, status)); + } + + icu::UnicodeString date_ustr(match.group(7, status)); + std::unique_ptr formatter( + icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort)); + UErrorCode parse_status = U_ZERO_ERROR; + UDate udate = formatter->parse(date_ustr, parse_status); + EXPECT_TRUE(U_SUCCESS(parse_status)); + base::Time date = base::Time::FromJsTime(udate); + EXPECT_FALSE(date.is_null()); + } + + GURL CreateFileSystemURL(const std::string& path) { + return GURL(kFileSystemURLPrefix + path); + } + + std::unique_ptr TestLoad(const GURL& url) { + auto client = TestLoadHelper(url, /*extra_headers=*/nullptr, + file_system_context_.get()); + client->RunUntilComplete(); + return client; + } + + std::unique_ptr TestLoadWithContext( + const GURL& url, + FileSystemContext* file_system_context) { + auto client = + TestLoadHelper(url, /*extra_headers=*/nullptr, file_system_context); + client->RunUntilComplete(); + return client; + } + + std::unique_ptr TestLoadWithHeaders( + const GURL& url, + const net::HttpRequestHeaders* extra_headers) { + auto client = + TestLoadHelper(url, extra_headers, file_system_context_.get()); + client->RunUntilComplete(); + return client; + } + + std::unique_ptr TestLoadNoRun(const GURL& url) { + return TestLoadHelper(url, /*extra_headers=*/nullptr, + file_system_context_.get()); + } + + // |temp_dir_| must be deleted last. + base::ScopedTempDir temp_dir_; + network::mojom::URLLoaderPtr loader_; + + private: + storage::FileSystemFileUtil* file_util() { + return file_system_context_->sandbox_delegate()->sync_file_util(); + } + + FileSystemOperationContext* NewOperationContext() { + FileSystemOperationContext* context( + new FileSystemOperationContext(file_system_context_.get())); + context->set_allowed_bytes_growth(1024); + return context; + } + + void OnOpenFileSystem(const GURL& root_url, + const std::string& name, + base::File::Error result) { + ASSERT_EQ(base::File::FILE_OK, result); + } + + RenderFrameHost* render_frame_host() const { + return shell()->web_contents()->GetMainFrame(); + } + + // Starts |request| using |loader_factory| and sets |out_loader| and + // |out_loader_client| to the resulting URLLoader and its URLLoaderClient. The + // caller can then use functions like client.RunUntilComplete() to wait for + // completion. + void StartRequest( + URLLoaderFactory* loader_factory, + const network::ResourceRequest& request, + network::mojom::URLLoaderPtr* out_loader, + std::unique_ptr* out_loader_client) { + *out_loader_client = std::make_unique(); + loader_factory->CreateLoaderAndStart( + mojo::MakeRequest(out_loader), 0, 0, network::mojom::kURLLoadOptionNone, + request, (*out_loader_client)->CreateInterfacePtr(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + } + + content::WebContents* GetWebContents() { return shell()->web_contents(); } + + std::unique_ptr TestLoadHelper( + const GURL& url, + const net::HttpRequestHeaders* extra_headers, + FileSystemContext* file_system_context) { + network::ResourceRequest request; + request.url = url; + if (extra_headers) + request.headers.MergeFrom(*extra_headers); + const std::string storage_domain = url.GetOrigin().host(); + + auto factory = content::CreateFileSystemURLLoaderFactory( + render_frame_host(), /*is_navigation=*/false, file_system_context, + storage_domain); + std::unique_ptr client; + StartRequest(factory.get(), request, &loader_, &client); + return client; + } + + base::test::ScopedFeatureList feature_list_; + scoped_refptr special_storage_policy_; + scoped_refptr file_system_context_; + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(FileSystemURLLoaderFactoryTest); +}; + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, DirectoryListing) { + base::ScopedAllowBlockingForTesting allow_blocking; + CreateDirectory("foo"); + CreateDirectory("foo/bar"); + CreateDirectory("foo/bar/baz"); + + EnsureFileExists("foo/bar/hoge"); + TruncateFile("foo/bar/hoge", 10); + + auto client = TestLoad(CreateFileSystemURL("foo/bar/")); + + ASSERT_TRUE(client->has_received_response()); + ASSERT_TRUE(client->has_received_completion()); + + std::string response_text = ReadDataPipe(client->response_body_release()); + EXPECT_GT(response_text.size(), 0ul); + + std::istringstream in(response_text); + std::string line; + + std::string listing_header; + std::vector listing_entries; + while (!!std::getline(in, line)) { + if (listing_header.empty() && IsDirectoryListingTitle(line)) { + listing_header = line; + continue; + } + if (IsDirectoryListingLine(line)) + listing_entries.push_back(line); + } + +#if defined(OS_WIN) + EXPECT_EQ("", listing_header); +#elif defined(OS_POSIX) + EXPECT_EQ("", listing_header); +#endif + + ASSERT_EQ(2U, listing_entries.size()); + std::sort(listing_entries.begin(), listing_entries.end()); + VerifyListingEntry(listing_entries[0], "baz", "baz", true, 0); + VerifyListingEntry(listing_entries[1], "hoge", "hoge", false, 10); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, InvalidURL) { + base::ScopedAllowBlockingForTesting allow_blocking; + auto client = TestLoad(GURL("filesystem:/foo/bar/baz")); + ASSERT_FALSE(client->has_received_response()); + ASSERT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_INVALID_URL, client->completion_status().error_code); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, NoSuchRoot) { + base::ScopedAllowBlockingForTesting allow_blocking; + auto client = TestLoad(GURL("filesystem:http://remote/persistent/somedir/")); + ASSERT_FALSE(client->has_received_response()); + ASSERT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client->completion_status().error_code); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, NoSuchDirectory) { + base::ScopedAllowBlockingForTesting allow_blocking; + auto client = TestLoad(CreateFileSystemURL("somedir/")); + ASSERT_FALSE(client->has_received_response()); + ASSERT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client->completion_status().error_code); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, Cancel) { + base::ScopedAllowBlockingForTesting allow_blocking; + CreateDirectory("foo"); + auto client = TestLoadNoRun(CreateFileSystemURL("foo/")); + ASSERT_FALSE(client->has_received_response()); + ASSERT_FALSE(client->has_received_completion()); + + client.reset(); + loader_.reset(); + base::RunLoop().RunUntilIdle(); + // If we get here, success! we didn't crash! +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, Incognito) { + base::ScopedAllowBlockingForTesting allow_blocking; + CreateDirectory("foo"); + + scoped_refptr file_system_context = + CreateIncognitoFileSystemContextForTesting(nullptr, temp_dir_.GetPath()); + + auto client = + TestLoadWithContext(CreateFileSystemURL("/"), file_system_context.get()); + ASSERT_TRUE(client->has_received_response()); + ASSERT_TRUE(client->has_received_completion()); + + std::string response_text = ReadDataPipe(client->response_body_release()); + EXPECT_GT(response_text.size(), 0ul); + + std::istringstream in(response_text); + + int num_entries = 0; + std::string line; + while (!!std::getline(in, line)) { + if (IsDirectoryListingLine(line)) + num_entries++; + } + + EXPECT_EQ(0, num_entries); + + client = TestLoadWithContext(CreateFileSystemURL("foo"), + file_system_context.get()); + ASSERT_FALSE(client->has_received_response()); + ASSERT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client->completion_status().error_code); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, + AutoMountDirectoryListing) { + base::ScopedAllowBlockingForTesting allow_blocking; + base::FilePath mnt_point; + SetUpAutoMountContext(&mnt_point); + EXPECT_TRUE(base::CreateDirectory(mnt_point)); + EXPECT_TRUE(base::CreateDirectory(mnt_point.AppendASCII("foo"))); + EXPECT_EQ(10, + base::WriteFile(mnt_point.AppendASCII("bar"), "1234567890", 10)); + + auto client = + TestLoad(GURL("filesystem:http://automount/external/mnt_name/")); + + ASSERT_TRUE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + + std::string response_text = ReadDataPipe(client->response_body_release()); + EXPECT_GT(response_text.size(), 0ul); + + std::istringstream in(response_text); + 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 listing_entries; + while (!!std::getline(in, line)) { + if (IsDirectoryListingLine(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); + + EXPECT_TRUE( + storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem( + kValidExternalMountPoint)); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, AutoMountInvalidRoot) { + base::ScopedAllowBlockingForTesting allow_blocking; + base::FilePath mnt_point; + SetUpAutoMountContext(&mnt_point); + auto client = TestLoad(GURL("filesystem:http://automount/external/invalid")); + + EXPECT_FALSE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client->completion_status().error_code); + + EXPECT_FALSE( + storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem( + "invalid")); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, AutoMountNoHandler) { + base::ScopedAllowBlockingForTesting allow_blocking; + base::FilePath mnt_point; + SetUpAutoMountContext(&mnt_point); + auto client = TestLoad(GURL("filesystem:http://noauto/external/mnt_name")); + + EXPECT_FALSE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client->completion_status().error_code); + + EXPECT_FALSE( + storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem( + kValidExternalMountPoint)); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, FileTest) { + base::ScopedAllowBlockingForTesting allow_blocking; + WriteFile("file1.dat", kTestFileData, base::size(kTestFileData) - 1); + auto client = TestLoad(CreateFileSystemURL("file1.dat")); + + EXPECT_TRUE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + std::string response_text = ReadDataPipe(client->response_body_release()); + EXPECT_EQ(kTestFileData, response_text); + ASSERT_TRUE(client->response_head().headers) << "No response headers"; + EXPECT_EQ(200, client->response_head().headers->response_code()); + std::string cache_control; + EXPECT_TRUE(client->response_head().headers->GetNormalizedHeader( + "cache-control", &cache_control)); + EXPECT_EQ("no-cache", cache_control); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, + FileTestFullSpecifiedRange) { + base::ScopedAllowBlockingForTesting allow_blocking; + const size_t buffer_size = 4000; + std::unique_ptr buffer(new char[buffer_size]); + FillBuffer(buffer.get(), buffer_size); + WriteFile("bigfile", buffer.get(), buffer_size); + + const size_t first_byte_position = 500; + const size_t last_byte_position = buffer_size - first_byte_position; + std::string partial_buffer_string(buffer.get() + first_byte_position, + buffer.get() + last_byte_position + 1); + + net::HttpRequestHeaders headers; + headers.SetHeader( + net::HttpRequestHeaders::kRange, + net::HttpByteRange::Bounded(first_byte_position, last_byte_position) + .GetHeaderValue()); + auto client = TestLoadWithHeaders(CreateFileSystemURL("bigfile"), &headers); + + ASSERT_TRUE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + std::string response_text = ReadDataPipe(client->response_body_release()); + EXPECT_TRUE(partial_buffer_string == response_text); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, + FileTestHalfSpecifiedRange) { + base::ScopedAllowBlockingForTesting allow_blocking; + const size_t buffer_size = 4000; + std::unique_ptr buffer(new char[buffer_size]); + FillBuffer(buffer.get(), buffer_size); + WriteFile("bigfile", buffer.get(), buffer_size); + + const size_t first_byte_position = 500; + std::string partial_buffer_string(buffer.get() + first_byte_position, + buffer.get() + buffer_size); + + net::HttpRequestHeaders headers; + headers.SetHeader( + net::HttpRequestHeaders::kRange, + net::HttpByteRange::RightUnbounded(first_byte_position).GetHeaderValue()); + auto client = TestLoadWithHeaders(CreateFileSystemURL("bigfile"), &headers); + ASSERT_TRUE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed. + std::string response_text = ReadDataPipe(client->response_body_release()); + EXPECT_TRUE(partial_buffer_string == response_text); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, + FileTestMultipleRangesNotSupported) { + base::ScopedAllowBlockingForTesting allow_blocking; + WriteFile("file1.dat", kTestFileData, base::size(kTestFileData) - 1); + net::HttpRequestHeaders headers; + headers.SetHeader(net::HttpRequestHeaders::kRange, + "bytes=0-5,10-200,200-300"); + auto client = TestLoadWithHeaders(CreateFileSystemURL("file1.dat"), &headers); + EXPECT_FALSE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, + client->completion_status().error_code); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, FileRangeOutOfBounds) { + base::ScopedAllowBlockingForTesting allow_blocking; + WriteFile("file1.dat", kTestFileData, base::size(kTestFileData) - 1); + net::HttpRequestHeaders headers; + headers.SetHeader(net::HttpRequestHeaders::kRange, + net::HttpByteRange::Bounded(500, 1000).GetHeaderValue()); + auto client = TestLoadWithHeaders(CreateFileSystemURL("file1.dat"), &headers); + + EXPECT_FALSE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, + client->completion_status().error_code); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, FileDirRedirect) { + base::ScopedAllowBlockingForTesting allow_blocking; + CreateDirectory("dir"); + auto client = TestLoad(CreateFileSystemURL("dir")); + + EXPECT_TRUE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + EXPECT_TRUE(client->has_received_redirect()); + EXPECT_EQ(301, client->redirect_info().status_code); + EXPECT_EQ(CreateFileSystemURL("dir/"), client->redirect_info().new_url); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, FileNoSuchRoot) { + base::ScopedAllowBlockingForTesting allow_blocking; + auto client = TestLoad(GURL("filesystem:http://remote/persistent/somefile")); + EXPECT_FALSE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client->completion_status().error_code); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, NoSuchFile) { + base::ScopedAllowBlockingForTesting allow_blocking; + auto client = TestLoad(CreateFileSystemURL("somefile")); + EXPECT_FALSE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client->completion_status().error_code); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, FileCancel) { + base::ScopedAllowBlockingForTesting allow_blocking; + WriteFile("file1.dat", kTestFileData, base::size(kTestFileData) - 1); + auto client = TestLoadNoRun(CreateFileSystemURL("file1.dat")); + + // client.reset(); + base::RunLoop().RunUntilIdle(); + // If we get here, success! we didn't crash! +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, FileGetMimeType) { + base::ScopedAllowBlockingForTesting allow_blocking; + std::string file_data = + "test" + "foo"; + const char kFilename[] = "hoge.html"; + WriteFile(kFilename, file_data.data(), file_data.size()); + + std::string mime_type_direct; + base::FilePath::StringType extension = + base::FilePath().AppendASCII(kFilename).Extension(); + if (!extension.empty()) + extension = extension.substr(1); + EXPECT_TRUE( + net::GetWellKnownMimeTypeFromExtension(extension, &mime_type_direct)); + + auto client = TestLoad(CreateFileSystemURL(kFilename)); + EXPECT_TRUE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + + EXPECT_EQ(mime_type_direct, client->response_head().mime_type); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, FileIncognito) { + base::ScopedAllowBlockingForTesting allow_blocking; + WriteFile("file", kTestFileData, base::size(kTestFileData) - 1); + + // Creates a new filesystem context for incognito mode. + scoped_refptr file_system_context = + CreateIncognitoFileSystemContextForTesting(nullptr, temp_dir_.GetPath()); + + // The request should return NOT_FOUND error if it's in incognito mode. + auto client = TestLoadWithContext(CreateFileSystemURL("file"), + file_system_context.get()); + EXPECT_FALSE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client->completion_status().error_code); + + // Make sure it returns success with regular (non-incognito) context. + client = TestLoad(CreateFileSystemURL("file")); + ASSERT_TRUE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + std::string response_text = ReadDataPipe(client->response_body_release()); + EXPECT_EQ(kTestFileData, response_text); + EXPECT_EQ(200, client->response_head().headers->response_code()); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, FileAutoMountFileTest) { + base::ScopedAllowBlockingForTesting allow_blocking; + SetFileUpAutoMountContext(); + auto client = + TestLoad(GURL("filesystem:http://automount/external/mnt_name/foo")); + + ASSERT_TRUE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + std::string response_text = ReadDataPipe(client->response_body_release()); + EXPECT_EQ(kTestFileData, response_text); + EXPECT_EQ(200, client->response_head().headers->response_code()); + + std::string cache_control; + EXPECT_TRUE(client->response_head().headers->GetNormalizedHeader( + "cache-control", &cache_control)); + EXPECT_EQ("no-cache", cache_control); + + ASSERT_TRUE( + storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem( + kValidExternalMountPoint)); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, + FileAutoMountInvalidRoot) { + base::ScopedAllowBlockingForTesting allow_blocking; + SetFileUpAutoMountContext(); + auto client = + TestLoad(GURL("filesystem:http://automount/external/invalid/foo")); + + EXPECT_FALSE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client->completion_status().error_code); + + ASSERT_FALSE( + storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem( + "invalid")); +} + +IN_PROC_BROWSER_TEST_F(FileSystemURLLoaderFactoryTest, FileAutoMountNoHandler) { + base::ScopedAllowBlockingForTesting allow_blocking; + SetFileUpAutoMountContext(); + auto client = + TestLoad(GURL("filesystem:http://noauto/external/mnt_name/foo")); + + EXPECT_FALSE(client->has_received_response()); + EXPECT_TRUE(client->has_received_completion()); + EXPECT_EQ(net::ERR_FILE_NOT_FOUND, client->completion_status().error_code); + + ASSERT_FALSE( + storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem( + kValidExternalMountPoint)); +} + +} // namespace content diff --git a/chromium/content/browser/fileapi/fileapi_browsertest.cc b/chromium/content/browser/fileapi/fileapi_browsertest.cc index e50f1569a17..617593c8f18 100644 --- a/chromium/content/browser/fileapi/fileapi_browsertest.cc +++ b/chromium/content/browser/fileapi/fileapi_browsertest.cc @@ -20,7 +20,7 @@ class FileAPIBrowserTest : public ContentBrowserTest { IN_PROC_BROWSER_TEST_F(FileAPIBrowserTest, FileInputChooserParams) { base::FilePath file; - EXPECT_TRUE(PathService::Get(base::DIR_TEMP, &file)); + EXPECT_TRUE(base::PathService::Get(base::DIR_TEMP, &file)); file = file.AppendASCII("bar"); NavigateToURL(shell(), GetTestUrl(".", "file_input.html")); diff --git a/chromium/content/browser/fileapi/fileapi_message_filter.h b/chromium/content/browser/fileapi/fileapi_message_filter.h index f699cc7b53d..7188c686f4d 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter.h +++ b/chromium/content/browser/fileapi/fileapi_message_filter.h @@ -16,7 +16,6 @@ #include "base/callback.h" #include "base/containers/hash_tables.h" -#include "base/files/file_util_proxy.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/shared_memory.h" diff --git a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc index 3093189171d..c996e7c34e2 100644 --- a/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc +++ b/chromium/content/browser/fileapi/fileapi_message_filter_unittest.cc @@ -44,13 +44,11 @@ class FileAPIMessageFilterTest : public testing::Test { file_system_context_ = CreateFileSystemContextForTesting(nullptr, base::FilePath()); - std::vector types; - file_system_context_->GetFileSystemTypes(&types); - for (size_t i = 0; i < types.size(); ++i) { + for (const storage::FileSystemType& type : + file_system_context_->GetFileSystemTypes()) { ChildProcessSecurityPolicyImpl::GetInstance() ->RegisterFileSystemPermissionPolicy( - types[i], - storage::FileSystemContext::GetPermissionPolicy(types[i])); + type, storage::FileSystemContext::GetPermissionPolicy(type)); } blob_storage_context_ = ChromeBlobStorageContext::GetFor(&browser_context_); diff --git a/chromium/content/browser/find_request_manager.cc b/chromium/content/browser/find_request_manager.cc index b1d214c35f0..34c5307b0fa 100644 --- a/chromium/content/browser/find_request_manager.cc +++ b/chromium/content/browser/find_request_manager.cc @@ -9,9 +9,10 @@ #include "base/containers/queue.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/common/associated_interface_provider_impl.h" #include "content/common/frame_messages.h" -#include "content/common/input_messages.h" #include "content/public/browser/guest_mode.h" +#include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" namespace content { @@ -218,10 +219,8 @@ class FindRequestManager::FrameObserver : public WebContentsObserver { FindRequestManager::ActivateNearestFindResultState:: ActivateNearestFindResultState() = default; FindRequestManager::ActivateNearestFindResultState:: -ActivateNearestFindResultState(float x, float y) - : current_request_id(GetNextID()), - x(x), - y(y) {} + ActivateNearestFindResultState(float x, float y) + : current_request_id(GetNextID()), point(x, y) {} FindRequestManager::ActivateNearestFindResultState:: ~ActivateNearestFindResultState() {} @@ -271,8 +270,13 @@ void FindRequestManager::Find(int request_id, void FindRequestManager::StopFinding(StopFindAction action) { for (WebContentsImpl* contents : contents_->GetWebContentsAndAllInner()) { - contents->SendToAllFrames( - new FrameMsg_StopFinding(MSG_ROUTING_NONE, action)); + for (FrameTreeNode* node : contents->GetFrameTree()->Nodes()) { + RenderFrameHostImpl* rfh = node->current_frame_host(); + if (!CheckFrame(rfh) || !rfh->IsRenderFrameLive()) + continue; + rfh->GetFindInPage()->StopFinding( + static_cast(action)); + } } current_session_id_ = kInvalidId; @@ -284,7 +288,7 @@ void FindRequestManager::StopFinding(StopFindAction action) { #endif } -void FindRequestManager::OnFindReply(RenderFrameHost* rfh, +void FindRequestManager::OnFindReply(RenderFrameHostImpl* rfh, int request_id, int number_of_matches, const gfx::Rect& selection_rect, @@ -338,8 +342,7 @@ void FindRequestManager::OnFindReply(RenderFrameHost* rfh, // The new active match is in a different frame than the previous, so // the previous active frame needs to be informed (to clear its active // match highlighting). - active_frame_->Send(new FrameMsg_ClearActiveFindMatch( - active_frame_->GetRoutingID())); + ClearActiveFindMatch(); } active_frame_ = rfh; relative_active_match_ordinal_ = active_match_ordinal; @@ -374,6 +377,19 @@ void FindRequestManager::OnFindReply(RenderFrameHost* rfh, FinalUpdateReceived(request_id, rfh); } +void FindRequestManager::OnActivateNearestFindResultReply( + RenderFrameHostImpl* rfh, + int request_id, + const gfx::Rect& active_match_rect, + int number_of_matches, + int active_match_ordinal, + bool final_update) { + if (active_match_ordinal > 0) + contents_->SetFocusedFrame(rfh->frame_tree_node(), rfh->GetSiteInstance()); + OnFindReply(rfh, request_id, number_of_matches, active_match_rect, + active_match_ordinal, final_update); +} + void FindRequestManager::RemoveFrame(RenderFrameHost* rfh) { if (current_session_id_ == kInvalidId || !CheckFrame(rfh)) return; @@ -436,6 +452,10 @@ void FindRequestManager::RemoveFrame(RenderFrameHost* rfh) { } } +void FindRequestManager::ClearActiveFindMatch() { + active_frame_->GetFindInPage()->ClearActiveFindMatch(); +} + #if defined(OS_ANDROID) void FindRequestManager::ActivateNearestFindResult(float x, float y) { if (current_session_id_ == kInvalidId) @@ -447,20 +467,24 @@ void FindRequestManager::ActivateNearestFindResult(float x, float y) { // frame) from the point (x, y), defined in find-in-page coordinates. for (WebContentsImpl* contents : contents_->GetWebContentsAndAllInner()) { for (FrameTreeNode* node : contents->GetFrameTree()->Nodes()) { - RenderFrameHost* rfh = node->current_frame_host(); + RenderFrameHostImpl* rfh = node->current_frame_host(); if (!CheckFrame(rfh) || !rfh->IsRenderFrameLive()) continue; activate_.pending_replies.insert(rfh); - rfh->Send(new FrameMsg_GetNearestFindResult(rfh->GetRoutingID(), - activate_.current_request_id, - activate_.x, activate_.y)); + // Lifetime of FindRequestManager > RenderFrameHost > Mojo connection, + // so it's safe to bind |this| and |rfh|. + rfh->GetFindInPage()->GetNearestFindResult( + activate_.point, + base::BindOnce(&FindRequestManager::OnGetNearestFindResultReply, + base::Unretained(this), rfh, + activate_.current_request_id)); } } } -void FindRequestManager::OnGetNearestFindResultReply(RenderFrameHost* rfh, +void FindRequestManager::OnGetNearestFindResultReply(RenderFrameHostImpl* rfh, int request_id, float distance) { if (request_id != activate_.current_request_id || @@ -484,7 +508,7 @@ void FindRequestManager::RequestFindMatchRects(int current_version) { // Request the latest find match rects from each frame. for (WebContentsImpl* contents : contents_->GetWebContentsAndAllInner()) { for (FrameTreeNode* node : contents->GetFrameTree()->Nodes()) { - RenderFrameHost* rfh = node->current_frame_host(); + RenderFrameHostImpl* rfh = node->current_frame_host(); if (!CheckFrame(rfh) || !rfh->IsRenderFrameLive()) continue; @@ -493,7 +517,9 @@ void FindRequestManager::RequestFindMatchRects(int current_version) { auto it = match_rects_.frame_rects.find(rfh); int version = (it != match_rects_.frame_rects.end()) ? it->second.version : kInvalidId; - rfh->Send(new FrameMsg_FindMatchRects(rfh->GetRoutingID(), version)); + rfh->GetFindInPage()->FindMatchRects( + version, base::BindOnce(&FindRequestManager::OnFindMatchRectsReply, + base::Unretained(this), rfh)); } } } @@ -752,9 +778,14 @@ void FindRequestManager::RemoveNearestFindResultPendingReply( activate_.pending_replies.erase(it); if (activate_.pending_replies.empty() && CheckFrame(activate_.nearest_frame)) { - activate_.nearest_frame->Send(new FrameMsg_ActivateNearestFindResult( - activate_.nearest_frame->GetRoutingID(), - current_session_id_, activate_.x, activate_.y)); + // Lifetime of FindRequestManager > activate_.nearest_frame > Mojo + // connection, so it's safe to bind |this| and |activate_.nearest_frame| + activate_.nearest_frame->GetFindInPage()->ActivateNearestFindResult( + activate_.point, + base::BindOnce(&FindRequestManager::OnActivateNearestFindResultReply, + base::Unretained(this), + base::Unretained(activate_.nearest_frame), + current_session_id_)); } } diff --git a/chromium/content/browser/find_request_manager.h b/chromium/content/browser/find_request_manager.h index 1939bc57742..ae95c1325af 100644 --- a/chromium/content/browser/find_request_manager.h +++ b/chromium/content/browser/find_request_manager.h @@ -21,6 +21,7 @@ namespace content { class RenderFrameHost; +class RenderFrameHostImpl; class WebContentsImpl; // FindRequestManager manages all of the find-in-page requests/replies @@ -47,26 +48,37 @@ class CONTENT_EXPORT FindRequestManager { // Called when a reply is received from a frame with the results from a // find request. - void OnFindReply(RenderFrameHost* rfh, + void OnFindReply(RenderFrameHostImpl* rfh, int request_id, int number_of_matches, const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update); + // Called when a reply for ActivateNearestFindResult is received. + void OnActivateNearestFindResultReply(RenderFrameHostImpl* rfh, + int request_id, + const gfx::Rect& active_match_rect, + int number_of_matches, + int active_match_ordinal, + bool final_update); + // Removes a frame from the set of frames being searched. This should be // called whenever a frame is discovered to no longer exist. void RemoveFrame(RenderFrameHost* rfh); + // Tells active frame to clear the active match highlighting. + void ClearActiveFindMatch(); + #if defined(OS_ANDROID) // Selects and zooms to the find result nearest to the point (x, y), defined // in find-in-page coordinates. void ActivateNearestFindResult(float x, float y); // Called when a reply is received from a frame in response to the - // GetNearestFindResult IPC. - void OnGetNearestFindResultReply(RenderFrameHost* rfh, - int nearest_find_result_request_id, + // GetNearestFindResult mojo call. + void OnGetNearestFindResultReply(RenderFrameHostImpl* rfh, + int request_id, float distance); // Requests the rects of the current find matches from the renderer process. @@ -178,17 +190,13 @@ class CONTENT_EXPORT FindRequestManager { // its replies. int current_request_id = kInvalidId; - // The x value of the requested point, in find-in-page coordinates. - float x = 0.0f; - - // The y value of the requested point, in find-in-page coordinates. - float y = 0.0f; + // The value of the requested point, in find-in-page coordinates. + gfx::PointF point = gfx::PointF(0.0f, 0.0f); - // The distance to the nearest result found so far. float nearest_distance = FLT_MAX; // The frame containing the nearest result found so far. - RenderFrameHost* nearest_frame = nullptr; + RenderFrameHostImpl* nearest_frame = nullptr; // Nearest find result replies are still pending for these frames. std::unordered_set pending_replies; @@ -286,7 +294,7 @@ class CONTENT_EXPORT FindRequestManager { int number_of_matches_; // The frame containing the active match, if one exists, or nullptr otherwise. - RenderFrameHost* active_frame_; + RenderFrameHostImpl* active_frame_; // The active match ordinal relative to the matches found in its own frame. int relative_active_match_ordinal_; diff --git a/chromium/content/browser/frame_host/OWNERS b/chromium/content/browser/frame_host/OWNERS index 6f288e7a3bc..e9d9c4fcfaf 100644 --- a/chromium/content/browser/frame_host/OWNERS +++ b/chromium/content/browser/frame_host/OWNERS @@ -1 +1,4 @@ # COMPONENT: Internals>Sandbox>SiteIsolation + +# For surface ID propagation and synchronization +per-file render_widget_host_view_guest*=fsamuel@chromium.org diff --git a/chromium/content/browser/frame_host/ancestor_throttle.cc b/chromium/content/browser/frame_host/ancestor_throttle.cc index 86f1df5ad43..129c3777020 100644 --- a/chromium/content/browser/frame_host/ancestor_throttle.cc +++ b/chromium/content/browser/frame_host/ancestor_throttle.cc @@ -58,7 +58,14 @@ enum XFrameOptionsHistogram { // The 'frame-ancestors' CSP directive should take effect instead. BYPASS = 8, - XFRAMEOPTIONS_HISTOGRAM_MAX = BYPASS + // Navigation would have been blocked if we applied 'X-Frame-Options' to + // redirects. + // + // TODO(mkwst): Rename this when we make a decision around + // https://crbug.com/835465. + REDIRECT_WOULD_BE_BLOCKED = 9, + + XFRAMEOPTIONS_HISTOGRAM_MAX = REDIRECT_WOULD_BE_BLOCKED }; void RecordXFrameOptionsUsage(XFrameOptionsHistogram usage) { @@ -98,8 +105,32 @@ std::unique_ptr AncestorThrottle::MaybeCreateThrottleFor( AncestorThrottle::~AncestorThrottle() {} +NavigationThrottle::ThrottleCheckResult +AncestorThrottle::WillRedirectRequest() { + // During a redirect, we don't know which RenderFrameHost we'll end up in, + // so we can't log reliably to the console. We should be able to work around + // this iff we decide to ship the redirect-blocking behavior, but for now + // we'll just skip the console-logging bits to collect metrics. + NavigationThrottle::ThrottleCheckResult result = + ProcessResponseImpl(LoggingDisposition::DO_NOT_LOG_TO_CONSOLE); + + if (result.action() == NavigationThrottle::BLOCK_RESPONSE) + RecordXFrameOptionsUsage(REDIRECT_WOULD_BE_BLOCKED); + + // TODO(mkwst): We need to decide whether we'll be able to get away with + // tightening the XFO check to include redirect responses once we have a + // feel for the REDIRECT_WOULD_BE_BLOCKED numbers we're collecting above. + // Until then, we'll allow the response to proceed: https://crbug.com/835465. + return NavigationThrottle::PROCEED; +} + NavigationThrottle::ThrottleCheckResult AncestorThrottle::WillProcessResponse() { + return ProcessResponseImpl(LoggingDisposition::LOG_TO_CONSOLE); +} + +NavigationThrottle::ThrottleCheckResult AncestorThrottle::ProcessResponseImpl( + LoggingDisposition logging) { DCHECK(!navigation_handle()->IsInMainFrame()); NavigationHandleImpl* handle = @@ -116,18 +147,21 @@ AncestorThrottle::WillProcessResponse() { switch (disposition) { case HeaderDisposition::CONFLICT: - ParseError(header_value, disposition); + if (logging == LoggingDisposition::LOG_TO_CONSOLE) + ParseError(header_value, disposition); RecordXFrameOptionsUsage(CONFLICT); return NavigationThrottle::BLOCK_RESPONSE; case HeaderDisposition::INVALID: - ParseError(header_value, disposition); + if (logging == LoggingDisposition::LOG_TO_CONSOLE) + ParseError(header_value, disposition); RecordXFrameOptionsUsage(INVALID); // TODO(mkwst): Consider failing here. return NavigationThrottle::PROCEED; case HeaderDisposition::DENY: - ConsoleError(disposition); + if (logging == LoggingDisposition::LOG_TO_CONSOLE) + ConsoleError(disposition); RecordXFrameOptionsUsage(DENY); return NavigationThrottle::BLOCK_RESPONSE; @@ -139,7 +173,8 @@ AncestorThrottle::WillProcessResponse() { while (parent) { if (!parent->current_origin().IsSameOriginWith(current_origin)) { RecordXFrameOptionsUsage(SAMEORIGIN_BLOCKED); - ConsoleError(disposition); + if (logging == LoggingDisposition::LOG_TO_CONSOLE) + ConsoleError(disposition); // TODO(mkwst): Stop recording this metric once we convince other // vendors to follow our lead with XFO: SAMEORIGIN processing. diff --git a/chromium/content/browser/frame_host/ancestor_throttle.h b/chromium/content/browser/frame_host/ancestor_throttle.h index bef0114b8aa..c0c3d7d0dc9 100644 --- a/chromium/content/browser/frame_host/ancestor_throttle.h +++ b/chromium/content/browser/frame_host/ancestor_throttle.h @@ -38,16 +38,21 @@ class CONTENT_EXPORT AncestorThrottle : public NavigationThrottle { ~AncestorThrottle() override; + NavigationThrottle::ThrottleCheckResult WillRedirectRequest() override; NavigationThrottle::ThrottleCheckResult WillProcessResponse() override; const char* GetNameForLogging() override; private: + enum class LoggingDisposition { LOG_TO_CONSOLE, DO_NOT_LOG_TO_CONSOLE }; + FRIEND_TEST_ALL_PREFIXES(AncestorThrottleTest, ParsingXFrameOptions); FRIEND_TEST_ALL_PREFIXES(AncestorThrottleTest, ErrorsParsingXFrameOptions); FRIEND_TEST_ALL_PREFIXES(AncestorThrottleTest, IgnoreWhenFrameAncestorsPresent); explicit AncestorThrottle(NavigationHandle* handle); + NavigationThrottle::ThrottleCheckResult ProcessResponseImpl( + LoggingDisposition); void ParseError(const std::string& value, HeaderDisposition disposition); void ConsoleError(HeaderDisposition disposition); diff --git a/chromium/content/browser/frame_host/blocked_scheme_navigation_browsertest.cc b/chromium/content/browser/frame_host/blocked_scheme_navigation_browsertest.cc new file mode 100644 index 00000000000..f25ed52f307 --- /dev/null +++ b/chromium/content/browser/frame_host/blocked_scheme_navigation_browsertest.cc @@ -0,0 +1,1418 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base64.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/macros.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/strings/pattern.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" +#include "build/build_config.h" +#include "build/buildflag.h" +#include "content/browser/site_per_process_browsertest.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/browser_side_navigation_policy.h" +#include "content/public/common/content_features.h" +#include "content/public/common/content_paths.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/download_test_observer.h" +#include "content/public/test/test_navigation_observer.h" +#include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_download_manager_delegate.h" +#include "net/base/escape.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +#if BUILDFLAG(ENABLE_PLUGINS) +#include "content/public/browser/plugin_service.h" +#include "content/public/common/webplugininfo.h" +#endif + +namespace content { + +namespace { + +// The pattern to catch messages printed by the browser when navigation to a +// URL is blocked. +const char kNavigationBlockedMessage[] = + "Not allowed to navigate top frame to %s URL:*"; + +// The message printed by the data or filesystem URL when it successfully +// navigates. +const char kNavigationSuccessfulMessage[] = "NAVIGATION_SUCCESSFUL"; + +// A "Hello World" pdf. +const char kPDF[] = + "%PDF-1.7\n" + "1 0 obj << /Type /Page /Parent 3 0 R /Resources 5 0 R /Contents 2 0 R >>\n" + "endobj\n" + "2 0 obj << /Length 51 >>\n" + " stream BT\n" + " /F1 12 Tf\n" + " 1 0 0 1 100 20 Tm\n" + " (Hello World)Tj\n" + " ET\n" + " endstream\n" + "endobj\n" + "3 0 obj << /Type /Pages /Kids [ 1 0 R ] /Count 1 /MediaBox [ 0 0 300 50] " + ">>\n" + "endobj\n" + "4 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont/Arial >>\n" + "endobj\n" + "5 0 obj << /ProcSet[/PDF/Text] /Font <> >>\n" + "endobj\n" + "6 0 obj << /Type /Catalog /Pages 3 0 R >>\n" + "endobj\n" + "trailer << /Root 6 0 R >>\n"; + +enum ExpectedNavigationStatus { NAVIGATION_BLOCKED, NAVIGATION_ALLOWED }; + +// This class is similar to ConsoleObserverDelegate in that it listens and waits +// for specific console messages. The difference from ConsoleObserverDelegate is +// that this class immediately stops waiting if it sees a message matching +// fail_pattern, instead of waiting for a message matching success_pattern. +class BlockedURLWarningConsoleObserverDelegate : public WebContentsDelegate { + public: + enum Status { + NO_MESSAGE, + SAW_SUCCESS_MESSAGE, + SAW_FAILURE_MESSAGE, + }; + BlockedURLWarningConsoleObserverDelegate(WebContents* web_contents, + const std::string& success_filter, + const std::string& fail_filter) + : web_contents_(web_contents), + success_filter_(success_filter), + fail_filter_(fail_filter), + status_(NO_MESSAGE) {} + + ~BlockedURLWarningConsoleObserverDelegate() override {} + + // WebContentsDelegate method: + bool DidAddMessageToConsole(WebContents* source, + int32_t level, + const base::string16& message, + int32_t line_no, + const base::string16& source_id) override { + DCHECK(source == web_contents_); + const std::string ascii_message = base::UTF16ToASCII(message); + if (base::MatchPattern(ascii_message, fail_filter_)) { + status_ = SAW_FAILURE_MESSAGE; + run_loop_.Quit(); + } + if (base::MatchPattern(ascii_message, success_filter_)) { + status_ = SAW_SUCCESS_MESSAGE; + run_loop_.Quit(); + } + return false; + } + + void Wait() { run_loop_.Run(); } + + Status status() const { return status_; } + + private: + WebContents* web_contents_; + const std::string success_filter_; + const std::string fail_filter_; + base::RunLoop run_loop_; + Status status_; +}; + +#if BUILDFLAG(ENABLE_PLUGINS) +// This class registers a fake PDF plugin handler so that navigations with a PDF +// mime type end up with a navigation and don't simply download the file. +class ScopedPluginRegister { + public: + ScopedPluginRegister(content::PluginService* plugin_service) + : plugin_service_(plugin_service) { + const char kPluginName[] = "PDF"; + const char kPdfMimeType[] = "application/pdf"; + const char kPdfFileType[] = "pdf"; + WebPluginInfo plugin_info; + plugin_info.type = WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS; + plugin_info.name = base::ASCIIToUTF16(kPluginName); + plugin_info.mime_types.push_back( + WebPluginMimeType(kPdfMimeType, kPdfFileType, std::string())); + plugin_service_->RegisterInternalPlugin(plugin_info, false); + plugin_service_->RefreshPlugins(); + } + + ~ScopedPluginRegister() { + std::vector plugins; + plugin_service_->GetInternalPlugins(&plugins); + EXPECT_EQ(1u, plugins.size()); + plugin_service_->UnregisterInternalPlugin(plugins[0].path); + plugin_service_->RefreshPlugins(); + + plugins.clear(); + plugin_service_->GetInternalPlugins(&plugins); + EXPECT_TRUE(plugins.empty()); + } + + private: + content::PluginService* plugin_service_; +}; +#endif // BUILDFLAG(ENABLE_PLUGINS) + +} // namespace + +class BlockedSchemeNavigationBrowserTest + : public ContentBrowserTest, + public testing::WithParamInterface { + public: +#if BUILDFLAG(ENABLE_PLUGINS) + BlockedSchemeNavigationBrowserTest() + : scoped_plugin_register_(PluginService::GetInstance()) {} +#else + BlockedSchemeNavigationBrowserTest() {} +#endif // BUILDFLAG(ENABLE_PLUGINS) + + protected: + void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + + base::FilePath path; + ASSERT_TRUE(base::PathService::Get(content::DIR_TEST_DATA, &path)); + path = path.AppendASCII("data_url_navigations.html"); + ASSERT_TRUE(base::PathExists(path)); + + std::string contents; + ASSERT_TRUE(base::ReadFileToString(path, &contents)); + data_url_ = GURL(std::string("data:text/html,") + contents); + + ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir()); + ShellDownloadManagerDelegate* delegate = + static_cast( + shell() + ->web_contents() + ->GetBrowserContext() + ->GetDownloadManagerDelegate()); + delegate->SetDownloadBehaviorForTesting(downloads_directory_.GetPath()); + } + + void Navigate(const GURL& url) { + content::DOMMessageQueue message_queue; + EXPECT_TRUE(NavigateToURL(shell(), url)); + std::string message; + while (message_queue.WaitForMessage(&message)) { + if (message == "\"READY\"") + break; + } + } + + // Creates a filesystem: URL on the current origin. + GURL CreateFileSystemUrl(const std::string& filename, + const std::string& content, + const std::string& mime_type) { + const char kCreateFilesystemUrlScript[] = + "var contents = `%s`;" + "webkitRequestFileSystem(window.TEMPORARY, 1024, fs => {" + " fs.root.getFile('%s', {create: true}, entry => {" + " entry.createWriter(w => {" + " w.write(new Blob([contents], {type: '%s'}));" + " w.onwrite = function(evt) {" + " domAutomationController.send(entry.toURL());" + " }" + " });" + " });" + "});"; + std::string filesystem_url_string; + EXPECT_TRUE(ExecuteScriptAndExtractString( + shell()->web_contents()->GetMainFrame(), + base::StringPrintf(kCreateFilesystemUrlScript, content.c_str(), + filename.c_str(), mime_type.c_str()), + &filesystem_url_string)); + GURL filesystem_url(filesystem_url_string); + EXPECT_TRUE(filesystem_url.is_valid()); + EXPECT_TRUE(filesystem_url.SchemeIsFileSystem()); + return filesystem_url; + } + + bool IsDataURLTest() const { + return std::string(url::kDataScheme) == GetParam(); + } + + GURL CreateEmptyURLWithBlockedScheme() { + return CreateURLWithBlockedScheme("empty.html", "", + "text/html"); + } + + GURL CreateURLWithBlockedScheme(const std::string& filename, + const std::string& content, + const std::string& mimetype) { + if (IsDataURLTest()) { + return GURL( + base::StringPrintf("data:%s,%s", mimetype.c_str(), content.c_str())); + } + // We need an origin to create a filesystem URL on, so navigate to one. + NavigateToURL(shell(), + embedded_test_server()->GetURL("a.com", "/simple_page.html")); + return CreateFileSystemUrl(filename, content, mimetype); + } + + GURL GetTestURL() { + return embedded_test_server()->GetURL( + base::StringPrintf("/%s_url_navigations.html", GetParam())); + } + + // Adds an iframe to |rfh| pointing to |url|. + void AddIFrame(RenderFrameHost* rfh, const GURL& url) { + content::DOMMessageQueue message_queue; + const std::string javascript = base::StringPrintf( + "f = document.createElement('iframe'); f.src = '%s';" + "document.body.appendChild(f);", + url.spec().c_str()); + TestNavigationObserver observer(shell()->web_contents()); + EXPECT_TRUE(ExecuteScript(rfh, javascript)); + observer.Wait(); + std::string message; + while (message_queue.WaitForMessage(&message)) { + if (message == "\"READY\"") + break; + } + } + + // Runs |javascript| on the first child frame and checks for a navigation. + void TestNavigationFromFrame( + const std::string& scheme, + const std::string& javascript, + ExpectedNavigationStatus expected_navigation_status) { + RenderFrameHost* child = + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); + ASSERT_TRUE(child); + if (AreAllSitesIsolatedForTesting()) { + ASSERT_TRUE(child->IsCrossProcessSubframe()); + } + ExecuteScriptAndCheckNavigation(shell(), child, scheme, javascript, + expected_navigation_status); + } + + // Runs |javascript| on the first child frame and expects a download to occur. + void TestDownloadFromFrame(const std::string& javascript) { + RenderFrameHost* child = + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); + ASSERT_TRUE(child); + if (AreAllSitesIsolatedForTesting()) { + ASSERT_TRUE(child->IsCrossProcessSubframe()); + } + ExecuteScriptAndCheckDownload(child, javascript); + } + + // Runs |javascript| on the first child frame and checks for a navigation to + // the PDF file pointed by the test case. + void TestPDFNavigationFromFrame( + const std::string& scheme, + const std::string& javascript, + ExpectedNavigationStatus expected_navigation_status) { + RenderFrameHost* child = + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); + ASSERT_TRUE(child); + if (AreAllSitesIsolatedForTesting()) { + ASSERT_TRUE(child->IsCrossProcessSubframe()); + } + ExecuteScriptAndCheckPDFNavigation(child, scheme, javascript, + expected_navigation_status); + } + + // Same as TestNavigationFromFrame, but instead of navigating, the child frame + // tries to open a new window with a blocked URL (data or filesystem) + void TestWindowOpenFromFrame( + const std::string& scheme, + const std::string& javascript, + ExpectedNavigationStatus expected_navigation_status) { + RenderFrameHost* child = + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); + if (AreAllSitesIsolatedForTesting()) { + ASSERT_TRUE(child->IsCrossProcessSubframe()); + } + ExecuteScriptAndCheckWindowOpen(child, scheme, javascript, + expected_navigation_status); + } + + // Executes |javascript| on |rfh| and waits for a console message based on + // |expected_navigation_status|. + // - Blocked navigations should print kDataUrlBlockedPattern. + // - Allowed navigations should print kNavigationSuccessfulMessage. + void ExecuteScriptAndCheckNavigation( + Shell* shell, + RenderFrameHost* rfh, + const std::string& scheme, + const std::string& javascript, + ExpectedNavigationStatus expected_navigation_status) { + if (expected_navigation_status == NAVIGATION_ALLOWED) + ExecuteScriptAndCheckNavigationAllowed(shell, rfh, javascript, scheme); + else + ExecuteScriptAndCheckNavigationBlocked(shell, rfh, javascript, scheme); + } + + protected: + // Similar to ExecuteScriptAndCheckNavigation(), but doesn't wait for a + // console message if the navigation is expected to be allowed (this is + // because PDF files can't print to the console). + void ExecuteScriptAndCheckPDFNavigation( + RenderFrameHost* rfh, + const std::string& scheme, + const std::string& javascript, + ExpectedNavigationStatus expected_navigation_status) { + const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); + + const std::string expected_message = + (expected_navigation_status == NAVIGATION_ALLOWED) + ? std::string() + : base::StringPrintf(kNavigationBlockedMessage, scheme.c_str()); + + std::unique_ptr console_delegate; + if (!expected_message.empty()) { + console_delegate.reset(new ConsoleObserverDelegate( + shell()->web_contents(), expected_message)); + shell()->web_contents()->SetDelegate(console_delegate.get()); + } + + TestNavigationObserver navigation_observer(shell()->web_contents()); + EXPECT_TRUE(ExecuteScript(rfh, javascript)); + + if (console_delegate) { + console_delegate->Wait(); + shell()->web_contents()->SetDelegate(nullptr); + } + + switch (expected_navigation_status) { + case NAVIGATION_ALLOWED: + navigation_observer.Wait(); + // The new page should have the expected scheme. + EXPECT_TRUE( + shell()->web_contents()->GetLastCommittedURL().SchemeIs(scheme)); + EXPECT_TRUE(navigation_observer.last_navigation_url().SchemeIs(scheme)); + EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); + break; + + case NAVIGATION_BLOCKED: + // Original page shouldn't navigate away. + EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); + EXPECT_FALSE(navigation_observer.last_navigation_succeeded()); + break; + + default: + NOTREACHED(); + } + } + + // Executes |javascript| on |rfh| and waits for a new window to be opened. + // Does not check for console messages (it's currently not possible to + // concurrently wait for a new shell to be created and a console message to be + // printed on that new shell). + void ExecuteScriptAndCheckWindowOpen( + RenderFrameHost* rfh, + const std::string& scheme, + const std::string& javascript, + ExpectedNavigationStatus expected_navigation_status) { + ShellAddedObserver new_shell_observer; + EXPECT_TRUE(ExecuteScript(rfh, javascript)); + + Shell* new_shell = new_shell_observer.GetShell(); + WaitForLoadStop(new_shell->web_contents()); + + switch (expected_navigation_status) { + case NAVIGATION_ALLOWED: + EXPECT_TRUE( + new_shell->web_contents()->GetLastCommittedURL().SchemeIs(scheme)); + break; + + case NAVIGATION_BLOCKED: + EXPECT_TRUE( + new_shell->web_contents()->GetLastCommittedURL().is_empty()); + break; + + default: + NOTREACHED(); + } + } + + // Executes |javascript| on |rfh| and waits for a download to be started by + // a window.open call. + void ExecuteScriptAndCheckWindowOpenDownload(RenderFrameHost* rfh, + const std::string& javascript) { + const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); + ShellAddedObserver new_shell_observer; + DownloadManager* download_manager = BrowserContext::GetDownloadManager( + shell()->web_contents()->GetBrowserContext()); + + EXPECT_TRUE(ExecuteScript(rfh, javascript)); + Shell* new_shell = new_shell_observer.GetShell(); + + DownloadTestObserverTerminal download_observer( + download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); + + WaitForLoadStop(new_shell->web_contents()); + // If no download happens, this will timeout. + download_observer.WaitForFinished(); + + EXPECT_TRUE( + new_shell->web_contents()->GetLastCommittedURL().spec().empty()); + // No navigation should commit. + EXPECT_FALSE( + new_shell->web_contents()->GetController().GetLastCommittedEntry()); + // Original page shouldn't navigate away. + EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); + } + + // Executes |javascript| on |rfh| and waits for a download to be started. + void ExecuteScriptAndCheckDownload(RenderFrameHost* rfh, + const std::string& javascript) { + const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); + DownloadManager* download_manager = BrowserContext::GetDownloadManager( + shell()->web_contents()->GetBrowserContext()); + DownloadTestObserverTerminal download_observer( + download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); + + EXPECT_TRUE(ExecuteScript(rfh, javascript)); + // If no download happens, this will timeout. + download_observer.WaitForFinished(); + + // Original page shouldn't navigate away. + EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); + } + + // Initiates a browser initiated navigation to |url| and waits for a download + // to be started. + void NavigateAndCheckDownload(const GURL& url) { + const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); + DownloadManager* download_manager = BrowserContext::GetDownloadManager( + shell()->web_contents()->GetBrowserContext()); + DownloadTestObserverTerminal download_observer( + download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); + NavigateToURL(shell(), url); + + // If no download happens, this will timeout. + download_observer.WaitForFinished(); + + // Original page shouldn't navigate away. + EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); + } + + // data URL form of the file at content/test/data/data_url_navigations.html + GURL data_url() const { return data_url_; } + + std::string GetNavigationBlockedMessage() const { + return base::StringPrintf(kNavigationBlockedMessage, GetParam()); + } + + private: + // Executes |javascript| on |rfh| and waits for a console message that + // indicates the navigation has completed. |scheme| is the scheme being + // tested. + static void ExecuteScriptAndCheckNavigationAllowed( + Shell* shell, + RenderFrameHost* rfh, + const std::string& javascript, + const std::string& scheme) { + // Should see success message, should never see blocked message. + const std::string blocked_message = + base::StringPrintf(kNavigationBlockedMessage, scheme.c_str()); + BlockedURLWarningConsoleObserverDelegate console_delegate( + shell->web_contents(), kNavigationSuccessfulMessage, blocked_message); + shell->web_contents()->SetDelegate(&console_delegate); + + TestNavigationObserver navigation_observer(shell->web_contents()); + EXPECT_TRUE(ExecuteScript(rfh, javascript)); + console_delegate.Wait(); + EXPECT_EQ(BlockedURLWarningConsoleObserverDelegate::SAW_SUCCESS_MESSAGE, + console_delegate.status()); + shell->web_contents()->SetDelegate(nullptr); + navigation_observer.Wait(); + + // The new page should have the expected scheme. + EXPECT_EQ(navigation_observer.last_navigation_url(), + shell->web_contents()->GetLastCommittedURL()); + EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); + } + + // Similar to ExecuteScriptAndCheckNavigationAllowed. Executes |javascript| on + // |rfh| and waits for a console message that indicates the navigation has + // been blocked. |scheme| is the scheme being tested. + static void ExecuteScriptAndCheckNavigationBlocked( + Shell* shell, + RenderFrameHost* rfh, + const std::string& javascript, + const std::string& scheme) { + const GURL original_url(shell->web_contents()->GetLastCommittedURL()); + + // Should see blocked message, should never see success message. + const std::string blocked_message = + base::StringPrintf(kNavigationBlockedMessage, scheme.c_str()); + BlockedURLWarningConsoleObserverDelegate console_delegate( + shell->web_contents(), kNavigationSuccessfulMessage, blocked_message); + shell->web_contents()->SetDelegate(&console_delegate); + + TestNavigationObserver navigation_observer(shell->web_contents()); + EXPECT_TRUE(ExecuteScript(rfh, javascript)); + console_delegate.Wait(); + EXPECT_EQ(BlockedURLWarningConsoleObserverDelegate::SAW_FAILURE_MESSAGE, + console_delegate.status()); + shell->web_contents()->SetDelegate(nullptr); + + // Original page shouldn't navigate away. + EXPECT_EQ(original_url, shell->web_contents()->GetLastCommittedURL()); + EXPECT_FALSE(navigation_observer.last_navigation_succeeded()); + } + + base::ScopedTempDir downloads_directory_; + +#if BUILDFLAG(ENABLE_PLUGINS) + ScopedPluginRegister scoped_plugin_register_; +#endif // BUILDFLAG(ENABLE_PLUGINS) + + GURL data_url_; + + DISALLOW_COPY_AND_ASSIGN(BlockedSchemeNavigationBrowserTest); +}; + +INSTANTIATE_TEST_CASE_P(, + BlockedSchemeNavigationBrowserTest, + ::testing::Values(url::kDataScheme, + url::kFileSystemScheme)); + +//////////////////////////////////////////////////////////////////////////////// +// Blocked schemes with HTML mimetype +// +// Tests that a browser initiated navigation to a blocked scheme doesn't show a +// console warning and is not blocked. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + BrowserInitiated_Allow) { + const GURL kUrl(CreateURLWithBlockedScheme( + "test.html", + "", + "text/html")); + if (IsDataURLTest()) { + BlockedURLWarningConsoleObserverDelegate console_delegate( + shell()->web_contents(), kNavigationSuccessfulMessage, + GetNavigationBlockedMessage()); + + shell()->web_contents()->SetDelegate(&console_delegate); + EXPECT_TRUE(NavigateToURL(shell(), kUrl)); + console_delegate.Wait(); + shell()->web_contents()->SetDelegate(nullptr); + EXPECT_TRUE( + shell()->web_contents()->GetLastCommittedURL().SchemeIs(GetParam())); + + } else { + // Navigate to a.com and create a filesystem URL on it. + // For filesystem: tests we create a new shell and navigate that shell to + // the filesystem: URL created above. Navigating the a tab away from the + // original page may clear all filesystem: URLs associated with that origin, + // so we keep the origin around in the original shell. + ShellAddedObserver new_shell_observer; + EXPECT_TRUE( + ExecuteScript(shell()->web_contents(), "window.open('about:blank');")); + Shell* new_shell = new_shell_observer.GetShell(); + WaitForLoadStop(new_shell->web_contents()); + + BlockedURLWarningConsoleObserverDelegate console_delegate( + new_shell->web_contents(), kNavigationSuccessfulMessage, + GetNavigationBlockedMessage()); + new_shell->web_contents()->SetDelegate(&console_delegate); + EXPECT_TRUE(NavigateToURL(new_shell, kUrl)); + + console_delegate.Wait(); + new_shell->web_contents()->SetDelegate(nullptr); + EXPECT_TRUE( + new_shell->web_contents()->GetLastCommittedURL().SchemeIs(GetParam())); + } +} + +// Tests that a content initiated navigation to a blocked scheme is blocked. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + HTML_Navigation_Block) { + Navigate(GetTestURL()); + ExecuteScriptAndCheckNavigation( + shell(), shell()->web_contents()->GetMainFrame(), GetParam(), + "document.getElementById('navigate-top-frame-to-html').click()", + NAVIGATION_BLOCKED); +} + +class DataUrlNavigationBrowserTestWithFeatureFlag + : public BlockedSchemeNavigationBrowserTest { + public: + DataUrlNavigationBrowserTestWithFeatureFlag() { + scoped_feature_list_.InitAndEnableFeature( + features::kAllowContentInitiatedDataUrlNavigations); + } + ~DataUrlNavigationBrowserTestWithFeatureFlag() override {} + + private: + base::test::ScopedFeatureList scoped_feature_list_; + + DISALLOW_COPY_AND_ASSIGN(DataUrlNavigationBrowserTestWithFeatureFlag); +}; + +// Tests that a content initiated navigation to a data URL is allowed if +// blocking is disabled with a feature flag. +IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTestWithFeatureFlag, + HTML_Navigation_Allow_FeatureFlag) { + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("/data_url_navigations.html"))); + ExecuteScriptAndCheckNavigation( + shell(), shell()->web_contents()->GetMainFrame(), url::kDataScheme, + "document.getElementById('navigate-top-frame-to-html').click()", + NAVIGATION_ALLOWED); +} + +// Tests that a window.open to a blocked scheme with HTML mime type is blocked. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + HTML_WindowOpen_Block) { + Navigate(GetTestURL()); + ExecuteScriptAndCheckWindowOpen( + shell()->web_contents()->GetMainFrame(), GetParam(), + "document.getElementById('window-open-html').click()", + NAVIGATION_BLOCKED); +} + +// Tests that a form post to a blocked scheme with HTML mime type is blocked. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + HTML_FormPost_Block) { + Navigate(GetTestURL()); + ExecuteScriptAndCheckNavigation( + shell(), shell()->web_contents()->GetMainFrame(), GetParam(), + "document.getElementById('form-post-to-html').click()", + NAVIGATION_BLOCKED); +} + +// Tests that clicking link downloads the URL even with a blocked +// scheme. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, HTML_Download) { + Navigate(GetTestURL()); + ExecuteScriptAndCheckDownload( + shell()->web_contents()->GetMainFrame(), + "document.getElementById('download-link').click()"); +} + +// Tests that navigating the main frame to a blocked scheme with HTML mimetype +// from a subframe is blocked. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + HTML_NavigationFromFrame_Block) { + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); + AddIFrame( + shell()->web_contents()->GetMainFrame(), + embedded_test_server()->GetURL( + "b.com", base::StringPrintf("/%s_url_navigations.html", GetParam()))); + + TestNavigationFromFrame( + GetParam(), + "document.getElementById('navigate-top-frame-to-html').click()", + NAVIGATION_BLOCKED); +} + +// Tests that opening a new window with a blocked scheme from a subframe is +// blocked. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + HTML_WindowOpenFromFrame_Block) { + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); + AddIFrame( + shell()->web_contents()->GetMainFrame(), + embedded_test_server()->GetURL( + "b.com", base::StringPrintf("/%s_url_navigations.html", GetParam()))); + + TestWindowOpenFromFrame(GetParam(), + "document.getElementById('window-open-html').click()", + NAVIGATION_BLOCKED); +} + +// Tests that navigation to a blocked scheme is blocked even if the top frame is +// already has a blocked scheme. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + HTML_Navigation_SameScheme_Block) { + if (IsDataURLTest()) { + EXPECT_TRUE(NavigateToURL(shell(), data_url())); + ExecuteScriptAndCheckNavigation( + shell(), shell()->web_contents()->GetMainFrame(), url::kDataScheme, + "document.getElementById('navigate-top-frame-to-html').click()", + NAVIGATION_BLOCKED); + } else { + // We need an origin to create a filesystem URL on, so navigate to one. + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); + const GURL kFilesystemURL1( + CreateFileSystemUrl("empty1.html", "empty1", "text/html")); + const GURL kFilesystemURL2( + CreateFileSystemUrl("empty2.html", "empty2", "text/html")); + + // Create a new shell and navigate that shell to the filesystem: URL created + // above. Navigating the a tab away from the + // original page may clear all filesystem: URLs associated with that origin, + // so we keep the origin around in the original shell. + ShellAddedObserver new_shell_observer; + EXPECT_TRUE( + ExecuteScript(shell()->web_contents(), "window.open('about:blank');")); + Shell* new_shell = new_shell_observer.GetShell(); + WaitForLoadStop(new_shell->web_contents()); + + EXPECT_TRUE(NavigateToURL(new_shell, kFilesystemURL1)); + ExecuteScriptAndCheckNavigation( + new_shell, new_shell->web_contents()->GetMainFrame(), + url::kFileSystemScheme, + base::StringPrintf("window.location='%s';", + kFilesystemURL2.spec().c_str()), + NAVIGATION_BLOCKED); + } +} + +// Tests that a form post to a blocked scheme with HTML mime type is blocked +// even if the top frame is already a blocked scheme. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + HTML_FormPost_SameScheme_Block) { + if (IsDataURLTest()) { + EXPECT_TRUE(NavigateToURL(shell(), data_url())); + ExecuteScriptAndCheckNavigation( + shell(), shell()->web_contents()->GetMainFrame(), url::kDataScheme, + "document.getElementById('form-post-to-html').click()", + NAVIGATION_BLOCKED); + } else { + // We need an origin to create a filesystem URL on, so navigate to one. + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); + const GURL kFilesystemURL1( + CreateFileSystemUrl("target.html", "form target", "text/html")); + const GURL kFilesystemURL2(CreateFileSystemUrl( + "form.html", + base::StringPrintf("
", + kFilesystemURL1.spec().c_str()), + "text/html")); + + // Create a new shell and navigate that shell to the filesystem: URL created + // above. Navigating the a tab away from the + // original page may clear all filesystem: URLs associated with that origin, + // so we keep the origin around in the original shell. + ShellAddedObserver new_shell_observer; + // TODO(crbug/811558): about:blank might commit without needing to wait. + // Remove the wait. + EXPECT_TRUE( + ExecuteScript(shell()->web_contents(), "window.open('about:blank');")); + Shell* new_shell = new_shell_observer.GetShell(); + WaitForLoadStop(new_shell->web_contents()); + + EXPECT_TRUE(NavigateToURL(new_shell, kFilesystemURL2)); + ExecuteScriptAndCheckNavigation( + new_shell, new_shell->web_contents()->GetMainFrame(), + url::kFileSystemScheme, "document.getElementById('btn-submit').click()", + NAVIGATION_BLOCKED); + } +} + +// Tests that navigating the top frame to a blocked scheme with HTML mimetype is +// blocked even if the top frame already has a blocked scheme. +IN_PROC_BROWSER_TEST_P( + BlockedSchemeNavigationBrowserTest, + HTML_NavigationFromFrame_TopFrameHasBlockedScheme_Block) { + EXPECT_TRUE(NavigateToURL(shell(), CreateEmptyURLWithBlockedScheme())); + AddIFrame(shell()->web_contents()->GetMainFrame(), GetTestURL()); + + TestNavigationFromFrame( + GetParam(), + "document.getElementById('navigate-top-frame-to-html').click()", + NAVIGATION_BLOCKED); +} + +// Tests that opening a new window with a blocked scheme with HTML mimetype is +// blocked even if the top frame already has a blocked scheme. +IN_PROC_BROWSER_TEST_P( + BlockedSchemeNavigationBrowserTest, + HTML_WindowOpenFromFrame_TopFrameHasBlockedScheme_Block) { + // Create an empty URL with a blocked scheme, navigate to it, and add a frame. + EXPECT_TRUE(NavigateToURL(shell(), CreateEmptyURLWithBlockedScheme())); + AddIFrame(shell()->web_contents()->GetMainFrame(), GetTestURL()); + + TestWindowOpenFromFrame(GetParam(), + "document.getElementById('window-open-html').click()", + NAVIGATION_BLOCKED); +} + +//////////////////////////////////////////////////////////////////////////////// +// Blocked schemes with octet-stream mimetype (binary) +// +// Test direct navigations to a binary mime types. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + OctetStream_BrowserInitiated) { + const GURL kUrl(CreateURLWithBlockedScheme("test.html", "test", + "application/octet-stream")); + + if (IsDataURLTest()) { + // Navigations to data URLs with unknown mime types should end up as + // downloads. + NavigateAndCheckDownload(kUrl); + } else { + // Navigations to filesystem URLs never end up as downloads. + EXPECT_TRUE(NavigateToURL(shell(), kUrl)); + EXPECT_EQ(kUrl, shell()->web_contents()->GetLastCommittedURL()); + } +} + +#if defined(OS_ANDROID) +// Flaky on android: https://crbug.com/734563 +#define MAYBE_DataUrl_OctetStream_WindowOpen \ + DISABLED_DataUrl_OctetStream_WindowOpen +#else +#define MAYBE_DataUrl_OctetStream_WindowOpen DataUrl_OctetStream_WindowOpen +#endif + +// Test window.open to a data URL with binary mimetype. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + DataUrl_OctetStream_WindowOpen) { + Navigate(embedded_test_server()->GetURL( + base::StringPrintf("/data_url_navigations.html"))); + // Navigations to data URLs with unknown mime types should end up as + // downloads. + ExecuteScriptAndCheckWindowOpenDownload( + shell()->web_contents()->GetMainFrame(), + "document.getElementById('window-open-octetstream').click()"); +} + +// Test window.open to a filesystem URL with binary mimetype. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + FilesystemUrl_OctetStream_WindowOpen) { + Navigate(embedded_test_server()->GetURL( + base::StringPrintf("/filesystem_url_navigations.html"))); + // Navigations to filesystem URLs never end up as downloads. + ExecuteScriptAndCheckWindowOpen( + shell()->web_contents()->GetMainFrame(), url::kFileSystemScheme, + "document.getElementById('window-open-octetstream').click()", + NAVIGATION_BLOCKED); +} + +// Test navigation to a data URL with binary mimetype. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + DataUrl_OctetStream_Navigation) { + Navigate(embedded_test_server()->GetURL( + base::StringPrintf("/data_url_navigations.html"))); + // Navigations to data URLs with unknown mime types should end up as + // downloads. + ExecuteScriptAndCheckDownload( + shell()->web_contents()->GetMainFrame(), + "document.getElementById('navigate-top-frame-to-octetstream').click()"); +} + +// Test navigation to a filesystem URL with binary mimetype. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + FilesystemUrl_OctetStream_Navigation) { + Navigate(embedded_test_server()->GetURL( + base::StringPrintf("/filesystem_url_navigations.html"))); + // Navigations to filesystem URLs never end up as downloads. + ExecuteScriptAndCheckNavigation( + shell(), shell()->web_contents()->GetMainFrame(), url::kFileSystemScheme, + "document.getElementById('navigate-top-frame-to-octetstream').click()", + NAVIGATION_BLOCKED); +} + +// Test form post to a data URL with binary mimetype. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + DataUrl_OctetStream_FormPost) { + Navigate(embedded_test_server()->GetURL( + base::StringPrintf("/data_url_navigations.html"))); + // Form posts to data URLs with unknown mime types should end up as + // downloads. + ExecuteScriptAndCheckDownload( + shell()->web_contents()->GetMainFrame(), + "document.getElementById('form-post-to-octetstream').click()"); +} + +// Test form post to a filesystem URL with binary mimetype. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + FilesystemUrl_OctetStream_FormPost) { + Navigate(embedded_test_server()->GetURL( + base::StringPrintf("/filesystem_url_navigations.html"))); + // Navigations to filesystem URLs never end up as downloads. + ExecuteScriptAndCheckNavigation( + shell(), shell()->web_contents()->GetMainFrame(), url::kFileSystemScheme, + "document.getElementById('form-post-to-octetstream').click()", + NAVIGATION_BLOCKED); +} + +// Tests navigation of the main frame to a data URL with a binary mimetype +// from a subframe. These navigations should end up as downloads. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + DataUrl_OctetStream_NavigationFromFrame) { + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); + AddIFrame( + shell()->web_contents()->GetMainFrame(), + embedded_test_server()->GetURL("b.com", "/data_url_navigations.html")); + TestDownloadFromFrame( + "document.getElementById('navigate-top-frame-to-octetstream').click()"); +} + +// Tests navigation of the main frame to a filesystem URL with a binary mimetype +// from a subframe. Navigations to filesystem URLs never end up as downloads. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + FilesystemUrl_OctetStream_NavigationFromFrame) { + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); + AddIFrame(shell()->web_contents()->GetMainFrame(), + embedded_test_server()->GetURL("b.com", + "/filesystem_url_navigations.html")); + + TestNavigationFromFrame( + url::kFileSystemScheme, + "document.getElementById('navigate-top-frame-to-octetstream').click()", + NAVIGATION_BLOCKED); +} + +//////////////////////////////////////////////////////////////////////////////// +// URLs with unknown mimetype +// +// Test direct navigation to an unknown mime type. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + UnknownMimeType_BrowserInitiated_Download) { + const GURL kUrl( + CreateURLWithBlockedScheme("test.html", "test", "unknown/mimetype")); + + if (IsDataURLTest()) { + // Navigations to data URLs with unknown mime types should end up as + // downloads. + NavigateAndCheckDownload(kUrl); + } else { + // Navigations to filesystem URLs never end up as downloads. + EXPECT_TRUE(NavigateToURL(shell(), kUrl)); + EXPECT_EQ(kUrl, shell()->web_contents()->GetLastCommittedURL()); + } +} + +#if defined(OS_ANDROID) +// Flaky on android: https://crbug.com/734563 +#define MAYBE_UnknownMimeType_WindowOpen DISABLED_UnknownMimeType_WindowOpen +#else +#define MAYBE_UnknownMimeType_WindowOpen UnknownMimeType_WindowOpen +#endif + +// Test window.open to a blocked scheme with an unknown mime type. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + MAYBE_UnknownMimeType_WindowOpen) { + Navigate(GetTestURL()); + if (IsDataURLTest()) { + // Navigations to data URLs with unknown mime types should end up as + // downloads. + ExecuteScriptAndCheckWindowOpenDownload( + shell()->web_contents()->GetMainFrame(), + "document.getElementById('window-open-unknown-mimetype').click()"); + } else { + // Navigations to filesystem URLs never end up as downloads. + ExecuteScriptAndCheckWindowOpen( + shell()->web_contents()->GetMainFrame(), GetParam(), + "document.getElementById('window-open-unknown-mimetype').click()", + NAVIGATION_BLOCKED); + } +} + +// Test navigation to a data URL with an unknown mime type. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + DataUrl_UnknownMimeType_Navigation) { + Navigate(embedded_test_server()->GetURL( + base::StringPrintf("/data_url_navigations.html"))); + // Navigations to data URLs with unknown mime types should end up as + // downloads. + ExecuteScriptAndCheckDownload(shell()->web_contents()->GetMainFrame(), + "document.getElementById('navigate-top-frame-" + "to-unknown-mimetype').click()"); +} + +// Test navigation to a filesystem URL with an unknown mime type. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + FilesystemUrl_UnknownMimeType_Navigation) { + Navigate(embedded_test_server()->GetURL( + base::StringPrintf("/filesystem_url_navigations.html"))); + // Navigations to filesystem URLs never end up as downloads. + ExecuteScriptAndCheckNavigation( + shell(), shell()->web_contents()->GetMainFrame(), url::kFileSystemScheme, + "document.getElementById('navigate-top-frame-to-unknown-mimetype')." + "click()", + NAVIGATION_BLOCKED); +} + +// Test form post to a data URL with an unknown mime type. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + DataUrl_UnknownMimeType_FormPost) { + Navigate(embedded_test_server()->GetURL( + base::StringPrintf("/data_url_navigations.html"))); + // Form posts to data URLs with unknown mime types should end up as + // downloads. + ExecuteScriptAndCheckDownload( + shell()->web_contents()->GetMainFrame(), + "document.getElementById('form-post-to-unknown-mimetype').click()"); +} + +// Test form post to a filesystem URL with an unknown mime type. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + FilesystemUrl_UnknownMimeType_FormPost) { + Navigate(embedded_test_server()->GetURL( + base::StringPrintf("/filesystem_url_navigations.html"))); + // Navigations to filesystem URLs never end up as downloads. + ExecuteScriptAndCheckNavigation( + shell(), shell()->web_contents()->GetMainFrame(), url::kFileSystemScheme, + "document.getElementById('form-post-to-unknown-mimetype').click()", + NAVIGATION_BLOCKED); +} + +// Test navigation of the main frame to a data URL with an unknown mimetype from +// a subframe. These navigations should end up as downloads. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + DataUrl_UnknownMimeType_NavigationFromFrame) { + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); + AddIFrame( + shell()->web_contents()->GetMainFrame(), + embedded_test_server()->GetURL("b.com", "/data_url_navigations.html")); + + TestDownloadFromFrame( + "document.getElementById('navigate-top-frame-to-unknown-mimetype')." + "click()"); +} + +// Test navigation of the main frame to a filesystem URL with an unknown +// mimetype from a subframe. Navigations to filesystem URLs don't end up as +// downloads. +IN_PROC_BROWSER_TEST_F(BlockedSchemeNavigationBrowserTest, + FilesystemUrl_UnknownMimeType_NavigationFromFrame) { + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); + AddIFrame(shell()->web_contents()->GetMainFrame(), + embedded_test_server()->GetURL("b.com", + "/filesystem_url_navigations.html")); + + TestNavigationFromFrame(url::kFileSystemScheme, + "document.getElementById('navigate-top-frame-to-" + "unknown-mimetype').click()", + NAVIGATION_BLOCKED); +} + +//////////////////////////////////////////////////////////////////////////////// +// URLs with PDF mimetype +// +// Tests that a browser initiated navigation to a blocked scheme URL with PDF +// mime type is allowed, or initiates a download on Android. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + PDF_BrowserInitiatedNavigation_Allow) { + std::string pdf_base64; + base::Base64Encode(kPDF, &pdf_base64); + const GURL kPDFUrl(CreateURLWithBlockedScheme( + "test.pdf", IsDataURLTest() ? pdf_base64 : kPDF, "application/pdf")); + +#if !defined(OS_ANDROID) + TestNavigationObserver observer(shell()->web_contents()); + EXPECT_TRUE(NavigateToURL(shell(), kPDFUrl)); + EXPECT_EQ(kPDFUrl, observer.last_navigation_url()); + EXPECT_TRUE(observer.last_navigation_succeeded()); + EXPECT_TRUE( + shell()->web_contents()->GetLastCommittedURL().SchemeIs(GetParam())); +#else + NavigateAndCheckDownload(kPDFUrl); +#endif +} + +// Tests that a window.open to a blocked scheme is blocked if the URL has a +// mime type that will be handled by a plugin (PDF in this case). +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + PDF_WindowOpen_Block) { + Navigate(GetTestURL()); + +#if !defined(OS_ANDROID) + ExecuteScriptAndCheckWindowOpen( + shell()->web_contents()->GetMainFrame(), GetParam(), + "document.getElementById('window-open-pdf').click()", NAVIGATION_BLOCKED); +#else + if (IsDataURLTest()) { + // On Android, data URL PDFs are downloaded upon navigation. + ExecuteScriptAndCheckDownload( + shell()->web_contents()->GetMainFrame(), + "document.getElementById('window-open-pdf').click()"); + } else { + // On Android, filesystem PDF URLs are navigated and should be blocked. + ExecuteScriptAndCheckWindowOpen( + shell()->web_contents()->GetMainFrame(), GetParam(), + "document.getElementById('window-open-pdf').click()", + NAVIGATION_BLOCKED); + } +#endif +} + +// Test that a navigation to a blocked scheme URL is blocked if the URL has a +// mime type that will be handled by a plugin (PDF in this case). +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + PDF_Navigation_Block) { + Navigate(GetTestURL()); + +#if !defined(OS_ANDROID) + ExecuteScriptAndCheckPDFNavigation( + shell()->web_contents()->GetMainFrame(), GetParam(), + "document.getElementById('navigate-top-frame-to-pdf').click()", + NAVIGATION_BLOCKED); +#else + if (IsDataURLTest()) { + // On Android, data URL PDFs are downloaded upon navigation. + ExecuteScriptAndCheckDownload( + shell()->web_contents()->GetMainFrame(), + "document.getElementById('navigate-top-frame-to-pdf').click()"); + } else { + // On Android, filesystem PDF URLs are navigated and should be blocked. + ExecuteScriptAndCheckPDFNavigation( + shell()->web_contents()->GetMainFrame(), GetParam(), + "document.getElementById('navigate-top-frame-to-pdf').click()", + NAVIGATION_BLOCKED); + } +#endif +} + +// Test that a form post to a blocked scheme is blocked if the URL has a mime +// type that will be handled by a plugin (PDF in this case). +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, PDF_FormPost_Block) { + Navigate(GetTestURL()); + +#if !defined(OS_ANDROID) + ExecuteScriptAndCheckPDFNavigation( + shell()->web_contents()->GetMainFrame(), GetParam(), + "document.getElementById('form-post-to-pdf').click()", + NAVIGATION_BLOCKED); +#else + if (IsDataURLTest()) { + // On Android, data URL PDFs are downloaded upon navigation. + ExecuteScriptAndCheckDownload( + shell()->web_contents()->GetMainFrame(), + "document.getElementById('form-post-to-pdf').click()"); + } else { + // On Android, filesystem PDF URLs are navigated and should be blocked. + ExecuteScriptAndCheckPDFNavigation( + shell()->web_contents()->GetMainFrame(), GetParam(), + "document.getElementById('form-post-to-pdf').click()", + NAVIGATION_BLOCKED); + } +#endif +} + +// Tests that navigating the main frame to a blocked scheme with PDF mimetype +// from a subframe is blocked, or is downloaded on Android. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + PDF_NavigationFromFrame_Block) { + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); + AddIFrame( + shell()->web_contents()->GetMainFrame(), + embedded_test_server()->GetURL( + "b.com", base::StringPrintf("/%s_url_navigations.html", GetParam()))); + +#if !defined(OS_ANDROID) + TestPDFNavigationFromFrame( + GetParam(), + "document.getElementById('navigate-top-frame-to-pdf').click()", + NAVIGATION_BLOCKED); +#else + if (IsDataURLTest()) { + // On Android, data URL PDFs are downloaded upon navigation. + RenderFrameHost* child = + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); + ASSERT_TRUE(child); + if (AreAllSitesIsolatedForTesting()) { + ASSERT_TRUE(child->IsCrossProcessSubframe()); + } + ExecuteScriptAndCheckDownload( + child, "document.getElementById('navigate-top-frame-to-pdf').click()"); + } else { + // On Android, filesystem PDF URLs are navigated and should be blocked. + TestPDFNavigationFromFrame( + GetParam(), + "document.getElementById('navigate-top-frame-to-pdf').click()", + NAVIGATION_BLOCKED); + } +#endif +} + +// Tests that opening a window with a blocked scheme with PDF mimetype from a +// subframe is blocked, or is downloaded on Android. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + PDF_WindowOpenFromFrame_Block) { + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("a.com", "/simple_page.html"))); + AddIFrame(shell()->web_contents()->GetMainFrame(), + embedded_test_server()->GetURL( + base::StringPrintf("/%s_url_navigations.html", GetParam()))); + +#if !defined(OS_ANDROID) + TestWindowOpenFromFrame(GetParam(), + "document.getElementById('window-open-pdf').click()", + NAVIGATION_BLOCKED); +#else + if (IsDataURLTest()) { + // On Android, data URL PDFs are downloaded upon navigation. + RenderFrameHost* child = + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); + ASSERT_TRUE(child); + if (AreAllSitesIsolatedForTesting()) { + ASSERT_TRUE(child->IsCrossProcessSubframe()); + } + ExecuteScriptAndCheckDownload( + child, "document.getElementById('window-open-pdf').click()"); + } else { + // On Android, filesystem PDF URLs are navigated and should be blocked. + TestWindowOpenFromFrame( + GetParam(), "document.getElementById('window-open-pdf').click()", + NAVIGATION_BLOCKED); + } +#endif +} + +// Tests that navigating the top frame to a blocked scheme with PDF mimetype +// from a subframe is blocked even if the top frame already has a blocked +// scheme. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + PDF_NavigationFromFrame_TopFrameHasBlockedScheme_Block) { + EXPECT_TRUE(NavigateToURL(shell(), CreateEmptyURLWithBlockedScheme())); + AddIFrame(shell()->web_contents()->GetMainFrame(), GetTestURL()); + +#if !defined(OS_ANDROID) + TestPDFNavigationFromFrame( + GetParam(), + "document.getElementById('navigate-top-frame-to-pdf').click()", + NAVIGATION_BLOCKED); +#else + if (IsDataURLTest()) { + // On Android, data URL PDFs are downloaded upon navigation. + RenderFrameHost* child = + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); + ASSERT_TRUE(child); + if (AreAllSitesIsolatedForTesting()) { + ASSERT_TRUE(child->IsCrossProcessSubframe()); + } + ExecuteScriptAndCheckDownload( + child, "document.getElementById('navigate-top-frame-to-pdf').click()"); + } else { + // On Android, filesystem PDF URLs are navigated and should be blocked. + TestPDFNavigationFromFrame( + GetParam(), + "document.getElementById('navigate-top-frame-to-pdf').click()", + NAVIGATION_BLOCKED); + } +#endif +} + +// Tests that opening a window with a blocked scheme with PDF mimetype from a +// subframe is blocked even if the top frame already has a blocked scheme. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + PDF_WindowOpenFromFrame_TopFrameHasBlockedScheme_Block) { + EXPECT_TRUE(NavigateToURL(shell(), CreateEmptyURLWithBlockedScheme())); + AddIFrame(shell()->web_contents()->GetMainFrame(), GetTestURL()); + +#if !defined(OS_ANDROID) + TestWindowOpenFromFrame(GetParam(), + "document.getElementById('window-open-pdf').click()", + NAVIGATION_BLOCKED); +#else + if (IsDataURLTest()) { + // On Android, data URL PDFs are downloaded upon navigation. + RenderFrameHost* child = + ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); + ASSERT_TRUE(child); + if (AreAllSitesIsolatedForTesting()) { + ASSERT_TRUE(child->IsCrossProcessSubframe()); + } + ExecuteScriptAndCheckDownload( + child, "document.getElementById('window-open-pdf').click()"); + } else { + // On Android, filesystem PDF URLs are navigated to and should be blocked. + TestWindowOpenFromFrame( + GetParam(), "document.getElementById('window-open-pdf').click()", + NAVIGATION_BLOCKED); + } +#endif +} + +// Test case to verify that redirects to blocked schemes are properly +// disallowed, even when invoked through history navigations. See +// https://crbug.com/723796. +IN_PROC_BROWSER_TEST_P(BlockedSchemeNavigationBrowserTest, + WindowOpenRedirectAndBack) { + Navigate(GetTestURL()); + + // This test will need to navigate the newly opened window. + ShellAddedObserver new_shell_observer; + EXPECT_TRUE( + ExecuteScript(shell()->web_contents(), + "document.getElementById('window-open-redirect').click()")); + Shell* new_shell = new_shell_observer.GetShell(); + NavigationController* controller = + &new_shell->web_contents()->GetController(); + WaitForLoadStop(new_shell->web_contents()); + + // The window.open() should have resulted in an error page. The blocked + // URL should be in both the actual and the virtual URL. + { + EXPECT_EQ(0, controller->GetLastCommittedEntryIndex()); + NavigationEntry* entry = controller->GetLastCommittedEntry(); + EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType()); + EXPECT_FALSE(entry->GetURL().SchemeIs(url::kDataScheme)); + EXPECT_FALSE(entry->GetURL().SchemeIs(url::kFileSystemScheme)); + EXPECT_TRUE(base::StartsWith( + entry->GetURL().spec(), + embedded_test_server()->GetURL("/server-redirect?").spec(), + base::CompareCase::SENSITIVE)); + EXPECT_EQ(entry->GetURL(), entry->GetVirtualURL()); + } + + // Navigate forward and then go back to ensure the navigation to data: or + // filesystem: URL is blocked. Use a browser-initiated back navigation, + // equivalent to user pressing the back button. + EXPECT_TRUE( + NavigateToURL(new_shell, embedded_test_server()->GetURL("/title1.html"))); + EXPECT_EQ(1, controller->GetLastCommittedEntryIndex()); + { + TestNavigationObserver observer(new_shell->web_contents()); + controller->GoBack(); + observer.Wait(); + + NavigationEntry* entry = controller->GetLastCommittedEntry(); + EXPECT_EQ(0, controller->GetLastCommittedEntryIndex()); + EXPECT_FALSE(entry->GetURL().SchemeIs(url::kDataScheme)); + EXPECT_FALSE(entry->GetURL().SchemeIs(url::kFileSystemScheme)); + EXPECT_TRUE(base::StartsWith( + entry->GetURL().spec(), + embedded_test_server()->GetURL("/server-redirect?").spec(), + base::CompareCase::SENSITIVE)); + EXPECT_EQ(entry->GetURL(), entry->GetVirtualURL()); + } + + // Do another new navigation, but then use JavaScript to navigate back, + // equivalent to document executing JS. + EXPECT_TRUE( + NavigateToURL(new_shell, embedded_test_server()->GetURL("/title1.html"))); + EXPECT_EQ(1, controller->GetLastCommittedEntryIndex()); + { + TestNavigationObserver observer(new_shell->web_contents()); + EXPECT_TRUE(ExecuteScript(new_shell, "history.go(-1)")); + observer.Wait(); + + NavigationEntry* entry = controller->GetLastCommittedEntry(); + EXPECT_EQ(0, controller->GetLastCommittedEntryIndex()); + EXPECT_FALSE(entry->GetURL().SchemeIs(url::kDataScheme)); + EXPECT_FALSE(entry->GetURL().SchemeIs(url::kFileSystemScheme)); + EXPECT_TRUE(base::StartsWith( + entry->GetURL().spec(), + embedded_test_server()->GetURL("/server-redirect?").spec(), + base::CompareCase::SENSITIVE)); + EXPECT_EQ(entry->GetURL(), entry->GetVirtualURL()); + } +} + +} // namespace content diff --git a/chromium/content/browser/frame_host/blocked_scheme_navigation_throttle.cc b/chromium/content/browser/frame_host/blocked_scheme_navigation_throttle.cc new file mode 100644 index 00000000000..48613b4fb7c --- /dev/null +++ b/chromium/content/browser/frame_host/blocked_scheme_navigation_throttle.cc @@ -0,0 +1,68 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/frame_host/blocked_scheme_navigation_throttle.h" + +#include "base/feature_list.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "content/browser/frame_host/frame_tree.h" +#include "content/browser/frame_host/frame_tree_node.h" +#include "content/browser/frame_host/navigation_handle_impl.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/common/browser_side_navigation_policy.h" +#include "content/public/common/console_message_level.h" +#include "content/public/common/content_features.h" +#include "url/url_constants.h" + +namespace content { + +namespace { +const char kConsoleError[] = "Not allowed to navigate top frame to %s URL: %s"; +} + +BlockedSchemeNavigationThrottle::BlockedSchemeNavigationThrottle( + NavigationHandle* navigation_handle) + : NavigationThrottle(navigation_handle) {} + +BlockedSchemeNavigationThrottle::~BlockedSchemeNavigationThrottle() {} + +NavigationThrottle::ThrottleCheckResult +BlockedSchemeNavigationThrottle::WillProcessResponse() { + NavigationHandleImpl* handle = + static_cast(navigation_handle()); + if (handle->IsDownload()) + return PROCEED; + + RenderFrameHost* top_frame = + handle->frame_tree_node()->frame_tree()->root()->current_frame_host(); + top_frame->AddMessageToConsole( + CONSOLE_MESSAGE_LEVEL_ERROR, + base::StringPrintf(kConsoleError, handle->GetURL().scheme().c_str(), + handle->GetURL().spec().c_str())); + return CANCEL; +} + +const char* BlockedSchemeNavigationThrottle::GetNameForLogging() { + return "BlockedSchemeNavigationThrottle"; +} + +// static +std::unique_ptr +BlockedSchemeNavigationThrottle::CreateThrottleForNavigation( + NavigationHandle* navigation_handle) { + if (navigation_handle->IsInMainFrame() && + navigation_handle->IsRendererInitiated() && + !navigation_handle->IsSameDocument() && + (navigation_handle->GetURL().SchemeIs(url::kDataScheme) || + navigation_handle->GetURL().SchemeIs(url::kFileSystemScheme)) && + !base::FeatureList::IsEnabled( + features::kAllowContentInitiatedDataUrlNavigations)) { + return std::make_unique(navigation_handle); + } + return nullptr; +} + +} // namespace content diff --git a/chromium/content/browser/frame_host/blocked_scheme_navigation_throttle.h b/chromium/content/browser/frame_host/blocked_scheme_navigation_throttle.h new file mode 100644 index 00000000000..40e56d660aa --- /dev/null +++ b/chromium/content/browser/frame_host/blocked_scheme_navigation_throttle.h @@ -0,0 +1,35 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_FRAME_HOST_BLOCKED_SCHEME_NAVIGATION_THROTTLE_H_ +#define CONTENT_BROWSER_FRAME_HOST_BLOCKED_SCHEME_NAVIGATION_THROTTLE_H_ + +#include + +#include "base/macros.h" +#include "content/public/browser/navigation_throttle.h" + +namespace content { + +// Blocks renderer-initiated top-frame navigations to certain URL schemes +// (currently data: and filesystem:). +class BlockedSchemeNavigationThrottle : public NavigationThrottle { + public: + explicit BlockedSchemeNavigationThrottle(NavigationHandle* navigation_handle); + ~BlockedSchemeNavigationThrottle() override; + + // NavigationThrottle method: + ThrottleCheckResult WillProcessResponse() override; + const char* GetNameForLogging() override; + + static std::unique_ptr CreateThrottleForNavigation( + NavigationHandle* navigation_handle); + + private: + DISALLOW_COPY_AND_ASSIGN(BlockedSchemeNavigationThrottle); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_FRAME_HOST_BLOCKED_SCHEME_NAVIGATION_THROTTLE_H_ diff --git a/chromium/content/browser/frame_host/cross_process_frame_connector.cc b/chromium/content/browser/frame_host/cross_process_frame_connector.cc index 5a706cd6e3f..337d713ddfa 100644 --- a/chromium/content/browser/frame_host/cross_process_frame_connector.cc +++ b/chromium/content/browser/frame_host/cross_process_frame_connector.cc @@ -5,12 +5,14 @@ #include "content/browser/frame_host/cross_process_frame_connector.h" #include "base/bind.h" +#include "base/metrics/histogram_macros.h" #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" #include "components/viz/service/surfaces/surface.h" #include "components/viz/service/surfaces/surface_hittest.h" #include "content/browser/compositor/surface_utils.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_delegate.h" #include "content/browser/frame_host/render_frame_host_manager.h" #include "content/browser/frame_host/render_frame_proxy_host.h" #include "content/browser/renderer_host/cursor_manager.h" @@ -44,6 +46,13 @@ CrossProcessFrameConnector::CrossProcessFrameConnector( } CrossProcessFrameConnector::~CrossProcessFrameConnector() { + if (!IsVisible()) { + // MaybeLogCrash will check 1) if there was a crash or not and 2) if the + // crash might have been already logged earlier as kCrashedWhileVisible or + // kShownAfterCrashing. + MaybeLogCrash(CrashVisibility::kNeverVisibleAfterCrash); + } + // Notify the view of this object being destroyed, if the view still exists. SetView(nullptr); } @@ -52,11 +61,14 @@ bool CrossProcessFrameConnector::OnMessageReceived(const IPC::Message& msg) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(CrossProcessFrameConnector, msg) - IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateResizeParams, OnUpdateResizeParams) + IPC_MESSAGE_HANDLER(FrameHostMsg_SynchronizeVisualProperties, + OnSynchronizeVisualProperties) IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateViewportIntersection, OnUpdateViewportIntersection) IPC_MESSAGE_HANDLER(FrameHostMsg_VisibilityChanged, OnVisibilityChanged) IPC_MESSAGE_HANDLER(FrameHostMsg_SetIsInert, OnSetIsInert) + IPC_MESSAGE_HANDLER(FrameHostMsg_SetInheritedEffectiveTouchAction, + OnSetInheritedEffectiveTouchAction) IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateRenderThrottlingStatus, OnUpdateRenderThrottlingStatus) IPC_MESSAGE_UNHANDLED(handled = false) @@ -94,6 +106,14 @@ void CrossProcessFrameConnector::SetView(RenderWidgetHostViewChildFrame* view) { // visibility in case the frame owner is hidden in parent process. We should // try to move these updates to a single IPC (see https://crbug.com/750179). if (view_) { + if (has_crashed_ && !IsVisible()) { + // MaybeLogCrash will check 1) if there was a crash or not and 2) if the + // crash might have been already logged earlier as kCrashedWhileVisible or + // kShownAfterCrashing. + MaybeLogCrash(CrashVisibility::kNeverVisibleAfterCrash); + } + is_crash_already_logged_ = has_crashed_ = false; + view_->SetFrameConnectorDelegate(this); if (is_hidden_) OnVisibilityChanged(false); @@ -106,6 +126,21 @@ void CrossProcessFrameConnector::SetView(RenderWidgetHostViewChildFrame* view) { } void CrossProcessFrameConnector::RenderProcessGone() { + has_crashed_ = true; + + FrameTreeNode* node = frame_proxy_in_parent_renderer_->frame_tree_node(); + int process_id = node->current_frame_host()->GetProcess()->GetID(); + for (node = node->parent(); node; node = node->parent()) { + if (node->current_frame_host()->GetProcess()->GetID() == process_id) { + // The crash will be already logged by the ancestor - ignore this crash in + // the current instance of the CrossProcessFrameConnector. + is_crash_already_logged_ = true; + } + } + + if (IsVisible()) + MaybeLogCrash(CrashVisibility::kCrashedWhileVisible); + frame_proxy_in_parent_renderer_->Send(new FrameMsg_ChildFrameProcessGone( frame_proxy_in_parent_renderer_->GetRoutingID())); } @@ -140,7 +175,7 @@ gfx::PointF CrossProcessFrameConnector::TransformPointToRootCoordSpace( return transformed_point; } -bool CrossProcessFrameConnector::TransformPointToLocalCoordSpace( +bool CrossProcessFrameConnector::TransformPointToLocalCoordSpaceLegacy( const gfx::PointF& point, const viz::SurfaceId& original_surface, const viz::SurfaceId& local_surface_id, @@ -169,7 +204,8 @@ bool CrossProcessFrameConnector::TransformPointToCoordSpaceForView( const gfx::PointF& point, RenderWidgetHostViewBase* target_view, const viz::SurfaceId& local_surface_id, - gfx::PointF* transformed_point) { + gfx::PointF* transformed_point, + viz::EventSource source) { RenderWidgetHostViewBase* root_view = GetRootRenderWidgetHostView(); if (!root_view) return false; @@ -179,14 +215,14 @@ bool CrossProcessFrameConnector::TransformPointToCoordSpaceForView( // be siblings). To account for this, the point is first transformed into the // root coordinate space and then the root is asked to perform the conversion. if (!root_view->TransformPointToLocalCoordSpace(point, local_surface_id, - transformed_point)) + transformed_point, source)) return false; if (target_view == root_view) return true; return root_view->TransformPointToCoordSpaceForView( - *transformed_point, target_view, transformed_point); + *transformed_point, target_view, transformed_point, source); } void CrossProcessFrameConnector::ForwardProcessAckedTouchEvent( @@ -270,13 +306,15 @@ void CrossProcessFrameConnector::UnlockMouse() { root_view->UnlockMouse(); } -void CrossProcessFrameConnector::OnUpdateResizeParams( +void CrossProcessFrameConnector::OnSynchronizeVisualProperties( const viz::SurfaceId& surface_id, - const FrameResizeParams& resize_params) { + const FrameVisualProperties& visual_properties) { // If the |screen_space_rect| or |screen_info| of the frame has changed, then // the viz::LocalSurfaceId must also change. - if ((last_received_local_frame_size_ != resize_params.local_frame_size || - screen_info_ != resize_params.screen_info) && + if ((last_received_local_frame_size_ != visual_properties.local_frame_size || + screen_info_ != visual_properties.screen_info || + capture_sequence_number() != + visual_properties.capture_sequence_number) && local_surface_id_ == surface_id.local_surface_id()) { bad_message::ReceivedBadMessage( frame_proxy_in_parent_renderer_->GetProcess(), @@ -284,8 +322,8 @@ void CrossProcessFrameConnector::OnUpdateResizeParams( return; } - last_received_local_frame_size_ = resize_params.local_frame_size; - UpdateResizeParams(surface_id, resize_params); + last_received_local_frame_size_ = visual_properties.local_frame_size; + SynchronizeVisualProperties(surface_id, visual_properties); } void CrossProcessFrameConnector::OnUpdateViewportIntersection( @@ -296,10 +334,23 @@ void CrossProcessFrameConnector::OnUpdateViewportIntersection( if (view_) view_->UpdateViewportIntersection(viewport_intersection, compositor_visible_rect); + + if (IsVisible()) { + // MaybeLogCrash will check 1) if there was a crash or not and 2) if the + // crash might have been already logged earlier as kCrashedWhileVisible or + // kShownAfterCrashing. + MaybeLogCrash(CrashVisibility::kShownAfterCrashing); + } } void CrossProcessFrameConnector::OnVisibilityChanged(bool visible) { is_hidden_ = !visible; + if (IsVisible()) { + // MaybeLogCrash will check 1) if there was a crash or not and 2) if the + // crash might have been already logged earlier as kCrashedWhileVisible or + // kShownAfterCrashing. + MaybeLogCrash(CrashVisibility::kShownAfterCrashing); + } if (!view_) return; @@ -327,6 +378,13 @@ void CrossProcessFrameConnector::OnSetIsInert(bool inert) { view_->SetIsInert(); } +void CrossProcessFrameConnector::OnSetInheritedEffectiveTouchAction( + cc::TouchAction touch_action) { + inherited_effective_touch_action_ = touch_action; + if (view_) + view_->UpdateInheritedEffectiveTouchAction(); +} + RenderWidgetHostViewBase* CrossProcessFrameConnector::GetRootRenderWidgetHostView() { // Tests may not have frame_proxy_in_parent_renderer_ set. @@ -384,6 +442,11 @@ bool CrossProcessFrameConnector::IsInert() const { return is_inert_; } +cc::TouchAction CrossProcessFrameConnector::InheritedEffectiveTouchAction() + const { + return inherited_effective_touch_action_; +} + bool CrossProcessFrameConnector::IsHidden() const { return is_hidden_; } @@ -404,11 +467,10 @@ void CrossProcessFrameConnector::EmbedRendererWindowTreeClientInParent( } #endif -void CrossProcessFrameConnector::ResizeDueToAutoResize( - const gfx::Size& new_size, - uint64_t sequence_number) { - frame_proxy_in_parent_renderer_->Send(new FrameMsg_ResizeDueToAutoResize( - frame_proxy_in_parent_renderer_->GetRoutingID(), sequence_number)); +void CrossProcessFrameConnector::DidUpdateVisualProperties( + const cc::RenderFrameMetadata& metadata) { + frame_proxy_in_parent_renderer_->Send(new FrameMsg_DidUpdateVisualProperties( + frame_proxy_in_parent_renderer_->GetRoutingID(), metadata)); } void CrossProcessFrameConnector::SetVisibilityForChildViews( @@ -471,4 +533,34 @@ bool CrossProcessFrameConnector::IsSubtreeThrottled() const { return subtree_throttled_; } +void CrossProcessFrameConnector::MaybeLogCrash(CrashVisibility visibility) { + if (!has_crashed_) + return; + + // Only log once per renderer crash. + if (is_crash_already_logged_) + return; + is_crash_already_logged_ = true; + + // Actually log the UMA. + UMA_HISTOGRAM_ENUMERATION("Stability.ChildFrameCrash.Visibility", visibility); +} + +bool CrossProcessFrameConnector::IsVisible() { + if (is_hidden_) + return false; + if (viewport_intersection_rect().IsEmpty()) + return false; + + Visibility embedder_visibility = + frame_proxy_in_parent_renderer_->frame_tree_node() + ->current_frame_host() + ->delegate() + ->GetVisibility(); + if (embedder_visibility != Visibility::VISIBLE) + return false; + + return true; +} + } // namespace content diff --git a/chromium/content/browser/frame_host/cross_process_frame_connector.h b/chromium/content/browser/frame_host/cross_process_frame_connector.h index 95e72d659cb..21759b18be7 100644 --- a/chromium/content/browser/frame_host/cross_process_frame_connector.h +++ b/chromium/content/browser/frame_host/cross_process_frame_connector.h @@ -7,12 +7,13 @@ #include +#include "cc/input/touch_action.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/surfaces/local_surface_id.h" #include "components/viz/common/surfaces/surface_id.h" #include "content/browser/renderer_host/frame_connector_delegate.h" #include "content/common/content_export.h" -#include "content/common/frame_resize_params.h" +#include "content/common/frame_visual_properties.h" namespace IPC { class Message; @@ -84,15 +85,17 @@ class CONTENT_EXPORT CrossProcessFrameConnector gfx::PointF TransformPointToRootCoordSpace( const gfx::PointF& point, const viz::SurfaceId& surface_id) override; - bool TransformPointToLocalCoordSpace(const gfx::PointF& point, - const viz::SurfaceId& original_surface, - const viz::SurfaceId& local_surface_id, - gfx::PointF* transformed_point) override; + bool TransformPointToLocalCoordSpaceLegacy( + const gfx::PointF& point, + const viz::SurfaceId& original_surface, + const viz::SurfaceId& local_surface_id, + gfx::PointF* transformed_point) override; bool TransformPointToCoordSpaceForView( const gfx::PointF& point, RenderWidgetHostViewBase* target_view, const viz::SurfaceId& local_surface_id, - gfx::PointF* transformed_point) override; + gfx::PointF* transformed_point, + viz::EventSource source = viz::EventSource::ANY) override; void ForwardProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) override; void BubbleScrollEvent(const blink::WebGestureEvent& event) override; @@ -104,6 +107,7 @@ class CONTENT_EXPORT CrossProcessFrameConnector const gfx::Size& max_size) override; void DisableAutoResize() override; bool IsInert() const override; + cc::TouchAction InheritedEffectiveTouchAction() const override; bool IsHidden() const override; bool IsThrottled() const override; bool IsSubtreeThrottled() const override; @@ -111,8 +115,8 @@ class CONTENT_EXPORT CrossProcessFrameConnector void EmbedRendererWindowTreeClientInParent( ui::mojom::WindowTreeClientPtr window_tree_client) override; #endif - void ResizeDueToAutoResize(const gfx::Size& new_size, - uint64_t sequence_number) override; + void DidUpdateVisualProperties( + const cc::RenderFrameMetadata& metadata) override; // Set the visibility of immediate child views, i.e. views whose parent view // is |view_|. @@ -125,6 +129,26 @@ class CONTENT_EXPORT CrossProcessFrameConnector return GetRootRenderWidgetHostView(); } + // This enum backs a histogram - please do not modify or remove the existing + // enum values below (adding new values is okay, but please remember to also + // update enums.xml in this case). See enums.xml for descriptions of enum + // values. + enum class CrashVisibility { + kCrashedWhileVisible = 0, + kShownAfterCrashing = 1, + kNeverVisibleAfterCrash = 2, + kMaxValue = kNeverVisibleAfterCrash + }; + // Logs the Stability.ChildFrameCrash.Visibility metric after checking that a + // crash has indeed happened and checking that the crash has not already been + // logged in UMA. + void MaybeLogCrash(CrashVisibility visibility); + + // Returns whether the child widget is actually visible to the user. This is + // different from the IsHidden override, and takes into account viewport + // intersection as well as the visibility of the RenderFrameHostDelegate. + bool IsVisible(); + private: friend class MockCrossProcessFrameConnector; @@ -133,12 +157,14 @@ class CONTENT_EXPORT CrossProcessFrameConnector void ResetScreenSpaceRect(); // Handlers for messages received from the parent frame. - void OnUpdateResizeParams(const viz::SurfaceId& surface_id, - const FrameResizeParams& frame_resize_params); + void OnSynchronizeVisualProperties( + const viz::SurfaceId& surface_id, + const FrameVisualProperties& visual_properties); void OnUpdateViewportIntersection(const gfx::Rect& viewport_intersection, const gfx::Rect& compositor_visible_rect); void OnVisibilityChanged(bool visible); void OnSetIsInert(bool); + void OnSetInheritedEffectiveTouchAction(cc::TouchAction); void OnUpdateRenderThrottlingStatus(bool is_throttled, bool subtree_throttled); @@ -147,6 +173,8 @@ class CONTENT_EXPORT CrossProcessFrameConnector RenderFrameProxyHost* frame_proxy_in_parent_renderer_; bool is_inert_ = false; + cc::TouchAction inherited_effective_touch_action_ = + cc::TouchAction::kTouchActionAuto; bool is_throttled_ = false; bool subtree_throttled_ = false; @@ -157,6 +185,14 @@ class CONTENT_EXPORT CrossProcessFrameConnector bool is_scroll_bubbling_; + // Used to make sure we only log UMA once per renderer crash. + bool is_crash_already_logged_ = false; + + // Used to make sure that MaybeLogCrash only logs the UMA in case of an actual + // crash (in case it is called from the destructor of + // CrossProcessFrameConnector or when WebContentsImpl::WasShown is called). + bool has_crashed_ = false; + // The last pre-transform frame size received from the parent renderer. // |last_received_local_frame_size_| may be in DIP if use zoom for DSF is // off. @@ -168,4 +204,3 @@ class CONTENT_EXPORT CrossProcessFrameConnector } // namespace content #endif // CONTENT_BROWSER_FRAME_HOST_CROSS_PROCESS_FRAME_CONNECTOR_H_ - diff --git a/chromium/content/browser/frame_host/data_url_navigation_browsertest.cc b/chromium/content/browser/frame_host/data_url_navigation_browsertest.cc deleted file mode 100644 index 02d8c983202..00000000000 --- a/chromium/content/browser/frame_host/data_url_navigation_browsertest.cc +++ /dev/null @@ -1,1032 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/macros.h" -#include "base/path_service.h" -#include "base/strings/pattern.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/test/scoped_feature_list.h" -#include "build/build_config.h" -#include "build/buildflag.h" -#include "content/browser/site_per_process_browsertest.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/navigation_entry.h" -#include "content/public/browser/web_contents.h" -#include "content/public/common/browser_side_navigation_policy.h" -#include "content/public/common/content_features.h" -#include "content/public/common/content_paths.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/download_test_observer.h" -#include "content/public/test/test_navigation_observer.h" -#include "content/shell/browser/shell.h" -#include "content/shell/browser/shell_download_manager_delegate.h" -#include "net/base/escape.h" -#include "net/dns/mock_host_resolver.h" -#include "net/test/embedded_test_server/embedded_test_server.h" -#include "ppapi/buildflags/buildflags.h" - -#if BUILDFLAG(ENABLE_PLUGINS) -#include "content/public/browser/plugin_service.h" -#include "content/public/common/webplugininfo.h" -#endif - -namespace content { - -namespace { - -// The pattern to catch messages printed by the browser when a data URL -// navigation is blocked. -const char kDataUrlBlockedPattern[] = - "Not allowed to navigate top frame to data URL:*"; - -// The message printed by the data URL when it successfully navigates. -const char kDataUrlSuccessfulMessage[] = "NAVIGATION_SUCCESSFUL"; - -// A "Hello World" PDF encoded as a data URL. Source of this PDF: -// ------------------------- -// %PDF-1.7 -// 1 0 obj << /Type /Page /Parent 3 0 R /Resources 5 0 R /Contents 2 0 R >> -// endobj -// 2 0 obj << /Length 51 >> -// stream BT -// /F1 12 Tf -// 1 0 0 1 100 20 Tm -// (Hello World)Tj -// ET -// endstream -// endobj -// 3 0 obj << /Type /Pages /Kids [ 1 0 R ] /Count 1 /MediaBox [ 0 0 300 50] >> -// endobj -// 4 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont/Arial >> -// endobj -// 5 0 obj << /ProcSet[/PDF/Text] /Font <> >> -// endobj -// 6 0 obj << /Type /Catalog /Pages 3 0 R >> -// endobj -// trailer << /Root 6 0 R >> -// ------------------------- -const char kPdfUrl[] = - "data:application/pdf;base64,JVBERi0xLjcKMSAwIG9iaiA8PCAvVHlwZSAvUGFnZSAvUG" - "FyZW50IDMgMCBSIC9SZXNvdXJjZXMgNSAwIFIgL0NvbnRlbnRzIDIgMCBSID4+CmVuZG9iagoy" - "IDAgb2JqIDw8IC9MZW5ndGggNTEgPj4KIHN0cmVhbSBCVAogL0YxIDEyIFRmCiAxIDAgMCAxID" - "EwMCAyMCBUbQogKEhlbGxvIFdvcmxkKVRqCiBFVAogZW5kc3RyZWFtCmVuZG9iagozIDAgb2Jq" - "IDw8IC9UeXBlIC9QYWdlcyAvS2lkcyBbIDEgMCBSIF0gL0NvdW50IDEgL01lZGlhQm94IFsgMC" - "AwIDMwMCA1MF0gPj4KZW5kb2JqCjQgMCBvYmogPDwgL1R5cGUgL0ZvbnQgL1N1YnR5cGUgL1R5" - "cGUxIC9OYW1lIC9GMSAvQmFzZUZvbnQvQXJpYWwgPj4KZW5kb2JqCjUgMCBvYmogPDwgL1Byb2" - "NTZXRbL1BERi9UZXh0XSAvRm9udCA8PC9GMSA0IDAgUiA+PiA+PgplbmRvYmoKNiAwIG9iaiA8" - "PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMyAwIFIgPj4KZW5kb2JqCnRyYWlsZXIgPDwgL1Jvb3" - "QgNiAwIFIgPj4K"; - -enum ExpectedNavigationStatus { NAVIGATION_BLOCKED, NAVIGATION_ALLOWED }; - -// This class is similar to ConsoleObserverDelegate in that it listens and waits -// for specific console messages. The difference from ConsoleObserverDelegate is -// that this class immediately stops waiting if it sees a message matching -// fail_pattern, instead of waiting for a message matching success_pattern. -class DataURLWarningConsoleObserverDelegate : public WebContentsDelegate { - public: - DataURLWarningConsoleObserverDelegate( - WebContents* web_contents, - ExpectedNavigationStatus expected_navigation_status) - : web_contents_(web_contents), - success_filter_(expected_navigation_status == NAVIGATION_ALLOWED - ? kDataUrlSuccessfulMessage - : kDataUrlBlockedPattern), - fail_filter_(expected_navigation_status == NAVIGATION_ALLOWED - ? kDataUrlBlockedPattern - : kDataUrlSuccessfulMessage), - message_loop_runner_( - new MessageLoopRunner(MessageLoopRunner::QuitMode::IMMEDIATE)), - saw_failure_message_(false) {} - ~DataURLWarningConsoleObserverDelegate() override {} - - void Wait() { message_loop_runner_->Run(); } - - // WebContentsDelegate method: - bool DidAddMessageToConsole(WebContents* source, - int32_t level, - const base::string16& message, - int32_t line_no, - const base::string16& source_id) override { - DCHECK(source == web_contents_); - const std::string ascii_message = base::UTF16ToASCII(message); - if (base::MatchPattern(ascii_message, fail_filter_)) { - saw_failure_message_ = true; - message_loop_runner_->Quit(); - } - if (base::MatchPattern(ascii_message, success_filter_)) { - message_loop_runner_->Quit(); - } - return false; - } - - // Returns true if the observer encountered a message that matches - // |fail_filter_|. - bool saw_failure_message() const { return saw_failure_message_; } - - private: - WebContents* web_contents_; - const std::string success_filter_; - const std::string fail_filter_; - scoped_refptr message_loop_runner_; - bool saw_failure_message_; -}; - -#if BUILDFLAG(ENABLE_PLUGINS) -// This class registers a fake PDF plugin handler so that data URL navigations -// with a PDF mime type end up with a navigation and don't simply download the -// file. -class ScopedPluginRegister { - public: - ScopedPluginRegister(content::PluginService* plugin_service) - : plugin_service_(plugin_service) { - const char kPluginName[] = "PDF"; - const char kPdfMimeType[] = "application/pdf"; - const char kPdfFileType[] = "pdf"; - WebPluginInfo plugin_info; - plugin_info.type = WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS; - plugin_info.name = base::ASCIIToUTF16(kPluginName); - plugin_info.mime_types.push_back( - WebPluginMimeType(kPdfMimeType, kPdfFileType, std::string())); - plugin_service_->RegisterInternalPlugin(plugin_info, false); - plugin_service_->RefreshPlugins(); - } - - ~ScopedPluginRegister() { - std::vector plugins; - plugin_service_->GetInternalPlugins(&plugins); - EXPECT_EQ(1u, plugins.size()); - plugin_service_->UnregisterInternalPlugin(plugins[0].path); - plugin_service_->RefreshPlugins(); - - plugins.clear(); - plugin_service_->GetInternalPlugins(&plugins); - EXPECT_TRUE(plugins.empty()); - } - - private: - content::PluginService* plugin_service_; -}; -#endif // BUILDFLAG(ENABLE_PLUGINS) - -} // namespace - -class DataUrlNavigationBrowserTest : public ContentBrowserTest { - public: -#if BUILDFLAG(ENABLE_PLUGINS) - DataUrlNavigationBrowserTest() - : scoped_plugin_register_(PluginService::GetInstance()) {} -#else - DataUrlNavigationBrowserTest() {} -#endif // BUILDFLAG(ENABLE_PLUGINS) - - protected: - void SetUpOnMainThread() override { - host_resolver()->AddRule("*", "127.0.0.1"); - ASSERT_TRUE(embedded_test_server()->Start()); - - base::FilePath path; - ASSERT_TRUE(PathService::Get(content::DIR_TEST_DATA, &path)); - path = path.AppendASCII("data_url_navigations.html"); - ASSERT_TRUE(base::PathExists(path)); - - std::string contents; - ASSERT_TRUE(base::ReadFileToString(path, &contents)); - data_url_ = GURL(std::string("data:text/html,") + contents); - - ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir()); - ShellDownloadManagerDelegate* delegate = - static_cast( - shell() - ->web_contents() - ->GetBrowserContext() - ->GetDownloadManagerDelegate()); - delegate->SetDownloadBehaviorForTesting(downloads_directory_.GetPath()); - } - - // Adds an iframe to |rfh| pointing to |url|. - void AddIFrame(RenderFrameHost* rfh, const GURL& url) { - const std::string javascript = base::StringPrintf( - "f = document.createElement('iframe'); f.src = '%s';" - "document.body.appendChild(f);", - url.spec().c_str()); - TestNavigationObserver observer(shell()->web_contents()); - EXPECT_TRUE(ExecuteScript(rfh, javascript)); - observer.Wait(); - } - - // Runs |javascript| on the first child frame and checks for a navigation. - void TestNavigationFromFrame( - const std::string& javascript, - ExpectedNavigationStatus expected_navigation_status) { - RenderFrameHost* child = - ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); - ASSERT_TRUE(child); - if (AreAllSitesIsolatedForTesting()) { - ASSERT_TRUE(child->IsCrossProcessSubframe()); - } - ExecuteScriptAndCheckNavigation(child, javascript, - expected_navigation_status); - } - - // Runs |javascript| on the first child frame and expects a download to occur. - void TestDownloadFromFrame(const std::string& javascript) { - RenderFrameHost* child = - ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); - ASSERT_TRUE(child); - if (AreAllSitesIsolatedForTesting()) { - ASSERT_TRUE(child->IsCrossProcessSubframe()); - } - ExecuteScriptAndCheckNavigationDownload(child, javascript); - } - - // Runs |javascript| on the first child frame and checks for a navigation to - // the PDF file pointed by the test case. - void TestPDFNavigationFromFrame( - const std::string& javascript, - ExpectedNavigationStatus expected_navigation_status) { - RenderFrameHost* child = - ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); - ASSERT_TRUE(child); - if (AreAllSitesIsolatedForTesting()) { - ASSERT_TRUE(child->IsCrossProcessSubframe()); - } - ExecuteScriptAndCheckPDFNavigation(child, javascript, - expected_navigation_status); - } - - // Same as TestNavigationFromFrame, but instead of navigating, the child frame - // tries to open a new window with a data URL. - void TestWindowOpenFromFrame( - const std::string& javascript, - ExpectedNavigationStatus expected_navigation_status) { - RenderFrameHost* child = - ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); - if (AreAllSitesIsolatedForTesting()) { - ASSERT_TRUE(child->IsCrossProcessSubframe()); - } - ExecuteScriptAndCheckWindowOpen(child, javascript, - expected_navigation_status); - } - - // Executes |javascript| on |rfh| and waits for a console message based on - // |expected_navigation_status|. - // - Blocked navigations should print kDataUrlBlockedPattern. - // - Allowed navigations should print kDataUrlSuccessfulMessage. - void ExecuteScriptAndCheckNavigation( - RenderFrameHost* rfh, - const std::string& javascript, - ExpectedNavigationStatus expected_navigation_status) { - const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); - const std::string expected_message; - - DataURLWarningConsoleObserverDelegate console_delegate( - shell()->web_contents(), expected_navigation_status); - shell()->web_contents()->SetDelegate(&console_delegate); - - TestNavigationObserver navigation_observer(shell()->web_contents()); - EXPECT_TRUE(ExecuteScript(rfh, javascript)); - console_delegate.Wait(); - EXPECT_FALSE(console_delegate.saw_failure_message()); - shell()->web_contents()->SetDelegate(nullptr); - - switch (expected_navigation_status) { - case NAVIGATION_ALLOWED: - navigation_observer.Wait(); - // The new page should have a data URL. - EXPECT_TRUE(shell()->web_contents()->GetLastCommittedURL().SchemeIs( - url::kDataScheme)); - EXPECT_TRUE(navigation_observer.last_navigation_url().SchemeIs( - url::kDataScheme)); - EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); - break; - - case NAVIGATION_BLOCKED: - // Original page shouldn't navigate away. - EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); - EXPECT_FALSE(navigation_observer.last_navigation_succeeded()); - break; - - default: - NOTREACHED(); - } - } - - // Similar to ExecuteScriptAndCheckNavigation(), but doesn't wait for a - // console message if the navigation is expected to be allowed (this is - // because PDF files can't print to the console). - void ExecuteScriptAndCheckPDFNavigation( - RenderFrameHost* rfh, - const std::string& javascript, - ExpectedNavigationStatus expected_navigation_status) { - const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); - - const std::string expected_message = - (expected_navigation_status == NAVIGATION_ALLOWED) - ? std::string() - : kDataUrlBlockedPattern; - - std::unique_ptr console_delegate; - if (!expected_message.empty()) { - console_delegate.reset(new ConsoleObserverDelegate( - shell()->web_contents(), expected_message)); - shell()->web_contents()->SetDelegate(console_delegate.get()); - } - - TestNavigationObserver navigation_observer(shell()->web_contents()); - EXPECT_TRUE(ExecuteScript(rfh, javascript)); - - if (console_delegate) { - console_delegate->Wait(); - shell()->web_contents()->SetDelegate(nullptr); - } - - switch (expected_navigation_status) { - case NAVIGATION_ALLOWED: - navigation_observer.Wait(); - // The new page should have a data URL. - EXPECT_TRUE(shell()->web_contents()->GetLastCommittedURL().SchemeIs( - url::kDataScheme)); - EXPECT_TRUE(navigation_observer.last_navigation_url().SchemeIs( - url::kDataScheme)); - EXPECT_TRUE(navigation_observer.last_navigation_succeeded()); - break; - - case NAVIGATION_BLOCKED: - // Original page shouldn't navigate away. - EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); - EXPECT_FALSE(navigation_observer.last_navigation_succeeded()); - break; - - default: - NOTREACHED(); - } - } - - // Executes |javascript| on |rfh| and waits for a new window to be opened. - // Does not check for console messages (it's currently not possible to - // concurrently wait for a new shell to be created and a console message to be - // printed on that new shell). - void ExecuteScriptAndCheckWindowOpen( - RenderFrameHost* rfh, - const std::string& javascript, - ExpectedNavigationStatus expected_navigation_status) { - ShellAddedObserver new_shell_observer; - EXPECT_TRUE(ExecuteScript(rfh, javascript)); - - Shell* new_shell = new_shell_observer.GetShell(); - WaitForLoadStop(new_shell->web_contents()); - - switch (expected_navigation_status) { - case NAVIGATION_ALLOWED: - EXPECT_TRUE(new_shell->web_contents()->GetLastCommittedURL().SchemeIs( - url::kDataScheme)); - break; - - case NAVIGATION_BLOCKED: - EXPECT_TRUE( - new_shell->web_contents()->GetLastCommittedURL().is_empty()); - break; - - default: - NOTREACHED(); - } - } - - // Executes |javascript| on |rfh| and waits for a download to be started by - // a window.open call. - void ExecuteScriptAndCheckWindowOpenDownload(RenderFrameHost* rfh, - const std::string& javascript) { - const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); - ShellAddedObserver new_shell_observer; - DownloadManager* download_manager = BrowserContext::GetDownloadManager( - shell()->web_contents()->GetBrowserContext()); - - EXPECT_TRUE(ExecuteScript(rfh, javascript)); - Shell* new_shell = new_shell_observer.GetShell(); - - DownloadTestObserverTerminal download_observer( - download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); - - WaitForLoadStop(new_shell->web_contents()); - // If no download happens, this will timeout. - download_observer.WaitForFinished(); - - EXPECT_TRUE( - new_shell->web_contents()->GetLastCommittedURL().spec().empty()); - // No navigation should commit. - EXPECT_FALSE( - new_shell->web_contents()->GetController().GetLastCommittedEntry()); - // Original page shouldn't navigate away. - EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); - } - - // Executes |javascript| on |rfh| and waits for a download to be started. - void ExecuteScriptAndCheckNavigationDownload(RenderFrameHost* rfh, - const std::string& javascript) { - const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); - DownloadManager* download_manager = BrowserContext::GetDownloadManager( - shell()->web_contents()->GetBrowserContext()); - DownloadTestObserverTerminal download_observer( - download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); - - EXPECT_TRUE(ExecuteScript(rfh, javascript)); - // If no download happens, this will timeout. - download_observer.WaitForFinished(); - - // Original page shouldn't navigate away. - EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); - } - - // Initiates a browser initiated navigation to |url| and waits for a download - // to be started. - void NavigateAndCheckDownload(const GURL& url) { - const GURL original_url(shell()->web_contents()->GetLastCommittedURL()); - DownloadManager* download_manager = BrowserContext::GetDownloadManager( - shell()->web_contents()->GetBrowserContext()); - DownloadTestObserverTerminal download_observer( - download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); - NavigateToURL(shell(), url); - - // If no download happens, this will timeout. - download_observer.WaitForFinished(); - - // Original page shouldn't navigate away. - EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL()); - } - - // data URL form of the file at content/test/data/data_url_navigations.html - GURL data_url() const { return data_url_; } - - private: - base::ScopedTempDir downloads_directory_; - -#if BUILDFLAG(ENABLE_PLUGINS) - ScopedPluginRegister scoped_plugin_register_; -#endif // BUILDFLAG(ENABLE_PLUGINS) - - GURL data_url_; - - DISALLOW_COPY_AND_ASSIGN(DataUrlNavigationBrowserTest); -}; - -//////////////////////////////////////////////////////////////////////////////// -// data URLs with HTML mimetype -// -// Tests that a browser initiated navigation to a data URL doesn't show a -// console warning and is not blocked. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, BrowserInitiated_Allow) { - DataURLWarningConsoleObserverDelegate console_delegate( - shell()->web_contents(), NAVIGATION_ALLOWED); - shell()->web_contents()->SetDelegate(&console_delegate); - - NavigateToURL(shell(), GURL("data:text/" - "html,")); - console_delegate.Wait(); - shell()->web_contents()->SetDelegate(nullptr); - - EXPECT_TRUE(shell()->web_contents()->GetLastCommittedURL().SchemeIs( - url::kDataScheme)); -} - -// Tests that a content initiated navigation to a data URL is blocked. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, HTML_Navigation_Block) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - ExecuteScriptAndCheckNavigation( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('navigate-top-frame-to-html').click()", - NAVIGATION_BLOCKED); -} - -class DataUrlNavigationBrowserTestWithFeatureFlag - : public DataUrlNavigationBrowserTest { - public: - DataUrlNavigationBrowserTestWithFeatureFlag() { - scoped_feature_list_.InitAndEnableFeature( - features::kAllowContentInitiatedDataUrlNavigations); - } - ~DataUrlNavigationBrowserTestWithFeatureFlag() override {} - - private: - base::test::ScopedFeatureList scoped_feature_list_; - - DISALLOW_COPY_AND_ASSIGN(DataUrlNavigationBrowserTestWithFeatureFlag); -}; - -// Tests that a content initiated navigation to a data URL is allowed if -// blocking is disabled with a feature flag. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTestWithFeatureFlag, - HTML_Navigation_Allow_FeatureFlag) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - ExecuteScriptAndCheckNavigation( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('navigate-top-frame-to-html').click()", - NAVIGATION_ALLOWED); -} - -// Tests that a window.open to a data URL with HTML mime type is blocked. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, HTML_WindowOpen_Block) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - ExecuteScriptAndCheckWindowOpen( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('window-open-html').click()", - NAVIGATION_BLOCKED); -} - -// Tests that a form post to a data URL with HTML mime type is blocked. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, HTML_FormPost_Block) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - ExecuteScriptAndCheckNavigation( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('form-post-to-html').click()", - NAVIGATION_BLOCKED); -} - -// Tests that navigating the main frame to a data URL with HTML mimetype from a -// subframe is blocked. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - HTML_NavigationFromFrame_Block) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("a.com", "/simple_page.html")); - AddIFrame( - shell()->web_contents()->GetMainFrame(), - embedded_test_server()->GetURL("b.com", "/data_url_navigations.html")); - - TestNavigationFromFrame( - "document.getElementById('navigate-top-frame-to-html').click()", - NAVIGATION_BLOCKED); -} - -// Tests that opening a new data URL window from a subframe is blocked. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - HTML_WindowOpenFromFrame_Block) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("a.com", "/simple_page.html")); - AddIFrame( - shell()->web_contents()->GetMainFrame(), - embedded_test_server()->GetURL("b.com", "/data_url_navigations.html")); - - TestWindowOpenFromFrame("document.getElementById('window-open-html').click()", - NAVIGATION_BLOCKED); -} - -// Tests that navigation to a data URL is blocked even if the top frame is -// already a data URL. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - HTML_Navigation_DataToData_Block) { - NavigateToURL(shell(), data_url()); - ExecuteScriptAndCheckNavigation( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('navigate-top-frame-to-html').click()", - NAVIGATION_BLOCKED); -} - -// Tests that a form post to a data URL with HTML mime type is blocked even if -// the top frame is already a data URL. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - HTML_FormPost_DataToData_Block) { - NavigateToURL(shell(), data_url()); - ExecuteScriptAndCheckNavigation( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('form-post-to-html').click()", - NAVIGATION_BLOCKED); -} - -// Tests that navigating the top frame to a data URL with HTML mimetype is -// blocked even if the top frame is already a data URL. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - HTML_NavigationFromFrame_TopFrameIsDataURL_Block) { - const GURL top_url( - base::StringPrintf("data:text/html, ", - embedded_test_server() - ->GetURL("/data_url_navigations.html") - .spec() - .c_str())); - NavigateToURL(shell(), top_url); - - TestNavigationFromFrame( - "document.getElementById('navigate-top-frame-to-html').click()", - NAVIGATION_BLOCKED); -} - -// Tests that opening a new window with a data URL with HTML mimetype is blocked -// even if the top frame is already a data URL. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - HTML_WindowOpenFromFrame_TopFrameIsDataURL_Block) { - const GURL top_url( - base::StringPrintf("data:text/html, ", - embedded_test_server() - ->GetURL("/data_url_navigations.html") - .spec() - .c_str())); - NavigateToURL(shell(), top_url); - - TestWindowOpenFromFrame("document.getElementById('window-open-html').click()", - NAVIGATION_BLOCKED); -} - -//////////////////////////////////////////////////////////////////////////////// -// data URLs with octet-stream mimetype (binary) -// -// Test that a direct navigation to a binary mime type initiates a download. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - OctetStream_BrowserInitiated_Download) { - NavigateAndCheckDownload(GURL("data:application/octet-stream,test")); -} - -#if defined(OS_ANDROID) -// Flaky on android: https://crbug.com/734563 -#define MAYBE_OctetStream_WindowOpen_Download \ - DISABLED_OctetStream_WindowOpen_Download -#else -#define MAYBE_OctetStream_WindowOpen_Download OctetStream_WindowOpen_Download -#endif - -// Test that window.open to a data URL results in a download if the URL has a -// binary mime type. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - MAYBE_OctetStream_WindowOpen_Download) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - ExecuteScriptAndCheckWindowOpenDownload( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('window-open-octetstream').click()"); -} - -// Test that a navigation to a data URL results in a download if the URL has a -// binary mime type. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - OctetStream_Navigation_Download) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - ExecuteScriptAndCheckNavigationDownload( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('navigate-top-frame-to-octetstream').click()"); -} - -// Test that a form post to a data URL results in a download if the URL has a -// binary mime type. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - OctetStream_FormPost_Download) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - ExecuteScriptAndCheckNavigationDownload( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('form-post-to-octetstream').click()"); -} - -// Tests that navigating the main frame from a subframe results in a download -// if the URL has a binary mimetype. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - OctetStream_NavigationFromFrame_Download) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("a.com", "/simple_page.html")); - AddIFrame( - shell()->web_contents()->GetMainFrame(), - embedded_test_server()->GetURL("b.com", "/data_url_navigations.html")); - - TestDownloadFromFrame( - "document.getElementById('navigate-top-frame-to-octetstream').click()"); -} - -//////////////////////////////////////////////////////////////////////////////// -// data URLs with unknown mimetype -// -// Test that a direct navigation to an unknown mime type initiates a download. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - UnknownMimeType_BrowserInitiated_Download) { - NavigateAndCheckDownload(GURL("data:unknown/mimetype,test")); -} - -#if defined(OS_ANDROID) -// Flaky on android: https://crbug.com/734563 -#define MAYBE_UnknownMimeType_WindowOpen_Download \ - DISABLED_UnknownMimeType_WindowOpen_Download -#else -#define MAYBE_UnknownMimeType_WindowOpen_Download \ - UnknownMimeType_WindowOpen_Download -#endif - -// Test that window.open to a data URL results in a download if the URL has an -// unknown mime type. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - MAYBE_UnknownMimeType_WindowOpen_Download) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - ExecuteScriptAndCheckWindowOpenDownload( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('window-open-unknown-mimetype').click()"); -} - -// Test that a navigation to a data URL results in a download if the URL has an -// unknown mime type. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - UnknownMimeType_Navigation_Download) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - ExecuteScriptAndCheckNavigationDownload( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('navigate-top-" - "frame-to-unknown-mimetype').click()"); -} - -// Test that a form post to a data URL results in a download if the URL has an -// unknown mime type. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - UnknownMimeType_FormPost_Download) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - ExecuteScriptAndCheckNavigationDownload( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('form-post-to-unknown-mimetype').click()"); -} - -// Tests that navigating the main frame from a subframe results in a download -// if the URL has an unknown mimetype. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - UnknownMimeType_NavigationFromFrame_Download) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("a.com", "/simple_page.html")); - AddIFrame( - shell()->web_contents()->GetMainFrame(), - embedded_test_server()->GetURL("b.com", "/data_url_navigations.html")); - - TestDownloadFromFrame( - "document.getElementById('navigate-top-frame-to-unknown-mimetype').click(" - ")"); -} - -//////////////////////////////////////////////////////////////////////////////// -// data URLs with PDF mimetype -// -// Tests that a browser initiated navigation to a data URL with PDF mime type is -// allowed, or initiates a download on Android. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - PDF_BrowserInitiatedNavigation_Allow) { -#if !defined(OS_ANDROID) - TestNavigationObserver observer(shell()->web_contents()); - NavigateToURL(shell(), GURL(kPdfUrl)); - EXPECT_EQ(GURL(kPdfUrl), observer.last_navigation_url()); - EXPECT_TRUE(observer.last_navigation_succeeded()); - EXPECT_TRUE(shell()->web_contents()->GetLastCommittedURL().SchemeIs( - url::kDataScheme)); -#else - // On Android, PDFs are downloaded upon navigation. - NavigateAndCheckDownload(GURL(kPdfUrl)); -#endif -} - -// Tests that a window.open to a data URL is blocked if the data URL has a -// mime type that will be handled by a plugin (PDF in this case). -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, PDF_WindowOpen_Block) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - -#if !defined(OS_ANDROID) - ExecuteScriptAndCheckWindowOpen( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('window-open-pdf').click()", NAVIGATION_BLOCKED); -#else - // On Android, PDFs are downloaded upon navigation. - ExecuteScriptAndCheckNavigationDownload( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('window-open-pdf').click()"); -#endif -} - -// Test that a navigation to a data URL is blocked if the data URL has a mime -// type that will be handled by a plugin (PDF in this case). -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, PDF_Navigation_Block) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - -#if !defined(OS_ANDROID) - ExecuteScriptAndCheckPDFNavigation( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('navigate-top-frame-to-pdf').click()", - NAVIGATION_BLOCKED); -#else - // On Android, PDFs are downloaded upon navigation. - ExecuteScriptAndCheckNavigationDownload( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('navigate-top-frame-to-pdf').click()"); -#endif -} - -// Test that a form post to a data URL is blocked if the data URL has a mime -// type that will be handled by a plugin (PDF in this case). -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, PDF_FormPost_Block) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - -#if !defined(OS_ANDROID) - ExecuteScriptAndCheckPDFNavigation( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('form-post-to-pdf').click()", - NAVIGATION_BLOCKED); -#else - // On Android, PDFs are downloaded upon navigation. - ExecuteScriptAndCheckNavigationDownload( - shell()->web_contents()->GetMainFrame(), - "document.getElementById('form-post-to-pdf').click()"); -#endif -} - -// Tests that navigating the main frame to a data URL with PDF mimetype from a -// subframe is blocked. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - PDF_NavigationFromFrame_Block) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("a.com", "/simple_page.html")); - AddIFrame( - shell()->web_contents()->GetMainFrame(), - embedded_test_server()->GetURL("b.com", "/data_url_navigations.html")); - -#if !defined(OS_ANDROID) - TestPDFNavigationFromFrame( - "document.getElementById('navigate-top-frame-to-pdf').click()", - NAVIGATION_BLOCKED); -#else - // On Android, PDFs are downloaded upon navigation. - RenderFrameHost* child = - ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); - ASSERT_TRUE(child); - if (AreAllSitesIsolatedForTesting()) { - ASSERT_TRUE(child->IsCrossProcessSubframe()); - } - ExecuteScriptAndCheckNavigationDownload( - child, "document.getElementById('navigate-top-frame-to-pdf').click()"); -#endif -} - -// Tests that opening a window with a data URL with PDF mimetype from a -// subframe is blocked. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - PDF_WindowOpenFromFrame_Block) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("a.com", "/simple_page.html")); - AddIFrame( - shell()->web_contents()->GetMainFrame(), - embedded_test_server()->GetURL("b.com", "/data_url_navigations.html")); - -#if !defined(OS_ANDROID) - TestWindowOpenFromFrame("document.getElementById('window-open-pdf').click()", - NAVIGATION_BLOCKED); -#else - // On Android, PDFs are downloaded upon navigation. - RenderFrameHost* child = - ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); - ASSERT_TRUE(child); - if (AreAllSitesIsolatedForTesting()) { - ASSERT_TRUE(child->IsCrossProcessSubframe()); - } - ExecuteScriptAndCheckNavigationDownload( - child, "document.getElementById('window-open-pdf').click()"); -#endif -} - -// Tests that navigating the top frame to a data URL with PDF mimetype from a -// subframe is blocked even if the top frame is already a data URL. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - PDF_NavigationFromFrame_TopFrameIsDataURL_Block) { - const GURL top_url( - base::StringPrintf("data:text/html, ", - embedded_test_server() - ->GetURL("/data_url_navigations.html") - .spec() - .c_str())); - NavigateToURL(shell(), top_url); - -#if !defined(OS_ANDROID) - TestPDFNavigationFromFrame( - "document.getElementById('navigate-top-frame-to-pdf').click()", - NAVIGATION_BLOCKED); -#else - // On Android, PDFs are downloaded upon navigation. - RenderFrameHost* child = - ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); - ASSERT_TRUE(child); - if (AreAllSitesIsolatedForTesting()) { - ASSERT_TRUE(child->IsCrossProcessSubframe()); - } - ExecuteScriptAndCheckNavigationDownload( - child, "document.getElementById('navigate-top-frame-to-pdf').click()"); -#endif -} - -// Tests that opening a window with a data URL with PDF mimetype from a -// subframe is blocked even if the top frame is already a data URL. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - PDF_WindowOpenFromFrame_TopFrameIsDataURL_Block) { - const GURL top_url( - base::StringPrintf("data:text/html, ", - embedded_test_server() - ->GetURL("/data_url_navigations.html") - .spec() - .c_str())); - NavigateToURL(shell(), top_url); - -#if !defined(OS_ANDROID) - TestWindowOpenFromFrame("document.getElementById('window-open-pdf').click()", - NAVIGATION_BLOCKED); -#else - // On Android, PDFs are downloaded upon navigation. - RenderFrameHost* child = - ChildFrameAt(shell()->web_contents()->GetMainFrame(), 0); - ASSERT_TRUE(child); - if (AreAllSitesIsolatedForTesting()) { - ASSERT_TRUE(child->IsCrossProcessSubframe()); - } - ExecuteScriptAndCheckNavigationDownload( - child, "document.getElementById('window-open-pdf').click()"); -#endif -} - -// Test case to verify that redirects to data: URLs are properly disallowed, -// even when invoked through history navigations. -// See https://crbug.com/723796. -IN_PROC_BROWSER_TEST_F(DataUrlNavigationBrowserTest, - WindowOpenRedirectAndBack) { - NavigateToURL(shell(), - embedded_test_server()->GetURL("/data_url_navigations.html")); - - // This test will need to navigate the newly opened window. - ShellAddedObserver new_shell_observer; - EXPECT_TRUE( - ExecuteScript(shell()->web_contents(), - "document.getElementById('window-open-redirect').click()")); - Shell* new_shell = new_shell_observer.GetShell(); - NavigationController* controller = - &new_shell->web_contents()->GetController(); - WaitForLoadStop(new_shell->web_contents()); - - // The window.open() should have resulted in an error page. The blocked - // URL should be in both the actual and the virtual URL. - { - EXPECT_EQ(0, controller->GetLastCommittedEntryIndex()); - NavigationEntry* entry = controller->GetLastCommittedEntry(); - EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType()); - EXPECT_FALSE(entry->GetURL().SchemeIs(url::kDataScheme)); - EXPECT_TRUE(base::StartsWith( - entry->GetURL().spec(), - embedded_test_server()->GetURL("/server-redirect?").spec(), - base::CompareCase::SENSITIVE)); - EXPECT_EQ(entry->GetURL(), entry->GetVirtualURL()); - } - - // Navigate forward and then go back to ensure the navigation to data: URL - // is blocked. Use a browser-initiated back navigation, equivalent to user - // pressing the back button. - EXPECT_TRUE( - NavigateToURL(new_shell, embedded_test_server()->GetURL("/title1.html"))); - EXPECT_EQ(1, controller->GetLastCommittedEntryIndex()); - { - TestNavigationObserver observer(new_shell->web_contents()); - controller->GoBack(); - observer.Wait(); - - NavigationEntry* entry = controller->GetLastCommittedEntry(); - EXPECT_EQ(0, controller->GetLastCommittedEntryIndex()); - EXPECT_FALSE(entry->GetURL().SchemeIs(url::kDataScheme)); - EXPECT_TRUE(base::StartsWith( - entry->GetURL().spec(), - embedded_test_server()->GetURL("/server-redirect?").spec(), - base::CompareCase::SENSITIVE)); - EXPECT_EQ(entry->GetURL(), entry->GetVirtualURL()); - } - - // Do another new navigation, but then use JavaScript to navigate back, - // equivalent to document executing JS. - EXPECT_TRUE( - NavigateToURL(new_shell, embedded_test_server()->GetURL("/title1.html"))); - EXPECT_EQ(1, controller->GetLastCommittedEntryIndex()); - { - TestNavigationObserver observer(new_shell->web_contents()); - EXPECT_TRUE(ExecuteScript(new_shell, "history.go(-1)")); - observer.Wait(); - - NavigationEntry* entry = controller->GetLastCommittedEntry(); - EXPECT_EQ(0, controller->GetLastCommittedEntryIndex()); - EXPECT_FALSE(entry->GetURL().SchemeIs(url::kDataScheme)); - EXPECT_TRUE(base::StartsWith( - entry->GetURL().spec(), - embedded_test_server()->GetURL("/server-redirect?").spec(), - base::CompareCase::SENSITIVE)); - EXPECT_EQ(entry->GetURL(), entry->GetVirtualURL()); - } -} - -} // content diff --git a/chromium/content/browser/frame_host/data_url_navigation_throttle.cc b/chromium/content/browser/frame_host/data_url_navigation_throttle.cc deleted file mode 100644 index 2835bda8b7d..00000000000 --- a/chromium/content/browser/frame_host/data_url_navigation_throttle.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/frame_host/data_url_navigation_throttle.h" - -#include "base/feature_list.h" -#include "base/strings/stringprintf.h" -#include "build/build_config.h" -#include "content/browser/frame_host/frame_tree.h" -#include "content/browser/frame_host/frame_tree_node.h" -#include "content/browser/frame_host/navigation_handle_impl.h" -#include "content/public/browser/navigation_handle.h" -#include "content/public/browser/render_frame_host.h" -#include "content/public/common/browser_side_navigation_policy.h" -#include "content/public/common/console_message_level.h" -#include "content/public/common/content_features.h" -#include "url/url_constants.h" - -namespace content { - -namespace { -const char kConsoleError[] = - "Not allowed to navigate top frame to data URL: %s"; -} - -DataUrlNavigationThrottle::DataUrlNavigationThrottle( - NavigationHandle* navigation_handle) - : NavigationThrottle(navigation_handle) {} - -DataUrlNavigationThrottle::~DataUrlNavigationThrottle() {} - -NavigationThrottle::ThrottleCheckResult -DataUrlNavigationThrottle::WillProcessResponse() { - NavigationHandleImpl* handle = - static_cast(navigation_handle()); - if (handle->IsDownload()) - return PROCEED; - - // We treat
as a navigation, but it will always - // result in a download, not a top-level navigation, so not blocking it here. - if (handle->GetSuggestedFilename().has_value()) - return PROCEED; - - RenderFrameHost* top_frame = - handle->frame_tree_node()->frame_tree()->root()->current_frame_host(); - top_frame->AddMessageToConsole( - CONSOLE_MESSAGE_LEVEL_ERROR, - base::StringPrintf(kConsoleError, handle->GetURL().spec().c_str())); - return CANCEL; -} - -const char* DataUrlNavigationThrottle::GetNameForLogging() { - return "DataUrlNavigationThrottle"; -} - -// static -std::unique_ptr -DataUrlNavigationThrottle::CreateThrottleForNavigation( - NavigationHandle* navigation_handle) { - if (navigation_handle->IsInMainFrame() && - navigation_handle->IsRendererInitiated() && - !navigation_handle->IsSameDocument() && - navigation_handle->GetURL().SchemeIs(url::kDataScheme) && - !base::FeatureList::IsEnabled( - features::kAllowContentInitiatedDataUrlNavigations)) { - return std::make_unique(navigation_handle); - } - return nullptr; -} - -} // namespace content diff --git a/chromium/content/browser/frame_host/data_url_navigation_throttle.h b/chromium/content/browser/frame_host/data_url_navigation_throttle.h deleted file mode 100644 index 14a2038e74e..00000000000 --- a/chromium/content/browser/frame_host/data_url_navigation_throttle.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_FRAME_HOST_DATA_URL_NAVIGATION_THROTTLE_ -#define CONTENT_BROWSER_FRAME_HOST_DATA_URL_NAVIGATION_THROTTLE_ - -#include - -#include "base/macros.h" -#include "content/public/browser/navigation_throttle.h" - -namespace content { - -class DataUrlNavigationThrottle : public NavigationThrottle { - public: - explicit DataUrlNavigationThrottle(NavigationHandle* navigation_handle); - ~DataUrlNavigationThrottle() override; - - // NavigationThrottle method: - ThrottleCheckResult WillProcessResponse() override; - const char* GetNameForLogging() override; - - static std::unique_ptr CreateThrottleForNavigation( - NavigationHandle* navigation_handle); - - private: - DISALLOW_COPY_AND_ASSIGN(DataUrlNavigationThrottle); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_FRAME_HOST_DATA_URL_NAVIGATION_THROTTLE_ diff --git a/chromium/content/browser/frame_host/form_submission_throttle.cc b/chromium/content/browser/frame_host/form_submission_throttle.cc index 1201e5d114f..893445d5ebe 100644 --- a/chromium/content/browser/frame_host/form_submission_throttle.cc +++ b/chromium/content/browser/frame_host/form_submission_throttle.cc @@ -82,9 +82,10 @@ FormSubmissionThrottle::CheckContentSecurityPolicyFormAction(bool is_redirect) { // TODO(estark): Move this check into NavigationRequest and split it into (1) // check report-only CSP, (2) upgrade request if needed, (3) check enforced // CSP to match how frame-src works. https://crbug.com/713388 - if (render_frame->IsAllowedByCsp(CSPDirective::FormAction, url, is_redirect, - handle->source_location(), - CSPContext::CHECK_ALL_CSP)) { + if (render_frame->IsAllowedByCsp( + CSPDirective::FormAction, url, is_redirect, + false /* is_response_check */, handle->source_location(), + CSPContext::CHECK_ALL_CSP, true /* is_form_submission */)) { return NavigationThrottle::PROCEED; } diff --git a/chromium/content/browser/frame_host/form_submission_throttle_browsertest.cc b/chromium/content/browser/frame_host/form_submission_throttle_browsertest.cc index 92ef88f5459..96083c75c22 100644 --- a/chromium/content/browser/frame_host/form_submission_throttle_browsertest.cc +++ b/chromium/content/browser/frame_host/form_submission_throttle_browsertest.cc @@ -73,7 +73,6 @@ IN_PROC_BROWSER_TEST_F(FormSubmissionBrowserTest, false, // started_from_context_menu CSPDisposition::CHECK, // should_check_main_world_csp true, // is_form_submission - base::nullopt, // suggested_filename nullptr); // navigation_ui_data // Test the expectations with a FormSubmissionThrottle. @@ -111,7 +110,6 @@ IN_PROC_BROWSER_TEST_F(FormSubmissionBrowserTest, false, // started_from_context_menu CSPDisposition::DO_NOT_CHECK, // should_check_main_world_csp true, // is_form_submission - base::nullopt, // suggested_filename nullptr); // navigation_ui_data // Test that the navigation is allowed because "should_by_pass_main_world_csp" diff --git a/chromium/content/browser/frame_host/frame_navigation_entry.cc b/chromium/content/browser/frame_host/frame_navigation_entry.cc index 7768085a3c9..117f24c9b57 100644 --- a/chromium/content/browser/frame_host/frame_navigation_entry.cc +++ b/chromium/content/browser/frame_host/frame_navigation_entry.cc @@ -25,7 +25,8 @@ FrameNavigationEntry::FrameNavigationEntry( const std::vector& redirect_chain, const PageState& page_state, const std::string& method, - int64_t post_id) + int64_t post_id, + scoped_refptr blob_url_loader_factory) : frame_unique_name_(frame_unique_name), item_sequence_number_(item_sequence_number), document_sequence_number_(document_sequence_number), @@ -36,7 +37,8 @@ FrameNavigationEntry::FrameNavigationEntry( redirect_chain_(redirect_chain), page_state_(page_state), method_(method), - post_id_(post_id) {} + post_id_(post_id), + blob_url_loader_factory_(std::move(blob_url_loader_factory)) {} FrameNavigationEntry::~FrameNavigationEntry() { } @@ -48,7 +50,7 @@ FrameNavigationEntry* FrameNavigationEntry::Clone() const { copy->UpdateEntry(frame_unique_name_, item_sequence_number_, document_sequence_number_, site_instance_.get(), nullptr, url_, referrer_, redirect_chain_, page_state_, method_, - post_id_); + post_id_, nullptr /* blob_url_loader_factory */); return copy; } @@ -63,7 +65,8 @@ void FrameNavigationEntry::UpdateEntry( const std::vector& redirect_chain, const PageState& page_state, const std::string& method, - int64_t post_id) { + int64_t post_id, + scoped_refptr blob_url_loader_factory) { frame_unique_name_ = frame_unique_name; item_sequence_number_ = item_sequence_number; document_sequence_number_ = document_sequence_number; @@ -75,6 +78,7 @@ void FrameNavigationEntry::UpdateEntry( page_state_ = page_state; method_ = method; post_id_ = post_id; + blob_url_loader_factory_ = std::move(blob_url_loader_factory); } void FrameNavigationEntry::set_item_sequence_number( diff --git a/chromium/content/browser/frame_host/frame_navigation_entry.h b/chromium/content/browser/frame_host/frame_navigation_entry.h index 64e2033dca1..50076c1af7e 100644 --- a/chromium/content/browser/frame_host/frame_navigation_entry.h +++ b/chromium/content/browser/frame_host/frame_navigation_entry.h @@ -13,6 +13,7 @@ #include "content/public/common/page_state.h" #include "content/public/common/referrer.h" #include "services/network/public/cpp/resource_request_body.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" namespace content { @@ -31,34 +32,38 @@ class CONTENT_EXPORT FrameNavigationEntry : public base::RefCounted { public: FrameNavigationEntry(); - FrameNavigationEntry(const std::string& frame_unique_name, - int64_t item_sequence_number, - int64_t document_sequence_number, - scoped_refptr site_instance, - scoped_refptr source_site_instance, - const GURL& url, - const Referrer& referrer, - const std::vector& redirect_chain, - const PageState& page_state, - const std::string& method, - int64_t post_id); + FrameNavigationEntry( + const std::string& frame_unique_name, + int64_t item_sequence_number, + int64_t document_sequence_number, + scoped_refptr site_instance, + scoped_refptr source_site_instance, + const GURL& url, + const Referrer& referrer, + const std::vector& redirect_chain, + const PageState& page_state, + const std::string& method, + int64_t post_id, + scoped_refptr blob_url_loader_factory); // Creates a copy of this FrameNavigationEntry that can be modified // independently from the original. FrameNavigationEntry* Clone() const; // Updates all the members of this entry. - void UpdateEntry(const std::string& frame_unique_name, - int64_t item_sequence_number, - int64_t document_sequence_number, - SiteInstanceImpl* site_instance, - scoped_refptr source_site_instance, - const GURL& url, - const Referrer& referrer, - const std::vector& redirect_chain, - const PageState& page_state, - const std::string& method, - int64_t post_id); + void UpdateEntry( + const std::string& frame_unique_name, + int64_t item_sequence_number, + int64_t document_sequence_number, + SiteInstanceImpl* site_instance, + scoped_refptr source_site_instance, + const GURL& url, + const Referrer& referrer, + const std::vector& redirect_chain, + const PageState& page_state, + const std::string& method, + int64_t post_id, + scoped_refptr blob_url_loader_factory); // The unique name of the frame this entry is for. This is a stable name for // the frame based on its position in the tree and relation to other named @@ -137,6 +142,16 @@ class CONTENT_EXPORT FrameNavigationEntry scoped_refptr GetPostData( std::string* content_type) const; + // Optional URLLoaderFactory to facilitate blob URL loading. + scoped_refptr blob_url_loader_factory() + const { + return blob_url_loader_factory_; + } + void set_blob_url_loader_factory( + scoped_refptr factory) { + blob_url_loader_factory_ = std::move(factory); + } + private: friend class base::RefCounted; virtual ~FrameNavigationEntry(); @@ -165,6 +180,7 @@ class CONTENT_EXPORT FrameNavigationEntry PageState page_state_; std::string method_; int64_t post_id_; + scoped_refptr blob_url_loader_factory_; DISALLOW_COPY_AND_ASSIGN(FrameNavigationEntry); }; diff --git a/chromium/content/browser/frame_host/frame_tree_node.cc b/chromium/content/browser/frame_host/frame_tree_node.cc index fc076ea5376..a99f5ab0ee5 100644 --- a/chromium/content/browser/frame_host/frame_tree_node.cc +++ b/chromium/content/browser/frame_host/frame_tree_node.cc @@ -42,46 +42,6 @@ base::LazyInstance::DestructorAtExit const double kLoadingProgressMinimum = 0.1; const double kLoadingProgressDone = 1.0; -void RecordUniqueNameSize(FrameTreeNode* node) { - const auto& unique_name = node->current_replication_state().unique_name; - - // Don't record numbers for the root node, which always has an empty unique - // name. - if (!node->parent()) { - DCHECK(unique_name.empty()); - return; - } - - // The original requested name is derived from the browsing context name and - // is essentially unbounded in size... - UMA_HISTOGRAM_COUNTS_1M( - "SessionRestore.FrameUniqueNameOriginalRequestedNameSize", - node->current_replication_state().name.size()); - // If the name is a frame path, attempt to normalize the statistics based on - // the number of frames in the frame path. - if (base::StartsWith(unique_name, " + + + + + + + + + 📊 About Histograms + +

Stats accumulated from browser startup to previous page load; reload to get stats as of this page load. +

+ +
+ diff --git a/chromium/content/browser/resources/histograms/histograms_internals.js b/chromium/content/browser/resources/histograms/histograms_internals.js new file mode 100644 index 00000000000..428fc36261f --- /dev/null +++ b/chromium/content/browser/resources/histograms/histograms_internals.js @@ -0,0 +1,37 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * Initiates the request for histograms. + */ +function requestHistograms() { + let query = ''; + if (document.location.pathname) + query = document.location.pathname.substring(1); + cr.sendWithPromise('requestHistograms', query).then(addHistograms); +} + +/** + * Callback from backend with the list of histograms. Builds the UI. + * @param {!Array} histograms A list of trusted HTML strings + * representing histograms. + */ +function addHistograms(histograms) { + let htmlOutput = ''; + for (let histogram of histograms) + htmlOutput += histogram; + + // NOTE: This is generally unsafe due to XSS attacks. Make sure |htmlOutput| + // cannot be modified by an external party. + $('histograms').innerHTML = htmlOutput; +} + +/** + * Load the initial list of histograms. + */ +document.addEventListener('DOMContentLoaded', function() { + $('refresh').onclick = requestHistograms; + + requestHistograms(); +}); diff --git a/chromium/content/browser/resources/media/client_renderer.js b/chromium/content/browser/resources/media/client_renderer.js index acc8ef28a4d..0b5413e8ae7 100644 --- a/chromium/content/browser/resources/media/client_renderer.js +++ b/chromium/content/browser/resources/media/client_renderer.js @@ -67,7 +67,7 @@ var ClientRenderer = (function() { while (element.hasChildNodes()) { element.removeChild(element.lastChild); } - }; + } function createSelectableButton(id, groupName, buttonLabel, select_cb, isDestructed) { @@ -94,7 +94,7 @@ var ClientRenderer = (function() { }); return fragment; - }; + } function selectSelectableButton(id) { var element = document.getElementById(id); @@ -208,7 +208,7 @@ var ClientRenderer = (function() { table.appendChild(thead); var tbody = document.createElement('tbody'); for (var i=0; i < formats.length; ++i) { - var tr = document.createElement('tr') + var tr = document.createElement('tr'); for (var key in formats[i]) { var td = document.createElement('td'); td.appendChild(document.createTextNode(formats[i][key])); @@ -245,7 +245,7 @@ var ClientRenderer = (function() { cellElement = document.createTextNode( ((typeof value) == 'undefined') ? 'n/a' : value); } - tableCell.appendChild(cellElement) + tableCell.appendChild(cellElement); tableRow.appendChild(tableCell); } videoTableBodyElement.appendChild(tableRow); @@ -263,7 +263,7 @@ var ClientRenderer = (function() { baseName = 'Stream'; break; default: - baseName = 'UnknownType' + baseName = 'UnknownType'; console.error('Unrecognized component type: ' + componentType); break; } @@ -377,7 +377,7 @@ var ClientRenderer = (function() { label.appendChild(frame_node); } - var desc = [] + var desc = []; if (p.width && p.height) desc.push(p.width + 'x' + p.height); if (p.video_codec_name) @@ -468,7 +468,7 @@ var ClientRenderer = (function() { }, saveLog_: function() { - var strippedPlayers = [] + var strippedPlayers = []; for (var id in this.players) { var p = this.players[id]; strippedPlayers.push({properties: p.properties, events: p.allEvents}); diff --git a/chromium/content/browser/resources/media/main.js b/chromium/content/browser/resources/media/main.js index e6c9d8ccf14..add25a1fc38 100644 --- a/chromium/content/browser/resources/media/main.js +++ b/chromium/content/browser/resources/media/main.js @@ -24,8 +24,8 @@ var media = (function() { }; media.onReceiveVideoCaptureCapabilities = function(videoCaptureCapabilities) { - manager.updateVideoCaptureCapabilities(videoCaptureCapabilities) - } + manager.updateVideoCaptureCapabilities(videoCaptureCapabilities); + }; media.updateAudioComponent = function(component) { var uniqueComponentId = component.owner_id + ':' + component.component_id; diff --git a/chromium/content/browser/resources/media/peer_connection_update_table.js b/chromium/content/browser/resources/media/peer_connection_update_table.js index 4dcc476e90b..09998a6bf7d 100644 --- a/chromium/content/browser/resources/media/peer_connection_update_table.js +++ b/chromium/content/browser/resources/media/peer_connection_update_table.js @@ -128,7 +128,7 @@ var PeerConnectionUpdateTable = (function() { 'update-log-legacy-api-usage'); valueContainer.parentElement.title = update.type + ' is no longer ' + 'part of the WebRTC API and may be removed in future versions. ' + - 'Use the addTrack/removeTrack APIs instead.' + 'Use the addTrack/removeTrack APIs instead.'; } var value = update.value; diff --git a/chromium/content/browser/resources/media/ssrc_info_manager.js b/chromium/content/browser/resources/media/ssrc_info_manager.js index 6db5687c76a..2b47c8afe73 100644 --- a/chromium/content/browser/resources/media/ssrc_info_manager.js +++ b/chromium/content/browser/resources/media/ssrc_info_manager.js @@ -34,7 +34,7 @@ function GetSsrcFromReport(report) { } } return report.id; -}; +} /** * SsrcInfoManager stores the ssrc stream info extracted from SDP. diff --git a/chromium/content/browser/resources/media/stats_table.js b/chromium/content/browser/resources/media/stats_table.js index 53e71a209fa..8ad8f54d631 100644 --- a/chromium/content/browser/resources/media/stats_table.js +++ b/chromium/content/browser/resources/media/stats_table.js @@ -133,6 +133,7 @@ var StatsTable = (function(ssrcInfoManager) { updateStatsTableRow_: function(statsTable, rowName, value) { var trId = statsTable.id + '-' + rowName; var trElement = $(trId); + var activeConnectionClass = 'stats-table-active-connection'; if (!trElement) { trElement = document.createElement('tr'); trElement.id = trId; @@ -142,8 +143,13 @@ var StatsTable = (function(ssrcInfoManager) { 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'); + if (rowName == 'googActiveConnection') { + if (value === true) { + statsTable.parentElement.classList.add(activeConnectionClass); + } else { + statsTable.parentElement.classList.remove(activeConnectionClass); + } + } } }; diff --git a/chromium/content/browser/resources/media/timeline_graph_view.js b/chromium/content/browser/resources/media/timeline_graph_view.js index d2b12329da2..157ad392ebc 100644 --- a/chromium/content/browser/resources/media/timeline_graph_view.js +++ b/chromium/content/browser/resources/media/timeline_graph_view.js @@ -149,9 +149,13 @@ var TimelineGraphView = (function() { }, /** - * Draws the graph on |canvas_|. + * Draws the graph on |canvas_| when visible. */ repaint: function() { + if (this.canvas_.offsetParent === null) { + return; // do not repaint graphs that are not visible. + } + this.repaintTimerRunning_ = false; var width = this.canvas_.width; diff --git a/chromium/content/browser/resources/media/webrtc_internals.js b/chromium/content/browser/resources/media/webrtc_internals.js index 4f5fc0d971a..f41067a12de 100644 --- a/chromium/content/browser/resources/media/webrtc_internals.js +++ b/chromium/content/browser/resources/media/webrtc_internals.js @@ -26,7 +26,7 @@ var PeerConnectionRecord = (function() { updateLog: [], url: '', }; - }; + } PeerConnectionRecord.prototype = { /** @override */ diff --git a/chromium/content/browser/resources/process/process_internals.html b/chromium/content/browser/resources/process/process_internals.html new file mode 100644 index 00000000000..9e5acb2d80a --- /dev/null +++ b/chromium/content/browser/resources/process/process_internals.html @@ -0,0 +1,16 @@ + + + + + + + + + + Process Model Internals + +
Site Isolation mode: unknown
+
Isolated origins:
+ diff --git a/chromium/content/browser/resources/process/process_internals.js b/chromium/content/browser/resources/process/process_internals.js new file mode 100644 index 00000000000..a745e29b6ec --- /dev/null +++ b/chromium/content/browser/resources/process/process_internals.js @@ -0,0 +1,33 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +console.log('process internals initializing'); + +(function() { +'use strict'; + +/** + * Reference to the backend. + * @type {mojom.ProcessInternalsHandlerPtr} + */ +var uiHandler = null; + +document.addEventListener('DOMContentLoaded', function() { + // Setup Mojo interface to the backend. + uiHandler = new mojom.ProcessInternalsHandlerPtr; + Mojo.bindInterface( + mojom.ProcessInternalsHandler.name, + mojo.makeRequest(uiHandler).handle); + + // Get the Site Isolation mode and populate it. + uiHandler.getIsolationMode().then((response) => { + document.getElementById('isolation-mode').innerText = response.mode; + }); + uiHandler.getIsolatedOrigins().then((response) => { + document.getElementById('isolated-origins').innerText = + response.isolatedOrigins.join(', '); + }); +}); + +})(); diff --git a/chromium/content/browser/resources/service_worker/serviceworker_internals.js b/chromium/content/browser/resources/service_worker/serviceworker_internals.js index 087c5a65cf7..810676ab9d1 100644 --- a/chromium/content/browser/resources/service_worker/serviceworker_internals.js +++ b/chromium/content/browser/resources/service_worker/serviceworker_internals.js @@ -220,7 +220,7 @@ cr.define('serviceworker', function() { update(); } - function onRegistrationStored(scope) { + function onRegistrationCompleted(scope) { update(); } @@ -258,7 +258,7 @@ cr.define('serviceworker', function() { onErrorReported: onErrorReported, onConsoleMessageReported: onConsoleMessageReported, onVersionStateChanged: onVersionStateChanged, - onRegistrationStored: onRegistrationStored, + onRegistrationCompleted: onRegistrationCompleted, onRegistrationDeleted: onRegistrationDeleted, }; }); diff --git a/chromium/content/browser/sandbox_parameters_mac.mm b/chromium/content/browser/sandbox_parameters_mac.mm index a87788c2820..e9415b0de5f 100644 --- a/chromium/content/browser/sandbox_parameters_mac.mm +++ b/chromium/content/browser/sandbox_parameters_mac.mm @@ -22,6 +22,7 @@ #include "content/public/common/pepper_plugin_info.h" #include "sandbox/mac/seatbelt_exec.h" #include "services/service_manager/sandbox/mac/sandbox_mac.h" +#include "services/service_manager/sandbox/switches.h" namespace content { @@ -48,7 +49,7 @@ void SetupCommonSandboxParameters(sandbox::SeatbeltExecClient* client) { const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); bool enable_logging = - command_line->HasSwitch(switches::kEnableSandboxLogging); + command_line->HasSwitch(service_manager::switches::kEnableSandboxLogging); CHECK(client->SetBooleanParameter( service_manager::SandboxMac::kSandboxEnableLogging, enable_logging)); diff --git a/chromium/content/browser/screen_orientation/screen_orientation_delegate_android.h b/chromium/content/browser/screen_orientation/screen_orientation_delegate_android.h index bf9d91c31b5..120700af9fc 100644 --- a/chromium/content/browser/screen_orientation/screen_orientation_delegate_android.h +++ b/chromium/content/browser/screen_orientation/screen_orientation_delegate_android.h @@ -9,7 +9,7 @@ #include "base/macros.h" #include "content/public/browser/screen_orientation_delegate.h" -#include "third_party/blink/public/platform/modules/screen_orientation/web_screen_orientation_lock_type.h" +#include "third_party/blink/public/common/screen_orientation/web_screen_orientation_lock_type.h" namespace content { diff --git a/chromium/content/browser/screen_orientation/screen_orientation_provider.cc b/chromium/content/browser/screen_orientation/screen_orientation_provider.cc index 5d96c58eddc..8f1a0a31f05 100644 --- a/chromium/content/browser/screen_orientation/screen_orientation_provider.cc +++ b/chromium/content/browser/screen_orientation/screen_orientation_provider.cc @@ -13,7 +13,6 @@ #include "content/public/browser/render_widget_host.h" #include "content/public/browser/screen_orientation_delegate.h" #include "content/public/browser/web_contents.h" -#include "third_party/blink/public/platform/modules/screen_orientation/web_lock_orientation_error.h" namespace content { diff --git a/chromium/content/browser/screen_orientation/screen_orientation_provider.h b/chromium/content/browser/screen_orientation/screen_orientation_provider.h index 6ff35095bc1..9d16e0fef42 100644 --- a/chromium/content/browser/screen_orientation/screen_orientation_provider.h +++ b/chromium/content/browser/screen_orientation/screen_orientation_provider.h @@ -14,7 +14,7 @@ #include "content/public/browser/web_contents_observer.h" #include "services/device/public/mojom/screen_orientation.mojom.h" #include "services/device/public/mojom/screen_orientation_lock_types.mojom.h" -#include "third_party/blink/public/platform/modules/screen_orientation/web_screen_orientation_lock_type.h" +#include "third_party/blink/public/common/screen_orientation/web_screen_orientation_lock_type.h" namespace content { diff --git a/chromium/content/browser/screen_orientation/screen_orientation_provider_unittest.cc b/chromium/content/browser/screen_orientation/screen_orientation_provider_unittest.cc index 97e3b3ecf6a..64c4cacf214 100644 --- a/chromium/content/browser/screen_orientation/screen_orientation_provider_unittest.cc +++ b/chromium/content/browser/screen_orientation/screen_orientation_provider_unittest.cc @@ -11,7 +11,7 @@ #include "content/public/browser/web_contents_delegate.h" #include "content/test/test_render_view_host.h" #include "content/test/test_web_contents.h" -#include "third_party/blink/public/platform/modules/screen_orientation/web_screen_orientation_lock_type.h" +#include "third_party/blink/public/common/screen_orientation/web_screen_orientation_lock_type.h" namespace content { @@ -59,8 +59,10 @@ class FakeWebContentsDelegate : public WebContentsDelegate { FakeWebContentsDelegate() = default; ~FakeWebContentsDelegate() override = default; - void EnterFullscreenModeForTab(WebContents* web_contents, - const GURL& origin) override { + void EnterFullscreenModeForTab( + WebContents* web_contents, + const GURL& origin, + const blink::WebFullscreenOptions& options) override { fullscreened_contents_ = web_contents; } @@ -179,8 +181,8 @@ TEST_F(ScreenOrientationProviderTest, DelegateRequireFullScreenLockOnce) { EXPECT_EQ(0, delegate.lock_count()); // Simulates entering full screen. - main_test_rfh()->OnMessageReceived( - FrameHostMsg_ToggleFullscreen(main_test_rfh()->GetRoutingID(), true)); + main_test_rfh()->OnMessageReceived(FrameHostMsg_EnterFullscreen( + main_test_rfh()->GetRoutingID(), blink::WebFullscreenOptions())); ASSERT_TRUE(contents()->IsFullscreenForCurrentTab()); base::Optional result_2; @@ -282,8 +284,8 @@ TEST_F(ScreenOrientationProviderTest, UnlockWhenExitingFullScreen) { std::string()); // Simulates entering full screen. - main_test_rfh()->OnMessageReceived( - FrameHostMsg_ToggleFullscreen(main_test_rfh()->GetRoutingID(), true)); + main_test_rfh()->OnMessageReceived(FrameHostMsg_EnterFullscreen( + main_test_rfh()->GetRoutingID(), blink::WebFullscreenOptions())); ASSERT_TRUE(contents()->IsFullscreenForCurrentTab()); base::Optional result; @@ -298,7 +300,7 @@ TEST_F(ScreenOrientationProviderTest, UnlockWhenExitingFullScreen) { // Simulates exiting full screen. main_test_rfh()->OnMessageReceived( - FrameHostMsg_ToggleFullscreen(main_test_rfh()->GetRoutingID(), false)); + FrameHostMsg_ExitFullscreen(main_test_rfh()->GetRoutingID())); ASSERT_FALSE(contents()->IsFullscreenForCurrentTab()); // The pending lock request is cancelled. EXPECT_EQ(ScreenOrientationLockResult:: diff --git a/chromium/content/browser/security_exploit_browsertest.cc b/chromium/content/browser/security_exploit_browsertest.cc index 637fae1bf91..9c298b18244 100644 --- a/chromium/content/browser/security_exploit_browsertest.cc +++ b/chromium/content/browser/security_exploit_browsertest.cc @@ -104,7 +104,7 @@ RenderFrameHostImpl* PrepareToDuplicateHosts(Shell* shell, wc->GetFrameTree()->root()->current_frame_host(), extension_url, false, nullptr, std::string(), Referrer(), WindowOpenDisposition::CURRENT_TAB, false, true, blink::WebTriggeringEventInfo::kFromTrustedEvent, - base::nullopt); + nullptr /* blob_url_loader_factory */); // Since the navigation above requires a cross-process swap, there will be a // speculative/pending RenderFrameHost. Ensure it exists and is in a different @@ -211,7 +211,8 @@ class SecurityExploitBrowserTest : public ContentBrowserTest { void TryCreateDuplicateRequestIds(Shell* shell, bool block_loaders) { NavigateToURL(shell, GURL("http://foo.com/simple_page.html")); - RenderFrameHost* rfh = shell->web_contents()->GetMainFrame(); + RenderFrameHostImpl* rfh = static_cast( + shell->web_contents()->GetMainFrame()); if (block_loaders) { // Test the case where loaders are placed into blocked_loaders_map_. diff --git a/chromium/content/browser/service_manager/common_browser_interfaces.cc b/chromium/content/browser/service_manager/common_browser_interfaces.cc index 55e52930552..af1a24e735b 100644 --- a/chromium/content/browser/service_manager/common_browser_interfaces.cc +++ b/chromium/content/browser/service_manager/common_browser_interfaces.cc @@ -44,7 +44,7 @@ class ConnectionFilterImpl : public ConnectionFilter { #elif defined(OS_MACOSX) registry_.AddInterface(base::BindRepeating(&FontLoaderDispatcher::Create)); #endif - if (!features::IsMusEnabled()) { + if (!features::IsMashEnabled()) { // For mus, the mojom::discardable_memory::DiscardableSharedMemoryManager // is exposed from ui::Service. So we don't need bind the interface here. auto* browser_main_loop = BrowserMainLoop::GetInstance(); diff --git a/chromium/content/browser/service_manager/service_manager_context.cc b/chromium/content/browser/service_manager/service_manager_context.cc index cc276364172..6df4527d9a4 100644 --- a/chromium/content/browser/service_manager/service_manager_context.cc +++ b/chromium/content/browser/service_manager/service_manager_context.cc @@ -91,14 +91,6 @@ #include "ui/aura/env.h" #endif -#if BUILDFLAG(ENABLE_MUS) -#include "components/discardable_memory/service/discardable_shared_memory_manager.h" -#include "content/public/browser/discardable_shared_memory_manager.h" -#include "services/ui/common/image_cursors_set.h" -#include "services/ui/public/interfaces/constants.mojom.h" -#include "services/ui/service.h" -#endif - namespace content { namespace { @@ -263,6 +255,25 @@ class NullServiceProcessLauncherFactory DISALLOW_COPY_AND_ASSIGN(NullServiceProcessLauncherFactory); }; +// This class is intended for tests that want to load service binaries (rather +// than via the utility process). Production code uses +// NullServiceProcessLauncherFactory. +class ServiceBinaryLauncherFactory + : public service_manager::ServiceProcessLauncherFactory { + public: + ServiceBinaryLauncherFactory() = default; + ~ServiceBinaryLauncherFactory() override = default; + + private: + std::unique_ptr Create( + const base::FilePath& service_path) override { + return std::make_unique( + nullptr, service_path); + } + + DISALLOW_COPY_AND_ASSIGN(ServiceBinaryLauncherFactory); +}; + // Helper to invoke GetGeolocationRequestContext on the currently-set // ContentBrowserClient. void GetGeolocationRequestContextFromContentClient( @@ -282,44 +293,6 @@ bool ShouldEnableVizService() { #endif } -#if BUILDFLAG(ENABLE_MUS) -std::unique_ptr CreateEmbeddedUIService( - const scoped_refptr& task_runner, - base::WeakPtr image_cursors_set_weak_ptr, - discardable_memory::DiscardableSharedMemoryManager* memory_manager) { - ui::Service::InitParams params; - params.running_standalone = false; - params.resource_runner = task_runner; - params.image_cursors_set_weak_ptr = image_cursors_set_weak_ptr; - params.memory_manager = memory_manager; - params.should_host_viz = base::FeatureList::IsEnabled(features::kMash); - return std::make_unique(params); -} - -void RegisterUIServiceInProcessIfNecessary( - ServiceManagerConnection* connection) { - // Some tests don't create BrowserMainLoop. - if (!BrowserMainLoop::GetInstance()) - return; - // Do not embed the UI service when running in mash. - if (base::FeatureList::IsEnabled(features::kMash)) - return; - // Do not embed the UI service if not running with mus. - if (!features::IsMusEnabled()) - return; - - service_manager::EmbeddedServiceInfo info; - info.factory = base::Bind( - &CreateEmbeddedUIService, base::ThreadTaskRunnerHandle::Get(), - BrowserMainLoop::GetInstance()->image_cursors_set()->GetWeakPtr(), - GetDiscardableSharedMemoryManager()); - info.use_own_thread = true; - info.message_loop_type = base::MessageLoop::TYPE_UI; - info.thread_priority = base::ThreadPriority::DISPLAY; - connection->AddEmbeddedService(ui::mojom::kServiceName, info); -} -#endif - std::unique_ptr CreateNetworkService() { // The test interface doesn't need to be implemented in the in-process case. auto registry = std::make_unique(); @@ -364,8 +337,18 @@ class ServiceManagerContext::InProcessServiceManagerContext std::unique_ptr manifest_provider, service_manager::mojom::ServicePtrInfo packaged_services_service_info) { manifest_provider_ = std::move(manifest_provider); + std::unique_ptr + service_process_launcher_factory; + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableServiceBinaryLauncher)) { + service_process_launcher_factory = + std::make_unique(); + } else { + service_process_launcher_factory = + std::make_unique(); + } service_manager_ = std::make_unique( - std::make_unique(), nullptr, + std::move(service_process_launcher_factory), nullptr, manifest_provider_.get()); service_manager::mojom::ServicePtr packaged_services_service; @@ -541,29 +524,14 @@ ServiceManagerContext::ServiceManagerContext() { metrics::mojom::kMetricsServiceName, info); } - if (BrowserMainLoop* bml = BrowserMainLoop::GetInstance()) { - service_manager::EmbeddedServiceInfo info; - info.factory = base::BindRepeating( - [](BrowserMainLoop* bml) -> std::unique_ptr { - return audio::CreateEmbeddedService(bml->audio_manager()); - }, - bml); - info.task_runner = bml->audio_service_runner(); - packaged_services_connection_->AddEmbeddedService( - audio::mojom::kServiceName, info); - } - ContentBrowserClient::StaticServiceMap services; - GetContentClient()->browser()->RegisterInProcessServices(&services); + GetContentClient()->browser()->RegisterInProcessServices( + &services, packaged_services_connection_.get()); for (const auto& entry : services) { packaged_services_connection_->AddEmbeddedService(entry.first, entry.second); } -#if BUILDFLAG(ENABLE_MUS) - RegisterUIServiceInProcessIfNecessary(packaged_services_connection_.get()); -#endif - // This is safe to assign directly from any thread, because // ServiceManagerContext must be constructed before anyone can call // GetConnectorForIOThread(). @@ -600,6 +568,26 @@ ServiceManagerContext::ServiceManagerContext() { GetNetworkService(); } + if (BrowserMainLoop* bml = BrowserMainLoop::GetInstance()) { + if (bml->AudioServiceOutOfProcess()) { + DCHECK(base::FeatureList::IsEnabled(features::kAudioServiceAudioStreams)); + out_of_process_services[audio::mojom::kServiceName] = + base::ASCIIToUTF16("Audio Service"); + } else { + service_manager::EmbeddedServiceInfo info; + info.factory = base::BindRepeating( + [](BrowserMainLoop* bml) + -> std::unique_ptr { + return audio::CreateEmbeddedService(bml->audio_manager()); + }, + bml); + info.task_runner = bml->audio_service_runner(); + DCHECK(info.task_runner); + packaged_services_connection_->AddEmbeddedService( + audio::mojom::kServiceName, info); + } + } + if (features::IsVideoCaptureServiceEnabledForOutOfProcess()) { out_of_process_services[video_capture::mojom::kServiceName] = base::ASCIIToUTF16("Video Capture Service"); diff --git a/chromium/content/browser/service_worker/OWNERS b/chromium/content/browser/service_worker/OWNERS index c19a2565fc3..a0d918100a0 100644 --- a/chromium/content/browser/service_worker/OWNERS +++ b/chromium/content/browser/service_worker/OWNERS @@ -15,7 +15,6 @@ falken@chromium.org horo@chromium.org kinuko@chromium.org mek@chromium.org -michaeln@chromium.org nhiroki@chromium.org shimazu@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 19d8459d972..eef43481a14 100644 --- a/chromium/content/browser/service_worker/embedded_worker_instance.cc +++ b/chromium/content/browser/service_worker/embedded_worker_instance.cc @@ -7,6 +7,7 @@ #include #include "base/bind_helpers.h" +#include "base/feature_list.h" #include "base/macros.h" #include "base/metrics/histogram_macros.h" #include "base/trace_event/trace_event.h" @@ -16,10 +17,13 @@ #include "content/browser/service_worker/embedded_worker_status.h" #include "content/browser/service_worker/service_worker_content_settings_proxy_impl.h" #include "content/browser/service_worker/service_worker_context_core.h" +#include "content/browser/url_loader_factory_getter.h" #include "content/common/content_switches_internal.h" #include "content/common/renderer.mojom.h" #include "content/common/service_worker/service_worker_types.h" #include "content/common/service_worker/service_worker_utils.h" +#include "content/common/url_loader_factory_bundle.mojom.h" +#include "content/common/url_schemes.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_process_host.h" @@ -27,6 +31,8 @@ #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" #include "ipc/ipc_message.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" #include "third_party/blink/public/web/web_console_message.h" #include "url/gurl.h" @@ -76,30 +82,34 @@ using SetupProcessCallback = base::OnceCallback, - std::unique_ptr devtools_proxy)>; + std::unique_ptr, + std::unique_ptr, + blink::mojom::CacheStoragePtrInfo)>; // Allocates a renderer process for starting a worker and does setup like // registering with DevTools. Called on the UI thread. Calls |callback| on the // IO thread. |context| and |weak_context| are only for passing to DevTools and // must not be dereferenced here on the UI thread. -void SetupOnUIThread( - base::WeakPtr process_manager, - bool can_use_existing_process, - mojom::EmbeddedWorkerStartParamsPtr params, - mojom::EmbeddedWorkerInstanceClientAssociatedRequest request, - ServiceWorkerContextCore* context, - base::WeakPtr weak_context, - SetupProcessCallback callback) { +void SetupOnUIThread(base::WeakPtr process_manager, + bool can_use_existing_process, + mojom::EmbeddedWorkerStartParamsPtr params, + mojom::EmbeddedWorkerInstanceClientRequest request, + ServiceWorkerContextCore* context, + base::WeakPtr weak_context, + SetupProcessCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); auto process_info = std::make_unique(); std::unique_ptr devtools_proxy; + std::unique_ptr factory_bundle; + if (!process_manager) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce(std::move(callback), SERVICE_WORKER_ERROR_ABORT, std::move(params), std::move(process_info), - std::move(devtools_proxy))); + std::move(devtools_proxy), std::move(factory_bundle), + nullptr /* cache_storage */)); return; } @@ -111,7 +121,8 @@ void SetupOnUIThread( BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce(std::move(callback), status, std::move(params), - std::move(process_info), std::move(devtools_proxy))); + std::move(process_info), std::move(devtools_proxy), + std::move(factory_bundle), nullptr /* cache_storage */)); return; } const int process_id = process_info->process_id; @@ -120,6 +131,19 @@ void SetupOnUIThread( // crash reports agree. Consider also checking for rph->HasConnection(). CHECK(rph); + // Create cache storage now as an optimization, so the service worker can use + // the Cache Storage API immediately on startup. Don't do this when + // byte-to-byte check will be performed on the worker (|pause_after_download|) + // as most of those workers will have byte-to-byte equality and abort instead + // of running. + blink::mojom::CacheStoragePtr cache_storage; + if (base::FeatureList::IsEnabled( + blink::features::kEagerCacheStorageSetupForServiceWorkers) && + !params->pause_after_download) { + rph->BindCacheStorage(mojo::MakeRequest(&cache_storage), + url::Origin::Create(params->script_url)); + } + // Bind |request|, which is attached to |EmbeddedWorkerInstance::client_|, to // the process. If the process dies, |client_|'s connection error callback // will be called on the IO thread. @@ -128,6 +152,37 @@ void SetupOnUIThread( std::move(request)); } + // S13nServiceWorker: + // Create the loader factories for non-http(s) URLs, for example + // chrome-extension:// URLs. For performance, only do this step when the main + // script URL is non-http(s). We assume an http(s) service worker cannot + // importScripts a non-http(s) URL. + if (ServiceWorkerUtils::IsServicificationEnabled() && + !params->script_url.SchemeIsHTTPOrHTTPS()) { + ContentBrowserClient::NonNetworkURLLoaderFactoryMap factories; + GetContentClient() + ->browser() + ->RegisterNonNetworkSubresourceURLLoaderFactories( + rph->GetID(), MSG_ROUTING_NONE, &factories); + + factory_bundle = std::make_unique(); + for (auto& pair : factories) { + const std::string& scheme = pair.first; + std::unique_ptr factory = + std::move(pair.second); + + // To be safe, ignore schemes that aren't allowed to register service + // workers. We assume that importScripts should fail on such schemes. + if (!base::ContainsValue(GetServiceWorkerSchemes(), scheme)) + continue; + network::mojom::URLLoaderFactoryPtr factory_ptr; + mojo::MakeStrongBinding(std::move(factory), + mojo::MakeRequest(&factory_ptr)); + factory_bundle->factories_info().emplace(scheme, + factory_ptr.PassInterface()); + } + } + // Register to DevTools and update params accordingly. // TODO(dgozman): we can now remove this routing id and use something else // as id when talking to ServiceWorkerDevToolsManager. @@ -151,11 +206,12 @@ void SetupOnUIThread( GetContentClient()->browser()->IsDataSaverEnabled( process_manager->browser_context()); - // Continue on the IO thread. + // Continue to OnSetupCompleted on the IO thread. BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce(std::move(callback), status, std::move(params), - std::move(process_info), std::move(devtools_proxy))); + std::move(process_info), std::move(devtools_proxy), + std::move(factory_bundle), cache_storage.PassInterface())); } bool HasSentStartWorker(EmbeddedWorkerInstance::StartingPhase phase) { @@ -238,10 +294,11 @@ class EmbeddedWorkerInstance::DevToolsProxy { // UI thread. Lives on the IO thread. class EmbeddedWorkerInstance::WorkerProcessHandle { public: - WorkerProcessHandle(const base::WeakPtr& context, - int embedded_worker_id, - int process_id) - : context_(context), + WorkerProcessHandle( + const base::WeakPtr& process_manager, + int embedded_worker_id, + int process_id) + : process_manager_(process_manager), embedded_worker_id_(embedded_worker_id), process_id_(process_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -250,19 +307,17 @@ class EmbeddedWorkerInstance::WorkerProcessHandle { ~WorkerProcessHandle() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (!context_) - return; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::BindOnce(&ServiceWorkerProcessManager::ReleaseWorkerProcess, - context_->process_manager()->AsWeakPtr(), - embedded_worker_id_)); + process_manager_, embedded_worker_id_)); } int process_id() const { return process_id_; } private: - base::WeakPtr context_; + // Can be dereferenced on the UI thread only. + base::WeakPtr process_manager_; const int embedded_worker_id_; const int process_id_; @@ -282,7 +337,7 @@ class EmbeddedWorkerInstance::StartTask { StartTask(EmbeddedWorkerInstance* instance, const GURL& script_url, - mojom::EmbeddedWorkerInstanceClientAssociatedRequest request) + mojom::EmbeddedWorkerInstanceClientRequest request) : instance_(instance), request_(std::move(request)), state_(ProcessAllocationState::NOT_ALLOCATED), @@ -355,17 +410,19 @@ class EmbeddedWorkerInstance::StartTask { DCHECK_EQ(params->embedded_worker_id, instance_->embedded_worker_id_); TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ServiceWorker", "ALLOCATING_PROCESS", this); + base::WeakPtr context = instance_->context_; + base::WeakPtr process_manager = + context->process_manager()->AsWeakPtr(); + // Hop to the UI thread for process allocation and setup. We will continue // on the IO thread in StartTask::OnSetupCompleted(). BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::BindOnce(&SetupOnUIThread, - instance_->context_->process_manager()->AsWeakPtr(), - can_use_existing_process, std::move(params), - std::move(request_), instance_->context_.get(), - instance_->context_, - base::BindOnce(&StartTask::OnSetupCompleted, - weak_factory_.GetWeakPtr()))); + base::BindOnce( + &SetupOnUIThread, process_manager, can_use_existing_process, + std::move(params), std::move(request_), context.get(), context, + base::BindOnce(&StartTask::OnSetupCompleted, + weak_factory_.GetWeakPtr(), process_manager))); } static void RunStartCallback(StartTask* task, @@ -384,12 +441,28 @@ class EmbeddedWorkerInstance::StartTask { private: void OnSetupCompleted( + base::WeakPtr process_manager, ServiceWorkerStatusCode status, mojom::EmbeddedWorkerStartParamsPtr params, std::unique_ptr process_info, - std::unique_ptr devtools_proxy) { + std::unique_ptr devtools_proxy, + std::unique_ptr factory_bundle, + blink::mojom::CacheStoragePtrInfo cache_storage) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + + std::unique_ptr process_handle; + if (status == SERVICE_WORKER_OK) { + // If we allocated a process, WorkerProcessHandle has to be created before + // returning to ensure the process is eventually released. + process_handle = std::make_unique( + process_manager, instance_->embedded_worker_id(), + process_info->process_id); + + if (!instance_->context_) + status = SERVICE_WORKER_ERROR_ABORT; + } + if (status != SERVICE_WORKER_OK) { TRACE_EVENT_NESTABLE_ASYNC_END1("ServiceWorker", "ALLOCATING_PROCESS", this, "Error", @@ -416,23 +489,38 @@ class EmbeddedWorkerInstance::StartTask { // Notify the instance that a process is allocated. state_ = ProcessAllocationState::ALLOCATED; - instance_->OnProcessAllocated( - std::make_unique(instance_->context_, - instance_->embedded_worker_id(), - process_info->process_id), - start_situation); + instance_->OnProcessAllocated(std::move(process_handle), start_situation); // Notify the instance that it is registered to the DevTools manager. instance_->OnRegisteredToDevToolsManager(std::move(devtools_proxy), params->wait_for_debugger); - status = instance_->SendStartWorker(std::move(params)); - if (status != SERVICE_WORKER_OK) { - StatusCallback callback = std::move(start_callback_); - start_callback_.Reset(); - instance_->OnStartFailed(std::move(callback), status); - // |this| may be destroyed. + // S13nServiceWorker: Build the URLLoaderFactory for loading new scripts. + scoped_refptr factory_for_new_scripts; + if (ServiceWorkerUtils::IsServicificationEnabled()) { + if (factory_bundle) { + network::mojom::URLLoaderFactoryPtr network_factory_ptr; + // The factory from CloneNetworkFactory() doesn't support reconnection + // to the network service after a crash, but it's probably OK since it's + // used for a single service worker startup until installation finishes + // (with the exception of https://crbug.com/719052). + instance_->context_->loader_factory_getter()->CloneNetworkFactory( + mojo::MakeRequest(&network_factory_ptr)); + scoped_refptr factory = + base::MakeRefCounted( + std::move(factory_bundle)); + factory->SetDefaultFactory(std::move(network_factory_ptr)); + factory_for_new_scripts = std::move(factory); + } else { + factory_for_new_scripts = + instance_->context_->loader_factory_getter()->GetNetworkFactory(); + } } + + instance_->SendStartWorker(std::move(params), + std::move(factory_for_new_scripts), + std::move(cache_storage)); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ServiceWorker", "INITIALIZING_ON_RENDERER", this); // |this|'s work is done here, but |instance_| still uses its state until @@ -443,7 +531,7 @@ class EmbeddedWorkerInstance::StartTask { EmbeddedWorkerInstance* instance_; // Ownership is transferred by a PostTask() call after process allocation. - mojom::EmbeddedWorkerInstanceClientAssociatedRequest request_; + mojom::EmbeddedWorkerInstanceClientRequest request_; StatusCallback start_callback_; ProcessAllocationState state_; @@ -463,6 +551,8 @@ EmbeddedWorkerInstance::~EmbeddedWorkerInstance() { DCHECK(status_ == EmbeddedWorkerStatus::STOPPING || status_ == EmbeddedWorkerStatus::STOPPED) << static_cast(status_); + for (auto& observer : listener_list_) + observer.OnDestroyed(); devtools_proxy_.reset(); if (registry_->GetWorker(embedded_worker_id_)) registry_->RemoveWorker(process_id(), embedded_worker_id_); @@ -498,7 +588,7 @@ void EmbeddedWorkerInstance::Start(mojom::EmbeddedWorkerStartParamsPtr params, params->wait_for_debugger = false; params->v8_cache_options = GetV8CacheOptions(); - mojom::EmbeddedWorkerInstanceClientAssociatedRequest request = + mojom::EmbeddedWorkerInstanceClientRequest request = mojo::MakeRequest(&client_); client_.set_connection_error_handler( base::BindOnce(&EmbeddedWorkerInstance::Detach, base::Unretained(this))); @@ -603,25 +693,13 @@ void EmbeddedWorkerInstance::OnRegisteredToDevToolsManager( observer.OnRegisteredToDevToolsManager(); } -ServiceWorkerStatusCode EmbeddedWorkerInstance::SendStartWorker( - mojom::EmbeddedWorkerStartParamsPtr params) { - if (!context_) - return SERVICE_WORKER_ERROR_ABORT; - if (!context_->GetDispatcherHost(process_id())) { - // Check if there's a dispatcher host, which is a good sign the process is - // still alive. It's possible that previously the process crashed, and the - // Mojo connection error via |client_| detected it and this instance was - // detached, but on restart ServiceWorkerProcessManager assigned us the - // process again before RenderProcessHostImpl itself or - // ServiceWorkerProcessManager knew it crashed, and by the time we get here - // RenderProcessHostImpl::EnableSendQueue may have been called in - // anticipation of the RPHI being reused for another renderer process, so - // Mojo doesn't consider it an error. See https://crbug.com/732729. - return SERVICE_WORKER_ERROR_IPC_FAILED; - } +void EmbeddedWorkerInstance::SendStartWorker( + mojom::EmbeddedWorkerStartParamsPtr params, + scoped_refptr factory, + blink::mojom::CacheStoragePtrInfo cache_storage) { + DCHECK(context_); DCHECK(params->dispatcher_request.is_pending()); DCHECK(params->controller_request.is_pending()); - DCHECK(params->service_worker_host.is_valid()); DCHECK(!instance_host_binding_.is_bound()); instance_host_binding_.Bind(mojo::MakeRequest(¶ms->instance_host)); @@ -632,15 +710,12 @@ ServiceWorkerStatusCode EmbeddedWorkerInstance::SendStartWorker( const bool is_script_streaming = !params->installed_scripts_info.is_null(); inflight_start_task_->set_start_worker_sent_time(base::TimeTicks::Now()); - params->provider_info = std::move(provider_info_getter_).Run(process_id()); + params->provider_info = + std::move(provider_info_getter_).Run(process_id(), std::move(factory)); + params->provider_info->cache_storage = std::move(cache_storage); client_->StartWorker(std::move(params)); registry_->BindWorkerToProcess(process_id(), embedded_worker_id()); - OnStartWorkerMessageSent(is_script_streaming); - return SERVICE_WORKER_OK; -} -void EmbeddedWorkerInstance::OnStartWorkerMessageSent( - bool is_script_streaming) { if (!step_time_.is_null()) { base::TimeDelta duration = UpdateStepTime(); if (inflight_start_task_->is_installed()) { @@ -874,11 +949,11 @@ int EmbeddedWorkerInstance::worker_devtools_agent_route_id() const { return MSG_ROUTING_NONE; } -void EmbeddedWorkerInstance::AddListener(Listener* listener) { +void EmbeddedWorkerInstance::AddObserver(Listener* listener) { listener_list_.AddObserver(listener); } -void EmbeddedWorkerInstance::RemoveListener(Listener* listener) { +void EmbeddedWorkerInstance::RemoveObserver(Listener* listener) { listener_list_.RemoveObserver(listener); } diff --git a/chromium/content/browser/service_worker/embedded_worker_instance.h b/chromium/content/browser/service_worker/embedded_worker_instance.h index 5b6566ecb6d..2546eb0d4ae 100644 --- a/chromium/content/browser/service_worker/embedded_worker_instance.h +++ b/chromium/content/browser/service_worker/embedded_worker_instance.h @@ -31,6 +31,7 @@ #include "mojo/public/cpp/bindings/associated_binding.h" #include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom.h" +#include "third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom.h" #include "url/gurl.h" namespace content { @@ -73,7 +74,8 @@ class CONTENT_EXPORT EmbeddedWorkerInstance using ProviderInfoGetter = base::OnceCallback; + int /* process_id */, + scoped_refptr)>; class Listener { public: @@ -113,6 +115,9 @@ class CONTENT_EXPORT EmbeddedWorkerInstance const base::string16& message, int line_number, const GURL& source_url) {} + // The instance is being deleted, so it's not safe to call any methods that + // may result in a virtual method call. + virtual void OnDestroyed() {} }; ~EmbeddedWorkerInstance() override; @@ -160,8 +165,8 @@ class CONTENT_EXPORT EmbeddedWorkerInstance int thread_id() const { return thread_id_; } int worker_devtools_agent_route_id() const; - void AddListener(Listener* listener); - void RemoveListener(Listener* listener); + void AddObserver(Listener* listener); + void RemoveObserver(Listener* listener); void SetDevToolsAttached(bool attached); bool devtools_attached() const { return devtools_attached_; } @@ -236,12 +241,18 @@ class CONTENT_EXPORT EmbeddedWorkerInstance std::unique_ptr devtools_proxy, bool wait_for_debugger); - // Sends StartWorker message via Mojo. - ServiceWorkerStatusCode SendStartWorker( - mojom::EmbeddedWorkerStartParamsPtr params); - - // Called back from StartTask after a start worker message is sent. - void OnStartWorkerMessageSent(bool is_script_streaming); + // Sends the StartWorker message to the renderer. + // + // |cache_storage| is an optional optimization so the service worker can + // use the Cache Storage API immediately upon startup. + // + // S13nServiceWorker: + // |factory| is used for loading non-installed scripts. It can internally be a + // bundle of factories instead of just the direct network factory to support + // non-NetworkService schemes like chrome-extension:// URLs. + void SendStartWorker(mojom::EmbeddedWorkerStartParamsPtr params, + scoped_refptr factory, + blink::mojom::CacheStoragePtrInfo cache_storage); // Implements mojom::EmbeddedWorkerInstanceHost. // These functions all run on the IO thread. @@ -297,8 +308,10 @@ class CONTENT_EXPORT EmbeddedWorkerInstance std::unique_ptr process_handle_; int thread_id_; - // |client_| is used to send messages to the renderer process. - mojom::EmbeddedWorkerInstanceClientAssociatedPtr client_; + // |client_| is used to send messages to the renderer process. The browser + // process should not disconnect the pipe because associated interfaces may be + // using it. The renderer process will disconnect the pipe when appropriate. + mojom::EmbeddedWorkerInstanceClientPtr client_; // Binding for EmbeddedWorkerInstanceHost, runs on IO thread. mojo::AssociatedBinding instance_host_binding_; 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 b77c0716f69..9389f539804 100644 --- a/chromium/content/browser/service_worker/embedded_worker_instance_unittest.cc +++ b/chromium/content/browser/service_worker/embedded_worker_instance_unittest.cc @@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/run_loop.h" #include "base/stl_util.h" +#include "base/test/scoped_feature_list.h" #include "content/browser/service_worker/embedded_worker_registry.h" #include "content/browser/service_worker/embedded_worker_status.h" #include "content/browser/service_worker/embedded_worker_test_helper.h" @@ -28,6 +29,7 @@ #include "mojo/public/cpp/bindings/strong_binding.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" @@ -56,12 +58,6 @@ class ProviderHostEndpoints : public mojom::ServiceWorkerContainerHost { // Just keep the endpoints. mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info = mojom::ServiceWorkerProviderInfoForStartWorker::New(); - provider_info->registration = - blink::mojom::ServiceWorkerRegistrationObjectInfo::New(); - provider_info->registration->options = - blink::mojom::ServiceWorkerRegistrationOptions::New(); - registration_object_host_request_ = - mojo::MakeRequest(&(provider_info->registration->host_ptr_info)); binding_.Bind(mojo::MakeRequest(&provider_info->host_ptr_info)); provider_info->client_request = mojo::MakeRequest(&client_); mojo::MakeRequest(&provider_info->interface_provider); @@ -99,8 +95,6 @@ class ProviderHostEndpoints : public mojom::ServiceWorkerContainerHost { mojom::ServiceWorkerContainerAssociatedPtr client_; mojo::AssociatedBinding binding_; - blink::mojom::ServiceWorkerRegistrationObjectHostAssociatedRequest - registration_object_host_request_; DISALLOW_COPY_AND_ASSIGN(ProviderHostEndpoints); }; @@ -198,12 +192,12 @@ class EmbeddedWorkerInstanceTest : public testing::Test, params->dispatcher_request = CreateEventDispatcher(); params->controller_request = CreateController(); params->installed_scripts_info = GetInstalledScriptsInfoPtr(); - params->service_worker_host = GetServiceWorkerHostPtrInfo(); return params; } mojom::ServiceWorkerProviderInfoForStartWorkerPtr CreateProviderInfo( - int /* process_id */) { + int /* process_id */, + scoped_refptr) { provider_host_endpoints_.emplace_back( std::make_unique()); return provider_host_endpoints_.back()->CreateProviderInfoPtr(); @@ -229,12 +223,6 @@ class EmbeddedWorkerInstanceTest : public testing::Test, worker->status_ = status; } - ServiceWorkerStatusCode SimulateSendStartWorker( - EmbeddedWorkerInstance* worker, - mojom::EmbeddedWorkerStartParamsPtr params) { - return worker->SendStartWorker(std::move(params)); - } - blink::mojom::ServiceWorkerInstalledScriptsInfoPtr GetInstalledScriptsInfoPtr() { installed_scripts_managers_.emplace_back(); @@ -246,13 +234,6 @@ class EmbeddedWorkerInstanceTest : public testing::Test, return info; } - blink::mojom::ServiceWorkerHostAssociatedPtrInfo - GetServiceWorkerHostPtrInfo() { - blink::mojom::ServiceWorkerHostAssociatedPtrInfo ptr_info; - service_worker_host_requests_.push_back(mojo::MakeRequest(&ptr_info)); - return ptr_info; - } - ServiceWorkerContextCore* context() { return helper_->context(); } EmbeddedWorkerRegistry* embedded_worker_registry() { @@ -275,8 +256,6 @@ class EmbeddedWorkerInstanceTest : public testing::Test, installed_scripts_managers_; std::vector installed_scripts_manager_host_requests_; - std::vector - service_worker_host_requests_; std::vector> provider_host_endpoints_; TestBrowserThreadBundle thread_bundle_; @@ -302,7 +281,6 @@ class StalledInStartWorkerHelper : public EmbeddedWorkerTestHelper { bool pause_after_download, mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, mojom::ControllerServiceWorkerRequest controller_request, - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) @@ -316,9 +294,8 @@ class StalledInStartWorkerHelper : public EmbeddedWorkerTestHelper { EmbeddedWorkerTestHelper::OnStartWorker( embedded_worker_id, service_worker_version_id, scope, script_url, pause_after_download, std::move(dispatcher_request), - std::move(controller_request), std::move(service_worker_host), - std::move(instance_host), std::move(provider_info), - std::move(installed_scripts_info)); + std::move(controller_request), std::move(instance_host), + std::move(provider_info), std::move(installed_scripts_info)); } void OnStopWorker(int embedded_worker_id) override { @@ -352,7 +329,7 @@ TEST_F(EmbeddedWorkerInstanceTest, StartAndStop) { std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, worker->status()); - worker->AddListener(this); + worker->AddObserver(this); // Start should succeed. ServiceWorkerStatusCode status; @@ -559,7 +536,7 @@ TEST_F(EmbeddedWorkerInstanceTest, DetachDuringProcessAllocation) { const int64_t version_id = pair.second->version_id(); std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); - worker->AddListener(this); + worker->AddObserver(this); // Run the start worker sequence and detach during process allocation. ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; @@ -593,7 +570,7 @@ TEST_F(EmbeddedWorkerInstanceTest, DetachAfterSendingStartWorkerMessage) { const int64_t version_id = pair.second->version_id(); std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); - worker->AddListener(this); + worker->AddObserver(this); // Run the start worker sequence until a start worker message is sent. ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; @@ -633,7 +610,7 @@ TEST_F(EmbeddedWorkerInstanceTest, StopDuringProcessAllocation) { const int64_t version_id = pair.second->version_id(); std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); - worker->AddListener(this); + worker->AddObserver(this); // Stop the start worker sequence before a process is allocated. ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; @@ -708,7 +685,7 @@ TEST_F(EmbeddedWorkerInstanceTest, StopDuringPausedAfterDownload) { const int64_t version_id = pair.second->version_id(); std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); - worker->AddListener(this); + worker->AddObserver(this); // Run the start worker sequence until pause after download. ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; @@ -741,7 +718,7 @@ TEST_F(EmbeddedWorkerInstanceTest, StopAfterSendingStartWorkerMessage) { const int64_t version_id = pair.second->version_id(); std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); - worker->AddListener(this); + worker->AddObserver(this); // Run the start worker sequence until a start worker message is sent. ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; @@ -805,7 +782,7 @@ TEST_F(EmbeddedWorkerInstanceTest, Detach) { std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; - worker->AddListener(this); + worker->AddObserver(this); // Start the worker. base::RunLoop run_loop; @@ -840,7 +817,7 @@ TEST_F(EmbeddedWorkerInstanceTest, FailToSendStartIPC) { const int64_t version_id = pair.second->version_id(); std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); - worker->AddListener(this); + worker->AddObserver(this); // Attempt to start the worker. mojom::EmbeddedWorkerStartParamsPtr params = @@ -885,7 +862,7 @@ TEST_F(EmbeddedWorkerInstanceTest, RemoveRemoteInterface) { const int64_t version_id = pair.second->version_id(); std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); - worker->AddListener(this); + worker->AddObserver(this); // Attempt to start the worker. mojom::EmbeddedWorkerStartParamsPtr params = @@ -937,7 +914,7 @@ TEST_F(EmbeddedWorkerInstanceTest, AddMessageToConsole) { const int64_t version_id = pair.second->version_id(); std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); - worker->AddListener(this); + worker->AddObserver(this); // Attempt to start the worker and immediate AddMessageToConsole should not // cause a crash. @@ -972,21 +949,162 @@ TEST_F(EmbeddedWorkerInstanceTest, AddMessageToConsole) { EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, worker->status()); } -// Test that SendStartWorker checks if dispatcher host exists. -TEST_F(EmbeddedWorkerInstanceTest, NoDispatcherHost) { +// Records whether a CacheStoragePtr was sent as part of StartWorker. +class RecordCacheStorageHelper : public EmbeddedWorkerTestHelper { + public: + RecordCacheStorageHelper() : EmbeddedWorkerTestHelper(base::FilePath()) {} + ~RecordCacheStorageHelper() override {} + + void OnStartWorker( + int embedded_worker_id, + int64_t service_worker_version_id, + const GURL& scope, + const GURL& script_url, + bool pause_after_download, + mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, + mojom::ControllerServiceWorkerRequest controller_request, + mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, + mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, + blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) + override { + had_cache_storage_ = !!provider_info->cache_storage; + EmbeddedWorkerTestHelper::OnStartWorker( + embedded_worker_id, service_worker_version_id, scope, script_url, + pause_after_download, std::move(dispatcher_request), + std::move(controller_request), std::move(instance_host), + std::move(provider_info), std::move(installed_scripts_info)); + } + + bool had_cache_storage() const { return had_cache_storage_; } + + private: + bool had_cache_storage_ = false; +}; + +// Test that the worker is given a CacheStoragePtr during startup, when +// |pause_after_download| is false. +TEST_F(EmbeddedWorkerInstanceTest, CacheStorageOptimization) { + const GURL scope("http://example.com/"); + const GURL url("http://example.com/worker.js"); + auto helper = std::make_unique(); + auto* helper_rawptr = helper.get(); + helper_ = std::move(helper); + + RegistrationAndVersionPair pair = PrepareRegistrationAndVersion(scope, url); + const int64_t version_id = pair.second->version_id(); + std::unique_ptr worker = + embedded_worker_registry()->CreateWorker(pair.second.get()); + + // First, test a worker without pause after download. + { + // Start the worker. + ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; + base::RunLoop run_loop; + mojom::EmbeddedWorkerStartParamsPtr params = + CreateStartParams(version_id, scope, url); + worker->Start( + std::move(params), CreateProviderInfoGetter(), + base::BindOnce(&SaveStatusAndCall, &status, run_loop.QuitClosure())); + run_loop.Run(); + EXPECT_EQ(SERVICE_WORKER_OK, status); + + // Cache storage should have been sent. + EXPECT_TRUE(helper_rawptr->had_cache_storage()); + + // Stop the worker. + worker->Stop(); + base::RunLoop().RunUntilIdle(); + } + + // Second, test a worker with pause after download. + { + // Start the worker until paused. + ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; + mojom::EmbeddedWorkerStartParamsPtr params = + CreateStartParams(version_id, scope, url); + params->pause_after_download = true; + worker->Start( + std::move(params), CreateProviderInfoGetter(), + base::BindOnce(&SaveStatusAndCall, &status, base::DoNothing::Once<>())); + base::RunLoop().RunUntilIdle(); + + // Finish starting. + worker->ResumeAfterDownload(); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(SERVICE_WORKER_OK, status); + + // Cache storage should not have been sent. + EXPECT_FALSE(helper_rawptr->had_cache_storage()); + + // Stop the worker. + worker->Stop(); + base::RunLoop().RunUntilIdle(); + } +} + +// Test that the worker is not given a CacheStoragePtr during startup when +// the feature is disabled. +TEST_F(EmbeddedWorkerInstanceTest, CacheStorageOptimizationIsDisabled) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndDisableFeature( + blink::features::kEagerCacheStorageSetupForServiceWorkers); + const GURL scope("http://example.com/"); const GURL url("http://example.com/worker.js"); + auto helper = std::make_unique(); + auto* helper_rawptr = helper.get(); + helper_ = std::move(helper); RegistrationAndVersionPair pair = PrepareRegistrationAndVersion(scope, url); + const int64_t version_id = pair.second->version_id(); std::unique_ptr worker = embedded_worker_registry()->CreateWorker(pair.second.get()); - SetWorkerStatus(worker.get(), EmbeddedWorkerStatus::STARTING); - auto params = mojom::EmbeddedWorkerStartParams::New(); - ServiceWorkerStatusCode result = - SimulateSendStartWorker(worker.get(), std::move(params)); - EXPECT_EQ(SERVICE_WORKER_ERROR_IPC_FAILED, result); - // Set to STOPPED because EWInstance's destructor DCHECKs status. - SetWorkerStatus(worker.get(), EmbeddedWorkerStatus::STOPPED); + + // First, test a worker without pause after download. + { + // Start the worker. + ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; + base::RunLoop run_loop; + mojom::EmbeddedWorkerStartParamsPtr params = + CreateStartParams(version_id, scope, url); + worker->Start( + std::move(params), CreateProviderInfoGetter(), + base::BindOnce(&SaveStatusAndCall, &status, run_loop.QuitClosure())); + run_loop.Run(); + EXPECT_EQ(SERVICE_WORKER_OK, status); + + // Cache storage should not have been sent. + EXPECT_FALSE(helper_rawptr->had_cache_storage()); + + // Stop the worker. + worker->Stop(); + base::RunLoop().RunUntilIdle(); + } + + // Second, test a worker with pause after download. + { + // Start the worker until paused. + ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; + mojom::EmbeddedWorkerStartParamsPtr params = + CreateStartParams(version_id, scope, url); + params->pause_after_download = true; + worker->Start( + std::move(params), CreateProviderInfoGetter(), + base::BindOnce(&SaveStatusAndCall, &status, base::DoNothing::Once<>())); + base::RunLoop().RunUntilIdle(); + + // Finish starting. + worker->ResumeAfterDownload(); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(SERVICE_WORKER_OK, status); + + // Cache storage should not have been sent. + EXPECT_FALSE(helper_rawptr->had_cache_storage()); + + // Stop the worker. + worker->Stop(); + base::RunLoop().RunUntilIdle(); + } } } // namespace content diff --git a/chromium/content/browser/service_worker/embedded_worker_test_helper.cc b/chromium/content/browser/service_worker/embedded_worker_test_helper.cc index 963ff792cd5..4db8a45e0d1 100644 --- a/chromium/content/browser/service_worker/embedded_worker_test_helper.cc +++ b/chromium/content/browser/service_worker/embedded_worker_test_helper.cc @@ -12,7 +12,6 @@ #include "base/atomic_sequence_num.h" #include "base/bind.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -45,11 +44,8 @@ namespace { class MockServiceWorkerDispatcherHost : public ServiceWorkerDispatcherHost { public: - MockServiceWorkerDispatcherHost(int process_id, - ResourceContext* resource_context, - IPC::Sender* sender) - : ServiceWorkerDispatcherHost(process_id, resource_context), - sender_(sender) {} + MockServiceWorkerDispatcherHost(int process_id, IPC::Sender* sender) + : ServiceWorkerDispatcherHost(process_id), sender_(sender) {} bool Send(IPC::Message* message) override { return sender_->Send(message); } @@ -137,7 +133,7 @@ void EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient:: // static void EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient::Bind( const base::WeakPtr& helper, - mojom::EmbeddedWorkerInstanceClientAssociatedRequest request) { + mojom::EmbeddedWorkerInstanceClientRequest request) { std::vector>* clients = helper->mock_instance_clients(); size_t next_client_index = helper->mock_instance_clients_next_index_; @@ -159,20 +155,31 @@ class EmbeddedWorkerTestHelper::MockServiceWorkerEventDispatcher : public mojom::ServiceWorkerEventDispatcher { public: static void Create(const base::WeakPtr& helper, - int thread_id, + int embedded_worker_id, mojom::ServiceWorkerEventDispatcherRequest request) { - mojo::MakeStrongBinding( - std::make_unique(helper, thread_id), - std::move(request)); + mojo::MakeStrongBinding(std::make_unique( + helper, embedded_worker_id), + std::move(request)); } MockServiceWorkerEventDispatcher( const base::WeakPtr& helper, - int thread_id) - : helper_(helper), thread_id_(thread_id) {} + int embedded_worker_id) + : helper_(helper), embedded_worker_id_(embedded_worker_id) {} ~MockServiceWorkerEventDispatcher() override {} + void InitializeGlobalScope( + blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, + blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration_info) + override { + if (!helper_) + return; + helper_->OnInitializeGlobalScope(embedded_worker_id_, + std::move(service_worker_host), + std::move(registration_info)); + } + void DispatchInstallEvent( DispatchInstallEventCallback callback) override { if (!helper_) @@ -188,10 +195,13 @@ class EmbeddedWorkerTestHelper::MockServiceWorkerEventDispatcher void DispatchBackgroundFetchAbortEvent( const std::string& developer_id, + const std::string& unique_id, + const std::vector& fetches, DispatchBackgroundFetchAbortEventCallback callback) override { if (!helper_) return; - helper_->OnBackgroundFetchAbortEventStub(developer_id, std::move(callback)); + helper_->OnBackgroundFetchAbortEventStub(developer_id, unique_id, fetches, + std::move(callback)); } void DispatchBackgroundFetchClickEvent( @@ -206,11 +216,12 @@ class EmbeddedWorkerTestHelper::MockServiceWorkerEventDispatcher void DispatchBackgroundFetchFailEvent( const std::string& developer_id, + const std::string& unique_id, const std::vector& fetches, DispatchBackgroundFetchFailEventCallback callback) override { if (!helper_) return; - helper_->OnBackgroundFetchFailEventStub(developer_id, fetches, + helper_->OnBackgroundFetchFailEventStub(developer_id, unique_id, fetches, std::move(callback)); } @@ -225,6 +236,15 @@ class EmbeddedWorkerTestHelper::MockServiceWorkerEventDispatcher std::move(callback)); } + void DispatchCookieChangeEvent( + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause, + DispatchCookieChangeEventCallback callback) override { + if (!helper_) + return; + helper_->OnCookieChangeEventStub(cookie, cause, std::move(callback)); + } + void DispatchFetchEvent( mojom::DispatchFetchEventParamsPtr params, mojom::ServiceWorkerFetchResponseCallbackPtr response_callback, @@ -232,7 +252,7 @@ class EmbeddedWorkerTestHelper::MockServiceWorkerEventDispatcher if (!helper_) return; helper_->OnFetchEventStub( - thread_id_, params->request, std::move(params->preload_handle), + embedded_worker_id_, params->request, std::move(params->preload_handle), std::move(response_callback), std::move(callback)); } @@ -322,7 +342,7 @@ class EmbeddedWorkerTestHelper::MockServiceWorkerEventDispatcher private: base::WeakPtr helper_; - const int thread_id_; + const int embedded_worker_id_; }; class EmbeddedWorkerTestHelper::MockRendererInterface : public mojom::Renderer { @@ -342,8 +362,7 @@ class EmbeddedWorkerTestHelper::MockRendererInterface : public mojom::Renderer { void CreateView(mojom::CreateViewParamsPtr) override { NOTREACHED(); } void CreateFrame(mojom::CreateFrameParamsPtr) override { NOTREACHED(); } void SetUpEmbeddedWorkerChannelForServiceWorker( - mojom::EmbeddedWorkerInstanceClientAssociatedRequest client_request) - override { + mojom::EmbeddedWorkerInstanceClientRequest client_request) override { MockEmbeddedWorkerInstanceClient::Bind(helper_, std::move(client_request)); } void CreateFrameProxy( @@ -367,6 +386,7 @@ class EmbeddedWorkerTestHelper::MockRendererInterface : public mojom::Renderer { NOTREACHED(); } void SetWebKitSharedTimersSuspended(bool suspend) override { NOTREACHED(); } + void SetUserAgent(const std::string& user_agent) override { NOTREACHED(); } void UpdateScrollbarTheme( mojom::UpdateScrollbarThemeParamsPtr params) override { NOTREACHED(); @@ -468,8 +488,8 @@ void EmbeddedWorkerTestHelper::RegisterDispatcherHost( void EmbeddedWorkerTestHelper::EnsureDispatcherHostForProcess(int process_id) { if (context()->GetDispatcherHost(process_id)) return; - auto dispatcher_host = base::MakeRefCounted( - process_id, browser_context_->GetResourceContext(), this); + auto dispatcher_host = + base::MakeRefCounted(process_id, this); dispatcher_host->Init(wrapper_.get()); RegisterDispatcherHost(process_id, std::move(dispatcher_host)); } @@ -508,13 +528,12 @@ void EmbeddedWorkerTestHelper::OnStartWorker( bool pause_after_download, mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, mojom::ControllerServiceWorkerRequest controller_request, - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) { EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id); ASSERT_TRUE(worker); - MockServiceWorkerEventDispatcher::Create(AsWeakPtr(), worker->thread_id(), + MockServiceWorkerEventDispatcher::Create(AsWeakPtr(), embedded_worker_id, std::move(dispatcher_request)); embedded_worker_id_service_worker_version_id_map_[embedded_worker_id] = service_worker_version_id; @@ -562,6 +581,8 @@ void EmbeddedWorkerTestHelper::OnActivateEvent( void EmbeddedWorkerTestHelper::OnBackgroundFetchAbortEvent( const std::string& developer_id, + const std::string& unique_id, + const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchAbortEventCallback callback) { std::move(callback).Run(blink::mojom::ServiceWorkerEventStatus::COMPLETED, @@ -579,6 +600,7 @@ void EmbeddedWorkerTestHelper::OnBackgroundFetchClickEvent( void EmbeddedWorkerTestHelper::OnBackgroundFetchFailEvent( const std::string& developer_id, + const std::string& unique_id, const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchFailEventCallback callback) { @@ -596,6 +618,15 @@ void EmbeddedWorkerTestHelper::OnBackgroundFetchedEvent( base::Time::Now()); } +void EmbeddedWorkerTestHelper::OnCookieChangeEvent( + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause, + mojom::ServiceWorkerEventDispatcher::DispatchCookieChangeEventCallback + callback) { + std::move(callback).Run(blink::mojom::ServiceWorkerEventStatus::COMPLETED, + base::Time::Now()); +} + void EmbeddedWorkerTestHelper::OnExtendableMessageEvent( mojom::ExtendableMessageEventPtr event, mojom::ServiceWorkerEventDispatcher::DispatchExtendableMessageEventCallback @@ -778,6 +809,32 @@ void EmbeddedWorkerTestHelper::SimulateWorkerStopped(int embedded_worker_id) { } } +void EmbeddedWorkerTestHelper::OnInitializeGlobalScope( + int embedded_worker_id, + blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, + blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration_info) { + embedded_worker_id_host_map_[embedded_worker_id].Bind( + std::move(service_worker_host)); + // To enable the caller end points to make calls safely with no need to pass + // these associated interface requests through a message pipe endpoint. + mojo::AssociateWithDisconnectedPipe(registration_info->request.PassHandle()); + if (registration_info->installing) { + mojo::AssociateWithDisconnectedPipe( + registration_info->installing->request.PassHandle()); + } + if (registration_info->waiting) { + mojo::AssociateWithDisconnectedPipe( + registration_info->waiting->request.PassHandle()); + } + if (registration_info->active) { + mojo::AssociateWithDisconnectedPipe( + registration_info->active->request.PassHandle()); + } + // Keep all Mojo connections alive. + embedded_worker_id_registration_info_map_[embedded_worker_id] = + std::move(registration_info); +} + void EmbeddedWorkerTestHelper::OnStartWorkerStub( mojom::EmbeddedWorkerStartParamsPtr params) { EmbeddedWorkerInstance* worker = @@ -792,7 +849,6 @@ void EmbeddedWorkerTestHelper::OnStartWorkerStub( params->scope, params->script_url, params->pause_after_download, std::move(params->dispatcher_request), std::move(params->controller_request), - std::move(params->service_worker_host), std::move(params->instance_host), std::move(params->provider_info), std::move(params->installed_scripts_info))); } @@ -823,12 +879,15 @@ void EmbeddedWorkerTestHelper::OnActivateEventStub( void EmbeddedWorkerTestHelper::OnBackgroundFetchAbortEventStub( const std::string& developer_id, + const std::string& unique_id, + const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchAbortEventCallback callback) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&EmbeddedWorkerTestHelper::OnBackgroundFetchAbortEvent, - AsWeakPtr(), developer_id, std::move(callback))); + AsWeakPtr(), developer_id, unique_id, fetches, + std::move(callback))); } void EmbeddedWorkerTestHelper::OnBackgroundFetchClickEventStub( @@ -844,13 +903,15 @@ void EmbeddedWorkerTestHelper::OnBackgroundFetchClickEventStub( void EmbeddedWorkerTestHelper::OnBackgroundFetchFailEventStub( const std::string& developer_id, + const std::string& unique_id, const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchFailEventCallback callback) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&EmbeddedWorkerTestHelper::OnBackgroundFetchFailEvent, - AsWeakPtr(), developer_id, fetches, std::move(callback))); + AsWeakPtr(), developer_id, unique_id, fetches, + std::move(callback))); } void EmbeddedWorkerTestHelper::OnBackgroundFetchedEventStub( @@ -866,6 +927,17 @@ void EmbeddedWorkerTestHelper::OnBackgroundFetchedEventStub( std::move(callback))); } +void EmbeddedWorkerTestHelper::OnCookieChangeEventStub( + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause, + mojom::ServiceWorkerEventDispatcher::DispatchCookieChangeEventCallback + callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&EmbeddedWorkerTestHelper::OnCookieChangeEvent, + AsWeakPtr(), cookie, cause, std::move(callback))); +} + void EmbeddedWorkerTestHelper::OnExtendableMessageEventStub( mojom::ExtendableMessageEventPtr event, mojom::ServiceWorkerEventDispatcher::DispatchExtendableMessageEventCallback @@ -885,7 +957,7 @@ void EmbeddedWorkerTestHelper::OnInstallEventStub( } void EmbeddedWorkerTestHelper::OnFetchEventStub( - int thread_id, + int embedded_worker_id, const network::ResourceRequest& request, mojom::FetchEventPreloadHandlePtr preload_handle, mojom::ServiceWorkerFetchResponseCallbackPtr response_callback, @@ -894,9 +966,8 @@ void EmbeddedWorkerTestHelper::OnFetchEventStub( base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&EmbeddedWorkerTestHelper::OnFetchEvent, AsWeakPtr(), - thread_id_embedded_worker_id_map_[thread_id], request, - std::move(preload_handle), std::move(response_callback), - std::move(finish_callback))); + embedded_worker_id, request, std::move(preload_handle), + std::move(response_callback), std::move(finish_callback))); } void EmbeddedWorkerTestHelper::OnNotificationClickEventStub( diff --git a/chromium/content/browser/service_worker/embedded_worker_test_helper.h b/chromium/content/browser/service_worker/embedded_worker_test_helper.h index c0cd7275d30..609a99102b0 100644 --- a/chromium/content/browser/service_worker/embedded_worker_test_helper.h +++ b/chromium/content/browser/service_worker/embedded_worker_test_helper.h @@ -24,8 +24,10 @@ #include "content/common/service_worker/service_worker_status_code.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_test_sink.h" -#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "net/cookies/cookie_change_dispatcher.h" #include "net/http/http_response_info.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom.h" #include "url/gurl.h" @@ -71,9 +73,8 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, base::WeakPtr helper); ~MockEmbeddedWorkerInstanceClient() override; - static void Bind( - const base::WeakPtr& helper, - mojom::EmbeddedWorkerInstanceClientAssociatedRequest request); + static void Bind(const base::WeakPtr& helper, + mojom::EmbeddedWorkerInstanceClientRequest request); protected: // mojom::EmbeddedWorkerInstanceClient implementation. @@ -86,7 +87,7 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, blink::mojom::DevToolsAgentAssociatedRequest request) override {} base::WeakPtr helper_; - mojo::AssociatedBinding binding_; + mojo::Binding binding_; base::Optional embedded_worker_id_; @@ -153,10 +154,6 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, return render_process_host_.get(); } - std::map embedded_worker_id_service_worker_version_id_map() { - return embedded_worker_id_service_worker_version_id_map_; - } - // Only used for tests that force creating a new render process. int new_render_process_id() const { return new_mock_render_process_id_; } @@ -176,8 +173,8 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, protected: // StartWorker IPC handler routed through MockEmbeddedWorkerInstanceClient. - // This simulates each legacy IPC sent from the renderer and binds |request| - // to MockServiceWorkerEventDispatcher by default. + // This simulates behaviors in the renderer process. Binds + // |dispatcher_request| to MockServiceWorkerEventDispatcher by default. virtual void OnStartWorker( int embedded_worker_id, int64_t service_worker_version_id, @@ -186,7 +183,6 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, bool pause_after_download, mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, mojom::ControllerServiceWorkerRequest controller_request, - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, blink::mojom::ServiceWorkerInstalledScriptsInfoPtr @@ -203,6 +199,8 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, callback); virtual void OnBackgroundFetchAbortEvent( const std::string& developer_id, + const std::string& unique_id, + const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchAbortEventCallback callback); virtual void OnBackgroundFetchClickEvent( @@ -212,6 +210,7 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, DispatchBackgroundFetchClickEventCallback callback); virtual void OnBackgroundFetchFailEvent( const std::string& developer_id, + const std::string& unique_id, const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchFailEventCallback callback); @@ -221,6 +220,11 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchedEventCallback callback); + virtual void OnCookieChangeEvent( + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause, + mojom::ServiceWorkerEventDispatcher::DispatchCookieChangeEventCallback + callback); virtual void OnExtendableMessageEvent( mojom::ExtendableMessageEventPtr event, mojom::ServiceWorkerEventDispatcher:: @@ -277,6 +281,11 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, EmbeddedWorkerRegistry* registry(); + blink::mojom::ServiceWorkerHost* GetServiceWorkerHost( + int embedded_worker_id) { + return embedded_worker_id_host_map_[embedded_worker_id].get(); + } + private: class MockServiceWorkerEventDispatcher; class MockRendererInterface; @@ -284,6 +293,10 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, void DidSimulateWorkerScriptCached(int embedded_worker_id, bool pause_after_download); + void OnInitializeGlobalScope( + int embedded_worker_id, + blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, + blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration_info); void OnStartWorkerStub(mojom::EmbeddedWorkerStartParamsPtr params); void OnResumeAfterDownloadStub(int embedded_worker_id); void OnStopWorkerStub(int embedded_worker_id); @@ -292,6 +305,8 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, callback); void OnBackgroundFetchAbortEventStub( const std::string& developer_id, + const std::string& unique_id, + const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchAbortEventCallback callback); void OnBackgroundFetchClickEventStub( @@ -301,6 +316,7 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, DispatchBackgroundFetchClickEventCallback callback); void OnBackgroundFetchFailEventStub( const std::string& developer_id, + const std::string& unique_id, const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchFailEventCallback callback); @@ -310,6 +326,11 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, const std::vector& fetches, mojom::ServiceWorkerEventDispatcher:: DispatchBackgroundFetchedEventCallback callback); + void OnCookieChangeEventStub( + const net::CanonicalCookie& cookie, + ::network::mojom::CookieChangeCause cause, + mojom::ServiceWorkerEventDispatcher::DispatchCookieChangeEventCallback + callback); void OnExtendableMessageEventStub( mojom::ExtendableMessageEventPtr event, mojom::ServiceWorkerEventDispatcher:: @@ -318,7 +339,7 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, mojom::ServiceWorkerEventDispatcher::DispatchInstallEventCallback callback); void OnFetchEventStub( - int thread_id, + int embedded_worker_id, const network::ResourceRequest& request, mojom::FetchEventPreloadHandlePtr preload_handle, mojom::ServiceWorkerFetchResponseCallbackPtr response_callback, @@ -375,8 +396,6 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, dispatcher_hosts_; std::map embedded_worker_id_service_worker_version_id_map_; - std::map - thread_id_embedded_worker_id_map_; std::map< int /* embedded_worker_id */, @@ -387,6 +406,14 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, std::map embedded_worker_id_installed_scripts_info_map_; + std::map< + int /* embedded_worker_id */, + blink::mojom::ServiceWorkerHostAssociatedPtr /* service_worker_host */> + embedded_worker_id_host_map_; + std::map + embedded_worker_id_registration_info_map_; std::vector events_; scoped_refptr url_loader_factory_getter_; diff --git a/chromium/content/browser/service_worker/service_worker_browsertest.cc b/chromium/content/browser/service_worker/service_worker_browsertest.cc index baf6eb5e6ab..c12bec86b88 100644 --- a/chromium/content/browser/service_worker/service_worker_browsertest.cc +++ b/chromium/content/browser/service_worker/service_worker_browsertest.cc @@ -227,7 +227,8 @@ class WorkerActivatedObserver std::unique_ptr VerifyServiceWorkerHeaderInRequest( const net::test_server::HttpRequest& request) { - EXPECT_EQ(request.relative_url, "/service_worker/generated_sw.js"); + if (request.relative_url != "/service_worker/generated_sw.js") + return nullptr; auto it = request.headers.find("Service-Worker"); EXPECT_TRUE(it != request.headers.end()); EXPECT_EQ("script", it->second); @@ -240,6 +241,8 @@ VerifyServiceWorkerHeaderInRequest( std::unique_ptr VerifySaveDataHeaderInRequest( const net::test_server::HttpRequest& request) { + if (request.relative_url != "/service_worker/generated_sw.js") + return nullptr; auto it = request.headers.find("Save-Data"); EXPECT_NE(request.headers.end(), it); EXPECT_EQ("on", it->second); @@ -252,6 +255,8 @@ std::unique_ptr VerifySaveDataHeaderInRequest( std::unique_ptr VerifySaveDataHeaderNotInRequest(const net::test_server::HttpRequest& request) { + if (request.relative_url != "/service_worker/generated_sw.js") + return nullptr; auto it = request.headers.find("Save-Data"); EXPECT_EQ(request.headers.end(), it); return std::make_unique(); @@ -1160,7 +1165,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, "/service_worker/worker_install_rejected.js")); ConsoleListener console_listener; - RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::AddListener, + RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::AddObserver, base::Unretained(version_->embedded_worker()), &console_listener)); @@ -1179,7 +1184,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, console_listener.WaitForConsoleMessages(1); ASSERT_NE(base::string16::npos, console_listener.messages()[0].find(expected)); - RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::RemoveListener, + RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::RemoveObserver, base::Unretained(version_->embedded_worker()), &console_listener)); } @@ -1208,7 +1213,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, TimeoutStartingWorker) { base::RunLoop start_run_loop; base::RunLoop load_run_loop; WaitForLoaded wait_for_load(load_run_loop.QuitClosure()); - RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::AddListener, + RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::AddObserver, base::Unretained(version_->embedded_worker()), &wait_for_load)); BrowserThread::PostTask( @@ -1216,7 +1221,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, TimeoutStartingWorker) { base::BindOnce(&self::StartOnIOThread, base::Unretained(this), start_run_loop.QuitClosure(), &status)); load_run_loop.Run(); - RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::RemoveListener, + RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::RemoveObserver, base::Unretained(version_->embedded_worker()), &wait_for_load)); @@ -1337,7 +1342,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, SERVICE_WORKER_OK); ConsoleListener console_listener; - RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::AddListener, + RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::AddObserver, base::Unretained(version_->embedded_worker()), &console_listener)); @@ -1350,7 +1355,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, ASSERT_NE(base::string16::npos, console_listener.messages()[0].find(expected1)); ASSERT_EQ(0u, console_listener.messages()[1].find(expected2)); - RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::RemoveListener, + RunOnIOThread(base::BindOnce(&EmbeddedWorkerInstance::RemoveObserver, base::Unretained(version_->embedded_worker()), &console_listener)); @@ -2347,8 +2352,8 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, EXPECT_TRUE(entry->GetSSL().initialized); EXPECT_FALSE(!!(entry->GetSSL().content_status & SSLStatus::DISPLAYED_INSECURE_CONTENT)); - EXPECT_TRUE( - https_server.GetCertificate()->Equals(entry->GetSSL().certificate.get())); + EXPECT_TRUE(https_server.GetCertificate()->EqualsExcludingChain( + entry->GetSSL().certificate.get())); EXPECT_FALSE(net::IsCertStatusError(entry->GetSSL().cert_status)); shell()->Close(); @@ -2796,7 +2801,7 @@ class CacheStorageSideDataSizeChecker void OpenCacheOnIOThread(int* result, const base::Closure& continuation) { cache_storage_context_->cache_manager()->OpenCache( - url::Origin::Create(origin_), cache_name_, + url::Origin::Create(origin_), CacheStorageOwner::kCacheAPI, cache_name_, base::BindOnce(&self::OnCacheStorageOpenCallback, this, result, continuation)); } @@ -2810,7 +2815,7 @@ class CacheStorageSideDataSizeChecker new ServiceWorkerFetchRequest()); scoped_request->url = url_; CacheStorageCache* cache = cache_handle.value(); - cache->Match(std::move(scoped_request), CacheStorageCacheQueryParams(), + cache->Match(std::move(scoped_request), nullptr, base::BindOnce(&self::OnCacheStorageCacheMatchCallback, this, result, continuation, std::move(cache_handle))); } diff --git a/chromium/content/browser/service_worker/service_worker_client_info.cc b/chromium/content/browser/service_worker/service_worker_client_info.cc new file mode 100644 index 00000000000..cff6cf107f3 --- /dev/null +++ b/chromium/content/browser/service_worker/service_worker_client_info.cc @@ -0,0 +1,34 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/service_worker/service_worker_client_info.h" + +#include "content/public/common/child_process_host.h" +#include "ipc/ipc_message.h" + +namespace content { + +ServiceWorkerClientInfo::ServiceWorkerClientInfo() + : ServiceWorkerClientInfo( + ChildProcessHost::kInvalidUniqueID, + MSG_ROUTING_NONE, + base::RepeatingCallback(), + blink::mojom::ServiceWorkerProviderType::kUnknown) {} + +ServiceWorkerClientInfo::ServiceWorkerClientInfo( + int process_id, + int route_id, + const base::RepeatingCallback& web_contents_getter, + blink::mojom::ServiceWorkerProviderType type) + : process_id(process_id), + route_id(route_id), + web_contents_getter(web_contents_getter), + type(type) {} + +ServiceWorkerClientInfo::ServiceWorkerClientInfo( + const ServiceWorkerClientInfo& other) = default; + +ServiceWorkerClientInfo::~ServiceWorkerClientInfo() {} + +} // namespace content diff --git a/chromium/content/browser/service_worker/service_worker_client_info.h b/chromium/content/browser/service_worker/service_worker_client_info.h new file mode 100644 index 00000000000..245c198622f --- /dev/null +++ b/chromium/content/browser/service_worker/service_worker_client_info.h @@ -0,0 +1,43 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CLIENT_INFO_H_ +#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CLIENT_INFO_H_ + +#include "base/callback.h" +#include "content/common/content_export.h" +#include "content/public/browser/web_contents.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h" + +namespace content { + +// Holds information about a single service worker client: +// https://w3c.github.io/ServiceWorker/#client +struct CONTENT_EXPORT ServiceWorkerClientInfo { + ServiceWorkerClientInfo(); + ServiceWorkerClientInfo( + int process_id, + int route_id, + const base::RepeatingCallback& web_contents_getter, + blink::mojom::ServiceWorkerProviderType type); + ServiceWorkerClientInfo(const ServiceWorkerClientInfo& other); + ~ServiceWorkerClientInfo(); + + // The renderer process this client lives in. + int process_id; + // If this client is a document |route_id| is its frame id; otherwise it is + // MSG_ROUTING_NONE. + int route_id; + // Non-null if this client is a document and its corresponding + // ServiceWorkerProviderHost was pre-created for a navigation. Returns + // information indicating the tab where the navigation is occurring or + // occurred in. + base::RepeatingCallback web_contents_getter; + // The client type. + blink::mojom::ServiceWorkerProviderType type; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CLIENT_INFO_H_ diff --git a/chromium/content/browser/service_worker/service_worker_client_utils.cc b/chromium/content/browser/service_worker/service_worker_client_utils.cc index b27c2d353f7..947d1166e58 100644 --- a/chromium/content/browser/service_worker/service_worker_client_utils.cc +++ b/chromium/content/browser/service_worker/service_worker_client_utils.cc @@ -380,7 +380,8 @@ void GetNonWindowClients( AddNonWindowClient(controllee.second, std::move(options), clients.get()); } else if (controller->context()) { GURL origin = controller->script_url().GetOrigin(); - for (auto it = controller->context()->GetClientProviderHostIterator(origin); + for (auto it = controller->context()->GetClientProviderHostIterator( + origin, false /* include_reserved_clients */); !it->IsAtEnd(); it->Advance()) { AddNonWindowClient(it->GetProviderHost(), std::move(options), clients.get()); @@ -418,7 +419,8 @@ void GetWindowClients(const base::WeakPtr& controller, AddWindowClient(controllee.second, &clients_info); } else if (controller->context()) { GURL origin = controller->script_url().GetOrigin(); - for (auto it = controller->context()->GetClientProviderHostIterator(origin); + for (auto it = controller->context()->GetClientProviderHostIterator( + origin, false /* include_reserved_clients */); !it->IsAtEnd(); it->Advance()) { AddWindowClient(it->GetProviderHost(), &clients_info); } @@ -560,7 +562,8 @@ void DidNavigate(const base::WeakPtr& context, } for (std::unique_ptr it = - context->GetClientProviderHostIterator(origin); + context->GetClientProviderHostIterator( + origin, false /* include_reserved_clients */); !it->IsAtEnd(); it->Advance()) { ServiceWorkerProviderHost* provider_host = it->GetProviderHost(); if (provider_host->process_id() != render_process_id || diff --git a/chromium/content/browser/service_worker/service_worker_content_settings_proxy_impl.cc b/chromium/content/browser/service_worker/service_worker_content_settings_proxy_impl.cc index 2934d52f2c7..e4b82677f15 100644 --- a/chromium/content/browser/service_worker/service_worker_content_settings_proxy_impl.cc +++ b/chromium/content/browser/service_worker/service_worker_content_settings_proxy_impl.cc @@ -27,6 +27,11 @@ void ServiceWorkerContentSettingsProxyImpl::AllowIndexedDB( const base::string16& name, AllowIndexedDBCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + // May be shutting down. + if (!context_ || !context_->wrapper()->resource_context()) { + std::move(callback).Run(false); + return; + } if (origin_.unique()) { std::move(callback).Run(false); return; 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 d5b665ca761..46f20d354a1 100644 --- a/chromium/content/browser/service_worker/service_worker_context_core.cc +++ b/chromium/content/browser/service_worker/service_worker_context_core.cc @@ -87,19 +87,19 @@ void SuccessReportingCallback( } bool IsSameOriginClientProviderHost(const GURL& origin, + bool allow_reserved_client, ServiceWorkerProviderHost* host) { return host->IsProviderForClient() && host->document_url().GetOrigin() == origin && - // Don't expose "reserved" clients (clients that are not yet execution - // ready) to the Clients API. - host->is_execution_ready(); + (allow_reserved_client || host->is_execution_ready()); } bool IsSameOriginWindowProviderHost(const GURL& origin, ServiceWorkerProviderHost* host) { return host->provider_type() == blink::mojom::ServiceWorkerProviderType::kForWindow && - host->document_url().GetOrigin() == origin; + host->document_url().GetOrigin() == origin && + host->is_execution_ready(); } // Returns true if any of the frames specified by |frames| is a top-level frame. @@ -269,7 +269,7 @@ bool ServiceWorkerContextCore::ProviderHostIterator:: } ServiceWorkerContextCore::ServiceWorkerContextCore( - const base::FilePath& path, + const base::FilePath& user_data_directory, scoped_refptr database_task_runner, storage::QuotaManagerProxy* quota_manager_proxy, storage::SpecialStoragePolicy* special_storage_policy, @@ -282,17 +282,17 @@ ServiceWorkerContextCore::ServiceWorkerContextCore( provider_by_uuid_(std::make_unique()), loader_factory_getter_(url_loader_factory_getter), force_update_on_page_load_(false), - next_handle_id_(0), was_service_worker_registered_(false), observer_list_(observer_list), weak_factory_(this) { - // These get a WeakPtr from weak_factory_, so must be set after weak_factory_ - // is initialized. + DCHECK(observer_list_); + // These get a WeakPtr from |weak_factory_|, so must be set after + // |weak_factory_| is initialized. storage_ = ServiceWorkerStorage::Create( - path, AsWeakPtr(), std::move(database_task_runner), quota_manager_proxy, - special_storage_policy); + user_data_directory, AsWeakPtr(), std::move(database_task_runner), + quota_manager_proxy, special_storage_policy); embedded_worker_registry_ = EmbeddedWorkerRegistry::Create(AsWeakPtr()); - job_coordinator_.reset(new ServiceWorkerJobCoordinator(AsWeakPtr())); + job_coordinator_ = std::make_unique(AsWeakPtr()); } ServiceWorkerContextCore::ServiceWorkerContextCore( @@ -303,27 +303,25 @@ ServiceWorkerContextCore::ServiceWorkerContextCore( providers_(old_context->providers_.release()), provider_by_uuid_(old_context->provider_by_uuid_.release()), loader_factory_getter_(old_context->loader_factory_getter()), - next_handle_id_(old_context->next_handle_id_), was_service_worker_registered_( old_context->was_service_worker_registered_), observer_list_(old_context->observer_list_), weak_factory_(this) { - // These get a WeakPtr from weak_factory_, so must be set after weak_factory_ - // is initialized. + DCHECK(observer_list_); + + // These get a WeakPtr from |weak_factory_|, so must be set after + // |weak_factory_| is initialized. storage_ = ServiceWorkerStorage::Create(AsWeakPtr(), old_context->storage()); embedded_worker_registry_ = EmbeddedWorkerRegistry::Create( AsWeakPtr(), old_context->embedded_worker_registry()); - job_coordinator_.reset(new ServiceWorkerJobCoordinator(AsWeakPtr())); + job_coordinator_ = std::make_unique(AsWeakPtr()); } ServiceWorkerContextCore::~ServiceWorkerContextCore() { DCHECK(storage_); - for (VersionMap::iterator it = live_versions_.begin(); - it != live_versions_.end(); - ++it) { - it->second->RemoveListener(this); - } + for (const auto& it : live_versions_) + it.second->RemoveListener(this); weak_factory_.InvalidateWeakPtrs(); } @@ -366,6 +364,18 @@ void ServiceWorkerContextCore::AddProviderHost( map->AddWithID(std::move(host), provider_id); } +std::unique_ptr +ServiceWorkerContextCore::ReleaseProviderHost(int process_id, int provider_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ProviderMap* map = GetProviderMapForProcess(process_id); + if (!map || !map->Lookup(provider_id)) + return nullptr; + std::unique_ptr host = + map->Replace(provider_id, nullptr); + map->Remove(provider_id); + return host; +} + ServiceWorkerProviderHost* ServiceWorkerContextCore::GetProviderHost( int process_id, int provider_id) { @@ -392,10 +402,13 @@ void ServiceWorkerContextCore::RemoveAllProviderHostsForProcess( } std::unique_ptr -ServiceWorkerContextCore::GetClientProviderHostIterator(const GURL& origin) { +ServiceWorkerContextCore::GetClientProviderHostIterator( + const GURL& origin, + bool include_reserved_clients) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); return base::WrapUnique(new ProviderHostIterator( - providers_.get(), base::Bind(IsSameOriginClientProviderHost, origin))); + providers_.get(), base::BindRepeating(IsSameOriginClientProviderHost, + origin, include_reserved_clients))); } void ServiceWorkerContextCore::HasMainFrameProviderHost( @@ -403,7 +416,8 @@ void ServiceWorkerContextCore::HasMainFrameProviderHost( BoolCallback callback) const { DCHECK_CURRENTLY_ON(BrowserThread::IO); ProviderHostIterator provider_host_iterator( - providers_.get(), base::Bind(IsSameOriginWindowProviderHost, origin)); + providers_.get(), + base::BindRepeating(IsSameOriginWindowProviderHost, origin)); if (provider_host_iterator.IsAtEnd()) { base::ThreadTaskRunnerHandle::Get()->PostTask( @@ -494,9 +508,9 @@ void ServiceWorkerContextCore::DeleteForOrigin(const GURL& origin, DCHECK_CURRENTLY_ON(BrowserThread::IO); storage()->GetRegistrationsForOrigin( origin, - AdaptCallbackForRepeating(base::BindOnce( + base::BindOnce( &ServiceWorkerContextCore::DidGetRegistrationsForDeleteForOrigin, - AsWeakPtr(), std::move(callback)))); + AsWeakPtr(), std::move(callback))); } void ServiceWorkerContextCore::DidGetRegistrationsForDeleteForOrigin( @@ -524,8 +538,7 @@ void ServiceWorkerContextCore::DidGetRegistrationsForDeleteForOrigin( } UnregisterServiceWorker( registration->pattern(), - AdaptCallbackForRepeating(base::BindOnce(&SuccessCollectorCallback, - barrier, overall_success))); + base::BindOnce(&SuccessCollectorCallback, barrier, overall_success)); } } @@ -550,14 +563,11 @@ void ServiceWorkerContextCore::RegistrationComplete( DCHECK(registration); std::move(callback).Run(status, status_message, registration->id()); - // TODO(falken): At this point the registration promise is resolved, but we - // haven't persisted anything to storage yet. So we should either call - // OnRegistrationStored somewhere else or change its name. - if (observer_list_.get()) { - observer_list_->Notify( - FROM_HERE, &ServiceWorkerContextCoreObserver::OnRegistrationStored, - registration->id(), pattern); - } + // At this point the registration promise is resolved, but we haven't + // persisted anything to storage yet. + observer_list_->Notify( + FROM_HERE, &ServiceWorkerContextCoreObserver::OnRegistrationCompleted, + registration->id(), pattern); } void ServiceWorkerContextCore::UpdateComplete( @@ -582,7 +592,7 @@ void ServiceWorkerContextCore::UnregistrationComplete( int64_t registration_id, ServiceWorkerStatusCode status) { std::move(callback).Run(status); - if (status == SERVICE_WORKER_OK && observer_list_.get()) { + if (status == SERVICE_WORKER_OK) { observer_list_->Notify( FROM_HERE, &ServiceWorkerContextCoreObserver::OnRegistrationDeleted, registration_id, pattern); @@ -599,11 +609,9 @@ void ServiceWorkerContextCore::AddLiveRegistration( ServiceWorkerRegistration* registration) { DCHECK(!GetLiveRegistration(registration->id())); live_registrations_[registration->id()] = registration; - if (observer_list_.get()) { - observer_list_->Notify( - FROM_HERE, &ServiceWorkerContextCoreObserver::OnNewLiveRegistration, - registration->id(), registration->pattern()); - } + observer_list_->Notify( + FROM_HERE, &ServiceWorkerContextCoreObserver::OnNewLiveRegistration, + registration->id(), registration->pattern()); } void ServiceWorkerContextCore::RemoveLiveRegistration(int64_t id) { @@ -615,45 +623,16 @@ ServiceWorkerVersion* ServiceWorkerContextCore::GetLiveVersion(int64_t id) { return (it != live_versions_.end()) ? it->second : nullptr; } -// PlzNavigate -void ServiceWorkerContextCore::AddNavigationHandleCore( - int service_worker_provider_id, - ServiceWorkerNavigationHandleCore* handle) { - auto result = navigation_handle_cores_map_.insert( - std::pair( - service_worker_provider_id, handle)); - DCHECK(result.second) - << "Inserting a duplicate ServiceWorkerNavigationHandleCore"; -} - -// PlzNavigate -void ServiceWorkerContextCore::RemoveNavigationHandleCore( - int service_worker_provider_id) { - navigation_handle_cores_map_.erase(service_worker_provider_id); -} - -// PlzNavigate -ServiceWorkerNavigationHandleCore* -ServiceWorkerContextCore::GetNavigationHandleCore( - int service_worker_provider_id) { - auto result = navigation_handle_cores_map_.find(service_worker_provider_id); - if (result == navigation_handle_cores_map_.end()) - return nullptr; - return result->second; -} - void ServiceWorkerContextCore::AddLiveVersion(ServiceWorkerVersion* version) { // TODO(horo): If we will see crashes here, we have to find the root cause of // the version ID conflict. Otherwise change CHECK to DCHECK. CHECK(!GetLiveVersion(version->version_id())); live_versions_[version->version_id()] = version; version->AddListener(this); - if (observer_list_.get()) { - ServiceWorkerVersionInfo version_info = version->GetInfo(); - observer_list_->Notify(FROM_HERE, - &ServiceWorkerContextCoreObserver::OnNewLiveVersion, - version_info); - } + ServiceWorkerVersionInfo version_info = version->GetInfo(); + observer_list_->Notify(FROM_HERE, + &ServiceWorkerContextCoreObserver::OnNewLiveVersion, + version_info); } void ServiceWorkerContextCore::RemoveLiveVersion(int64_t id) { @@ -694,10 +673,6 @@ void ServiceWorkerContextCore::UnprotectVersion(int64_t version_id) { protected_versions_.erase(version_id); } -int ServiceWorkerContextCore::GetNewServiceWorkerHandleId() { - return next_handle_id_++; -} - void ServiceWorkerContextCore::ScheduleDeleteAndStartOver() const { storage_->Disable(); base::ThreadTaskRunnerHandle::Get()->PostTask( @@ -776,17 +751,21 @@ int ServiceWorkerContextCore::GetVersionFailureCount(int64_t version_id) { return it->second.count; } +void ServiceWorkerContextCore::NotifyRegistrationStored(int64_t registration_id, + const GURL& pattern) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + observer_list_->Notify( + FROM_HERE, &ServiceWorkerContextCoreObserver::OnRegistrationStored, + registration_id, pattern); +} + void ServiceWorkerContextCore::OnStorageWiped() { - if (!observer_list_) - return; observer_list_->Notify(FROM_HERE, &ServiceWorkerContextCoreObserver::OnStorageWiped); } void ServiceWorkerContextCore::OnRunningStateChanged( ServiceWorkerVersion* version) { - if (!observer_list_) - return; observer_list_->Notify( FROM_HERE, &ServiceWorkerContextCoreObserver::OnRunningStateChanged, version->version_id(), version->running_status()); @@ -794,8 +773,6 @@ void ServiceWorkerContextCore::OnRunningStateChanged( void ServiceWorkerContextCore::OnVersionStateChanged( ServiceWorkerVersion* version) { - if (!observer_list_) - return; observer_list_->Notify( FROM_HERE, &ServiceWorkerContextCoreObserver::OnVersionStateChanged, version->version_id(), version->status()); @@ -803,7 +780,7 @@ void ServiceWorkerContextCore::OnVersionStateChanged( void ServiceWorkerContextCore::OnDevToolsRoutingIdChanged( ServiceWorkerVersion* version) { - if (!observer_list_ || !version->embedded_worker()) + if (!version->embedded_worker()) return; observer_list_->Notify( FROM_HERE, @@ -814,8 +791,6 @@ void ServiceWorkerContextCore::OnDevToolsRoutingIdChanged( void ServiceWorkerContextCore::OnMainScriptHttpResponseInfoSet( ServiceWorkerVersion* version) { - if (!observer_list_) - return; const net::HttpResponseInfo* info = version->GetMainScriptHttpResponseInfo(); DCHECK(info); base::Time lastModified; @@ -833,8 +808,6 @@ void ServiceWorkerContextCore::OnErrorReported( int line_number, int column_number, const GURL& source_url) { - if (!observer_list_) - return; observer_list_->Notify( FROM_HERE, &ServiceWorkerContextCoreObserver::OnErrorReported, version->version_id(), version->embedded_worker()->process_id(), @@ -850,8 +823,6 @@ void ServiceWorkerContextCore::OnReportConsoleMessage( const base::string16& message, int line_number, const GURL& source_url) { - if (!observer_list_) - return; observer_list_->Notify( FROM_HERE, &ServiceWorkerContextCoreObserver::OnReportConsoleMessage, version->version_id(), version->embedded_worker()->process_id(), @@ -862,24 +833,19 @@ void ServiceWorkerContextCore::OnReportConsoleMessage( void ServiceWorkerContextCore::OnControlleeAdded( ServiceWorkerVersion* version, - ServiceWorkerProviderHost* provider_host) { - if (!observer_list_) - return; - observer_list_->Notify( - FROM_HERE, &ServiceWorkerContextCoreObserver::OnControlleeAdded, - version->version_id(), provider_host->client_uuid(), - provider_host->process_id(), provider_host->route_id(), - provider_host->web_contents_getter(), provider_host->provider_type()); + const std::string& client_uuid, + const ServiceWorkerClientInfo& client_info) { + observer_list_->Notify(FROM_HERE, + &ServiceWorkerContextCoreObserver::OnControlleeAdded, + version->version_id(), client_uuid, client_info); } void ServiceWorkerContextCore::OnControlleeRemoved( ServiceWorkerVersion* version, - ServiceWorkerProviderHost* provider_host) { - if (!observer_list_) - return; + const std::string& client_uuid) { observer_list_->Notify(FROM_HERE, &ServiceWorkerContextCoreObserver::OnControlleeRemoved, - version->version_id(), provider_host->client_uuid()); + version->version_id(), client_uuid); } ServiceWorkerProcessManager* ServiceWorkerContextCore::process_manager() { 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 18389af21fb..5168ea6801c 100644 --- a/chromium/content/browser/service_worker/service_worker_context_core.h +++ b/chromium/content/browser/service_worker/service_worker_context_core.h @@ -45,7 +45,6 @@ class ServiceWorkerContextCoreObserver; class ServiceWorkerContextWrapper; class ServiceWorkerDispatcherHost; class ServiceWorkerJobCoordinator; -class ServiceWorkerNavigationHandleCore; class ServiceWorkerProviderHost; class ServiceWorkerRegistration; class ServiceWorkerStorage; @@ -147,9 +146,10 @@ class CONTENT_EXPORT ServiceWorkerContextCore int line_number, const GURL& source_url) override; void OnControlleeAdded(ServiceWorkerVersion* version, - ServiceWorkerProviderHost* provider_host) override; + const std::string& client_uuid, + const ServiceWorkerClientInfo& client_info) override; void OnControlleeRemoved(ServiceWorkerVersion* version, - ServiceWorkerProviderHost* provider_host) override; + const std::string& client_uuid) override; ServiceWorkerContextWrapper* wrapper() const { return wrapper_; } ServiceWorkerStorage* storage() { return storage_.get(); } @@ -172,19 +172,24 @@ class CONTENT_EXPORT ServiceWorkerContextCore void AddProviderHost( std::unique_ptr provider_host); ServiceWorkerProviderHost* GetProviderHost(int process_id, int provider_id); + std::unique_ptr ReleaseProviderHost( + int process_id, + int provider_id); void RemoveProviderHost(int process_id, int provider_id); void RemoveAllProviderHostsForProcess(int process_id); // Returns a ProviderHost iterator for all service worker clients for the - // |origin|. This only returns clients that are execution ready (i.e., for - // windows, the navigation has been committed and for workers, the final - // response after redirects has been delivered). + // |origin|. If |include_reserved_clients| is false, this only returns clients + // that are execution ready (i.e., for windows, the navigation has been + // committed and for workers, the final response after redirects has been + // delivered). std::unique_ptr GetClientProviderHostIterator( - const GURL& origin); + const GURL& origin, + bool include_reserved_clients); // Runs the callback with true if there is a ProviderHost for |origin| of type // blink::mojom::ServiceWorkerProviderType::kForWindow which is a main - // (top-level) frame. + // (top-level) frame. Reserved clients are ignored. void HasMainFrameProviderHost(const GURL& origin, BoolCallback callback) const; @@ -245,15 +250,6 @@ class CONTENT_EXPORT ServiceWorkerContextCore return live_versions_; } - // PlzNavigate - // Methods to manage the map keeping track of all - // ServiceWorkerNavigationHandleCores registered for ongoing navigations. - void AddNavigationHandleCore(int service_worker_provider_id, - ServiceWorkerNavigationHandleCore* handle); - void RemoveNavigationHandleCore(int service_worker_provider_id); - ServiceWorkerNavigationHandleCore* GetNavigationHandleCore( - int service_worker_provider_id); - std::vector GetAllLiveRegistrationInfo(); std::vector GetAllLiveVersionInfo(); @@ -262,9 +258,6 @@ class CONTENT_EXPORT ServiceWorkerContextCore void ProtectVersion(const scoped_refptr& version); void UnprotectVersion(int64_t version_id); - // Returns new context-local unique ID. - int GetNewServiceWorkerHandleId(); - void ScheduleDeleteAndStartOver() const; // Deletes all files on disk and restarts the system. This leaves the system @@ -287,6 +280,9 @@ class CONTENT_EXPORT ServiceWorkerContextCore // version. The count resets to zero when the worker successfully starts. int GetVersionFailureCount(int64_t version_id); + // Called by ServiceWorkerStorage when StoreRegistration() succeeds. + void NotifyRegistrationStored(int64_t registration_id, const GURL& pattern); + URLLoaderFactoryGetter* loader_factory_getter() { return loader_factory_getter_.get(); } @@ -357,22 +353,18 @@ class CONTENT_EXPORT ServiceWorkerContextCore std::map failure_counts_; - // PlzNavigate - // Map of ServiceWorkerNavigationHandleCores used for navigation requests. - std::map - navigation_handle_cores_map_; - // IsServicificationEnabled scoped_refptr loader_factory_getter_; bool force_update_on_page_load_; - int next_handle_id_; // Set in RegisterServiceWorker(), cleared in ClearAllServiceWorkersForTest(). // This is used to avoid unnecessary disk read operation in tests. This value // is false if Chrome was relaunched after service workers were registered. bool was_service_worker_registered_; - scoped_refptr> - observer_list_; + using ServiceWorkerContextObserverList = + base::ObserverListThreadSafe; + const scoped_refptr observer_list_; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(ServiceWorkerContextCore); diff --git a/chromium/content/browser/service_worker/service_worker_context_core_observer.h b/chromium/content/browser/service_worker/service_worker_context_core_observer.h index 1453739b55a..99966ec319b 100644 --- a/chromium/content/browser/service_worker/service_worker_context_core_observer.h +++ b/chromium/content/browser/service_worker/service_worker_context_core_observer.h @@ -79,16 +79,23 @@ class ServiceWorkerContextCoreObserver { int process_id, int thread_id, const ConsoleMessage& message) {} - // |web_contents_getter| is only set in PlzNavigate. - virtual void OnControlleeAdded( - int64_t version_id, - const std::string& uuid, - int process_id, - int route_id, - const base::Callback& web_contents_getter, - blink::mojom::ServiceWorkerProviderType type) {} + virtual void OnControlleeAdded(int64_t version_id, + const std::string& uuid, + const ServiceWorkerClientInfo& info) {} virtual void OnControlleeRemoved(int64_t version_id, const std::string& uuid) {} + // Called when the ServiceWorkerContainer.register() promise is resolved. + // + // This is called before the service worker registration is persisted to + // storage. The implementation cannot assume that the ServiceWorkerContextCore + // will find the registration at this point. + virtual void OnRegistrationCompleted(int64_t registration_id, + const GURL& pattern) {} + // Called after a service worker registration is persisted to storage. + // + // This happens after OnRegistrationCompleted(). The implementation can assume + // that ServiceWorkerContextCore will find the registration, and can safely + // add user data to the registration. virtual void OnRegistrationStored(int64_t registration_id, const GURL& pattern) {} virtual void OnRegistrationDeleted(int64_t registration_id, 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 index eaf2f2b0759..4722982af26 100644 --- a/chromium/content/browser/service_worker/service_worker_context_request_handler.cc +++ b/chromium/content/browser/service_worker/service_worker_context_request_handler.cc @@ -12,6 +12,7 @@ #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 "content/common/service_worker/service_worker_utils.h" #include "content/public/common/content_switches.h" #include "net/base/load_flags.h" #include "net/log/net_log.h" @@ -23,31 +24,6 @@ namespace content { -namespace { - -bool ShouldBypassCacheDueToUpdateViaCache( - bool is_main_script, - blink::mojom::ServiceWorkerUpdateViaCache cache_mode) { - // TODO(https://crbug.com/675540): Remove the command line check and always - // respect cache_mode when shipping updateViaCache flag to stable. - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableExperimentalWebPlatformFeatures)) { - return false; - } - switch (cache_mode) { - case blink::mojom::ServiceWorkerUpdateViaCache::kImports: - return is_main_script; - case blink::mojom::ServiceWorkerUpdateViaCache::kNone: - return true; - case blink::mojom::ServiceWorkerUpdateViaCache::kAll: - return false; - } - NOTREACHED() << static_cast(cache_mode); - return false; -} - -} // namespace - ServiceWorkerContextRequestHandler::ServiceWorkerContextRequestHandler( base::WeakPtr context, base::WeakPtr provider_host, @@ -220,8 +196,8 @@ net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJobImpl( base::TimeDelta time_since_last_check = base::Time::Now() - registration->last_update_check(); - if (ShouldBypassCacheDueToUpdateViaCache(is_main_script, - registration->update_via_cache()) || + if (ServiceWorkerUtils::ShouldBypassCacheDueToUpdateViaCache( + is_main_script, registration->update_via_cache()) || time_since_last_check > kServiceWorkerScriptMaxCacheAge || version_->force_bypass_cache_for_scripts()) { extra_load_flags = net::LOAD_BYPASS_CACHE; diff --git a/chromium/content/browser/service_worker/service_worker_context_request_handler_unittest.cc b/chromium/content/browser/service_worker/service_worker_context_request_handler_unittest.cc index c7e1e2194f7..9c653a1e5e2 100644 --- a/chromium/content/browser/service_worker/service_worker_context_request_handler_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_context_request_handler_unittest.cc @@ -137,11 +137,6 @@ class ServiceWorkerContextRequestHandlerTest : public testing::Test { void TestBypassCache(const GURL& url, ResourceType resource_type, bool expect_bypass) { - // TODO(https://crbug.com/675540): Remove the following command line switch - // when updateViaCache is shipped to stable. - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kEnableExperimentalWebPlatformFeatures); - std::unique_ptr request(CreateRequest(url)); std::unique_ptr handler( CreateHandler(resource_type)); @@ -190,37 +185,9 @@ TEST_F(ServiceWorkerContextRequestHandlerTest, UpdateBefore24Hours) { TestBypassCacheForImportedScript(false); } -// TODO(https://crbug.com/675540): Remove the -// UpdateBefore24HoursWithoutUpdateViaCache test when the update_via_cache flag -// is shipped to stable as this is to test the legacy behavior. -TEST_F(ServiceWorkerContextRequestHandlerTest, - UpdateBefore24HoursWithoutUpdateViaCache) { - registration_->set_last_update_check(base::Time::Now()); - version_->SetStatus(ServiceWorkerVersion::NEW); - - // Conduct a resource fetch for the main script. - base::HistogramTester histograms; - std::unique_ptr request(CreateRequest(script_url_)); - std::unique_ptr handler( - CreateHandler(RESOURCE_TYPE_SERVICE_WORKER)); - std::unique_ptr job( - handler->MaybeCreateJob(request.get(), nullptr, nullptr)); - ASSERT_TRUE(job.get()); - ServiceWorkerWriteToCacheJob* sw_job = - static_cast(job.get()); - histograms.ExpectUniqueSample( - "ServiceWorker.ContextRequestHandlerStatus.NewWorker.MainScript", - static_cast( - ServiceWorkerContextRequestHandler::CreateJobStatus::WRITE_JOB), - 1); - - // Verify the net request is not initialized to bypass the browser cache. - EXPECT_FALSE(sw_job->net_request_->load_flags() & net::LOAD_BYPASS_CACHE); -} - TEST_F(ServiceWorkerContextRequestHandlerTest, UpdateBefore24HoursWithUpdateViaCacheAll) { - registration_->set_update_via_cache( + registration_->SetUpdateViaCache( blink::mojom::ServiceWorkerUpdateViaCache::kAll); // Give the registration a very recent last update time and pretend // we're installing a new version. @@ -233,7 +200,7 @@ TEST_F(ServiceWorkerContextRequestHandlerTest, TEST_F(ServiceWorkerContextRequestHandlerTest, UpdateBefore24HoursWithUpdateViaCacheNone) { - registration_->set_update_via_cache( + registration_->SetUpdateViaCache( blink::mojom::ServiceWorkerUpdateViaCache::kNone); // Give the registration a very recent last update time and pretend // we're installing a new version. @@ -257,7 +224,7 @@ TEST_F(ServiceWorkerContextRequestHandlerTest, UpdateAfter24Hours) { TEST_F(ServiceWorkerContextRequestHandlerTest, UpdateAfter24HoursWithUpdateViaCacheAll) { - registration_->set_update_via_cache( + registration_->SetUpdateViaCache( blink::mojom::ServiceWorkerUpdateViaCache::kAll); // Give the registration a old update time and pretend // we're installing a new version. @@ -271,7 +238,7 @@ TEST_F(ServiceWorkerContextRequestHandlerTest, TEST_F(ServiceWorkerContextRequestHandlerTest, UpdateAfter24HoursWithUpdateViaCacheNone) { - registration_->set_update_via_cache( + registration_->SetUpdateViaCache( blink::mojom::ServiceWorkerUpdateViaCache::kNone); // Give the registration a old update time and pretend // we're installing a new version. @@ -284,7 +251,7 @@ TEST_F(ServiceWorkerContextRequestHandlerTest, } TEST_F(ServiceWorkerContextRequestHandlerTest, UpdateForceBypassCache) { - registration_->set_update_via_cache( + registration_->SetUpdateViaCache( blink::mojom::ServiceWorkerUpdateViaCache::kAll); // Give the registration a very recent last update time and pretend // we're installing a new version. 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 06decdcd47b..df64683c72e 100644 --- a/chromium/content/browser/service_worker/service_worker_context_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_context_unittest.cc @@ -8,7 +8,6 @@ #include "base/files/scoped_temp_dir.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/time/time.h" #include "content/browser/service_worker/embedded_worker_registry.h" @@ -112,6 +111,7 @@ class RejectActivateTestHelper : public EmbeddedWorkerTestHelper { }; enum NotificationType { + REGISTRATION_COMPLETED, REGISTRATION_STORED, REGISTRATION_DELETED, STORAGE_RECOVERED, @@ -139,6 +139,14 @@ class ServiceWorkerContextTest : public ServiceWorkerContextCoreObserver, void TearDown() override { helper_.reset(); } // ServiceWorkerContextCoreObserver overrides. + void OnRegistrationCompleted(int64_t registration_id, + const GURL& pattern) override { + NotificationLog log; + log.type = REGISTRATION_COMPLETED; + log.pattern = pattern; + log.registration_id = registration_id; + notifications_.push_back(log); + } void OnRegistrationStored(int64_t registration_id, const GURL& pattern) override { NotificationLog log; @@ -200,8 +208,8 @@ class RecordableEmbeddedWorkerInstanceClient // Make sure basic registration is working. TEST_F(ServiceWorkerContextTest, Register) { - GURL pattern("http://www.example.com/"); - GURL script_url("http://www.example.com/service_worker.js"); + GURL pattern("https://www.example.com/"); + GURL script_url("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = pattern; @@ -218,8 +226,8 @@ TEST_F(ServiceWorkerContextTest, Register) { base::RunLoop().RunUntilIdle(); EXPECT_TRUE(called); - ASSERT_EQ(2UL, helper_->dispatched_events()->size()); - ASSERT_EQ(1UL, client->events().size()); + ASSERT_EQ(2u, helper_->dispatched_events()->size()); + ASSERT_EQ(1u, client->events().size()); EXPECT_EQ(RecordableEmbeddedWorkerInstanceClient::Message::StartWorker, client->events()[0]); EXPECT_EQ(EmbeddedWorkerTestHelper::Event::Install, @@ -229,10 +237,13 @@ TEST_F(ServiceWorkerContextTest, Register) { EXPECT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, registration_id); - ASSERT_EQ(1u, notifications_.size()); - EXPECT_EQ(REGISTRATION_STORED, notifications_[0].type); + ASSERT_EQ(2u, notifications_.size()); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type); EXPECT_EQ(pattern, notifications_[0].pattern); EXPECT_EQ(registration_id, notifications_[0].registration_id); + EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type); + EXPECT_EQ(pattern, notifications_[1].pattern); + EXPECT_EQ(registration_id, notifications_[1].registration_id); context()->storage()->FindRegistrationForId( registration_id, pattern.GetOrigin(), @@ -245,8 +256,8 @@ TEST_F(ServiceWorkerContextTest, Register) { // registration callback should indicate success, but there should be no waiting // or active worker in the registration. TEST_F(ServiceWorkerContextTest, Register_RejectInstall) { - GURL pattern("http://www.example.com/"); - GURL script_url("http://www.example.com/service_worker.js"); + GURL pattern("https://www.example.com/"); + GURL script_url("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = pattern; @@ -267,8 +278,8 @@ TEST_F(ServiceWorkerContextTest, Register_RejectInstall) { base::RunLoop().RunUntilIdle(); EXPECT_TRUE(called); - ASSERT_EQ(1UL, helper_->dispatched_events()->size()); - ASSERT_EQ(2UL, client->events().size()); + ASSERT_EQ(1u, helper_->dispatched_events()->size()); + ASSERT_EQ(2u, client->events().size()); EXPECT_EQ(RecordableEmbeddedWorkerInstanceClient::Message::StartWorker, client->events()[0]); EXPECT_EQ(EmbeddedWorkerTestHelper::Event::Install, @@ -279,7 +290,7 @@ TEST_F(ServiceWorkerContextTest, Register_RejectInstall) { EXPECT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, registration_id); ASSERT_EQ(1u, notifications_.size()); - EXPECT_EQ(REGISTRATION_STORED, notifications_[0].type); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type); EXPECT_EQ(pattern, notifications_[0].pattern); EXPECT_EQ(registration_id, notifications_[0].registration_id); @@ -293,8 +304,8 @@ TEST_F(ServiceWorkerContextTest, Register_RejectInstall) { // Test registration when the service worker rejects the activate event. The // worker should be activated anyway. TEST_F(ServiceWorkerContextTest, Register_RejectActivate) { - GURL pattern("http://www.example.com/"); - GURL script_url("http://www.example.com/service_worker.js"); + GURL pattern("https://www.example.com/"); + GURL script_url("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = pattern; @@ -315,8 +326,8 @@ TEST_F(ServiceWorkerContextTest, Register_RejectActivate) { base::RunLoop().RunUntilIdle(); EXPECT_TRUE(called); - ASSERT_EQ(2UL, helper_->dispatched_events()->size()); - ASSERT_EQ(1UL, client->events().size()); + ASSERT_EQ(2u, helper_->dispatched_events()->size()); + ASSERT_EQ(1u, client->events().size()); EXPECT_EQ(RecordableEmbeddedWorkerInstanceClient::Message::StartWorker, client->events()[0]); EXPECT_EQ(EmbeddedWorkerTestHelper::Event::Install, @@ -326,10 +337,13 @@ TEST_F(ServiceWorkerContextTest, Register_RejectActivate) { EXPECT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, registration_id); - ASSERT_EQ(1u, notifications_.size()); - EXPECT_EQ(REGISTRATION_STORED, notifications_[0].type); + ASSERT_EQ(2u, notifications_.size()); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type); EXPECT_EQ(pattern, notifications_[0].pattern); EXPECT_EQ(registration_id, notifications_[0].registration_id); + EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type); + EXPECT_EQ(pattern, notifications_[1].pattern); + EXPECT_EQ(registration_id, notifications_[1].registration_id); context()->storage()->FindRegistrationForId( registration_id, pattern.GetOrigin(), @@ -340,14 +354,14 @@ TEST_F(ServiceWorkerContextTest, Register_RejectActivate) { // Make sure registrations are cleaned up when they are unregistered. TEST_F(ServiceWorkerContextTest, Unregister) { - GURL pattern("http://www.example.com/"); + GURL pattern("https://www.example.com/"); blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = pattern; bool called = false; int64_t registration_id = blink::mojom::kInvalidServiceWorkerRegistrationId; context()->RegisterServiceWorker( - GURL("http://www.example.com/service_worker.js"), options, + GURL("https://www.example.com/service_worker.js"), options, MakeRegisteredCallback(&called, ®istration_id)); ASSERT_FALSE(called); @@ -369,21 +383,24 @@ TEST_F(ServiceWorkerContextTest, Unregister) { false /* expect_waiting */, false /* expect_active */)); base::RunLoop().RunUntilIdle(); - ASSERT_EQ(2u, notifications_.size()); - EXPECT_EQ(REGISTRATION_STORED, notifications_[0].type); + ASSERT_EQ(3u, notifications_.size()); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type); EXPECT_EQ(pattern, notifications_[0].pattern); EXPECT_EQ(registration_id, notifications_[0].registration_id); - EXPECT_EQ(REGISTRATION_DELETED, notifications_[1].type); + EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type); EXPECT_EQ(pattern, notifications_[1].pattern); EXPECT_EQ(registration_id, notifications_[1].registration_id); + EXPECT_EQ(REGISTRATION_DELETED, notifications_[2].type); + EXPECT_EQ(pattern, notifications_[2].pattern); + EXPECT_EQ(registration_id, notifications_[2].registration_id); } // Make sure registrations are cleaned up when they are unregistered in bulk. TEST_F(ServiceWorkerContextTest, UnregisterMultiple) { - GURL origin1_p1("http://www.example.com/test"); - GURL origin1_p2("http://www.example.com/hello"); - GURL origin2_p1("http://www.example.com:8080/again"); - GURL origin3_p1("http://www.other.com/"); + GURL origin1_p1("https://www.example.com/test"); + GURL origin1_p2("https://www.example.com/hello"); + GURL origin2_p1("https://www.example.com:8080/again"); + GURL origin3_p1("https://www.other.com/"); int64_t registration_id1 = blink::mojom::kInvalidServiceWorkerRegistrationId; int64_t registration_id2 = blink::mojom::kInvalidServiceWorkerRegistrationId; int64_t registration_id3 = blink::mojom::kInvalidServiceWorkerRegistrationId; @@ -394,7 +411,7 @@ TEST_F(ServiceWorkerContextTest, UnregisterMultiple) { blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = origin1_p1; context()->RegisterServiceWorker( - GURL("http://www.example.com/service_worker.js"), options, + GURL("https://www.example.com/service_worker.js"), options, MakeRegisteredCallback(&called, ®istration_id1)); ASSERT_FALSE(called); base::RunLoop().RunUntilIdle(); @@ -406,7 +423,7 @@ TEST_F(ServiceWorkerContextTest, UnregisterMultiple) { blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = origin1_p2; context()->RegisterServiceWorker( - GURL("http://www.example.com/service_worker2.js"), options, + GURL("https://www.example.com/service_worker2.js"), options, MakeRegisteredCallback(&called, ®istration_id2)); ASSERT_FALSE(called); base::RunLoop().RunUntilIdle(); @@ -418,7 +435,7 @@ TEST_F(ServiceWorkerContextTest, UnregisterMultiple) { blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = origin2_p1; context()->RegisterServiceWorker( - GURL("http://www.example.com:8080/service_worker3.js"), options, + GURL("https://www.example.com:8080/service_worker3.js"), options, MakeRegisteredCallback(&called, ®istration_id3)); ASSERT_FALSE(called); base::RunLoop().RunUntilIdle(); @@ -430,7 +447,7 @@ TEST_F(ServiceWorkerContextTest, UnregisterMultiple) { blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = origin3_p1; context()->RegisterServiceWorker( - GURL("http://www.other.com/service_worker4.js"), options, + GURL("https://www.other.com/service_worker4.js"), options, MakeRegisteredCallback(&called, ®istration_id4)); ASSERT_FALSE(called); base::RunLoop().RunUntilIdle(); @@ -474,30 +491,42 @@ TEST_F(ServiceWorkerContextTest, UnregisterMultiple) { base::RunLoop().RunUntilIdle(); - ASSERT_EQ(6u, notifications_.size()); - EXPECT_EQ(REGISTRATION_STORED, notifications_[0].type); + ASSERT_EQ(10u, notifications_.size()); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type); EXPECT_EQ(registration_id1, notifications_[0].registration_id); EXPECT_EQ(origin1_p1, notifications_[0].pattern); EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type); - EXPECT_EQ(origin1_p2, notifications_[1].pattern); - EXPECT_EQ(registration_id2, notifications_[1].registration_id); - EXPECT_EQ(REGISTRATION_STORED, notifications_[2].type); - EXPECT_EQ(origin2_p1, notifications_[2].pattern); - EXPECT_EQ(registration_id3, notifications_[2].registration_id); + EXPECT_EQ(registration_id1, notifications_[1].registration_id); + EXPECT_EQ(origin1_p1, notifications_[1].pattern); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[2].type); + EXPECT_EQ(origin1_p2, notifications_[2].pattern); + EXPECT_EQ(registration_id2, notifications_[2].registration_id); EXPECT_EQ(REGISTRATION_STORED, notifications_[3].type); - EXPECT_EQ(origin3_p1, notifications_[3].pattern); - EXPECT_EQ(registration_id4, notifications_[3].registration_id); - EXPECT_EQ(REGISTRATION_DELETED, notifications_[4].type); - EXPECT_EQ(origin1_p1, notifications_[4].pattern); - EXPECT_EQ(registration_id1, notifications_[4].registration_id); - EXPECT_EQ(REGISTRATION_DELETED, notifications_[5].type); - EXPECT_EQ(origin1_p2, notifications_[5].pattern); - EXPECT_EQ(registration_id2, notifications_[5].registration_id); + EXPECT_EQ(origin1_p2, notifications_[3].pattern); + EXPECT_EQ(registration_id2, notifications_[3].registration_id); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[4].type); + EXPECT_EQ(origin2_p1, notifications_[4].pattern); + EXPECT_EQ(registration_id3, notifications_[4].registration_id); + EXPECT_EQ(REGISTRATION_STORED, notifications_[5].type); + EXPECT_EQ(origin2_p1, notifications_[5].pattern); + EXPECT_EQ(registration_id3, notifications_[5].registration_id); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[6].type); + EXPECT_EQ(origin3_p1, notifications_[6].pattern); + EXPECT_EQ(registration_id4, notifications_[6].registration_id); + EXPECT_EQ(REGISTRATION_STORED, notifications_[7].type); + EXPECT_EQ(origin3_p1, notifications_[7].pattern); + EXPECT_EQ(registration_id4, notifications_[7].registration_id); + EXPECT_EQ(REGISTRATION_DELETED, notifications_[8].type); + EXPECT_EQ(origin1_p1, notifications_[8].pattern); + EXPECT_EQ(registration_id1, notifications_[8].registration_id); + EXPECT_EQ(REGISTRATION_DELETED, notifications_[9].type); + EXPECT_EQ(origin1_p2, notifications_[9].pattern); + EXPECT_EQ(registration_id2, notifications_[9].registration_id); } // Make sure registering a new script shares an existing registration. TEST_F(ServiceWorkerContextTest, RegisterNewScript) { - GURL pattern("http://www.example.com/"); + GURL pattern("https://www.example.com/"); blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = pattern; @@ -505,7 +534,7 @@ TEST_F(ServiceWorkerContextTest, RegisterNewScript) { int64_t old_registration_id = blink::mojom::kInvalidServiceWorkerRegistrationId; context()->RegisterServiceWorker( - GURL("http://www.example.com/service_worker.js"), options, + GURL("https://www.example.com/service_worker.js"), options, MakeRegisteredCallback(&called, &old_registration_id)); ASSERT_FALSE(called); @@ -518,7 +547,7 @@ TEST_F(ServiceWorkerContextTest, RegisterNewScript) { int64_t new_registration_id = blink::mojom::kInvalidServiceWorkerRegistrationId; context()->RegisterServiceWorker( - GURL("http://www.example.com/service_worker_new.js"), options, + GURL("https://www.example.com/service_worker_new.js"), options, MakeRegisteredCallback(&called, &new_registration_id)); ASSERT_FALSE(called); @@ -529,20 +558,26 @@ TEST_F(ServiceWorkerContextTest, RegisterNewScript) { new_registration_id); EXPECT_EQ(old_registration_id, new_registration_id); - ASSERT_EQ(2u, notifications_.size()); - EXPECT_EQ(REGISTRATION_STORED, notifications_[0].type); + ASSERT_EQ(4u, notifications_.size()); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type); EXPECT_EQ(pattern, notifications_[0].pattern); EXPECT_EQ(old_registration_id, notifications_[0].registration_id); EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type); EXPECT_EQ(pattern, notifications_[1].pattern); - EXPECT_EQ(new_registration_id, notifications_[1].registration_id); + EXPECT_EQ(old_registration_id, notifications_[1].registration_id); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[2].type); + EXPECT_EQ(pattern, notifications_[2].pattern); + EXPECT_EQ(new_registration_id, notifications_[2].registration_id); + EXPECT_EQ(REGISTRATION_STORED, notifications_[3].type); + EXPECT_EQ(pattern, notifications_[3].pattern); + EXPECT_EQ(new_registration_id, notifications_[3].registration_id); } // Make sure that when registering a duplicate pattern+script_url // combination, that the same registration is used. TEST_F(ServiceWorkerContextTest, RegisterDuplicateScript) { - GURL pattern("http://www.example.com/"); - GURL script_url("http://www.example.com/service_worker.js"); + GURL pattern("https://www.example.com/"); + GURL script_url("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = pattern; @@ -571,20 +606,23 @@ TEST_F(ServiceWorkerContextTest, RegisterDuplicateScript) { ASSERT_TRUE(called); EXPECT_EQ(old_registration_id, new_registration_id); - ASSERT_EQ(2u, notifications_.size()); - EXPECT_EQ(REGISTRATION_STORED, notifications_[0].type); + ASSERT_EQ(3u, notifications_.size()); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type); EXPECT_EQ(pattern, notifications_[0].pattern); EXPECT_EQ(old_registration_id, notifications_[0].registration_id); EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type); EXPECT_EQ(pattern, notifications_[1].pattern); EXPECT_EQ(old_registration_id, notifications_[1].registration_id); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[2].type); + EXPECT_EQ(pattern, notifications_[2].pattern); + EXPECT_EQ(old_registration_id, notifications_[2].registration_id); } TEST_F(ServiceWorkerContextTest, ProviderHostIterator) { const int kRenderProcessId1 = 1; const int kRenderProcessId2 = 2; - const GURL kOrigin1 = GURL("http://www.example.com/"); - const GURL kOrigin2 = GURL("https://www.example.com/"); + const GURL kOrigin1 = GURL("https://www.example.com/"); + const GURL kOrigin2 = GURL("https://another-origin.example.net/"); int provider_id = 1; std::vector remote_endpoints; @@ -617,14 +655,15 @@ TEST_F(ServiceWorkerContextTest, ProviderHostIterator) { // CreateProviderHostForServiceWorkerContext, the provider_id is not a fixed // number. blink::mojom::ServiceWorkerRegistrationOptions registration_opt; - registration_opt.scope = GURL("http://www.example.com/test/"); + registration_opt.scope = GURL("https://another-origin.example.net/test/"); scoped_refptr registration = base::MakeRefCounted( registration_opt, 1L /* registration_id */, helper_->context()->AsWeakPtr()); scoped_refptr version = base::MakeRefCounted( - registration.get(), GURL("http://www.example.com/test/script_url"), + registration.get(), + GURL("https://another-origin.example.net/test/script_url"), 1L /* version_id */, helper_->context()->AsWeakPtr()); // CreateProviderHostForServiceWorkerContext calls // ServiceWorkerProviderHost::CompleteStartWorkerPreparation, which requires a @@ -649,7 +688,8 @@ TEST_F(ServiceWorkerContextTest, ProviderHostIterator) { // Iterate over the client provider hosts that belong to kOrigin1. std::set results; - for (auto it = context()->GetClientProviderHostIterator(kOrigin1); + for (auto it = context()->GetClientProviderHostIterator( + kOrigin1, true /* include_reserved_clients */); !it->IsAtEnd(); it->Advance()) { results.insert(it->GetProviderHost()); } @@ -660,7 +700,8 @@ TEST_F(ServiceWorkerContextTest, ProviderHostIterator) { // Iterate over the provider hosts that belong to kOrigin2. // (This should not include host4 as it's not for controllee.) results.clear(); - for (auto it = context()->GetClientProviderHostIterator(kOrigin2); + for (auto it = context()->GetClientProviderHostIterator( + kOrigin2, true /* include_reserved_clients */); !it->IsAtEnd(); it->Advance()) { results.insert(it->GetProviderHost()); } @@ -685,8 +726,8 @@ class ServiceWorkerContextRecoveryTest }; TEST_P(ServiceWorkerContextRecoveryTest, DeleteAndStartOver) { - GURL pattern("http://www.example.com/"); - GURL script_url("http://www.example.com/service_worker.js"); + GURL pattern("https://www.example.com/"); + GURL script_url("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = pattern; @@ -713,10 +754,6 @@ TEST_P(ServiceWorkerContextRecoveryTest, DeleteAndStartOver) { false /* expect_waiting */, true /* expect_active */)); content::RunAllTasksUntilIdle(); - // Next handle id should be 1 (the next call should return 2) because - // registered worker should have taken ID 0. - EXPECT_EQ(1, context()->GetNewServiceWorkerHandleId()); - context()->ScheduleDeleteAndStartOver(); // The storage is disabled while the recovery process is running, so the @@ -749,18 +786,20 @@ TEST_P(ServiceWorkerContextRecoveryTest, DeleteAndStartOver) { false /* expect_waiting */, true /* expect_active */)); content::RunAllTasksUntilIdle(); - // The new context should take over next handle id. ID 2 should have been - // taken by the running registration, so the following method calls return 3. - EXPECT_EQ(3, context()->GetNewServiceWorkerHandleId()); - - ASSERT_EQ(3u, notifications_.size()); - EXPECT_EQ(REGISTRATION_STORED, notifications_[0].type); + ASSERT_EQ(5u, notifications_.size()); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type); EXPECT_EQ(pattern, notifications_[0].pattern); EXPECT_EQ(registration_id, notifications_[0].registration_id); - EXPECT_EQ(STORAGE_RECOVERED, notifications_[1].type); - EXPECT_EQ(REGISTRATION_STORED, notifications_[2].type); - EXPECT_EQ(pattern, notifications_[2].pattern); - EXPECT_EQ(registration_id, notifications_[2].registration_id); + EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type); + EXPECT_EQ(pattern, notifications_[1].pattern); + EXPECT_EQ(registration_id, notifications_[1].registration_id); + EXPECT_EQ(STORAGE_RECOVERED, notifications_[2].type); + EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[3].type); + EXPECT_EQ(pattern, notifications_[3].pattern); + EXPECT_EQ(registration_id, notifications_[3].registration_id); + EXPECT_EQ(REGISTRATION_STORED, notifications_[4].type); + EXPECT_EQ(pattern, notifications_[4].pattern); + EXPECT_EQ(registration_id, notifications_[4].registration_id); } INSTANTIATE_TEST_CASE_P(ServiceWorkerContextRecoveryTest, diff --git a/chromium/content/browser/service_worker/service_worker_context_watcher.cc b/chromium/content/browser/service_worker/service_worker_context_watcher.cc index 983547fe8ed..e4d1f2758c4 100644 --- a/chromium/content/browser/service_worker/service_worker_context_watcher.cc +++ b/chromium/content/browser/service_worker/service_worker_context_watcher.cc @@ -337,17 +337,13 @@ void ServiceWorkerContextWatcher::OnReportConsoleMessage( void ServiceWorkerContextWatcher::OnControlleeAdded( int64_t version_id, const std::string& uuid, - int process_id, - int route_id, - const base::Callback& web_contents_getter, - blink::mojom::ServiceWorkerProviderType type) { + const ServiceWorkerClientInfo& info) { DCHECK_CURRENTLY_ON(BrowserThread::IO); auto it = version_info_map_.find(version_id); if (it == version_info_map_.end()) return; ServiceWorkerVersionInfo* version = it->second.get(); - version->clients[uuid] = ServiceWorkerVersionInfo::ClientInfo( - process_id, route_id, web_contents_getter, type); + version->clients[uuid] = info; SendVersionInfo(*version); } @@ -362,8 +358,9 @@ void ServiceWorkerContextWatcher::OnControlleeRemoved(int64_t version_id, SendVersionInfo(*version); } -void ServiceWorkerContextWatcher::OnRegistrationStored(int64_t registration_id, - const GURL& pattern) { +void ServiceWorkerContextWatcher::OnRegistrationCompleted( + int64_t registration_id, + const GURL& pattern) { DCHECK_CURRENTLY_ON(BrowserThread::IO); SendRegistrationInfo(registration_id, pattern, ServiceWorkerRegistrationInfo::IS_NOT_DELETED); diff --git a/chromium/content/browser/service_worker/service_worker_context_watcher.h b/chromium/content/browser/service_worker/service_worker_context_watcher.h index aa432522b51..68f86f49e37 100644 --- a/chromium/content/browser/service_worker/service_worker_context_watcher.h +++ b/chromium/content/browser/service_worker/service_worker_context_watcher.h @@ -103,17 +103,13 @@ class CONTENT_EXPORT ServiceWorkerContextWatcher int process_id, int thread_id, const ConsoleMessage& message) override; - void OnControlleeAdded( - int64_t version_id, - const std::string& uuid, - int process_id, - int route_id, - const base::Callback& web_contents_getter, - blink::mojom::ServiceWorkerProviderType type) override; + void OnControlleeAdded(int64_t version_id, + const std::string& uuid, + const ServiceWorkerClientInfo& info) override; void OnControlleeRemoved(int64_t version_id, const std::string& uuid) override; - void OnRegistrationStored(int64_t registration_id, - const GURL& pattern) override; + void OnRegistrationCompleted(int64_t registration_id, + const GURL& pattern) override; void OnRegistrationDeleted(int64_t registration_id, const GURL& pattern) override; 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 241193f74fd..b91bad88fcd 100644 --- a/chromium/content/browser/service_worker/service_worker_context_wrapper.cc +++ b/chromium/content/browser/service_worker/service_worker_context_wrapper.cc @@ -165,12 +165,9 @@ bool ServiceWorkerContext::ScopeMatches(const GURL& scope, const GURL& url) { ServiceWorkerContextWrapper::ServiceWorkerContextWrapper( BrowserContext* browser_context) : core_observer_list_( - new base::ObserverListThreadSafe()), + base::MakeRefCounted()), process_manager_( - std::make_unique(browser_context)), - is_incognito_(false), - storage_partition_(nullptr), - resource_context_(nullptr) { + std::make_unique(browser_context)) { DCHECK_CURRENTLY_ON(BrowserThread::UI); // Add this object as an observer of the wrapped |context_core_|. This lets us @@ -245,10 +242,11 @@ ResourceContext* ServiceWorkerContextWrapper::resource_context() { return resource_context_; } -void ServiceWorkerContextWrapper::OnRegistrationStored(int64_t registration_id, - const GURL& pattern) { +void ServiceWorkerContextWrapper::OnRegistrationCompleted( + int64_t registration_id, + const GURL& pattern) { for (auto& observer : observer_list_) - observer.OnRegistrationStored(pattern); + observer.OnRegistrationCompleted(pattern); } void ServiceWorkerContextWrapper::AddObserver( @@ -527,7 +525,8 @@ ServiceWorkerContextWrapper::GetProviderHostIds(const GURL& origin) const { new std::vector>()); for (std::unique_ptr it = - context_core_->GetClientProviderHostIterator(origin); + context_core_->GetClientProviderHostIterator( + origin, false /* include_reserved_clients */); !it->IsAtEnd(); it->Advance()) { ServiceWorkerProviderHost* provider_host = it->GetProviderHost(); provider_host_ids->push_back( @@ -858,10 +857,10 @@ void ServiceWorkerContextWrapper::InitInternal( quota_manager_proxy->RegisterClient(new ServiceWorkerQuotaClient(this)); } - context_core_.reset(new ServiceWorkerContextCore( + context_core_ = std::make_unique( user_data_directory, std::move(database_task_runner), quota_manager_proxy, special_storage_policy, loader_factory_getter, core_observer_list_.get(), - this)); + this); } void ServiceWorkerContextWrapper::ShutdownOnIO() { 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 96ec90d9a1c..e06300d2141 100644 --- a/chromium/content/browser/service_worker/service_worker_context_wrapper.h +++ b/chromium/content/browser/service_worker/service_worker_context_wrapper.h @@ -98,8 +98,8 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper } // ServiceWorkerContextCoreObserver implementation: - void OnRegistrationStored(int64_t registration_id, - const GURL& pattern) override; + void OnRegistrationCompleted(int64_t registration_id, + const GURL& pattern) override; // ServiceWorkerContext implementation: void AddObserver(ServiceWorkerContextObserver* observer) override; @@ -343,9 +343,9 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper // Observers of |context_core_| which live within content's implementation // boundary. Shared with |context_core_|. - const scoped_refptr< - base::ObserverListThreadSafe> - core_observer_list_; + using ServiceWorkerContextObserverList = + base::ObserverListThreadSafe; + const scoped_refptr core_observer_list_; // Observers which live outside content's implementation boundary. Observer // methods will always be dispatched on the UI thread. @@ -356,13 +356,13 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper std::unique_ptr context_core_; // Initialized in Init(); true if the user data directory is empty. - bool is_incognito_; + bool is_incognito_ = false; // Raw pointer to the StoragePartitionImpl owning |this|. - StoragePartitionImpl* storage_partition_; + StoragePartitionImpl* storage_partition_ = nullptr; // The ResourceContext associated with this context. - ResourceContext* resource_context_; + ResourceContext* resource_context_ = nullptr; DISALLOW_COPY_AND_ASSIGN(ServiceWorkerContextWrapper); }; 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 index ea569859aef..d2dcaa67d40 100644 --- a/chromium/content/browser/service_worker/service_worker_controllee_request_handler.cc +++ b/chromium/content/browser/service_worker/service_worker_controllee_request_handler.cc @@ -268,8 +268,13 @@ ServiceWorkerControlleeRequestHandler::MaybeCreateSubresourceLoaderParams() { controller_info->endpoint = provider_host_->GetControllerServiceWorkerPtr().PassInterface(); controller_info->client_id = provider_host_->client_uuid(); - controller_info->object_info = provider_host_->GetOrCreateServiceWorkerHandle( - provider_host_->controller()); + base::WeakPtr handle = + provider_host_->GetOrCreateServiceWorkerHandle( + provider_host_->controller()); + if (handle) { + params.controller_service_worker_handle = handle; + controller_info->object_info = handle->CreateIncompleteObjectInfo(); + } params.controller_service_worker_info = std::move(controller_info); return base::Optional(std::move(params)); } diff --git a/chromium/content/browser/service_worker/service_worker_database.cc b/chromium/content/browser/service_worker/service_worker_database.cc index 746351acfb8..c34711b2eb7 100644 --- a/chromium/content/browser/service_worker/service_worker_database.cc +++ b/chromium/content/browser/service_worker/service_worker_database.cc @@ -1257,7 +1257,7 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen( leveldb_env::Options options; options.create_if_missing = create_if_missing; if (IsDatabaseInMemory()) { - env_.reset(leveldb_chrome::NewMemEnv(leveldb::Env::Default())); + env_ = leveldb_chrome::NewMemEnv("service-worker"); options.env = env_.get(); } else { options.env = g_service_worker_env.Pointer(); @@ -1466,15 +1466,10 @@ void ServiceWorkerDatabase::WriteRegistrationDataInBatch( for (uint32_t feature : registration.used_features) data.add_used_features(feature); - // TODO(https://crbug.com/675540): Remove the the command line check and - // always set to data when shipping the updateViaCache flag to stable. - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableExperimentalWebPlatformFeatures)) { - data.set_update_via_cache( - static_cast< - ServiceWorkerRegistrationData_ServiceWorkerUpdateViaCacheType>( - registration.update_via_cache)); - } + data.set_update_via_cache( + static_cast< + ServiceWorkerRegistrationData_ServiceWorkerUpdateViaCacheType>( + registration.update_via_cache)); std::string value; bool success = data.SerializeToString(&value); 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 5401fdb46d8..0f7a0bbd637 100644 --- a/chromium/content/browser/service_worker/service_worker_dispatcher_host.cc +++ b/chromium/content/browser/service_worker/service_worker_dispatcher_host.cc @@ -50,16 +50,12 @@ const uint32_t kServiceWorkerFilteredMessageClasses[] = { } // namespace -ServiceWorkerDispatcherHost::ServiceWorkerDispatcherHost( - int render_process_id, - ResourceContext* resource_context) +ServiceWorkerDispatcherHost::ServiceWorkerDispatcherHost(int render_process_id) : BrowserMessageFilter(kServiceWorkerFilteredMessageClasses, arraysize(kServiceWorkerFilteredMessageClasses)), BrowserAssociatedInterface(this, this), render_process_id_(render_process_id), - resource_context_(resource_context), - channel_ready_(false), weak_ptr_factory_(this) {} ServiceWorkerDispatcherHost::~ServiceWorkerDispatcherHost() { @@ -90,18 +86,6 @@ void ServiceWorkerDispatcherHost::Init( phase_ = Phase::kAddedToContext; } -void ServiceWorkerDispatcherHost::OnFilterAdded(IPC::Channel* channel) { - TRACE_EVENT0("ServiceWorker", - "ServiceWorkerDispatcherHost::OnFilterAdded"); - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - channel_ready_ = true; - std::vector> messages; - messages.swap(pending_messages_); - for (auto& message : messages) { - BrowserMessageFilter::Send(message.release()); - } -} - void ServiceWorkerDispatcherHost::OnFilterRemoved() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Don't wait until the destructor to teardown since a new dispatcher host @@ -112,7 +96,6 @@ void ServiceWorkerDispatcherHost::OnFilterRemoved() { } phase_ = Phase::kRemovedFromContext; context_wrapper_ = nullptr; - channel_ready_ = false; } void ServiceWorkerDispatcherHost::OnDestruct() const { @@ -126,17 +109,6 @@ bool ServiceWorkerDispatcherHost::OnMessageReceived( return false; } -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(base::WrapUnique(message)); - return true; -} - base::WeakPtr ServiceWorkerDispatcherHost::AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); @@ -164,13 +136,10 @@ void ServiceWorkerDispatcherHost::OnProviderCreated( return; } - // Retrieve the provider host previously created for navigation requests. - std::unique_ptr provider_host; - ServiceWorkerNavigationHandleCore* navigation_handle_core = - GetContext()->GetNavigationHandleCore(info.provider_id); - if (navigation_handle_core != nullptr) - provider_host = navigation_handle_core->RetrievePreCreatedHost(); - + // Retrieve the provider host pre-created for the navigation. + std::unique_ptr provider_host = + GetContext()->ReleaseProviderHost(ChildProcessHost::kInvalidUniqueID, + info.provider_id); // If no host is found, create one. // TODO(crbug.com/789111#c14): This is probably not right, see bug. if (!provider_host) { 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 89a2aae8739..0a096b2d367 100644 --- a/chromium/content/browser/service_worker/service_worker_dispatcher_host.h +++ b/chromium/content/browser/service_worker/service_worker_dispatcher_host.h @@ -25,7 +25,6 @@ namespace content { -class ResourceContext; class ServiceWorkerContextCore; class ServiceWorkerContextWrapper; @@ -58,33 +57,23 @@ FORWARD_DECLARE_TEST(BackgroundSyncManagerTest, // InterfacePtrs on the same render process are bound to the same // content::ServiceWorkerDispatcherHost. This can be overridden only for // testing. +// +// TODO(leonhsl): This class no longer needs to be a BrowserMessageFilter +// because we are already in a pure Mojo world. class CONTENT_EXPORT ServiceWorkerDispatcherHost : public BrowserMessageFilter, public BrowserAssociatedInterface, public mojom::ServiceWorkerDispatcherHost { public: - ServiceWorkerDispatcherHost( - int render_process_id, - ResourceContext* resource_context); + explicit ServiceWorkerDispatcherHost(int render_process_id); void Init(ServiceWorkerContextWrapper* context_wrapper); // BrowserMessageFilter implementation - void OnFilterAdded(IPC::Channel* channel) override; void OnFilterRemoved() override; void OnDestruct() const override; 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. - bool Send(IPC::Message* message) override; - - ResourceContext* resource_context() { return resource_context_; } - base::WeakPtr AsWeakPtr(); protected: @@ -117,15 +106,11 @@ class CONTENT_EXPORT ServiceWorkerDispatcherHost ServiceWorkerContextCore* GetContext(); const int render_process_id_; - ResourceContext* resource_context_; // Only accessed on the IO thread. Phase phase_ = Phase::kInitial; // Only accessed on the IO thread. scoped_refptr context_wrapper_; - bool channel_ready_; // True after BrowserMessageFilter::sender_ != NULL. - std::vector> pending_messages_; - base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDispatcherHost); 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 b4394c09853..1505284dc5f 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 @@ -25,7 +25,6 @@ #include "content/common/service_worker/service_worker_utils.h" #include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/content_switches.h" -#include "content/public/test/mock_resource_context.h" #include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h" @@ -87,9 +86,8 @@ std::unique_ptr CreateNavigationHandleCore( class TestingServiceWorkerDispatcherHost : public ServiceWorkerDispatcherHost { public: TestingServiceWorkerDispatcherHost(int process_id, - ResourceContext* resource_context, EmbeddedWorkerTestHelper* helper) - : ServiceWorkerDispatcherHost(process_id, resource_context), + : ServiceWorkerDispatcherHost(process_id), bad_messages_received_count_(0), helper_(helper) {} @@ -130,8 +128,8 @@ class ServiceWorkerDispatcherHostTest : public testing::Test { helper_.reset(helper.release()); // Replace the default dispatcher host. int process_id = helper_->mock_render_process_id(); - dispatcher_host_ = new TestingServiceWorkerDispatcherHost( - process_id, &resource_context_, helper_.get()); + dispatcher_host_ = + new TestingServiceWorkerDispatcherHost(process_id, helper_.get()); helper_->RegisterDispatcherHost(process_id, nullptr); dispatcher_host_->Init(context_wrapper()); } @@ -179,7 +177,6 @@ class ServiceWorkerDispatcherHostTest : public testing::Test { } TestBrowserThreadBundle browser_thread_bundle_; - content::MockResourceContext resource_context_; std::unique_ptr helper_; scoped_refptr dispatcher_host_; scoped_refptr registration_; @@ -198,7 +195,7 @@ TEST_F(ServiceWorkerDispatcherHostTest, ProviderCreatedAndDestroyed) { navigation_handle_core = CreateNavigationHandleCore(helper_->context_wrapper()); ASSERT_TRUE(navigation_handle_core); - std::unique_ptr host1 = + base::WeakPtr host1 = ServiceWorkerProviderHost::PreCreateNavigationHost( context()->AsWeakPtr(), true /* are_ancestors_secure */, base::RepeatingCallback()); @@ -210,7 +207,7 @@ TEST_F(ServiceWorkerDispatcherHostTest, ProviderCreatedAndDestroyed) { host1->provider_id(), 1 /* route_id */, host1->provider_type(), host1->is_parent_frame_secure()); RemoteProviderInfo remote_info_1 = SetupProviderHostInfoPtrs(&host_info_1); - navigation_handle_core->DidPreCreateProviderHost(std::move(host1)); + navigation_handle_core->DidPreCreateProviderHost(host1->provider_id()); dispatcher_host_->OnProviderCreated(std::move(host_info_1)); EXPECT_TRUE(context()->GetProviderHost(process_id, kProviderId1)); @@ -229,8 +226,7 @@ TEST_F(ServiceWorkerDispatcherHostTest, ProviderCreatedAndDestroyed) { navigation_handle_core = CreateNavigationHandleCore(helper_->context_wrapper()); ASSERT_TRUE(navigation_handle_core); - // ProviderHost should be created before OnProviderCreated. - std::unique_ptr host2 = + base::WeakPtr host2 = ServiceWorkerProviderHost::PreCreateNavigationHost( context()->AsWeakPtr(), true /* are_ancestors_secure */, base::RepeatingCallback()); @@ -239,7 +235,7 @@ TEST_F(ServiceWorkerDispatcherHostTest, ProviderCreatedAndDestroyed) { host2->provider_id(), 2 /* route_id */, host2->provider_type(), host2->is_parent_frame_secure()); RemoteProviderInfo remote_info_2 = SetupProviderHostInfoPtrs(&host_info_2); - navigation_handle_core->DidPreCreateProviderHost(std::move(host2)); + navigation_handle_core->DidPreCreateProviderHost(host2->provider_id()); // Deletion of the dispatcher_host should cause providers for that // process to get deleted as well. @@ -251,8 +247,8 @@ TEST_F(ServiceWorkerDispatcherHostTest, ProviderCreatedAndDestroyed) { } TEST_F(ServiceWorkerDispatcherHostTest, CleanupOnRendererCrash) { - GURL pattern = GURL("http://www.example.com/"); - GURL script_url = GURL("http://www.example.com/service_worker.js"); + GURL pattern = GURL("https://www.example.com/"); + GURL script_url = GURL("https://www.example.com/service_worker.js"); int process_id = helper_->mock_render_process_id(); SendProviderCreated(blink::mojom::ServiceWorkerProviderType::kForWindow, @@ -286,9 +282,9 @@ TEST_F(ServiceWorkerDispatcherHostTest, CleanupOnRendererCrash) { // We should be able to hook up a new dispatcher host although the old object // is not yet destroyed. This is what the browser does when reusing a crashed // render process. - scoped_refptr new_dispatcher_host( - new TestingServiceWorkerDispatcherHost(process_id, &resource_context_, - helper_.get())); + auto new_dispatcher_host = + base::MakeRefCounted(process_id, + helper_.get()); new_dispatcher_host->Init(context_wrapper()); // To show the new dispatcher can operate, simulate provider creation. Since diff --git a/chromium/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/chromium/content/browser/service_worker/service_worker_fetch_dispatcher.cc index da9ecafedbf..740ca70679f 100644 --- a/chromium/content/browser/service_worker/service_worker_fetch_dispatcher.cc +++ b/chromium/content/browser/service_worker/service_worker_fetch_dispatcher.cc @@ -53,7 +53,13 @@ class DelegatingURLLoader final : public network::mojom::URLLoader { : binding_(this), loader_(std::move(loader)) {} ~DelegatingURLLoader() override {} - void FollowRedirect() override { loader_->FollowRedirect(); } + void FollowRedirect(const base::Optional& + modified_request_headers) override { + DCHECK(!modified_request_headers.has_value()) + << "Redirect with modified headers was not supported yet. " + "crbug.com/845683"; + loader_->FollowRedirect(base::nullopt); + } void ProceedWithResponse() override { NOTREACHED(); } void SetPriority(net::RequestPriority priority, @@ -103,13 +109,13 @@ void NotifyNavigationPreloadRequestSentOnUI( void NotifyNavigationPreloadResponseReceivedOnUI( const GURL& url, - const network::ResourceResponseHead& head, + scoped_refptr response, const std::pair& worker_id, const std::string& request_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); ServiceWorkerDevToolsManager::GetInstance() ->NavigationPreloadResponseReceived(worker_id.first, worker_id.second, - request_id, url, head); + request_id, url, response->head); } void NotifyNavigationPreloadCompletedOnUI( @@ -134,9 +140,12 @@ class DelegatingURLLoaderClient final : public network::mojom::URLLoaderClient { : binding_(this), client_(std::move(client)), on_response_(std::move(on_response)), - url_(request.url) { + url_(request.url), + devtools_enabled_(request.report_raw_headers) { + if (!devtools_enabled_) + return; AddDevToolsCallback( - base::Bind(&NotifyNavigationPreloadRequestSentOnUI, request)); + base::BindOnce(&NotifyNavigationPreloadRequestSentOnUI, request)); } ~DelegatingURLLoaderClient() override { if (!completed_) { @@ -144,8 +153,10 @@ class DelegatingURLLoaderClient final : public network::mojom::URLLoaderClient { network::URLLoaderCompletionStatus status; status.error_code = net::ERR_ABORTED; client_->OnComplete(status); + if (!devtools_enabled_) + return; AddDevToolsCallback( - base::Bind(&NotifyNavigationPreloadCompletedOnUI, status)); + base::BindOnce(&NotifyNavigationPreloadCompletedOnUI, status)); } } @@ -176,8 +187,14 @@ class DelegatingURLLoaderClient final : public network::mojom::URLLoaderClient { client_->OnReceiveResponse(head, std::move(downloaded_file)); DCHECK(on_response_); std::move(on_response_).Run(); + if (!devtools_enabled_) + return; + // Make a deep copy of ResourceResponseHead before passing it cross-thread. + auto resource_response = base::MakeRefCounted(); + resource_response->head = head; AddDevToolsCallback( - base::Bind(&NotifyNavigationPreloadResponseReceivedOnUI, url_, head)); + base::BindOnce(&NotifyNavigationPreloadResponseReceivedOnUI, url_, + resource_response->DeepCopy())); } void OnReceiveRedirect(const net::RedirectInfo& redirect_info, const network::ResourceResponseHead& head) override { @@ -186,11 +203,18 @@ class DelegatingURLLoaderClient final : public network::mojom::URLLoaderClient { // OnReceiveRedirect IPC and don't send OnComplete IPC. The service worker // will clean up the preload request when OnReceiveRedirect() is called. client_->OnReceiveRedirect(redirect_info, head); + + if (!devtools_enabled_) + return; + // Make a deep copy of ResourceResponseHead before passing it cross-thread. + auto resource_response = base::MakeRefCounted(); + resource_response->head = head; AddDevToolsCallback( - base::Bind(&NotifyNavigationPreloadResponseReceivedOnUI, url_, head)); + base::BindOnce(&NotifyNavigationPreloadResponseReceivedOnUI, url_, + resource_response->DeepCopy())); network::URLLoaderCompletionStatus status; AddDevToolsCallback( - base::Bind(&NotifyNavigationPreloadCompletedOnUI, status)); + base::BindOnce(&NotifyNavigationPreloadCompletedOnUI, status)); } void OnStartLoadingResponseBody( mojo::ScopedDataPipeConsumerHandle body) override { @@ -201,8 +225,10 @@ class DelegatingURLLoaderClient final : public network::mojom::URLLoaderClient { return; completed_ = true; client_->OnComplete(status); + if (!devtools_enabled_) + return; AddDevToolsCallback( - base::Bind(&NotifyNavigationPreloadCompletedOnUI, status)); + base::BindOnce(&NotifyNavigationPreloadCompletedOnUI, status)); } void Bind(network::mojom::URLLoaderClientPtr* ptr_info) { @@ -211,7 +237,7 @@ class DelegatingURLLoaderClient final : public network::mojom::URLLoaderClient { private: void MaybeRunDevToolsCallbacks() { - if (!worker_id_) + if (!worker_id_ || !devtools_enabled_) return; while (!devtools_callbacks.empty()) { BrowserThread::PostTask( @@ -222,7 +248,7 @@ class DelegatingURLLoaderClient final : public network::mojom::URLLoaderClient { } } void AddDevToolsCallback( - base::Callback callback) { + base::OnceCallback callback) { devtools_callbacks.push(std::move(callback)); MaybeRunDevToolsCallbacks(); } @@ -232,10 +258,11 @@ class DelegatingURLLoaderClient final : public network::mojom::URLLoaderClient { base::OnceClosure on_response_; bool completed_ = false; const GURL url_; + const bool devtools_enabled_; base::Optional> worker_id_; std::string devtools_request_id_; - base::queue> + base::queue> devtools_callbacks; DISALLOW_COPY_AND_ASSIGN(DelegatingURLLoaderClient); }; diff --git a/chromium/content/browser/service_worker/service_worker_handle.cc b/chromium/content/browser/service_worker/service_worker_handle.cc index 1b87b9edab0..777cea9149d 100644 --- a/chromium/content/browser/service_worker/service_worker_handle.cc +++ b/chromium/content/browser/service_worker/service_worker_handle.cc @@ -11,6 +11,7 @@ #include "content/browser/service_worker/service_worker_type_converters.h" #include "content/common/service_worker/service_worker_types.h" #include "content/common/service_worker/service_worker_utils.h" +#include "content/public/browser/browser_thread.h" #include "content/public/common/browser_side_navigation_policy.h" namespace content { @@ -87,6 +88,9 @@ bool SetSourceClientInfo( return true; } +// The output |event| must be sent over Mojo immediately after this function +// returns. See ServiceWorkerHandle::CreateCompleteObjectInfoToSend() for +// details. bool SetSourceServiceWorkerInfo(scoped_refptr worker, base::WeakPtr source_service_worker_provider_host, @@ -98,9 +102,16 @@ bool SetSourceServiceWorkerInfo(scoped_refptr worker, DCHECK_EQ(blink::mojom::ServiceWorkerProviderType::kForServiceWorker, source_service_worker_provider_host->provider_type()); - blink::mojom::ServiceWorkerObjectInfoPtr source_worker_info = + blink::mojom::ServiceWorkerObjectInfoPtr source_worker_info; + base::WeakPtr service_worker_handle = worker->provider_host()->GetOrCreateServiceWorkerHandle( source_service_worker_provider_host->running_hosted_version()); + if (service_worker_handle) { + // CreateCompleteObjectInfoToSend() is safe because |source_worker_info| + // will be sent immediately by the caller of this function. + source_worker_info = + service_worker_handle->CreateCompleteObjectInfoToSend(); + } (*event)->source_info_for_service_worker = std::move(source_worker_info); // Hide the service worker url if the service worker has a unique origin. @@ -159,7 +170,6 @@ ServiceWorkerHandle::ServiceWorkerHandle( provider_host_(provider_host), provider_origin_(url::Origin::Create(provider_host->document_url())), provider_id_(provider_host->provider_id()), - handle_id_(context->GetNewServiceWorkerHandleId()), version_(std::move(version)), weak_ptr_factory_(this) { DCHECK(context_ && provider_host_ && version_); @@ -170,20 +180,37 @@ ServiceWorkerHandle::ServiceWorkerHandle( } ServiceWorkerHandle::~ServiceWorkerHandle() { + // TODO(crbug.com/838410): These CHECKs are temporary debugging for the linked + // bug. + CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + CHECK(!in_dtor_); + in_dtor_ = true; + version_->RemoveListener(this); } void ServiceWorkerHandle::OnVersionStateChanged(ServiceWorkerVersion* version) { DCHECK(version); - provider_host_->SendServiceWorkerStateChangedMessage( - handle_id_, - mojo::ConvertTo(version->status())); + blink::mojom::ServiceWorkerState state = + mojo::ConvertTo(version->status()); + remote_objects_.ForAllPtrs( + [state](blink::mojom::ServiceWorkerObject* remote_object) { + remote_object->StateChanged(state); + }); } blink::mojom::ServiceWorkerObjectInfoPtr -ServiceWorkerHandle::CreateObjectInfo() { +ServiceWorkerHandle::CreateCompleteObjectInfoToSend() { + auto info = CreateIncompleteObjectInfo(); + blink::mojom::ServiceWorkerObjectAssociatedPtr remote_object; + info->request = mojo::MakeRequest(&remote_object); + remote_objects_.AddPtr(std::move(remote_object)); + return info; +} + +blink::mojom::ServiceWorkerObjectInfoPtr +ServiceWorkerHandle::CreateIncompleteObjectInfo() { auto info = blink::mojom::ServiceWorkerObjectInfo::New(); - info->handle_id = handle_id_; info->url = version_->script_url(); info->state = mojo::ConvertTo(version_->status()); @@ -192,6 +219,23 @@ ServiceWorkerHandle::CreateObjectInfo() { return info; } +void ServiceWorkerHandle::AddRemoteObjectPtrAndUpdateState( + blink::mojom::ServiceWorkerObjectAssociatedPtrInfo remote_object_ptr_info, + blink::mojom::ServiceWorkerState sent_state) { + DCHECK(remote_object_ptr_info.is_valid()); + blink::mojom::ServiceWorkerObjectAssociatedPtr remote_object; + remote_object.Bind(std::move(remote_object_ptr_info)); + auto state = + mojo::ConvertTo(version_->status()); + if (sent_state != state) + remote_object->StateChanged(state); + remote_objects_.AddPtr(std::move(remote_object)); +} + +base::WeakPtr ServiceWorkerHandle::AsWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + void ServiceWorkerHandle::PostMessageToServiceWorker( ::blink::TransferableMessage message) { // When this method is called the encoded_message inside message could just @@ -248,10 +292,6 @@ void ServiceWorkerHandle::DispatchExtendableMessageEvent( NOTREACHED() << provider_host_->provider_type(); } -base::WeakPtr ServiceWorkerHandle::AsWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); -} - void ServiceWorkerHandle::OnConnectionError() { // If there are still bindings, |this| is still being used. if (!bindings_.empty()) diff --git a/chromium/content/browser/service_worker/service_worker_handle.h b/chromium/content/browser/service_worker/service_worker_handle.h index b6a2d421279..948f68283ac 100644 --- a/chromium/content/browser/service_worker/service_worker_handle.h +++ b/chromium/content/browser/service_worker/service_worker_handle.h @@ -14,6 +14,7 @@ #include "content/common/content_export.h" #include "content/common/service_worker/service_worker_types.h" #include "mojo/public/cpp/bindings/associated_binding_set.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" #include "url/origin.h" @@ -46,13 +47,40 @@ class CONTENT_EXPORT ServiceWorkerHandle // ServiceWorkerVersion::Listener overrides. void OnVersionStateChanged(ServiceWorkerVersion* version) override; - // Establishes a new mojo connection into |bindings_|. - blink::mojom::ServiceWorkerObjectInfoPtr CreateObjectInfo(); + // Returns an info for the ServiceWorker object of this handle. The info + // contains a Mojo ptr to |this| which ensures |this| stays alive while the + // info is alive. Furthermore, it contains a Mojo request for the + // ServiceWorkerObject interface in the renderer. |this| will make calls to + // the ServiceWorkerObject to update its state. + // + // WARNING: The returned info must be sent immediately over Mojo, because + // |this| will start making calls on an associated interface ptr to + // ServiceWorkerObject, which crashes unless the request inside the info has + // been sent. If the info cannot be sent immediately, use + // CreateIncompleteObjectInfo() instead. + blink::mojom::ServiceWorkerObjectInfoPtr CreateCompleteObjectInfoToSend(); + + // Similar to CreateCompleteObjectInfoToSend(), except the returned info has + // an empty Mojo request for ServiceWorkerObject. To make the info usable, the + // caller should add a request to the info, send the info over Mojo, and + // then call AddRemoteObjectPtrAndUpdateState(). + // + // This function is useful when an info is needed before it can be sent over + // Mojo. + blink::mojom::ServiceWorkerObjectInfoPtr CreateIncompleteObjectInfo(); + + // Starts to use the |remote_object_ptr_info| as a valid Mojo pipe endpoint, + // and triggers statechanged event if |sent_state| is old and needs to be + // updated. + void AddRemoteObjectPtrAndUpdateState( + blink::mojom::ServiceWorkerObjectAssociatedPtrInfo remote_object_ptr_info, + blink::mojom::ServiceWorkerState sent_state); int provider_id() const { return provider_id_; } - int handle_id() const { return handle_id_; } ServiceWorkerVersion* version() { return version_.get(); } + base::WeakPtr AsWeakPtr(); + private: friend class service_worker_handle_unittest::ServiceWorkerHandleTest; @@ -68,8 +96,6 @@ class CONTENT_EXPORT ServiceWorkerHandle ::blink::TransferableMessage message, base::OnceCallback callback); - base::WeakPtr AsWeakPtr(); - void OnConnectionError(); base::WeakPtr context_; @@ -82,9 +108,20 @@ class CONTENT_EXPORT ServiceWorkerHandle // object. const url::Origin provider_origin_; const int provider_id_; - const int handle_id_; scoped_refptr version_; mojo::AssociatedBindingSet bindings_; + // Typically |remote_objects_| contains only one Mojo connection, + // corresponding to the content::WebServiceWorkerImpl in the renderer which + // corresponds to the ServiceWorker JavaScript object. However, multiple Mojo + // connections may exist while propagating multiple service worker object + // infos to the renderer process, but only the first one that arrived there + // will be used to create the new content::WebServiceWorkerImpl instance and + // be bound to it. + mojo::AssociatedInterfacePtrSet + remote_objects_; + + // TODO(crbug.com/838410): Temporary debugging for the linked bug. + bool in_dtor_ = false; base::WeakPtrFactory weak_ptr_factory_; diff --git a/chromium/content/browser/service_worker/service_worker_handle_unittest.cc b/chromium/content/browser/service_worker/service_worker_handle_unittest.cc index 1c31c0c11ff..6122d08b40c 100644 --- a/chromium/content/browser/service_worker/service_worker_handle_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_handle_unittest.cc @@ -16,17 +16,13 @@ #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_types.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.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/public/test/test_renderer_host.h" #include "content/public/test/web_contents_tester.h" -#include "ipc/ipc_message.h" -#include "ipc/ipc_test_sink.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_state.mojom.h" @@ -34,17 +30,6 @@ namespace content { namespace service_worker_handle_unittest { -void VerifyStateChangedMessage(int expected_handle_id, - blink::mojom::ServiceWorkerState expected_state, - const IPC::Message* message) { - ASSERT_TRUE(message != nullptr); - ServiceWorkerMsg_ServiceWorkerStateChanged::Param param; - ASSERT_TRUE(ServiceWorkerMsg_ServiceWorkerStateChanged::Read( - message, ¶m)); - EXPECT_EQ(expected_handle_id, std::get<1>(param)); - EXPECT_EQ(expected_state, std::get<2>(param)); -} - static void SaveStatusCallback(bool* called, ServiceWorkerStatusCode* out, ServiceWorkerStatusCode status) { @@ -94,7 +79,6 @@ class FailToStartWorkerTestHelper : public ExtendableMessageEventTestHelper { bool pause_after_download, mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, mojom::ControllerServiceWorkerRequest controller_request, - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) @@ -106,24 +90,26 @@ class FailToStartWorkerTestHelper : public ExtendableMessageEventTestHelper { } }; -class TestingServiceWorkerDispatcherHost : public ServiceWorkerDispatcherHost { +class MockServiceWorkerObject : public blink::mojom::ServiceWorkerObject { public: - TestingServiceWorkerDispatcherHost(int process_id, - ResourceContext* resource_context, - EmbeddedWorkerTestHelper* helper) - : ServiceWorkerDispatcherHost(process_id, resource_context), - bad_message_received_count_(0), - helper_(helper) {} - - bool Send(IPC::Message* message) override { return helper_->Send(message); } + explicit MockServiceWorkerObject( + blink::mojom::ServiceWorkerObjectInfoPtr info) + : info_(std::move(info)), + state_(info_->state), + binding_(this, std::move(info_->request)) {} + ~MockServiceWorkerObject() override = default; - void ShutdownForBadMessage() override { ++bad_message_received_count_; } + blink::mojom::ServiceWorkerState state() const { return state_; } - int bad_message_received_count_; + private: + // Implements blink::mojom::ServiceWorkerObject. + void StateChanged(blink::mojom::ServiceWorkerState state) override { + state_ = state; + } - protected: - EmbeddedWorkerTestHelper* helper_; - ~TestingServiceWorkerDispatcherHost() override {} + blink::mojom::ServiceWorkerObjectInfoPtr info_; + blink::mojom::ServiceWorkerState state_; + mojo::AssociatedBinding binding_; }; class ServiceWorkerHandleTest : public testing::Test { @@ -134,8 +120,8 @@ class ServiceWorkerHandleTest : public testing::Test { void Initialize(std::unique_ptr helper) { helper_ = std::move(helper); - dispatcher_host_ = new TestingServiceWorkerDispatcherHost( - helper_->mock_render_process_id(), &resource_context_, helper_.get()); + dispatcher_host_ = base::MakeRefCounted( + helper_->mock_render_process_id()); helper_->RegisterDispatcherHost(helper_->mock_render_process_id(), dispatcher_host_); dispatcher_host_->Init(helper_->context_wrapper()); @@ -201,16 +187,38 @@ class ServiceWorkerHandleTest : public testing::Test { return nullptr; } - IPC::TestSink* ipc_sink() { return helper_->ipc_sink(); } + blink::mojom::ServiceWorkerRegistrationObjectInfoPtr + GetRegistrationFromRemote(mojom::ServiceWorkerContainerHost* container_host, + const GURL& scope) { + blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration_info; + base::RunLoop run_loop; + container_host->GetRegistration( + scope, base::BindOnce( + [](base::OnceClosure quit_closure, + blink::mojom::ServiceWorkerRegistrationObjectInfoPtr* + out_registration_info, + blink::mojom::ServiceWorkerErrorType error, + const base::Optional& error_msg, + blink::mojom::ServiceWorkerRegistrationObjectInfoPtr + registration) { + ASSERT_EQ(blink::mojom::ServiceWorkerErrorType::kNone, + error); + *out_registration_info = std::move(registration); + std::move(quit_closure).Run(); + }, + run_loop.QuitClosure(), ®istration_info)); + run_loop.Run(); + EXPECT_TRUE(registration_info); + return registration_info; + } TestBrowserThreadBundle browser_thread_bundle_; - MockResourceContext resource_context_; base::SimpleTestTickClock tick_clock_; std::unique_ptr helper_; scoped_refptr registration_; scoped_refptr version_; - scoped_refptr dispatcher_host_; + scoped_refptr dispatcher_host_; private: DISALLOW_COPY_AND_ASSIGN(ServiceWorkerHandleTest); @@ -223,6 +231,7 @@ TEST_F(ServiceWorkerHandleTest, OnVersionStateChanged) { const GURL script_url("https://www.example.com/service_worker.js"); Initialize(std::make_unique(base::FilePath())); SetUpRegistration(pattern, script_url); + registration_->SetInstallingVersion(version_); ServiceWorkerRemoteProviderEndpoint remote_endpoint; std::unique_ptr provider_host = @@ -230,32 +239,21 @@ TEST_F(ServiceWorkerHandleTest, OnVersionStateChanged) { helper_->mock_render_process_id(), kProviderId, helper_->context()->AsWeakPtr(), kRenderFrameId, dispatcher_host_.get(), &remote_endpoint); - blink::mojom::ServiceWorkerObjectInfoPtr info = - provider_host->GetOrCreateServiceWorkerHandle(version_.get()); - ServiceWorkerHandle* handle = - GetServiceWorkerHandle(provider_host.get(), version_->version_id()); - - // Start the worker, and then... - ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; - version_->StartWorker(ServiceWorkerMetrics::EventType::UNKNOWN, - CreateReceiverOnCurrentThread(&status)); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(SERVICE_WORKER_OK, status); + provider_host->SetDocumentUrl(pattern); + blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration_info = + GetRegistrationFromRemote(remote_endpoint.host_ptr()->get(), pattern); + // |version_| is the installing version of |registration_| now. + EXPECT_TRUE(registration_info->installing); + EXPECT_EQ(version_->version_id(), registration_info->installing->version_id); + auto mock_object = std::make_unique( + std::move(registration_info->installing)); // ...update state to installed. + EXPECT_EQ(blink::mojom::ServiceWorkerState::kInstalling, + mock_object->state()); version_->SetStatus(ServiceWorkerVersion::INSTALLED); - - ASSERT_EQ(0L, dispatcher_host_->bad_message_received_count_); - - const IPC::Message* message = nullptr; - // StartWorker shouldn't be recorded here. - ASSERT_EQ(1UL, ipc_sink()->message_count()); - message = ipc_sink()->GetMessageAt(0); - - // StateChanged (state == Installed). - VerifyStateChangedMessage(handle->handle_id(), - blink::mojom::ServiceWorkerState::kInstalled, - message); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(blink::mojom::ServiceWorkerState::kInstalled, mock_object->state()); } TEST_F(ServiceWorkerHandleTest, @@ -290,7 +288,8 @@ TEST_F(ServiceWorkerHandleTest, // object in the service worker execution context for |version_|. ServiceWorkerProviderHost* provider_host = version_->provider_host(); blink::mojom::ServiceWorkerObjectInfoPtr info = - provider_host->GetOrCreateServiceWorkerHandle(version_.get()); + provider_host->GetOrCreateServiceWorkerHandle(version_.get()) + ->CreateCompleteObjectInfoToSend(); ServiceWorkerHandle* sender_worker_handle = GetServiceWorkerHandle(provider_host, version_->version_id()); EXPECT_EQ(1u, GetBindingsCount(sender_worker_handle)); @@ -318,8 +317,6 @@ TEST_F(ServiceWorkerHandleTest, EXPECT_EQ(1u, events.size()); EXPECT_FALSE(events[0]->source_info_for_client); EXPECT_TRUE(events[0]->source_info_for_service_worker); - EXPECT_EQ(sender_worker_handle->handle_id(), - events[0]->source_info_for_service_worker->handle_id); EXPECT_EQ(version_->version_id(), events[0]->source_info_for_service_worker->version_id); @@ -353,7 +350,8 @@ TEST_F(ServiceWorkerHandleTest, DispatchExtendableMessageEvent_FromClient) { provider_host->SetDocumentUrl(pattern); // Prepare a ServiceWorkerHandle for the above |provider_host|. blink::mojom::ServiceWorkerObjectInfoPtr info = - provider_host->GetOrCreateServiceWorkerHandle(version_.get()); + provider_host->GetOrCreateServiceWorkerHandle(version_.get()) + ->CreateCompleteObjectInfoToSend(); ServiceWorkerHandle* handle = GetServiceWorkerHandle(provider_host.get(), version_->version_id()); @@ -408,7 +406,8 @@ TEST_F(ServiceWorkerHandleTest, DispatchExtendableMessageEvent_Fail) { provider_host->SetDocumentUrl(pattern); // Prepare a ServiceWorkerHandle for the above |provider_host|. blink::mojom::ServiceWorkerObjectInfoPtr info = - provider_host->GetOrCreateServiceWorkerHandle(version_.get()); + provider_host->GetOrCreateServiceWorkerHandle(version_.get()) + ->CreateCompleteObjectInfoToSend(); ServiceWorkerHandle* handle = GetServiceWorkerHandle(provider_host.get(), version_->version_id()); diff --git a/chromium/content/browser/service_worker/service_worker_info.cc b/chromium/content/browser/service_worker/service_worker_info.cc index c6412175033..0b2b348664b 100644 --- a/chromium/content/browser/service_worker/service_worker_info.cc +++ b/chromium/content/browser/service_worker/service_worker_info.cc @@ -14,28 +14,6 @@ namespace content { -ServiceWorkerVersionInfo::ClientInfo::ClientInfo() - : ClientInfo(ChildProcessHost::kInvalidUniqueID, - MSG_ROUTING_NONE, - base::Callback(), - blink::mojom::ServiceWorkerProviderType::kUnknown) {} - -ServiceWorkerVersionInfo::ClientInfo::ClientInfo( - int process_id, - int route_id, - const base::Callback& web_contents_getter, - blink::mojom::ServiceWorkerProviderType type) - : process_id(process_id), - route_id(route_id), - web_contents_getter(web_contents_getter), - type(type) {} - -ServiceWorkerVersionInfo::ClientInfo::ClientInfo( - const ServiceWorkerVersionInfo::ClientInfo& other) = default; - -ServiceWorkerVersionInfo::ClientInfo::~ClientInfo() { -} - ServiceWorkerVersionInfo::ServiceWorkerVersionInfo() : running_status(EmbeddedWorkerStatus::STOPPED), status(ServiceWorkerVersion::NEW), diff --git a/chromium/content/browser/service_worker/service_worker_info.h b/chromium/content/browser/service_worker/service_worker_info.h index 945008dadcd..1da804ee2cf 100644 --- a/chromium/content/browser/service_worker/service_worker_info.h +++ b/chromium/content/browser/service_worker/service_worker_info.h @@ -7,6 +7,7 @@ #include +#include #include #include "base/callback.h" @@ -20,25 +21,10 @@ namespace content { enum class EmbeddedWorkerStatus; +struct ServiceWorkerClientInfo; struct CONTENT_EXPORT ServiceWorkerVersionInfo { public: - struct CONTENT_EXPORT ClientInfo { - public: - ClientInfo(); - ClientInfo(int process_id, - int route_id, - const base::Callback& web_contents_getter, - blink::mojom::ServiceWorkerProviderType type); - ClientInfo(const ClientInfo& other); - ~ClientInfo(); - int process_id; - int route_id; - // |web_contents_getter| is only set for PlzNavigate. - base::Callback web_contents_getter; - blink::mojom::ServiceWorkerProviderType type; - }; - ServiceWorkerVersionInfo(); ServiceWorkerVersionInfo( EmbeddedWorkerStatus running_status, @@ -65,7 +51,7 @@ struct CONTENT_EXPORT ServiceWorkerVersionInfo { int devtools_agent_route_id; base::Time script_response_time; base::Time script_last_modified; - std::map clients; + std::map clients; }; struct CONTENT_EXPORT ServiceWorkerRegistrationInfo { diff --git a/chromium/content/browser/service_worker/service_worker_installed_script_loader.cc b/chromium/content/browser/service_worker/service_worker_installed_script_loader.cc index 8283e936fdc..09bba617925 100644 --- a/chromium/content/browser/service_worker/service_worker_installed_script_loader.cc +++ b/chromium/content/browser/service_worker/service_worker_installed_script_loader.cc @@ -40,7 +40,7 @@ ServiceWorkerInstalledScriptLoader::~ServiceWorkerInstalledScriptLoader() = void ServiceWorkerInstalledScriptLoader::OnStarted( std::string encoding, - std::unordered_map headers, + base::flat_map headers, mojo::ScopedDataPipeConsumerHandle body_handle, uint64_t body_size, mojo::ScopedDataPipeConsumerHandle metadata_handle, @@ -110,7 +110,8 @@ void ServiceWorkerInstalledScriptLoader::OnFinished(FinishedReason reason) { client_->OnComplete(network::URLLoaderCompletionStatus(net_error)); } -void ServiceWorkerInstalledScriptLoader::FollowRedirect() { +void ServiceWorkerInstalledScriptLoader::FollowRedirect( + const base::Optional& modified_request_headers) { // This class never returns a redirect response to its client, so should never // be asked to follow one. NOTREACHED(); diff --git a/chromium/content/browser/service_worker/service_worker_installed_script_loader.h b/chromium/content/browser/service_worker/service_worker_installed_script_loader.h index ad4844c1d64..89a412e2270 100644 --- a/chromium/content/browser/service_worker/service_worker_installed_script_loader.h +++ b/chromium/content/browser/service_worker/service_worker_installed_script_loader.h @@ -37,7 +37,7 @@ class CONTENT_EXPORT ServiceWorkerInstalledScriptLoader // ServiceWorkerInstalledScriptReader::Client overrides: void OnStarted(std::string encoding, - std::unordered_map headers, + base::flat_map headers, mojo::ScopedDataPipeConsumerHandle body_handle, uint64_t body_size, mojo::ScopedDataPipeConsumerHandle meta_data_handle, @@ -48,7 +48,8 @@ class CONTENT_EXPORT ServiceWorkerInstalledScriptLoader ServiceWorkerInstalledScriptReader::FinishedReason reason) override; // network::mojom::URLLoader overrides: - void FollowRedirect() override; + void FollowRedirect(const base::Optional& + modified_request_headers) override; void ProceedWithResponse() override; void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override; diff --git a/chromium/content/browser/service_worker/service_worker_installed_script_reader.cc b/chromium/content/browser/service_worker/service_worker_installed_script_reader.cc index a70872a74d8..8c42abbd0ae 100644 --- a/chromium/content/browser/service_worker/service_worker_installed_script_reader.cc +++ b/chromium/content/browser/service_worker/service_worker_installed_script_reader.cc @@ -154,7 +154,7 @@ void ServiceWorkerInstalledScriptReader::OnReadInfoComplete( headers->GetCharset(&charset); // Create a map of response headers. - std::unordered_map header_strings; + base::flat_map header_strings; size_t iter = 0; std::string key; std::string value; diff --git a/chromium/content/browser/service_worker/service_worker_installed_script_reader.h b/chromium/content/browser/service_worker/service_worker_installed_script_reader.h index 334dc38620d..4c5a2a13a7e 100644 --- a/chromium/content/browser/service_worker/service_worker_installed_script_reader.h +++ b/chromium/content/browser/service_worker/service_worker_installed_script_reader.h @@ -7,8 +7,8 @@ #include #include -#include +#include "base/containers/flat_map.h" #include "content/browser/service_worker/service_worker_disk_cache.h" #include "content/common/content_export.h" #include "mojo/public/cpp/system/data_pipe.h" @@ -38,7 +38,7 @@ class ServiceWorkerInstalledScriptReader { class Client { public: virtual void OnStarted(std::string encoding, - std::unordered_map headers, + base::flat_map headers, mojo::ScopedDataPipeConsumerHandle body_handle, uint64_t body_size, mojo::ScopedDataPipeConsumerHandle meta_data_handle, diff --git a/chromium/content/browser/service_worker/service_worker_installed_scripts_sender.cc b/chromium/content/browser/service_worker/service_worker_installed_scripts_sender.cc index a7f29950858..c7dd5fee1e2 100644 --- a/chromium/content/browser/service_worker/service_worker_installed_scripts_sender.cc +++ b/chromium/content/browser/service_worker/service_worker_installed_scripts_sender.cc @@ -81,7 +81,7 @@ void ServiceWorkerInstalledScriptsSender::StartSendingScript( void ServiceWorkerInstalledScriptsSender::OnStarted( std::string encoding, - std::unordered_map headers, + base::flat_map headers, mojo::ScopedDataPipeConsumerHandle body_handle, uint64_t body_size, mojo::ScopedDataPipeConsumerHandle meta_data_handle, diff --git a/chromium/content/browser/service_worker/service_worker_installed_scripts_sender.h b/chromium/content/browser/service_worker/service_worker_installed_scripts_sender.h index 81531ca5c3a..7817a40aaf4 100644 --- a/chromium/content/browser/service_worker/service_worker_installed_scripts_sender.h +++ b/chromium/content/browser/service_worker/service_worker_installed_scripts_sender.h @@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_INSTALLED_SCRIPTS_SENDER_H_ #define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_INSTALLED_SCRIPTS_SENDER_H_ +#include "base/containers/flat_map.h" #include "base/containers/queue.h" #include "content/browser/service_worker/service_worker_installed_script_reader.h" #include "content/common/content_export.h" @@ -75,7 +76,7 @@ class CONTENT_EXPORT ServiceWorkerInstalledScriptsSender // Implements ServiceWorkerInstalledScriptReader::Client. void OnStarted(std::string encoding, - std::unordered_map headers, + base::flat_map headers, mojo::ScopedDataPipeConsumerHandle body_handle, uint64_t body_size, mojo::ScopedDataPipeConsumerHandle meta_data_handle, diff --git a/chromium/content/browser/service_worker/service_worker_internals_ui.cc b/chromium/content/browser/service_worker/service_worker_internals_ui.cc index 6770fe5ac9d..5fba22499c9 100644 --- a/chromium/content/browser/service_worker/service_worker_internals_ui.cc +++ b/chromium/content/browser/service_worker/service_worker_internals_ui.cc @@ -82,7 +82,7 @@ base::ProcessId GetRealProcessId(int process_host_id) { if (!rph) return base::kNullProcessId; - base::ProcessHandle handle = rph->GetHandle(); + base::ProcessHandle handle = rph->GetProcess().Handle(); if (handle == base::kNullProcessHandle) return base::kNullProcessId; // TODO(nhiroki): On Windows, |rph->GetHandle()| does not duplicate ownership @@ -306,11 +306,11 @@ class ServiceWorkerInternalsUI::PartitionObserver web_ui_->CallJavascriptFunctionUnsafe( "serviceworker.onConsoleMessageReported", ConvertToRawPtrVector(args)); } - void OnRegistrationStored(int64_t registration_id, - const GURL& pattern) override { + void OnRegistrationCompleted(int64_t registration_id, + const GURL& pattern) override { DCHECK_CURRENTLY_ON(BrowserThread::UI); - web_ui_->CallJavascriptFunctionUnsafe("serviceworker.onRegistrationStored", - Value(pattern.spec())); + web_ui_->CallJavascriptFunctionUnsafe( + "serviceworker.onRegistrationCompleted", Value(pattern.spec())); } void OnRegistrationDeleted(int64_t registration_id, const GURL& pattern) override { diff --git a/chromium/content/browser/service_worker/service_worker_job_unittest.cc b/chromium/content/browser/service_worker/service_worker_job_unittest.cc index 78c577fe053..f39721041ba 100644 --- a/chromium/content/browser/service_worker/service_worker_job_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_job_unittest.cc @@ -140,7 +140,7 @@ class ServiceWorkerJobTest : public testing::Test { scoped_refptr FindRegistrationForPattern( const GURL& pattern, ServiceWorkerStatusCode expected_status = SERVICE_WORKER_OK); - std::unique_ptr CreateControllee(); + ServiceWorkerProviderHost* CreateControllee(); TestBrowserThreadBundle browser_thread_bundle_; std::unique_ptr helper_; @@ -189,29 +189,32 @@ ServiceWorkerJobTest::FindRegistrationForPattern( return registration; } -std::unique_ptr -ServiceWorkerJobTest::CreateControllee() { +ServiceWorkerProviderHost* ServiceWorkerJobTest::CreateControllee() { + static int s_next_provider_id = 1; remote_endpoints_.emplace_back(); std::unique_ptr host = CreateProviderHostForWindow( - 33 /* dummy render process id */, 1 /* dummy provider_id */, + 33 /* dummy render process id */, s_next_provider_id++, true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr(), &remote_endpoints_.back()); - return host; + auto* host_ptr = host.get(); + helper_->context()->AddProviderHost(std::move(host)); + return host_ptr; } TEST_F(ServiceWorkerJobTest, SameDocumentSameRegistration) { blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); scoped_refptr original_registration = - RunRegisterJob(GURL("http://www.example.com/service_worker.js"), options); + RunRegisterJob(GURL("https://www.example.com/service_worker.js"), + options); bool called; scoped_refptr registration1; storage()->FindRegistrationForDocument( - GURL("http://www.example.com/"), + GURL("https://www.example.com/"), SaveFoundRegistration(SERVICE_WORKER_OK, &called, ®istration1)); scoped_refptr registration2; storage()->FindRegistrationForDocument( - GURL("http://www.example.com/"), + GURL("https://www.example.com/"), SaveFoundRegistration(SERVICE_WORKER_OK, &called, ®istration2)); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(called); @@ -223,22 +226,23 @@ TEST_F(ServiceWorkerJobTest, SameDocumentSameRegistration) { TEST_F(ServiceWorkerJobTest, SameMatchSameRegistration) { bool called; blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); scoped_refptr original_registration = - RunRegisterJob(GURL("http://www.example.com/service_worker.js"), options); + RunRegisterJob(GURL("https://www.example.com/service_worker.js"), + options); ASSERT_NE(static_cast(nullptr), original_registration.get()); scoped_refptr registration1; storage()->FindRegistrationForDocument( - GURL("http://www.example.com/one"), + GURL("https://www.example.com/one"), SaveFoundRegistration(SERVICE_WORKER_OK, &called, ®istration1)); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(called); scoped_refptr registration2; storage()->FindRegistrationForDocument( - GURL("http://www.example.com/two"), + GURL("https://www.example.com/two"), SaveFoundRegistration(SERVICE_WORKER_OK, &called, ®istration2)); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(called); @@ -249,18 +253,18 @@ TEST_F(ServiceWorkerJobTest, SameMatchSameRegistration) { TEST_F(ServiceWorkerJobTest, DifferentMatchDifferentRegistration) { bool called1; blink::mojom::ServiceWorkerRegistrationOptions options1; - options1.scope = GURL("http://www.example.com/one/"); + options1.scope = GURL("https://www.example.com/one/"); scoped_refptr original_registration1; job_coordinator()->Register( - GURL("http://www.example.com/service_worker.js"), options1, + GURL("https://www.example.com/service_worker.js"), options1, SaveRegistration(SERVICE_WORKER_OK, &called1, &original_registration1)); bool called2; blink::mojom::ServiceWorkerRegistrationOptions options2; - options2.scope = GURL("http://www.example.com/two/"); + options2.scope = GURL("https://www.example.com/two/"); scoped_refptr original_registration2; job_coordinator()->Register( - GURL("http://www.example.com/service_worker.js"), options2, + GURL("https://www.example.com/service_worker.js"), options2, SaveRegistration(SERVICE_WORKER_OK, &called2, &original_registration2)); EXPECT_FALSE(called1); @@ -271,11 +275,11 @@ TEST_F(ServiceWorkerJobTest, DifferentMatchDifferentRegistration) { scoped_refptr registration1; storage()->FindRegistrationForDocument( - GURL("http://www.example.com/one/"), + GURL("https://www.example.com/one/"), SaveFoundRegistration(SERVICE_WORKER_OK, &called1, ®istration1)); scoped_refptr registration2; storage()->FindRegistrationForDocument( - GURL("http://www.example.com/two/"), + GURL("https://www.example.com/two/"), SaveFoundRegistration(SERVICE_WORKER_OK, &called2, ®istration2)); base::RunLoop().RunUntilIdle(); @@ -287,9 +291,9 @@ TEST_F(ServiceWorkerJobTest, DifferentMatchDifferentRegistration) { // Make sure basic registration is working. TEST_F(ServiceWorkerJobTest, Register) { blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); - scoped_refptr registration = - RunRegisterJob(GURL("http://www.example.com/service_worker.js"), options); + options.scope = GURL("https://www.example.com/"); + scoped_refptr registration = RunRegisterJob( + GURL("https://www.example.com/service_worker.js"), options); EXPECT_TRUE(registration); EXPECT_EQ(EmbeddedWorkerTestHelper::Event::Install, @@ -301,9 +305,9 @@ TEST_F(ServiceWorkerJobTest, Register) { // Make sure registrations are cleaned up when they are unregistered. TEST_F(ServiceWorkerJobTest, Unregister) { blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); - scoped_refptr registration = - RunRegisterJob(GURL("http://www.example.com/service_worker.js"), options); + options.scope = GURL("https://www.example.com/"); + scoped_refptr registration = RunRegisterJob( + GURL("https://www.example.com/service_worker.js"), options); scoped_refptr version = registration->active_version(); ServiceWorkerProviderHost* provider_host = @@ -334,7 +338,7 @@ TEST_F(ServiceWorkerJobTest, Unregister) { } TEST_F(ServiceWorkerJobTest, Unregister_NothingRegistered) { - GURL pattern("http://www.example.com/"); + GURL pattern("https://www.example.com/"); RunUnregisterJob(pattern, SERVICE_WORKER_ERROR_NOT_FOUND); } @@ -343,10 +347,10 @@ TEST_F(ServiceWorkerJobTest, Unregister_NothingRegistered) { // existing registration. TEST_F(ServiceWorkerJobTest, RegisterNewScript) { blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); - scoped_refptr old_registration = - RunRegisterJob(GURL("http://www.example.com/service_worker.js"), options); + scoped_refptr old_registration = RunRegisterJob( + GURL("https://www.example.com/service_worker.js"), options); scoped_refptr old_registration_by_pattern = FindRegistrationForPattern(options.scope); @@ -355,7 +359,7 @@ TEST_F(ServiceWorkerJobTest, RegisterNewScript) { old_registration_by_pattern = nullptr; scoped_refptr new_registration = RunRegisterJob( - GURL("http://www.example.com/service_worker_new.js"), options); + GURL("https://www.example.com/service_worker_new.js"), options); ASSERT_EQ(old_registration, new_registration); @@ -368,9 +372,9 @@ TEST_F(ServiceWorkerJobTest, RegisterNewScript) { // Make sure that when registering a duplicate pattern+script_url // combination, that the same registration is used. TEST_F(ServiceWorkerJobTest, RegisterDuplicateScript) { - GURL script_url("http://www.example.com/service_worker.js"); + GURL script_url("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); scoped_refptr old_registration = RunRegisterJob(script_url, options); @@ -471,7 +475,6 @@ class FailToStartWorkerTestHelper : public EmbeddedWorkerTestHelper { bool pause_after_download, mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, mojom::ControllerServiceWorkerRequest controller_request, - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) @@ -487,9 +490,9 @@ TEST_F(ServiceWorkerJobTest, Register_FailToStartWorker) { helper_.reset(new FailToStartWorkerTestHelper); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); scoped_refptr registration = - RunRegisterJob(GURL("http://www.example.com/service_worker.js"), options, + RunRegisterJob(GURL("https://www.example.com/service_worker.js"), options, SERVICE_WORKER_ERROR_START_WORKER_FAILED); ASSERT_EQ(scoped_refptr(nullptr), registration); @@ -498,9 +501,9 @@ TEST_F(ServiceWorkerJobTest, Register_FailToStartWorker) { // Register and then unregister the pattern, in parallel. Job coordinator should // process jobs until the last job. TEST_F(ServiceWorkerJobTest, ParallelRegUnreg) { - GURL script_url("http://www.example.com/service_worker.js"); + GURL script_url("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); bool registration_called = false; scoped_refptr registration; @@ -529,9 +532,9 @@ TEST_F(ServiceWorkerJobTest, ParallelRegUnreg) { // registration should win, and the old registration should have been // shutdown. TEST_F(ServiceWorkerJobTest, ParallelRegNewScript) { - GURL pattern("http://www.example.com/"); + GURL pattern("https://www.example.com/"); - GURL script_url1("http://www.example.com/service_worker1.js"); + GURL script_url1("https://www.example.com/service_worker1.js"); bool registration1_called = false; scoped_refptr registration1; job_coordinator()->Register( @@ -541,7 +544,7 @@ TEST_F(ServiceWorkerJobTest, ParallelRegNewScript) { SaveRegistration(SERVICE_WORKER_OK, ®istration1_called, ®istration1)); - GURL script_url2("http://www.example.com/service_worker2.js"); + GURL script_url2("https://www.example.com/service_worker2.js"); bool registration2_called = false; scoped_refptr registration2; job_coordinator()->Register( @@ -568,9 +571,9 @@ TEST_F(ServiceWorkerJobTest, ParallelRegNewScript) { // object. TEST_F(ServiceWorkerJobTest, ParallelRegSameScript) { blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); - GURL script_url("http://www.example.com/service_worker1.js"); + GURL script_url("https://www.example.com/service_worker1.js"); bool registration1_called = false; scoped_refptr registration1; job_coordinator()->Register( @@ -601,9 +604,9 @@ TEST_F(ServiceWorkerJobTest, ParallelRegSameScript) { // Call simulataneous unregister calls. TEST_F(ServiceWorkerJobTest, ParallelUnreg) { - GURL pattern("http://www.example.com/"); + GURL pattern("https://www.example.com/"); - GURL script_url("http://www.example.com/service_worker.js"); + GURL script_url("https://www.example.com/service_worker.js"); bool unregistration1_called = false; job_coordinator()->Unregister( pattern, @@ -632,13 +635,13 @@ TEST_F(ServiceWorkerJobTest, ParallelUnreg) { } TEST_F(ServiceWorkerJobTest, AbortAll_Register) { - GURL script_url1("http://www1.example.com/service_worker.js"); - GURL script_url2("http://www2.example.com/service_worker.js"); + GURL script_url1("https://www1.example.com/service_worker.js"); + GURL script_url2("https://www2.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options1; - options1.scope = GURL("http://www1.example.com/"); + options1.scope = GURL("https://www1.example.com/"); blink::mojom::ServiceWorkerRegistrationOptions options2; - options2.scope = GURL("http://www2.example.com/"); + options2.scope = GURL("https://www2.example.com/"); bool registration_called1 = false; scoped_refptr registration1; @@ -680,8 +683,8 @@ TEST_F(ServiceWorkerJobTest, AbortAll_Register) { } TEST_F(ServiceWorkerJobTest, AbortAll_Unregister) { - GURL pattern1("http://www1.example.com/"); - GURL pattern2("http://www2.example.com/"); + GURL pattern1("https://www1.example.com/"); + GURL pattern2("https://www2.example.com/"); bool unregistration_called1 = false; scoped_refptr registration1; @@ -706,9 +709,9 @@ TEST_F(ServiceWorkerJobTest, AbortAll_Unregister) { } TEST_F(ServiceWorkerJobTest, AbortAll_RegUnreg) { - GURL script_url("http://www.example.com/service_worker.js"); + GURL script_url("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); bool registration_called = false; scoped_refptr registration; @@ -739,9 +742,9 @@ TEST_F(ServiceWorkerJobTest, AbortAll_RegUnreg) { // Tests that the waiting worker enters the 'redundant' state upon // unregistration. TEST_F(ServiceWorkerJobTest, UnregisterWaitingSetsRedundant) { - GURL script_url("http://www.example.com/service_worker.js"); + GURL script_url("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); scoped_refptr registration = RunRegisterJob(script_url, options); ASSERT_TRUE(registration.get()); @@ -763,7 +766,7 @@ TEST_F(ServiceWorkerJobTest, UnregisterWaitingSetsRedundant) { EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, version->running_status()); EXPECT_EQ(ServiceWorkerVersion::INSTALLED, version->status()); - RunUnregisterJob(GURL("http://www.example.com/")); + RunUnregisterJob(GURL("https://www.example.com/")); // The version should be stopped since there is no controllee after // unregistration. @@ -775,16 +778,16 @@ TEST_F(ServiceWorkerJobTest, UnregisterWaitingSetsRedundant) { // unregistration. TEST_F(ServiceWorkerJobTest, UnregisterActiveSetsRedundant) { blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); - scoped_refptr registration = - RunRegisterJob(GURL("http://www.example.com/service_worker.js"), options); + options.scope = GURL("https://www.example.com/"); + scoped_refptr registration = RunRegisterJob( + GURL("https://www.example.com/service_worker.js"), options); ASSERT_TRUE(registration.get()); scoped_refptr version = registration->active_version(); EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, version->running_status()); EXPECT_EQ(ServiceWorkerVersion::ACTIVATED, version->status()); - RunUnregisterJob(GURL("http://www.example.com/")); + RunUnregisterJob(GURL("https://www.example.com/")); // The version should be stopped since there is no controllee after // unregistration. @@ -797,25 +800,25 @@ TEST_F(ServiceWorkerJobTest, UnregisterActiveSetsRedundant) { TEST_F(ServiceWorkerJobTest, UnregisterActiveSetsRedundant_WaitForNoControllee) { blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); - scoped_refptr registration = - RunRegisterJob(GURL("http://www.example.com/service_worker.js"), options); + options.scope = GURL("https://www.example.com/"); + scoped_refptr registration = RunRegisterJob( + GURL("https://www.example.com/service_worker.js"), options); ASSERT_TRUE(registration.get()); - std::unique_ptr host = CreateControllee(); - registration->active_version()->AddControllee(host.get()); + ServiceWorkerProviderHost* host = CreateControllee(); + registration->active_version()->AddControllee(host); scoped_refptr version = registration->active_version(); EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, version->running_status()); EXPECT_EQ(ServiceWorkerVersion::ACTIVATED, version->status()); - RunUnregisterJob(GURL("http://www.example.com/")); + RunUnregisterJob(GURL("https://www.example.com/")); // The version should be running since there is still a controllee. EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, version->running_status()); EXPECT_EQ(ServiceWorkerVersion::ACTIVATED, version->status()); - registration->active_version()->RemoveControllee(host.get()); + registration->active_version()->RemoveControllee(host->client_uuid()); base::RunLoop().RunUntilIdle(); // The version should be stopped since there is no controllee. @@ -825,8 +828,8 @@ TEST_F(ServiceWorkerJobTest, namespace { // Helpers for the update job tests. -const GURL kNoChangeOrigin("http://nochange/"); -const GURL kNewVersionOrigin("http://newversion/"); +const GURL kNoChangeOrigin("https://nochange/"); +const GURL kNewVersionOrigin("https://newversion/"); const std::string kScope("scope/"); const std::string kScript("script.js"); @@ -935,7 +938,6 @@ class UpdateJobTestHelper bool pause_after_download, mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, mojom::ControllerServiceWorkerRequest controller_request, - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) @@ -997,8 +999,8 @@ class UpdateJobTestHelper EmbeddedWorkerTestHelper::OnStartWorker( embedded_worker_id, version_id, scope, script, pause_after_download, std::move(dispatcher_request), std::move(controller_request), - std::move(service_worker_host), std::move(instance_host), - std::move(provider_info), std::move(installed_scripts_info)); + std::move(instance_host), std::move(provider_info), + std::move(installed_scripts_info)); } void OnStopWorker(int embedded_worker_id) override { @@ -1073,7 +1075,6 @@ class EvictIncumbentVersionHelper : public UpdateJobTestHelper { bool pause_after_download, mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, mojom::ControllerServiceWorkerRequest controller_request, - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) @@ -1092,8 +1093,8 @@ class EvictIncumbentVersionHelper : public UpdateJobTestHelper { UpdateJobTestHelper::OnStartWorker( embedded_worker_id, version_id, scope, script, pause_after_download, std::move(dispatcher_request), std::move(controller_request), - std::move(service_worker_host), std::move(instance_host), - std::move(provider_info), std::move(installed_scripts_info)); + std::move(instance_host), std::move(provider_info), + std::move(installed_scripts_info)); } void OnRegistrationFailed(ServiceWorkerRegistration* registration) override { @@ -1287,15 +1288,15 @@ TEST_F(ServiceWorkerJobTest, Update_NewVersion) { TEST_F(ServiceWorkerJobTest, Update_ScriptUrlChanged) { // Create a registration with an active version. blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/one/"); - scoped_refptr registration = - RunRegisterJob(GURL("http://www.example.com/service_worker.js"), options); + options.scope = GURL("https://www.example.com/one/"); + scoped_refptr registration = RunRegisterJob( + GURL("https://www.example.com/service_worker.js"), options); // Queue an Update. When this runs, it will use the waiting version's script. job_coordinator()->Update(registration.get(), false); // Add a waiting version with a new script. - GURL new_script("http://www.example.com/new_worker.js"); + GURL new_script("https://www.example.com/new_worker.js"); scoped_refptr version = new ServiceWorkerVersion( registration.get(), new_script, 2L /* dummy version id */, helper_->context()->AsWeakPtr()); @@ -1344,16 +1345,16 @@ TEST_F(ServiceWorkerJobTest, Update_EvictedIncumbent) { TEST_F(ServiceWorkerJobTest, Update_UninstallingRegistration) { blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/one/"); + options.scope = GURL("https://www.example.com/one/"); bool called; - scoped_refptr registration = - RunRegisterJob(GURL("http://www.example.com/service_worker.js"), options); + scoped_refptr registration = RunRegisterJob( + GURL("https://www.example.com/service_worker.js"), options); // Add a controllee and queue an unregister to force the uninstalling state. - std::unique_ptr host = CreateControllee(); + ServiceWorkerProviderHost* host = CreateControllee(); ServiceWorkerVersion* active_version = registration->active_version(); - active_version->AddControllee(host.get()); - job_coordinator()->Unregister(GURL("http://www.example.com/one/"), + active_version->AddControllee(host); + job_coordinator()->Unregister(GURL("https://www.example.com/one/"), SaveUnregistration(SERVICE_WORKER_OK, &called)); // Update should abort after it starts and sees uninstalling. @@ -1371,10 +1372,10 @@ TEST_F(ServiceWorkerJobTest, Update_UninstallingRegistration) { } TEST_F(ServiceWorkerJobTest, RegisterWhileUninstalling) { - GURL script1("http://www.example.com/service_worker.js"); - GURL script2("http://www.example.com/service_worker.js?new"); + GURL script1("https://www.example.com/service_worker.js"); + GURL script2("https://www.example.com/service_worker.js?new"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/one/"); + options.scope = GURL("https://www.example.com/one/"); scoped_refptr registration = RunRegisterJob(script1, options); @@ -1383,10 +1384,10 @@ TEST_F(ServiceWorkerJobTest, RegisterWhileUninstalling) { registration->SetTaskRunnerForTest(runner); // Add a controllee and queue an unregister to force the uninstalling state. - std::unique_ptr host = CreateControllee(); + ServiceWorkerProviderHost* host = CreateControllee(); scoped_refptr old_version = registration->active_version(); - old_version->AddControllee(host.get()); + old_version->AddControllee(host); RunUnregisterJob(options.scope); // Register another script. @@ -1404,7 +1405,7 @@ TEST_F(ServiceWorkerJobTest, RegisterWhileUninstalling) { EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, new_version->running_status()); EXPECT_EQ(ServiceWorkerVersion::INSTALLED, new_version->status()); - old_version->RemoveControllee(host.get()); + old_version->RemoveControllee(host->client_uuid()); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(registration->is_uninstalling()); @@ -1423,19 +1424,19 @@ TEST_F(ServiceWorkerJobTest, RegisterWhileUninstalling) { } TEST_F(ServiceWorkerJobTest, RegisterAndUnregisterWhileUninstalling) { - GURL script1("http://www.example.com/service_worker.js"); - GURL script2("http://www.example.com/service_worker.js?new"); + GURL script1("https://www.example.com/service_worker.js"); + GURL script2("https://www.example.com/service_worker.js?new"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/one/"); + options.scope = GURL("https://www.example.com/one/"); scoped_refptr registration = RunRegisterJob(script1, options); // Add a controllee and queue an unregister to force the uninstalling state. - std::unique_ptr host = CreateControllee(); + ServiceWorkerProviderHost* host = CreateControllee(); scoped_refptr old_version = registration->active_version(); - old_version->AddControllee(host.get()); + old_version->AddControllee(host); RunUnregisterJob(options.scope); EXPECT_EQ(registration, RunRegisterJob(script2, options)); @@ -1459,7 +1460,7 @@ TEST_F(ServiceWorkerJobTest, RegisterAndUnregisterWhileUninstalling) { EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, new_version->running_status()); EXPECT_EQ(ServiceWorkerVersion::INSTALLED, new_version->status()); - old_version->RemoveControllee(host.get()); + old_version->RemoveControllee(host->client_uuid()); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(registration->is_uninstalling()); @@ -1472,10 +1473,10 @@ TEST_F(ServiceWorkerJobTest, RegisterAndUnregisterWhileUninstalling) { } TEST_F(ServiceWorkerJobTest, RegisterSameScriptMultipleTimesWhileUninstalling) { - GURL script1("http://www.example.com/service_worker.js"); - GURL script2("http://www.example.com/service_worker.js?new"); + GURL script1("https://www.example.com/service_worker.js"); + GURL script2("https://www.example.com/service_worker.js?new"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/one/"); + options.scope = GURL("https://www.example.com/one/"); scoped_refptr registration = RunRegisterJob(script1, options); @@ -1484,10 +1485,10 @@ TEST_F(ServiceWorkerJobTest, RegisterSameScriptMultipleTimesWhileUninstalling) { registration->SetTaskRunnerForTest(runner); // Add a controllee and queue an unregister to force the uninstalling state. - std::unique_ptr host = CreateControllee(); + ServiceWorkerProviderHost* host = CreateControllee(); scoped_refptr old_version = registration->active_version(); - old_version->AddControllee(host.get()); + old_version->AddControllee(host); RunUnregisterJob(options.scope); EXPECT_EQ(registration, RunRegisterJob(script2, options)); @@ -1505,7 +1506,7 @@ TEST_F(ServiceWorkerJobTest, RegisterSameScriptMultipleTimesWhileUninstalling) { EXPECT_FALSE(registration->is_uninstalling()); EXPECT_EQ(new_version, registration->waiting_version()); - old_version->RemoveControllee(host.get()); + old_version->RemoveControllee(host->client_uuid()); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(registration->is_uninstalling()); @@ -1524,11 +1525,11 @@ TEST_F(ServiceWorkerJobTest, RegisterSameScriptMultipleTimesWhileUninstalling) { } TEST_F(ServiceWorkerJobTest, RegisterMultipleTimesWhileUninstalling) { - GURL script1("http://www.example.com/service_worker.js?first"); - GURL script2("http://www.example.com/service_worker.js?second"); - GURL script3("http://www.example.com/service_worker.js?third"); + GURL script1("https://www.example.com/service_worker.js?first"); + GURL script2("https://www.example.com/service_worker.js?second"); + GURL script3("https://www.example.com/service_worker.js?third"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/one/"); + options.scope = GURL("https://www.example.com/one/"); scoped_refptr registration = RunRegisterJob(script1, options); @@ -1537,10 +1538,10 @@ TEST_F(ServiceWorkerJobTest, RegisterMultipleTimesWhileUninstalling) { registration->SetTaskRunnerForTest(runner); // Add a controllee and queue an unregister to force the uninstalling state. - std::unique_ptr host = CreateControllee(); + ServiceWorkerProviderHost* host = CreateControllee(); scoped_refptr first_version = registration->active_version(); - first_version->AddControllee(host.get()); + first_version->AddControllee(host); RunUnregisterJob(options.scope); EXPECT_EQ(registration, RunRegisterJob(script2, options)); @@ -1562,7 +1563,7 @@ TEST_F(ServiceWorkerJobTest, RegisterMultipleTimesWhileUninstalling) { EXPECT_FALSE(registration->is_uninstalling()); EXPECT_EQ(ServiceWorkerVersion::REDUNDANT, second_version->status()); - first_version->RemoveControllee(host.get()); + first_version->RemoveControllee(host->client_uuid()); base::RunLoop().RunUntilIdle(); EXPECT_FALSE(registration->is_uninstalling()); @@ -1629,25 +1630,25 @@ TEST_F(ServiceWorkerJobTest, RemoveControlleeDuringInstall) { EventCallbackHelper* helper = new EventCallbackHelper; helper_.reset(helper); - GURL script1("http://www.example.com/service_worker.js"); - GURL script2("http://www.example.com/service_worker.js?new"); + GURL script1("https://www.example.com/service_worker.js"); + GURL script2("https://www.example.com/service_worker.js?new"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/one/"); + options.scope = GURL("https://www.example.com/one/"); scoped_refptr registration = RunRegisterJob(script1, options); // Add a controllee and queue an unregister to force the uninstalling state. - std::unique_ptr host = CreateControllee(); + ServiceWorkerProviderHost* host = CreateControllee(); scoped_refptr old_version = registration->active_version(); - old_version->AddControllee(host.get()); + old_version->AddControllee(host); RunUnregisterJob(options.scope); // Register another script. While installing, old_version loses controllee. helper->set_install_callback( - base::Bind(&ServiceWorkerVersion::RemoveControllee, - old_version, host.get())); + base::BindRepeating(&ServiceWorkerVersion::RemoveControllee, old_version, + host->client_uuid())); EXPECT_EQ(registration, RunRegisterJob(script2, options)); EXPECT_FALSE(registration->is_uninstalling()); @@ -1670,26 +1671,26 @@ TEST_F(ServiceWorkerJobTest, RemoveControlleeDuringRejectedInstall) { EventCallbackHelper* helper = new EventCallbackHelper; helper_.reset(helper); - GURL script1("http://www.example.com/service_worker.js"); - GURL script2("http://www.example.com/service_worker.js?new"); + GURL script1("https://www.example.com/service_worker.js"); + GURL script2("https://www.example.com/service_worker.js?new"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/one/"); + options.scope = GURL("https://www.example.com/one/"); scoped_refptr registration = RunRegisterJob(script1, options); // Add a controllee and queue an unregister to force the uninstalling state. - std::unique_ptr host = CreateControllee(); + ServiceWorkerProviderHost* host = CreateControllee(); scoped_refptr old_version = registration->active_version(); - old_version->AddControllee(host.get()); + old_version->AddControllee(host); RunUnregisterJob(options.scope); // Register another script that fails to install. While installing, // old_version loses controllee. helper->set_install_callback( - base::Bind(&ServiceWorkerVersion::RemoveControllee, - old_version, host.get())); + base::BindRepeating(&ServiceWorkerVersion::RemoveControllee, old_version, + host->client_uuid())); helper->set_install_event_result( blink::mojom::ServiceWorkerEventStatus::REJECTED); EXPECT_EQ(registration, RunRegisterJob(script2, options)); @@ -1708,26 +1709,26 @@ TEST_F(ServiceWorkerJobTest, RemoveControlleeDuringInstall_RejectActivate) { EventCallbackHelper* helper = new EventCallbackHelper; helper_.reset(helper); - GURL script1("http://www.example.com/service_worker.js"); - GURL script2("http://www.example.com/service_worker.js?new"); + GURL script1("https://www.example.com/service_worker.js"); + GURL script2("https://www.example.com/service_worker.js?new"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/one/"); + options.scope = GURL("https://www.example.com/one/"); scoped_refptr registration = RunRegisterJob(script1, options); // Add a controllee and queue an unregister to force the uninstalling state. - std::unique_ptr host = CreateControllee(); + ServiceWorkerProviderHost* host = CreateControllee(); scoped_refptr old_version = registration->active_version(); - old_version->AddControllee(host.get()); + old_version->AddControllee(host); RunUnregisterJob(options.scope); // Register another script that fails to activate. While installing, // old_version loses controllee. helper->set_install_callback( - base::Bind(&ServiceWorkerVersion::RemoveControllee, - old_version, host.get())); + base::BindRepeating(&ServiceWorkerVersion::RemoveControllee, old_version, + host->client_uuid())); helper->set_activate_event_result( blink::mojom::ServiceWorkerEventStatus::REJECTED); EXPECT_EQ(registration, RunRegisterJob(script2, options)); @@ -1746,9 +1747,9 @@ TEST_F(ServiceWorkerJobTest, HasFetchHandler) { EventCallbackHelper* helper = new EventCallbackHelper; helper_.reset(helper); - GURL script("http://www.example.com/service_worker.js"); + GURL script("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); scoped_refptr registration; helper->set_has_fetch_handler(true); @@ -1823,9 +1824,9 @@ TEST_F(ServiceWorkerJobTest, Update_PauseAfterDownload) { TEST_F(ServiceWorkerJobTest, ActivateCancelsOnShutdown) { UpdateJobTestHelper* update_helper = new UpdateJobTestHelper; helper_.reset(update_helper); - GURL script("http://www.example.com/service_worker.js"); + GURL script("https://www.example.com/service_worker.js"); blink::mojom::ServiceWorkerRegistrationOptions options; - options.scope = GURL("http://www.example.com/"); + options.scope = GURL("https://www.example.com/"); scoped_refptr registration = RunRegisterJob(script, options); @@ -1834,10 +1835,10 @@ TEST_F(ServiceWorkerJobTest, ActivateCancelsOnShutdown) { registration->SetTaskRunnerForTest(runner); // Add a controllee. - std::unique_ptr host = CreateControllee(); + ServiceWorkerProviderHost* host = CreateControllee(); scoped_refptr first_version = registration->active_version(); - first_version->AddControllee(host.get()); + first_version->AddControllee(host); // Update. The new version should be waiting. registration->AddListener(update_helper); @@ -1857,7 +1858,7 @@ TEST_F(ServiceWorkerJobTest, ActivateCancelsOnShutdown) { // Remove the controllee. The new version should be activating, and delayed // until the runner runs again. - first_version->RemoveControllee(host.get()); + first_version->RemoveControllee(host->client_uuid()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(new_version.get(), registration->active_version()); EXPECT_EQ(ServiceWorkerVersion::ACTIVATING, new_version->status()); @@ -1874,6 +1875,44 @@ TEST_F(ServiceWorkerJobTest, ActivateCancelsOnShutdown) { EXPECT_EQ(new_version.get(), registration->active_version()); EXPECT_EQ(ServiceWorkerVersion::ACTIVATING, new_version->status()); registration->RemoveListener(update_helper); + // Dispatch Mojo messages for those Mojo interfaces bound on |runner| to avoid + // possible memory leak. + runner->RunUntilIdle(); +} + +// Test that clients are alerted of new registrations if they are +// in-scope, so that Clients.claim() or ServiceWorkerContainer.ready work +// correctly. +TEST_F(ServiceWorkerJobTest, AddRegistrationToMatchingProviderHosts) { + GURL scope("https://www.example.com/scope/"); + GURL in_scope("https://www.example.com/scope/page"); + GURL out_scope("https://www.example.com/page"); + + // Make an in-scope client. + ServiceWorkerProviderHost* client = CreateControllee(); + client->SetDocumentUrl(in_scope); + + // Make an in-scope reserved client. + base::WeakPtr reserved_client = + ServiceWorkerProviderHost::PreCreateNavigationHost( + helper_->context()->AsWeakPtr(), true /* are_ancestors_secure */, + {} /* web_contents_getter */); + reserved_client->SetDocumentUrl(in_scope); + + // Make an out-scope client. + ServiceWorkerProviderHost* out_scope_client = CreateControllee(); + out_scope_client->SetDocumentUrl(out_scope); + + // Make a new registration. + GURL script("https://www.example.com/service_worker.js"); + blink::mojom::ServiceWorkerRegistrationOptions options; + options.scope = scope; + scoped_refptr registration = + RunRegisterJob(script, options); + + EXPECT_EQ(registration.get(), client->MatchRegistration()); + EXPECT_EQ(registration.get(), reserved_client->MatchRegistration()); + EXPECT_NE(registration.get(), out_scope_client->MatchRegistration()); } } // namespace content diff --git a/chromium/content/browser/service_worker/service_worker_metrics.cc b/chromium/content/browser/service_worker/service_worker_metrics.cc index 463d9e7d2dd..f5fc5073da2 100644 --- a/chromium/content/browser/service_worker/service_worker_metrics.cc +++ b/chromium/content/browser/service_worker/service_worker_metrics.cc @@ -94,6 +94,8 @@ std::string EventTypeToSuffix(ServiceWorkerMetrics::EventType event_type) { return "_CAN_MAKE_PAYMENT"; case ServiceWorkerMetrics::EventType::ABORT_PAYMENT: return "_ABORT_PAYMENT"; + case ServiceWorkerMetrics::EventType::COOKIE_CHANGE: + return "_COOKIE_CHANGE"; case ServiceWorkerMetrics::EventType::NUM_TYPES: NOTREACHED() << static_cast(event_type); } @@ -339,8 +341,10 @@ const char* ServiceWorkerMetrics::EventTypeToString(EventType event_type) { return "Navigation Hint"; case EventType::CAN_MAKE_PAYMENT: return "Can Make Payment"; - case ServiceWorkerMetrics::EventType::ABORT_PAYMENT: + case EventType::ABORT_PAYMENT: return "Abort Payment"; + case EventType::COOKIE_CHANGE: + return "Cookie Change"; case EventType::NUM_TYPES: break; } @@ -717,6 +721,9 @@ void ServiceWorkerMetrics::RecordEventDuration(EventType event, case EventType::ABORT_PAYMENT: UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.AbortPaymentEvent.Time", time); break; + case EventType::COOKIE_CHANGE: + UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.CookieChangeEvent.Time", time); + break; case EventType::NAVIGATION_HINT: // The navigation hint should not be sent as an event. diff --git a/chromium/content/browser/service_worker/service_worker_metrics.h b/chromium/content/browser/service_worker/service_worker_metrics.h index e0a5ce15932..5c3c51296be 100644 --- a/chromium/content/browser/service_worker/service_worker_metrics.h +++ b/chromium/content/browser/service_worker/service_worker_metrics.h @@ -128,6 +128,7 @@ class ServiceWorkerMetrics { NAVIGATION_HINT = 27, CAN_MAKE_PAYMENT = 28, ABORT_PAYMENT = 29, + COOKIE_CHANGE = 30, // Add new events to record here. NUM_TYPES }; diff --git a/chromium/content/browser/service_worker/service_worker_navigation_handle.h b/chromium/content/browser/service_worker/service_worker_navigation_handle.h index e1f32a87465..fffae169e15 100644 --- a/chromium/content/browser/service_worker/service_worker_navigation_handle.h +++ b/chromium/content/browser/service_worker/service_worker_navigation_handle.h @@ -27,9 +27,9 @@ class ServiceWorkerNavigationHandleCore; // 2) When the navigation request is sent to the IO thread, we include a // pointer to the ServiceWorkerNavigationHandleCore. // -// 3) If we pre-create a ServiceWorkerProviderHost for this navigation, its -// ownership is passed to the ServiceWorkerNavigationHandleCore. The -// ServiceWorkerNavigationHandleCore id is updated. +// 3) If we pre-create a ServiceWorkerProviderHost for this navigation, it +// is added to ServiceWorkerContextCore and the id is passed to +// ServiceWorkerNavigationHandleCore. // // 4) The ServiceWorkerNavigationHandleCore informs the // ServiceWorkerNavigationHandle on the UI thread that the service worker @@ -40,10 +40,11 @@ class ServiceWorkerNavigationHandleCore; // ServiceWorkerNavigationHandle. // // 6) If the commit leads to the creation of a ServiceWorkerNetworkProvider -// in the renderer, a ServiceWorkerHostMsg_ProviderCreated will be received -// in the browser. The ServiceWorkerDispatcherHost will retrieve the -// ServiceWorkerProviderHost from the ServiceWorkerNavigationHandleCore and -// put it in the ServiceWorkerContextCore map of ServiceWorkerProviderHosts. +// in the renderer, an OnProviderCreated IPC will be received in the browser. +// The ServiceWorkerDispatcherHost will take the ServiceWorkerProviderHost +// from ServiceWorkerContextCore and complete its initialization, including +// setting the process id. It is then re-added to ServiceWorkerContextCore +// since it now has a correct process id. // // 7) When the navigation finishes, the ServiceWorkerNavigationHandle is // destroyed. The destructor of the ServiceWorkerNavigationHandle posts a diff --git a/chromium/content/browser/service_worker/service_worker_navigation_handle_core.cc b/chromium/content/browser/service_worker/service_worker_navigation_handle_core.cc index a840a8aa76b..686fa1312a9 100644 --- a/chromium/content/browser/service_worker/service_worker_navigation_handle_core.cc +++ b/chromium/content/browser/service_worker/service_worker_navigation_handle_core.cc @@ -12,7 +12,9 @@ #include "content/browser/service_worker/service_worker_navigation_handle.h" #include "content/browser/service_worker/service_worker_provider_host.h" #include "content/common/service_worker/service_worker_types.h" +#include "content/common/service_worker/service_worker_utils.h" #include "content/public/browser/browser_thread.h" +#include "content/public/common/child_process_host.h" namespace content { @@ -27,39 +29,28 @@ ServiceWorkerNavigationHandleCore::ServiceWorkerNavigationHandleCore( ServiceWorkerNavigationHandleCore::~ServiceWorkerNavigationHandleCore() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (precreated_host_.get() && context_wrapper_->context()) { - context_wrapper_->context()->RemoveNavigationHandleCore( - precreated_host_->provider_id()); + if (provider_id_ == kInvalidServiceWorkerProviderId) + return; + // Remove the provider host if it was never completed (navigation failed). + ServiceWorkerContextCore* context = context_wrapper_->context(); + if (!context || !context->GetProviderHost(ChildProcessHost::kInvalidUniqueID, + provider_id_)) { + return; } + context->RemoveProviderHost(ChildProcessHost::kInvalidUniqueID, provider_id_); } void ServiceWorkerNavigationHandleCore::DidPreCreateProviderHost( - std::unique_ptr precreated_host) { + int provider_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(precreated_host.get()); - DCHECK(context_wrapper_->context()); + DCHECK(ServiceWorkerUtils::IsBrowserAssignedProviderId(provider_id)); - precreated_host_ = std::move(precreated_host); - context_wrapper_->context()->AddNavigationHandleCore( - precreated_host_->provider_id(), this); + provider_id_ = provider_id; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::BindOnce( &ServiceWorkerNavigationHandle::DidCreateServiceWorkerProviderHost, - ui_handle_, precreated_host_->provider_id())); -} - -std::unique_ptr -ServiceWorkerNavigationHandleCore::RetrievePreCreatedHost() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(precreated_host_); - // Remove the ServiceWorkerNavigationHandleCore from the list of - // ServiceWorkerNavigationHandleCores since it will no longer hold a - // ServiceWorkerProviderHost. - DCHECK(context_wrapper_->context()); - context_wrapper_->context()->RemoveNavigationHandleCore( - precreated_host_->provider_id()); - return std::move(precreated_host_); + ui_handle_, provider_id_)); } } // namespace content diff --git a/chromium/content/browser/service_worker/service_worker_navigation_handle_core.h b/chromium/content/browser/service_worker/service_worker_navigation_handle_core.h index 8e98b058739..ce581432ea9 100644 --- a/chromium/content/browser/service_worker/service_worker_navigation_handle_core.h +++ b/chromium/content/browser/service_worker/service_worker_navigation_handle_core.h @@ -11,6 +11,7 @@ #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" namespace content { @@ -33,21 +34,15 @@ class CONTENT_EXPORT ServiceWorkerNavigationHandleCore { ~ServiceWorkerNavigationHandleCore(); // Called when a ServiceWorkerProviderHost was pre-created for the navigation - // tracked by this ServiceWorkerNavigationHandleCore. Takes ownership of - // |precreated_host|. - void DidPreCreateProviderHost( - std::unique_ptr precreated_host); - - // Called when the renderer created a ServiceWorkerNetworkProvider matching - // |precreated_host_|. This releases ownership of |precreated_host_|. - std::unique_ptr RetrievePreCreatedHost(); + // tracked by this ServiceWorkerNavigationHandleCore. + void DidPreCreateProviderHost(int provider_id); ServiceWorkerContextWrapper* context_wrapper() const { return context_wrapper_.get(); } private: - std::unique_ptr precreated_host_; + int provider_id_ = kInvalidServiceWorkerProviderId; scoped_refptr context_wrapper_; base::WeakPtr ui_handle_; diff --git a/chromium/content/browser/service_worker/service_worker_navigation_loader.cc b/chromium/content/browser/service_worker/service_worker_navigation_loader.cc index 241e860824a..a5e6d0f8e73 100644 --- a/chromium/content/browser/service_worker/service_worker_navigation_loader.cc +++ b/chromium/content/browser/service_worker/service_worker_navigation_loader.cc @@ -359,7 +359,8 @@ void ServiceWorkerNavigationLoader::StartErrorResponse( // URLLoader implementation---------------------------------------- -void ServiceWorkerNavigationLoader::FollowRedirect() { +void ServiceWorkerNavigationLoader::FollowRedirect( + const base::Optional& modified_request_headers) { NOTIMPLEMENTED(); } diff --git a/chromium/content/browser/service_worker/service_worker_navigation_loader.h b/chromium/content/browser/service_worker/service_worker_navigation_loader.h index 4f12d5b6d15..3aef0461d18 100644 --- a/chromium/content/browser/service_worker/service_worker_navigation_loader.h +++ b/chromium/content/browser/service_worker/service_worker_navigation_loader.h @@ -57,8 +57,7 @@ class CONTENT_EXPORT ServiceWorkerNavigationLoader // determines how the request should be served (e.g. should fallback // to network or should be sent to the SW). If it decides to fallback // to the network this will call |loader_callback| with a null - // RequestHandler, which will be then handled by - // NavigationURLLoaderNetworkService. + // RequestHandler, which will be then handled by NavigationURLLoaderImpl. // 2. If it is decided that the request should be sent to the SW, // this job dispatches a FetchEvent in StartRequest. // 3. In DidDispatchFetchEvent() this job determines the request's @@ -69,12 +68,12 @@ class CONTENT_EXPORT ServiceWorkerNavigationLoader // StartResponse(). // 5. Then StartResponse() will be called with a // network::mojom::URLLoaderClientPtr that is connected to - // NavigationURLLoaderNetworkService (for resource loading for navigation). + // NavigationURLLoaderImpl (for resource loading for navigation). // This forwards the blob/stream data pipe to the NavigationURLLoader if // the response body was sent as a blob/stream. // // Loads for shared workers work similarly, except SharedWorkerScriptLoader - // is used instead of NavigationURLLoaderNetworkService. + // is used instead of NavigationURLLoaderImpl. ServiceWorkerNavigationLoader( NavigationLoaderInterceptor::LoaderCallback loader_callback, Delegate* delegate, @@ -140,7 +139,8 @@ class CONTENT_EXPORT ServiceWorkerNavigationLoader void ReturnNetworkError(); // network::mojom::URLLoader: - void FollowRedirect() override; + void FollowRedirect(const base::Optional& + modified_request_headers) override; void ProceedWithResponse() override; void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override; diff --git a/chromium/content/browser/service_worker/service_worker_navigation_loader_unittest.cc b/chromium/content/browser/service_worker/service_worker_navigation_loader_unittest.cc index e37883b9028..362281ad037 100644 --- a/chromium/content/browser/service_worker/service_worker_navigation_loader_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_navigation_loader_unittest.cc @@ -191,11 +191,12 @@ class MockNetworkURLLoaderFactory final client->OnComplete(status); } - void Clone(network::mojom::URLLoaderFactoryRequest factory) override { - NOTREACHED(); + void Clone(network::mojom::URLLoaderFactoryRequest request) override { + bindings_.AddBinding(this, std::move(request)); } private: + mojo::BindingSet bindings_; DISALLOW_COPY_AND_ASSIGN(MockNetworkURLLoaderFactory); }; diff --git a/chromium/content/browser/service_worker/service_worker_new_script_loader.cc b/chromium/content/browser/service_worker/service_worker_new_script_loader.cc index 578d4ad6511..99d175d307f 100644 --- a/chromium/content/browser/service_worker/service_worker_new_script_loader.cc +++ b/chromium/content/browser/service_worker/service_worker_new_script_loader.cc @@ -16,6 +16,7 @@ #include "content/browser/service_worker/service_worker_write_to_cache_job.h" #include "content/browser/url_loader_factory_getter.h" #include "content/common/service_worker/service_worker_utils.h" +#include "net/base/load_flags.h" #include "net/cert/cert_status_flags.h" #include "services/network/public/cpp/resource_response.h" #include "third_party/blink/public/common/mime_util/mime_util.h" @@ -31,20 +32,23 @@ ServiceWorkerNewScriptLoader::ServiceWorkerNewScriptLoader( int32_t routing_id, int32_t request_id, uint32_t options, - const network::ResourceRequest& resource_request, + const network::ResourceRequest& original_request, network::mojom::URLLoaderClientPtr client, scoped_refptr version, - scoped_refptr loader_factory_getter, + scoped_refptr loader_factory, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) - : request_url_(resource_request.url), - resource_type_(static_cast(resource_request.resource_type)), + : request_url_(original_request.url), + resource_type_(static_cast(original_request.resource_type)), version_(version), network_client_binding_(this), network_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL, base::SequencedTaskRunnerHandle::Get()), + loader_factory_(std::move(loader_factory)), client_(std::move(client)), weak_factory_(this) { + network::ResourceRequest resource_request(original_request); + // ServiceWorkerNewScriptLoader is used for fetching the service worker main // script (RESOURCE_TYPE_SERVICE_WORKER) during worker startup or // importScripts() (RESOURCE_TYPE_SCRIPT). @@ -64,10 +68,13 @@ ServiceWorkerNewScriptLoader::ServiceWorkerNewScriptLoader( // |incumbent_cache_resource_id| is valid if the incumbent service worker // exists and it's required to do the byte-for-byte check. int64_t incumbent_cache_resource_id = kInvalidServiceWorkerResourceId; - if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) { - scoped_refptr registration = - version_->context()->GetLiveRegistration(version_->registration_id()); - DCHECK(registration); + scoped_refptr registration = + version_->context()->GetLiveRegistration(version_->registration_id()); + // ServiceWorkerVersion keeps the registration alive while the service + // worker is starting up, and it must be starting up here. + DCHECK(registration); + const bool is_main_script = resource_type_ == RESOURCE_TYPE_SERVICE_WORKER; + if (is_main_script) { ServiceWorkerVersion* stored_version = registration->waiting_version() ? registration->waiting_version() : registration->active_version(); @@ -80,6 +87,10 @@ ServiceWorkerNewScriptLoader::ServiceWorkerNewScriptLoader( } } + if (ServiceWorkerUtils::ShouldBypassCacheDueToUpdateViaCache( + is_main_script, registration->update_via_cache())) + resource_request.load_flags |= net::LOAD_BYPASS_CACHE; + // Create response readers only when we have to do the byte-for-byte check. std::unique_ptr compare_reader; std::unique_ptr copy_reader; @@ -96,20 +107,21 @@ ServiceWorkerNewScriptLoader::ServiceWorkerNewScriptLoader( cache_resource_id); AdvanceState(State::kStarted); - // Disable MIME sniffing sniffing. The spec requires the header list to have - // a JavaScript MIME type. Therefore, no sniffing is needed. + // Disable MIME sniffing. The spec requires the header list to have a + // JavaScript MIME type. Therefore, no sniffing is needed. options &= ~network::mojom::kURLLoadOptionSniffMimeType; network::mojom::URLLoaderClientPtr network_client; network_client_binding_.Bind(mojo::MakeRequest(&network_client)); - loader_factory_getter->GetNetworkFactory()->CreateLoaderAndStart( + loader_factory_->CreateLoaderAndStart( mojo::MakeRequest(&network_loader_), routing_id, request_id, options, resource_request, std::move(network_client), traffic_annotation); } ServiceWorkerNewScriptLoader::~ServiceWorkerNewScriptLoader() = default; -void ServiceWorkerNewScriptLoader::FollowRedirect() { +void ServiceWorkerNewScriptLoader::FollowRedirect( + const base::Optional& modified_request_headers) { // Resource requests for service worker scripts should not follow redirects. // See comments in OnReceiveRedirect(). NOTREACHED(); diff --git a/chromium/content/browser/service_worker/service_worker_new_script_loader.h b/chromium/content/browser/service_worker/service_worker_new_script_loader.h index 621ce8ef458..c26100f4ca9 100644 --- a/chromium/content/browser/service_worker/service_worker_new_script_loader.h +++ b/chromium/content/browser/service_worker/service_worker_new_script_loader.h @@ -19,7 +19,6 @@ namespace content { class ServiceWorkerCacheWriter; class ServiceWorkerVersion; -class URLLoaderFactoryGetter; struct HttpResponseInfoIOBuffer; // S13nServiceWorker: @@ -44,23 +43,32 @@ struct HttpResponseInfoIOBuffer; // this class also performs the "byte-for-byte" comparison for updating the // worker. If the script is identical, the load succeeds but no script is // written, and ServiceWorkerVersion is told to terminate startup. +// +// NOTE: To perform the network request, this class uses |loader_factory_| which +// may internally use a non-NetworkService factory if URL has a non-http(s) +// scheme, e.g., a chrome-extension:// URL. Regardless, that is still called a +// "network" request in comments and naming. "network" is meant to distinguish +// from the load this URLLoader does for its client: +// "network" <------> SWNewScriptLoader <------> client class CONTENT_EXPORT ServiceWorkerNewScriptLoader : public network::mojom::URLLoader, public network::mojom::URLLoaderClient { public: + // |loader_factory| is used to load the script, see class comments. ServiceWorkerNewScriptLoader( int32_t routing_id, int32_t request_id, uint32_t options, - const network::ResourceRequest& resource_request, + const network::ResourceRequest& original_request, network::mojom::URLLoaderClientPtr client, scoped_refptr version, - scoped_refptr loader_factory_getter, + scoped_refptr loader_factory, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation); ~ServiceWorkerNewScriptLoader() override; // network::mojom::URLLoader: - void FollowRedirect() override; + void FollowRedirect(const base::Optional& + modified_request_headers) override; void ProceedWithResponse() override; void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override; @@ -132,12 +140,14 @@ class CONTENT_EXPORT ServiceWorkerNewScriptLoader std::unique_ptr cache_writer_; - // Used for fetching the script from network. + // Used for fetching the script from network, which might not actually + // use the direct network factory, see class comments. network::mojom::URLLoaderPtr network_loader_; mojo::Binding network_client_binding_; mojo::ScopedDataPipeConsumerHandle network_consumer_; mojo::SimpleWatcher network_watcher_; bool network_load_completed_ = false; + scoped_refptr loader_factory_; // Used for responding with the fetched script to this loader's client. network::mojom::URLLoaderClientPtr client_; diff --git a/chromium/content/browser/service_worker/service_worker_new_script_loader_unittest.cc b/chromium/content/browser/service_worker/service_worker_new_script_loader_unittest.cc index c034c9c80d5..100e0c71be4 100644 --- a/chromium/content/browser/service_worker/service_worker_new_script_loader_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_new_script_loader_unittest.cc @@ -227,7 +227,7 @@ class ServiceWorkerNewScriptLoaderTest : public testing::Test { client_ = std::make_unique(); loader_ = std::make_unique( routing_id, request_id, options, request, client_->CreateInterfacePtr(), - version_, helper_->url_loader_factory_getter(), + version_, helper_->url_loader_factory_getter()->GetNetworkFactory(), net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); } diff --git a/chromium/content/browser/service_worker/service_worker_process_manager_unittest.cc b/chromium/content/browser/service_worker/service_worker_process_manager_unittest.cc index 0139091213d..ab6899ad6ae 100644 --- a/chromium/content/browser/service_worker/service_worker_process_manager_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_process_manager_unittest.cc @@ -4,6 +4,8 @@ #include "content/browser/service_worker/service_worker_process_manager.h" +#include + #include "base/macros.h" #include "base/test/scoped_feature_list.h" #include "content/browser/renderer_host/render_process_host_impl.h" @@ -68,14 +70,14 @@ class ServiceWorkerProcessManagerTest : public testing::Test { script_url_ = GURL("http://www.example.com/sw.js"); render_process_host_factory_.reset( new SiteInstanceRenderProcessHostFactory()); - RenderProcessHostImpl::set_render_process_host_factory( + RenderProcessHostImpl::set_render_process_host_factory_for_testing( render_process_host_factory_.get()); } void TearDown() override { process_manager_->Shutdown(); process_manager_.reset(); - RenderProcessHostImpl::set_render_process_host_factory(nullptr); + RenderProcessHostImpl::set_render_process_host_factory_for_testing(nullptr); render_process_host_factory_.reset(); } 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 bdbd4e18287..4208300ee9b 100644 --- a/chromium/content/browser/service_worker/service_worker_provider_host.cc +++ b/chromium/content/browser/service_worker/service_worker_provider_host.cc @@ -19,6 +19,7 @@ #include "content/browser/service_worker/service_worker_consts.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_context_wrapper.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_registration_object_host.h" @@ -27,7 +28,6 @@ #include "content/browser/service_worker/service_worker_version.h" #include "content/browser/url_loader_factory_getter.h" #include "content/browser/web_contents/web_contents_impl.h" -#include "content/common/service_worker/service_worker_messages.h" #include "content/common/service_worker/service_worker_types.h" #include "content/common/service_worker/service_worker_utils.h" #include "content/public/browser/browser_thread.h" @@ -35,7 +35,6 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" -#include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/child_process_host.h" #include "content/public/common/content_client.h" #include "content/public/common/origin_util.h" @@ -146,36 +145,15 @@ ServiceWorkerMetrics::EventType PurposeToEventType( return ServiceWorkerMetrics::EventType::UNKNOWN; } -// TODO(crbug/831255): Temporary debugging for the linked bug. -std::string URLStringForBadMessage(const GURL& url) { - return url.possibly_invalid_spec().substr(0, 64); -} - -// TODO(crbug/831255): Temporary debugging for the linked bug. -std::string ProviderTypeStringForBadMessage( - blink::mojom::ServiceWorkerProviderType type) { - switch (type) { - case blink::mojom::ServiceWorkerProviderType::kUnknown: - return "unknown"; - case blink::mojom::ServiceWorkerProviderType::kForSharedWorker: - return "shared"; - case blink::mojom::ServiceWorkerProviderType::kForServiceWorker: - return "service"; - case blink::mojom::ServiceWorkerProviderType::kForWindow: - return "window"; - } - NOTREACHED(); - return std::string(); -} - } // anonymous namespace // static -std::unique_ptr +base::WeakPtr ServiceWorkerProviderHost::PreCreateNavigationHost( base::WeakPtr context, bool are_ancestors_secure, const WebContentsGetter& web_contents_getter) { + DCHECK(context); auto host = base::WrapUnique(new ServiceWorkerProviderHost( ChildProcessHost::kInvalidUniqueID, ServiceWorkerProviderHostInfo( @@ -184,7 +162,10 @@ ServiceWorkerProviderHost::PreCreateNavigationHost( are_ancestors_secure), context, nullptr /* dispatcher_host */)); host->web_contents_getter_ = web_contents_getter; - return host; + + auto weak_ptr = host->AsWeakPtr(); + context->AddProviderHost(std::move(host)); + return weak_ptr; } // static @@ -282,17 +263,30 @@ ServiceWorkerProviderHost::ServiceWorkerProviderHost( } ServiceWorkerProviderHost::~ServiceWorkerProviderHost() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // TODO(crbug.com/838410): The CHECKs are temporary debugging for the linked + // bug. + CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + CHECK(!in_dtor_); + in_dtor_ = true; + if (context_) context_->UnregisterProviderHostByClientID(client_uuid_); + if (controller_) + controller_->RemoveControllee(client_uuid_); - // Clear docurl so the deferred activation of a waiting worker - // won't associate the new version with a provider being destroyed. - document_url_ = GURL(); - if (controller_.get()) - controller_->RemoveControllee(this); - + // Remove |this| as an observer of ServiceWorkerRegistrations. + // TODO(falken): Use ScopedObserver instead of this explicit call. RemoveAllMatchingRegistrations(); + + // Explicitly destroy the ServiceWorkerObjectHosts and + // ServiceWorkerRegistrationObjectHosts owned by |this|. Otherwise, this + // destructor can trigger their Mojo connection error handlers, which would + // call back into halfway destroyed |this|. This is because they are + // associated with the ServiceWorkerEventDispatcher interface, which can + // be destroyed while in this destructor (|running_hosted_version_|'s + // |event_dispatcher_|). See https://crbug.com/854993. + handles_.clear(); + registration_object_hosts_.clear(); } int ServiceWorkerProviderHost::frame_id() const { @@ -407,7 +401,7 @@ void ServiceWorkerProviderHost::SetControllerVersionAttribute( version->AddControllee(this); if (previous_version.get()) - previous_version->RemoveControllee(this); + previous_version->RemoveControllee(client_uuid_); // SetController message should be sent only for clients. DCHECK(IsProviderForClient()); @@ -462,7 +456,6 @@ void ServiceWorkerProviderHost::AssociateRegistration( void ServiceWorkerProviderHost::DisassociateRegistration() { DCHECK(IsProviderForClient()); - queued_events_.clear(); if (!associated_registration_.get()) return; associated_registration_ = nullptr; @@ -517,9 +510,10 @@ void ServiceWorkerProviderHost::RemoveServiceWorkerHandle(int64_t version_id) { } bool ServiceWorkerProviderHost::AllowServiceWorker(const GURL& scope) { + DCHECK(IsContextAlive()); return GetContentClient()->browser()->AllowServiceWorker( scope, IsProviderForClient() ? topmost_frame_url() : document_url(), - dispatcher_host_->resource_context(), + context_->wrapper()->resource_context(), base::Bind(&WebContentsImpl::FromRenderFrameHostID, render_process_id_, frame_id())); } @@ -572,7 +566,7 @@ ServiceWorkerProviderHost::CreateRequestHandler( return std::unique_ptr(); } -blink::mojom::ServiceWorkerObjectInfoPtr +base::WeakPtr ServiceWorkerProviderHost::GetOrCreateServiceWorkerHandle( ServiceWorkerVersion* version) { if (!context_ || !version) @@ -581,11 +575,11 @@ ServiceWorkerProviderHost::GetOrCreateServiceWorkerHandle( const int64_t version_id = version->version_id(); auto existing_handle = handles_.find(version_id); if (existing_handle != handles_.end()) - return existing_handle->second->CreateObjectInfo(); + return existing_handle->second->AsWeakPtr(); handles_[version_id] = std::make_unique(context_, this, version); - return handles_[version_id]->CreateObjectInfo(); + return handles_[version_id]->AsWeakPtr(); } bool ServiceWorkerProviderHost::CanAssociateRegistration( @@ -606,8 +600,12 @@ void ServiceWorkerProviderHost::PostMessageToClient( if (!dispatcher_host_) return; - container_->PostMessageToClient(GetOrCreateServiceWorkerHandle(version), - std::move(message)); + blink::mojom::ServiceWorkerObjectInfoPtr info; + base::WeakPtr handle = + GetOrCreateServiceWorkerHandle(version); + if (handle) + info = handle->CreateCompleteObjectInfoToSend(); + container_->PostMessageToClient(std::move(info), std::move(message)); } void ServiceWorkerProviderHost::CountFeature(blink::mojom::WebFeature feature) { @@ -661,19 +659,17 @@ void ServiceWorkerProviderHost::CompleteNavigationInitialized( if (!controller_) return; - // In S13nServiceWorker/NavigationMojoResponse case the controller is already - // sent in navigation commit, but we still need this for - // S13nServiceWorker/NavigationMojoResponse case for setting the use counter - // correctly. - // TODO(kinuko): Stop doing this in S13nServiceWorker/NavigationMojoResponse - // case. + // The controller is already sent in navigation commit, but we still need this + // for setting the use counter correctly. + // TODO(kinuko): Stop doing this. SendSetControllerServiceWorker(false /* notify_controllerchange */); } mojom::ServiceWorkerProviderInfoForStartWorkerPtr ServiceWorkerProviderHost::CompleteStartWorkerPreparation( int process_id, - scoped_refptr hosted_version) { + scoped_refptr hosted_version, + scoped_refptr loader_factory) { DCHECK(context_); DCHECK_EQ(kInvalidEmbeddedWorkerThreadId, render_thread_id_); DCHECK_EQ(ChildProcessHost::kInvalidUniqueID, render_process_id_); @@ -690,20 +686,11 @@ ServiceWorkerProviderHost::CompleteStartWorkerPreparation( DCHECK(dispatcher_host); render_process_id_ = process_id; dispatcher_host_ = dispatcher_host->AsWeakPtr(); - SetDocumentUrl(running_hosted_version()->script_url()); - - // Retrieve the registration associated with |version|. The registration - // must be alive because the version keeps it during starting worker. - ServiceWorkerRegistration* registration = context_->GetLiveRegistration( - running_hosted_version()->registration_id()); - DCHECK(registration); // Initialize provider_info. mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info = mojom::ServiceWorkerProviderInfoForStartWorker::New(); provider_info->provider_id = provider_id(); - provider_info->registration = CreateServiceWorkerRegistrationObjectInfo( - scoped_refptr(registration)); provider_info->client_request = mojo::MakeRequest(&container_); network::mojom::URLLoaderFactoryAssociatedPtrInfo @@ -711,7 +698,7 @@ ServiceWorkerProviderHost::CompleteStartWorkerPreparation( if (ServiceWorkerUtils::IsServicificationEnabled()) { mojo::MakeStrongAssociatedBinding( std::make_unique( - context_, AsWeakPtr(), context_->loader_factory_getter()), + context_, AsWeakPtr(), std::move(loader_factory)), mojo::MakeRequest(&script_loader_factory_ptr_info)); provider_info->script_loader_factory_ptr_info = std::move(script_loader_factory_ptr_info); @@ -734,33 +721,6 @@ void ServiceWorkerProviderHost::CompleteSharedWorkerPreparation() { is_execution_ready_ = true; } -void ServiceWorkerProviderHost::SendServiceWorkerStateChangedMessage( - int worker_handle_id, - blink::mojom::ServiceWorkerState state) { - if (!dispatcher_host_) - return; - - if (!IsReadyToSendMessages()) { - queued_events_.push_back(base::Bind( - &ServiceWorkerProviderHost::SendServiceWorkerStateChangedMessage, - AsWeakPtr(), worker_handle_id, state)); - return; - } - - Send(new ServiceWorkerMsg_ServiceWorkerStateChanged(render_thread_id_, - worker_handle_id, state)); -} - -void ServiceWorkerProviderHost::SetReadyToSendMessagesToWorker( - int render_thread_id) { - DCHECK(!IsReadyToSendMessages()); - render_thread_id_ = render_thread_id; - - for (const auto& event : queued_events_) - event.Run(); - queued_events_.clear(); -} - void ServiceWorkerProviderHost::SyncMatchingRegistrations() { DCHECK(context_); RemoveAllMatchingRegistrations(); @@ -802,20 +762,10 @@ void ServiceWorkerProviderHost::ReturnRegistrationForReadyIfNeeded() { scoped_refptr(registration))); } -bool ServiceWorkerProviderHost::IsReadyToSendMessages() const { - return render_thread_id_ != kInvalidEmbeddedWorkerThreadId; -} - bool ServiceWorkerProviderHost::IsContextAlive() { return context_ != nullptr; } -void ServiceWorkerProviderHost::Send(IPC::Message* message) const { - DCHECK(dispatcher_host_); - DCHECK(IsReadyToSendMessages()); - dispatcher_host_->Send(message); -} - void ServiceWorkerProviderHost::SendSetControllerServiceWorker( bool notify_controllerchange) { if (!dispatcher_host_) @@ -834,8 +784,10 @@ void ServiceWorkerProviderHost::SendSetControllerServiceWorker( DCHECK_EQ(associated_registration_->active_version(), controller_.get()); // Set the info for the JavaScript ServiceWorkerContainer#controller object. - controller_info->object_info = + base::WeakPtr handle = GetOrCreateServiceWorkerHandle(controller_.get()); + if (handle) + controller_info->object_info = handle->CreateCompleteObjectInfoToSend(); // Populate used features for UseCounter purposes. std::vector used_features; @@ -1123,41 +1075,17 @@ bool ServiceWorkerProviderHost::IsValidRegisterMessage( *out_error = ServiceWorkerConsts::kBadMessageFromNonWindow; return false; } - // TODO(crbug/831255): Temporary detailed messages for the linked bug. - // These message are compact since there are only 256 characters available. - const std::string provider_str = - ProviderTypeStringForBadMessage(provider_type()); - std::stringstream stream; - if (!document_url_.is_valid()) { - stream << "SWPH_REGISTER_INVALID_DOCUMENT:" << provider_str - << "d=" << URLStringForBadMessage(document_url_); - *out_error = stream.str(); - return false; - } - if (!options.scope.is_valid()) { - stream << "SWPH_REGISTER_INVALID_SCOPE:" << provider_str - << "p=" << URLStringForBadMessage(options.scope); - *out_error = stream.str(); - return false; - } - if (!script_url.is_valid()) { - stream << "SWPH_REGISTER_INVALID_SCRIPT: " << provider_str - << "s=" << URLStringForBadMessage(script_url); - *out_error = stream.str(); + if (!options.scope.is_valid() || !script_url.is_valid()) { + *out_error = ServiceWorkerConsts::kBadMessageInvalidURL; return false; } - if (ServiceWorkerUtils::ContainsDisallowedCharacter(options.scope, script_url, out_error)) { return false; } - std::vector urls = {document_url_, options.scope, script_url}; + std::vector urls = {document_url(), options.scope, script_url}; if (!ServiceWorkerUtils::AllOriginsMatchAndCanAccessServiceWorkers(urls)) { - stream << "SWPH_REGISTER:" << provider_str - << ",d=" << URLStringForBadMessage(document_url_) - << ",p=" << URLStringForBadMessage(options.scope) - << ",s=" << URLStringForBadMessage(script_url); - *out_error = stream.str(); + *out_error = ServiceWorkerConsts::kBadMessageImproperOrigins; return false; } 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 a82b1ffd2b5..9b69c149e3a 100644 --- a/chromium/content/browser/service_worker/service_worker_provider_host.h +++ b/chromium/content/browser/service_worker/service_worker_provider_host.h @@ -133,11 +133,16 @@ class CONTENT_EXPORT ServiceWorkerProviderHost // Used to pre-create a ServiceWorkerProviderHost for a navigation. The // ServiceWorkerNetworkProvider will later be created in the renderer, should - // the navigation succeed. |is_parent_frame_is_secure| should be true for main + // the navigation succeed. |are_ancestors_secure| should be true for main // frames. Otherwise it is true iff all ancestor frames of this frame have a // secure origin. |web_contents_getter| indicates the tab where the navigation // is occurring. - static std::unique_ptr PreCreateNavigationHost( + // + // The returned host is owned by |context|. Upon successful navigation, the + // caller should remove it from |context| and re-add it after calling + // CompleteNavigationInitialized() to update it with the correct process id. + // If navigation fails, the caller should remove it from |context|. + static base::WeakPtr PreCreateNavigationHost( base::WeakPtr context, bool are_ancestors_secure, const WebContentsGetter& web_contents_getter); @@ -332,13 +337,26 @@ class CONTENT_EXPORT ServiceWorkerProviderHost scoped_refptr body, bool skip_service_worker); - // Used to get a ServiceWorkerObjectInfo to send to the renderer. - // The object info holds a Mojo connection to the ServiceWorkerHandle for the - // |version| to ensure the handle stays alive while the object info is alive. - // A new handle is created if one does not already exist. + // Returns an object info representing |registration|. The object info holds a + // Mojo connection to the ServiceWorkerRegistrationObjectHost for the + // |registration| to ensure the host stays alive while the object info is + // alive. A new ServiceWorkerRegistrationObjectHost instance is created if one + // can not be found in |registration_object_hosts_|. + // + // NOTE: The registration object info should be sent over Mojo in the same + // task with calling this method. Otherwise, some Mojo calls to + // blink::mojom::ServiceWorkerRegistrationObject or + // blink::mojom::ServiceWorkerObject may happen before establishing the + // connections, and they'll end up with crashes. + blink::mojom::ServiceWorkerRegistrationObjectInfoPtr + CreateServiceWorkerRegistrationObjectInfo( + scoped_refptr registration); + + // Returns a ServiceWorkerHandle instance for |version| for this provider + // host. A new instance is created if one does not already exist. // TODO(leonhsl): Make |version| be a scoped_refptr because we'll take its // ownership. - blink::mojom::ServiceWorkerObjectInfoPtr GetOrCreateServiceWorkerHandle( + base::WeakPtr GetOrCreateServiceWorkerHandle( ServiceWorkerVersion* version); // Returns true if |registration| can be associated with this provider. @@ -364,36 +382,35 @@ class CONTENT_EXPORT ServiceWorkerProviderHost // |registration| claims the document to be controlled. void ClaimedByRegistration(ServiceWorkerRegistration* registration); - // Completes initialization of provider hosts used for navigation requests. + // For service worker clients. Completes initialization of + // provider hosts used for navigation requests. void CompleteNavigationInitialized( int process_id, ServiceWorkerProviderHostInfo info, base::WeakPtr dispatcher_host); - // Completes initialization of this provider host (which is for hosting a - // service worker). It is called once a renderer process has been found to - // host the worker. Returns the info needed for creating a - // ServiceWorkerNetworkProvider on the renderer which will be connected to - // this instance. This instance will keep the reference to |hosted_version|, - // so please be careful not to create a reference cycle. + // For service worker execution contexts. Completes initialization of this + // provider host. It is called once a renderer process has been found to host + // the worker. Returns the info needed for creating a provider on the renderer + // which will be connected to this provider host. This instance will take the + // reference to |hosted_version|, so be careful not to create a reference + // cycle. + // + // S13nServiceWorker: + // |loader_factory| is the factory to use for "network" requests for the + // service worker main script and import scripts. It is possibly not the + // simple direct network factory, since service worker scripts can have + // non-NetworkService schemes, e.g., chrome-extension:// URLs. mojom::ServiceWorkerProviderInfoForStartWorkerPtr CompleteStartWorkerPreparation( int process_id, - scoped_refptr hosted_version); + scoped_refptr hosted_version, + scoped_refptr loader_factory); // Called when the shared worker main script resource has finished loading. // After this is called, is_execution_ready() returns true. void CompleteSharedWorkerPreparation(); - // Sends event messages to the renderer. Events for the worker are queued up - // until the worker thread id is known via SetReadyToSendMessagesToWorker(). - void SendServiceWorkerStateChangedMessage( - int worker_handle_id, - blink::mojom::ServiceWorkerState state); - - // Sets the worker thread id and flushes queued events. - void SetReadyToSendMessagesToWorker(int render_thread_id); - void AddMatchingRegistration(ServiceWorkerRegistration* registration); void RemoveMatchingRegistration(ServiceWorkerRegistration* registration); @@ -482,9 +499,6 @@ class CONTENT_EXPORT ServiceWorkerProviderHost void ReturnRegistrationForReadyIfNeeded(); - bool IsReadyToSendMessages() const; - void Send(IPC::Message* message) const; - // Sends information about the controller to the providers of the service // worker clients in the renderer. If |notify_controllerchange| is true, // instructs the renderer to dispatch a 'controllerchange' event. @@ -545,15 +559,6 @@ class CONTENT_EXPORT ServiceWorkerProviderHost void GetInterface(const std::string& interface_name, mojo::ScopedMessagePipeHandle interface_pipe) override; - // Returns an object info representing |registration|. The object info holds a - // Mojo connection to the ServiceWorkerRegistrationObjectHost for the - // |registration| to ensure the host stays alive while the object info is - // alive. A new ServiceWorkerRegistrationObjectHost instance is created if one - // can not be found in |registration_object_hosts_|. - blink::mojom::ServiceWorkerRegistrationObjectInfoPtr - CreateServiceWorkerRegistrationObjectInfo( - scoped_refptr registration); - // Perform common checks that need to run before ContainerHost methods that // come from a child process are handled. // |scope| is checked if it is allowed to run a service worker. @@ -663,8 +668,6 @@ class CONTENT_EXPORT ServiceWorkerProviderHost mojo::BindingSet bindings_for_worker_threads_; - std::vector queued_events_; - // For service worker execution contexts. mojo::Binding interface_provider_binding_; @@ -674,6 +677,9 @@ class CONTENT_EXPORT ServiceWorkerProviderHost // redirects. bool is_execution_ready_ = false; + // TODO(crbug.com/838410): Temporary debugging for the linked bug. + bool in_dtor_ = false; + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHost); }; 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 index 21722145594..d5a07710e2f 100644 --- a/chromium/content/browser/service_worker/service_worker_provider_host_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_provider_host_unittest.cc @@ -161,6 +161,24 @@ class ServiceWorkerProviderHostTest : public testing::Test { return host_raw; } + void FinishNavigation(ServiceWorkerProviderHost* host, + ServiceWorkerProviderHostInfo info) { + // In production code, the loader/request handler does this. + host->SetDocumentUrl(GURL("https://www.example.com/page")); + host->SetTopmostFrameUrl(GURL("https://www.example.com/page")); + + // In production code, the OnProviderCreated IPC is received which + // does this. + std::unique_ptr owned_host = + helper_->context()->ReleaseProviderHost(host->process_id(), + host->provider_id()); + host->CompleteNavigationInitialized( + helper_->mock_render_process_id(), std::move(info), + helper_->GetDispatcherHostForProcess(helper_->mock_render_process_id()) + ->AsWeakPtr()); + helper_->context()->AddProviderHost(std::move(owned_host)); + } + blink::mojom::ServiceWorkerErrorType Register( mojom::ServiceWorkerContainerHost* container_host, GURL pattern, @@ -228,7 +246,8 @@ class ServiceWorkerProviderHostTest : public testing::Test { bool CanFindClientProviderHost(ServiceWorkerProviderHost* host) { for (std::unique_ptr it = context_->GetClientProviderHostIterator( - host->document_url().GetOrigin()); + host->document_url().GetOrigin(), + false /* include_reserved_clients */); !it->IsAtEnd(); it->Advance()) { if (host == it->GetProviderHost()) return true; @@ -255,7 +274,7 @@ class ServiceWorkerProviderHostTest : public testing::Test { const GURL& document_url, const GURL& topmost_frame_url, ServiceWorkerRemoteProviderEndpoint* remote_endpoint) { - std::unique_ptr host = + base::WeakPtr host = ServiceWorkerProviderHost::PreCreateNavigationHost( helper_->context()->AsWeakPtr(), true, base::Callback()); @@ -264,16 +283,19 @@ class ServiceWorkerProviderHostTest : public testing::Test { blink::mojom::ServiceWorkerProviderType::kForWindow, true /* is_parent_frame_secure */); remote_endpoint->BindWithProviderHostInfo(&info); + + std::unique_ptr owned_host = + helper_->context()->ReleaseProviderHost(host->process_id(), + host->provider_id()); host->CompleteNavigationInitialized( helper_->mock_render_process_id(), std::move(info), helper_->GetDispatcherHostForProcess(helper_->mock_render_process_id()) ->AsWeakPtr()); - host->SetDocumentUrl(document_url); host->SetTopmostFrameUrl(topmost_frame_url); - ServiceWorkerProviderHost* host_raw = host.get(); - context_->AddProviderHost(std::move(host)); - return host_raw; + helper_->context()->AddProviderHost(std::move(owned_host)); + + return host.get(); } DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHostTest); @@ -407,7 +429,7 @@ class MockServiceWorkerContainer : public mojom::ServiceWorkerContainer { TEST_F(ServiceWorkerProviderHostTest, Controller) { // Create a host. - std::unique_ptr host = + base::WeakPtr host = ServiceWorkerProviderHost::PreCreateNavigationHost( helper_->context()->AsWeakPtr(), true /* are_ancestors_secure */, base::Callback()); @@ -427,12 +449,7 @@ TEST_F(ServiceWorkerProviderHostTest, Controller) { registration1_->SetActiveVersion(version); // Finish the navigation. - host->SetDocumentUrl(GURL("https://www.example.com/page")); - host->CompleteNavigationInitialized( - helper_->mock_render_process_id(), std::move(info), - helper_->GetDispatcherHostForProcess(helper_->mock_render_process_id()) - ->AsWeakPtr()); - + FinishNavigation(host.get(), std::move(info)); host->AssociateRegistration(registration1_.get(), false /* notify_controllerchange */); base::RunLoop().RunUntilIdle(); @@ -446,7 +463,7 @@ TEST_F(ServiceWorkerProviderHostTest, Controller) { TEST_F(ServiceWorkerProviderHostTest, ActiveIsNotController) { // Create a host. - std::unique_ptr host = + base::WeakPtr host = ServiceWorkerProviderHost::PreCreateNavigationHost( helper_->context()->AsWeakPtr(), true /* are_ancestors_secure */, base::Callback()); @@ -467,12 +484,7 @@ TEST_F(ServiceWorkerProviderHostTest, ActiveIsNotController) { // Finish the navigation. - host->SetDocumentUrl(GURL("https://www.example.com/page")); - host->CompleteNavigationInitialized( - helper_->mock_render_process_id(), std::move(info), - helper_->GetDispatcherHostForProcess(helper_->mock_render_process_id()) - ->AsWeakPtr()); - + FinishNavigation(host.get(), std::move(info)); host->AssociateRegistration(registration1_.get(), false /* notify_controllerchange */); // Promote the worker to active while navigation is still happening. @@ -809,9 +821,9 @@ TEST_F(ServiceWorkerProviderHostTest, EXPECT_EQ(1u, bad_messages_.size()); } -// Test that a "reserved" (i.e., not execution ready) shared worker client is -// not included when iterating over client provider hosts. If it were, it'd be -// undesirably exposed via the Clients API. +// Test that a "reserved" (i.e., not execution ready) client is not included +// when iterating over client provider hosts. If it were, it'd be undesirably +// exposed via the Clients API. TEST_F(ServiceWorkerProviderHostTest, ReservedClientsAreNotExposedToClientsAPI) { { @@ -828,7 +840,7 @@ TEST_F(ServiceWorkerProviderHostTest, } { - std::unique_ptr host = + base::WeakPtr host = ServiceWorkerProviderHost::PreCreateNavigationHost( helper_->context()->AsWeakPtr(), true, base::RepeatingCallback()); @@ -841,13 +853,8 @@ TEST_F(ServiceWorkerProviderHostTest, host->SetDocumentUrl(GURL("https://www.example.com/page")); EXPECT_FALSE(CanFindClientProviderHost(host.get())); - host->CompleteNavigationInitialized( - helper_->mock_render_process_id(), std::move(info), - helper_->GetDispatcherHostForProcess(helper_->mock_render_process_id()) - ->AsWeakPtr()); - auto* host_rawptr = host.get(); - context_->AddProviderHost(std::move(host)); - EXPECT_TRUE(CanFindClientProviderHost(host_rawptr)); + FinishNavigation(host.get(), std::move(info)); + EXPECT_TRUE(CanFindClientProviderHost(host.get())); } } 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 d1d5416741b..69d7b3154e2 100644 --- a/chromium/content/browser/service_worker/service_worker_register_job.cc +++ b/chromium/content/browser/service_worker/service_worker_register_job.cc @@ -47,6 +47,7 @@ ServiceWorkerRegisterJob::ServiceWorkerRegisterJob( force_bypass_cache_(false), skip_script_comparison_(false), promise_resolved_status_(SERVICE_WORKER_OK), + observer_(this), weak_factory_(this) {} ServiceWorkerRegisterJob::ServiceWorkerRegisterJob( @@ -65,6 +66,7 @@ ServiceWorkerRegisterJob::ServiceWorkerRegisterJob( force_bypass_cache_(force_bypass_cache), skip_script_comparison_(skip_script_comparison), promise_resolved_status_(SERVICE_WORKER_OK), + observer_(this), weak_factory_(this) { internal_.registration = registration; } @@ -241,7 +243,7 @@ void ServiceWorkerRegisterJob::ContinueWithRegistration( // "Invoke Set Registration algorithm with job’s scope url and // job’s update via cache mode." - existing_registration->set_update_via_cache(update_via_cache_); + existing_registration->SetUpdateViaCache(update_via_cache_); set_registration(existing_registration); // "Return the result of running the [[Update]] algorithm, or its equivalent, // passing registration as the argument." @@ -356,7 +358,7 @@ void ServiceWorkerRegisterJob::UpdateAndContinue() { new_version()->set_force_bypass_cache_for_scripts(force_bypass_cache_); if (registration()->has_installed_version() && !skip_script_comparison_) { new_version()->set_pause_after_download(true); - new_version()->embedded_worker()->AddListener(this); + observer_.Add(new_version()->embedded_worker()); } else { new_version()->set_pause_after_download(false); } @@ -533,7 +535,7 @@ void ServiceWorkerRegisterJob::CompleteInternal( if (new_version()) { new_version()->set_pause_after_download(false); - new_version()->embedded_worker()->RemoveListener(this); + observer_.RemoveAll(); } if (status != SERVICE_WORKER_OK) { @@ -588,7 +590,8 @@ void ServiceWorkerRegisterJob::AddRegistrationToMatchingProviderHosts( DCHECK(registration); for (std::unique_ptr it = context_->GetClientProviderHostIterator( - registration->pattern().GetOrigin()); + registration->pattern().GetOrigin(), + true /* include_reserved_clients */); !it->IsAtEnd(); it->Advance()) { ServiceWorkerProviderHost* host = it->GetProviderHost(); if (!ServiceWorkerUtils::ScopeMatches(registration->pattern(), @@ -620,6 +623,16 @@ void ServiceWorkerRegisterJob::OnScriptLoaded() { new_version()->embedded_worker()->ResumeAfterDownload(); } +void ServiceWorkerRegisterJob::OnDestroyed() { + // The version's EmbeddedWorkerInstance is getting destructed, so + // remove the observer to avoid a use-after-free. We expect to continue when + // the StartWorker() callback is called with failure. + // TODO(crbug.com/855852): Remove the EmbeddedWorkerInstance::Listener + // interface and have this class listen to ServiceWorkerVersion directly. + if (observer_.IsObserving(new_version()->embedded_worker())) + observer_.Remove(new_version()->embedded_worker()); +} + void ServiceWorkerRegisterJob::BumpLastUpdateCheckTimeIfNeeded() { // Bump the last update check time only when the register/update job fetched // the version having bypassed the network cache. We assume that the 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 cd726a0661b..29cd3f2e423 100644 --- a/chromium/content/browser/service_worker/service_worker_register_job.h +++ b/chromium/content/browser/service_worker/service_worker_register_job.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/scoped_observer.h" #include "base/time/time.h" #include "content/browser/service_worker/embedded_worker_instance.h" #include "content/browser/service_worker/service_worker_register_job_base.h" @@ -139,6 +140,7 @@ class ServiceWorkerRegisterJob : public ServiceWorkerRegisterJobBase, // EmbeddedWorkerInstance::Listener implementation: void OnScriptLoaded() override; + void OnDestroyed() override; void BumpLastUpdateCheckTimeIfNeeded(); @@ -160,6 +162,8 @@ class ServiceWorkerRegisterJob : public ServiceWorkerRegisterJobBase, ServiceWorkerStatusCode promise_resolved_status_; std::string promise_resolved_status_message_; scoped_refptr promise_resolved_registration_; + ScopedObserver + observer_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRegisterJob); diff --git a/chromium/content/browser/service_worker/service_worker_registration.cc b/chromium/content/browser/service_worker/service_worker_registration.cc index 3791d4e7af3..98c613109de 100644 --- a/chromium/content/browser/service_worker/service_worker_registration.cc +++ b/chromium/content/browser/service_worker/service_worker_registration.cc @@ -194,6 +194,15 @@ void ServiceWorkerRegistration::UnsetVersionInternal( } } +void ServiceWorkerRegistration::SetUpdateViaCache( + blink::mojom::ServiceWorkerUpdateViaCache update_via_cache) { + if (update_via_cache_ == update_via_cache) + return; + update_via_cache_ = update_via_cache; + for (auto& observer : listeners_) + observer.OnUpdateViaCacheChanged(this); +} + void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() { DCHECK(waiting_version()); should_activate_when_ready_ = true; @@ -219,7 +228,8 @@ void ServiceWorkerRegistration::ClaimClients() { DCHECK(active_version()); for (std::unique_ptr it = - context_->GetClientProviderHostIterator(pattern_.GetOrigin()); + context_->GetClientProviderHostIterator( + pattern_.GetOrigin(), false /* include_reserved_clients */); !it->IsAtEnd(); it->Advance()) { ServiceWorkerProviderHost* host = it->GetProviderHost(); if (host->controller() == active_version()) @@ -271,6 +281,9 @@ void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion* version) { return; DCHECK_EQ(active_version(), version); if (is_uninstalling_) { + // TODO(falken): This can destroy the caller during this observer function + // call, which is impolite and dangerous. Try to make this async, or make + // OnNoControllees not an observer function. Clear(); return; } @@ -543,6 +556,14 @@ void ServiceWorkerRegistration::Clear() { is_uninstalling_ = false; is_uninstalled_ = true; should_activate_when_ready_ = false; + + // Some callbacks, at least OnRegistrationFinishedUninstalling and + // NotifyDoneUninstallingRegistration, may drop their references to + // |this|, so protect it first. + // TODO(falken): Clean this up, can we call the observers from a task + // or make the observers more polite? + auto protect = base::WrapRefCounted(this); + if (context_) context_->storage()->NotifyDoneUninstallingRegistration(this); diff --git a/chromium/content/browser/service_worker/service_worker_registration.h b/chromium/content/browser/service_worker/service_worker_registration.h index 164a9ac1960..032fa94be09 100644 --- a/chromium/content/browser/service_worker/service_worker_registration.h +++ b/chromium/content/browser/service_worker/service_worker_registration.h @@ -47,6 +47,8 @@ class CONTENT_EXPORT ServiceWorkerRegistration ServiceWorkerRegistration* registration, ChangedVersionAttributesMask changed_mask, const ServiceWorkerRegistrationInfo& info) {} + virtual void OnUpdateViaCacheChanged( + ServiceWorkerRegistration* registation) {} virtual void OnRegistrationFailed( ServiceWorkerRegistration* registration) {} virtual void OnRegistrationFinishedUninstalling( @@ -68,10 +70,6 @@ class CONTENT_EXPORT ServiceWorkerRegistration blink::mojom::ServiceWorkerUpdateViaCache update_via_cache() const { return update_via_cache_; } - void set_update_via_cache( - blink::mojom::ServiceWorkerUpdateViaCache update_via_cache) { - update_via_cache_ = update_via_cache; - } bool is_deleted() const { return is_deleted_; } void set_is_deleted(bool deleted) { is_deleted_ = deleted; } @@ -137,6 +135,11 @@ class CONTENT_EXPORT ServiceWorkerRegistration // listeners via OnVersionAttributesChanged. void UnsetVersion(ServiceWorkerVersion* version); + // Sets the updateViaCache attribute, and notifies listeners via + // OnUpdateViaCacheChanged. + void SetUpdateViaCache( + blink::mojom::ServiceWorkerUpdateViaCache update_via_cache); + // Triggers the [[Activate]] algorithm when the currently active version is // ready to become redundant (see IsReadyToActivate()). The algorithm is // triggered immediately if it's already ready. diff --git a/chromium/content/browser/service_worker/service_worker_registration_object_host.cc b/chromium/content/browser/service_worker/service_worker_registration_object_host.cc index 11837fba49e..38ea97ec4ac 100644 --- a/chromium/content/browser/service_worker/service_worker_registration_object_host.cc +++ b/chromium/content/browser/service_worker/service_worker_registration_object_host.cc @@ -14,6 +14,22 @@ namespace content { +namespace { + +// Returns an object info to send over Mojo. The info must be sent immediately. +// See ServiceWorkerHandle::CreateCompleteObjectInfoToSend() for details. +blink::mojom::ServiceWorkerObjectInfoPtr CreateCompleteObjectInfoToSend( + ServiceWorkerProviderHost* provider_host, + ServiceWorkerVersion* version) { + base::WeakPtr service_worker_handle = + provider_host->GetOrCreateServiceWorkerHandle(version); + if (!service_worker_handle) + return nullptr; + return service_worker_handle->CreateCompleteObjectInfoToSend(); +} + +} // anonymous namespace + ServiceWorkerRegistrationObjectHost::ServiceWorkerRegistrationObjectHost( base::WeakPtr context, ServiceWorkerProviderHost* provider_host, @@ -42,15 +58,14 @@ ServiceWorkerRegistrationObjectHost::CreateObjectInfo() { registration_->pattern(), registration_->update_via_cache()); info->registration_id = registration_->id(); bindings_.AddBinding(this, mojo::MakeRequest(&info->host_ptr_info)); - if (!remote_registration_) - info->request = mojo::MakeRequest(&remote_registration_); - - info->installing = provider_host_->GetOrCreateServiceWorkerHandle( - registration_->installing_version()); - info->waiting = provider_host_->GetOrCreateServiceWorkerHandle( - registration_->waiting_version()); - info->active = provider_host_->GetOrCreateServiceWorkerHandle( - registration_->active_version()); + info->request = mojo::MakeRequest(&remote_registration_); + + info->installing = CreateCompleteObjectInfoToSend( + provider_host_, registration_->installing_version()); + info->waiting = CreateCompleteObjectInfoToSend( + provider_host_, registration_->waiting_version()); + info->active = CreateCompleteObjectInfoToSend( + provider_host_, registration_->active_version()); return info; } @@ -64,6 +79,11 @@ void ServiceWorkerRegistrationObjectHost::OnVersionAttributesChanged( registration->active_version()); } +void ServiceWorkerRegistrationObjectHost::OnUpdateViaCacheChanged( + ServiceWorkerRegistration* registration) { + remote_registration_->SetUpdateViaCache(registration->update_via_cache()); +} + void ServiceWorkerRegistrationObjectHost::OnRegistrationFailed( ServiceWorkerRegistration* registration) { DCHECK_EQ(registration->id(), registration_->id()); @@ -276,12 +296,12 @@ void ServiceWorkerRegistrationObjectHost::SetVersionAttributes( blink::mojom::ServiceWorkerObjectInfoPtr active; if (changed_mask.installing_changed()) { installing = - provider_host_->GetOrCreateServiceWorkerHandle(installing_version); + CreateCompleteObjectInfoToSend(provider_host_, installing_version); } if (changed_mask.waiting_changed()) - waiting = provider_host_->GetOrCreateServiceWorkerHandle(waiting_version); + waiting = CreateCompleteObjectInfoToSend(provider_host_, waiting_version); if (changed_mask.active_changed()) - active = provider_host_->GetOrCreateServiceWorkerHandle(active_version); + active = CreateCompleteObjectInfoToSend(provider_host_, active_version); DCHECK(remote_registration_); remote_registration_->SetVersionAttributes( changed_mask.changed(), std::move(installing), std::move(waiting), diff --git a/chromium/content/browser/service_worker/service_worker_registration_object_host.h b/chromium/content/browser/service_worker/service_worker_registration_object_host.h index 9b978c6761c..6af91e91ccd 100644 --- a/chromium/content/browser/service_worker/service_worker_registration_object_host.h +++ b/chromium/content/browser/service_worker/service_worker_registration_object_host.h @@ -51,6 +51,8 @@ class CONTENT_EXPORT ServiceWorkerRegistrationObjectHost ServiceWorkerRegistration* registration, ChangedVersionAttributesMask changed_mask, const ServiceWorkerRegistrationInfo& info) override; + void OnUpdateViaCacheChanged( + ServiceWorkerRegistration* registration) override; void OnRegistrationFailed(ServiceWorkerRegistration* registration) override; void OnUpdateFound(ServiceWorkerRegistration* registration) override; @@ -110,6 +112,8 @@ class CONTENT_EXPORT ServiceWorkerRegistrationObjectHost // |this|. ServiceWorkerProviderHost* provider_host_; base::WeakPtr context_; + scoped_refptr registration_; + mojo::AssociatedBindingSet bindings_; // Mojo connection to the content::WebServiceWorkerRegistrationImpl in the @@ -118,8 +122,6 @@ class CONTENT_EXPORT ServiceWorkerRegistrationObjectHost blink::mojom::ServiceWorkerRegistrationObjectAssociatedPtr remote_registration_; - scoped_refptr registration_; - base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRegistrationObjectHost); 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 c3c86f49727..2a7e84d67f4 100644 --- a/chromium/content/browser/service_worker/service_worker_registration_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_registration_unittest.cc @@ -23,7 +23,6 @@ #include "content/browser/service_worker/service_worker_registration_object_host.h" #include "content/browser/service_worker/service_worker_test_utils.h" #include "content/common/service_worker/service_worker_utils.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 "mojo/edk/embedder/embedder.h" @@ -78,6 +77,9 @@ class MockServiceWorkerRegistrationObject int set_version_attributes_called_count() const { return set_version_attributes_called_count_; }; + int set_update_via_cache_called_count() const { + return set_update_via_cache_called_count_; + } int changed_mask() const { return changed_mask_; } const blink::mojom::ServiceWorkerObjectInfoPtr& installing() const { return installing_; @@ -88,6 +90,9 @@ class MockServiceWorkerRegistrationObject const blink::mojom::ServiceWorkerObjectInfoPtr& active() const { return active_; } + blink::mojom::ServiceWorkerUpdateViaCache update_via_cache() const { + return update_via_cache_; + } private: // Implements blink::mojom::ServiceWorkerRegistrationObject. @@ -102,14 +107,22 @@ class MockServiceWorkerRegistrationObject waiting_ = std::move(waiting); active_ = std::move(active); } + void SetUpdateViaCache( + blink::mojom::ServiceWorkerUpdateViaCache update_via_cache) override { + set_update_via_cache_called_count_++; + update_via_cache_ = update_via_cache; + } void UpdateFound() override { update_found_called_count_++; } int update_found_called_count_ = 0; int set_version_attributes_called_count_ = 0; + int set_update_via_cache_called_count_ = 0; int changed_mask_ = 0; blink::mojom::ServiceWorkerObjectInfoPtr installing_; blink::mojom::ServiceWorkerObjectInfoPtr waiting_; blink::mojom::ServiceWorkerObjectInfoPtr active_; + blink::mojom::ServiceWorkerUpdateViaCache update_via_cache_ = + blink::mojom::ServiceWorkerUpdateViaCache::kImports; mojo::AssociatedBinding binding_; @@ -258,8 +271,7 @@ TEST_F(ServiceWorkerRegistrationTest, FailedRegistrationNoCrash) { auto registration = base::MakeRefCounted( options, kRegistrationId, context()->AsWeakPtr()); auto dispatcher_host = base::MakeRefCounted( - helper_->mock_render_process_id(), - helper_->browser_context()->GetResourceContext()); + helper_->mock_render_process_id()); // Prepare a ServiceWorkerProviderHost. ServiceWorkerRemoteProviderEndpoint remote_endpoint; std::unique_ptr provider_host = @@ -448,7 +460,7 @@ TEST_F(ServiceWorkerActivationTest, NoInflightRequest) { // Remove the controllee. Since there is an in-flight request, // activation should not yet happen. - version_1->RemoveControllee(controllee()); + version_1->RemoveControllee(controllee()->client_uuid()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(version_1.get(), reg->active_version()); @@ -473,7 +485,7 @@ TEST_F(ServiceWorkerActivationTest, NoControllee) { EXPECT_EQ(version_1.get(), reg->active_version()); // Remove the controllee. Activation should happen. - version_1->RemoveControllee(controllee()); + version_1->RemoveControllee(controllee()->client_uuid()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(version_2.get(), reg->active_version()); } @@ -603,7 +615,7 @@ TEST_F(ServiceWorkerActivationTest, LameDuckTime_NoControllee) { // Remove the controllee. Since there is still an in-flight request, // activation should not happen. But the lame duck timer should start. EXPECT_FALSE(IsLameDuckTimerRunning()); - version_1->RemoveControllee(controllee()); + version_1->RemoveControllee(controllee()->client_uuid()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(version_1.get(), reg->active_version()); EXPECT_TRUE(IsLameDuckTimerRunning()); @@ -618,7 +630,7 @@ TEST_F(ServiceWorkerActivationTest, LameDuckTime_NoControllee) { EXPECT_TRUE(IsLameDuckTimerRunning()); // Remove the controllee. - version_1->RemoveControllee(controllee()); + version_1->RemoveControllee(controllee()->client_uuid()); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(IsLameDuckTimerRunning()); @@ -884,10 +896,11 @@ TEST_F(ServiceWorkerRegistrationObjectHostTest, Unregister_Success) { blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info = GetRegistrationFromRemote(remote_endpoint.host_ptr()->get(), kScope); registration_host_ptr.Bind(std::move(info->host_ptr_info)); - // Ignore the messages to the registration object, otherwise the callbacks - // issued from |registration_host_ptr| may wait for receiving the messages to - // |info->request|. + // Ignore the messages to the registration object and corresponding service + // worker objects, otherwise the callbacks issued from |registration_host_ptr| + // may wait for receiving the messages to them. info->request = nullptr; + info->waiting->request = nullptr; EXPECT_EQ(SERVICE_WORKER_OK, FindRegistrationInStorage(registration_id, kScope)); @@ -1043,6 +1056,58 @@ TEST_F(ServiceWorkerRegistrationObjectHostTest, SetVersionAttributes) { } } +TEST_F(ServiceWorkerRegistrationObjectHostTest, SetUpdateViaCache) { + const GURL kScope("https://www.example.com/"); + const GURL kScriptUrl("https://www.example.com/sw.js"); + int64_t registration_id = SetUpRegistration(kScope, kScriptUrl); + const int64_t kProviderId = 99; // Dummy value + ServiceWorkerRemoteProviderEndpoint remote_endpoint = + PrepareProviderHost(kProviderId, kScope); + blink::mojom::ServiceWorkerRegistrationObjectInfoPtr info = + GetRegistrationFromRemote(remote_endpoint.host_ptr()->get(), kScope); + EXPECT_EQ(registration_id, info->registration_id); + EXPECT_TRUE(info->request.is_pending()); + auto mock_registration_object = + std::make_unique( + std::move(info->request)); + + ServiceWorkerRegistration* registration = + context()->GetLiveRegistration(registration_id); + ASSERT_EQ(blink::mojom::ServiceWorkerUpdateViaCache::kImports, + registration->update_via_cache()); + ASSERT_EQ(0, mock_registration_object->set_update_via_cache_called_count()); + ASSERT_EQ(blink::mojom::ServiceWorkerUpdateViaCache::kImports, + mock_registration_object->update_via_cache()); + + registration->SetUpdateViaCache( + blink::mojom::ServiceWorkerUpdateViaCache::kImports); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, mock_registration_object->set_update_via_cache_called_count()); + EXPECT_EQ(blink::mojom::ServiceWorkerUpdateViaCache::kImports, + mock_registration_object->update_via_cache()); + + registration->SetUpdateViaCache( + blink::mojom::ServiceWorkerUpdateViaCache::kAll); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, mock_registration_object->set_update_via_cache_called_count()); + EXPECT_EQ(blink::mojom::ServiceWorkerUpdateViaCache::kAll, + mock_registration_object->update_via_cache()); + + registration->SetUpdateViaCache( + blink::mojom::ServiceWorkerUpdateViaCache::kNone); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(2, mock_registration_object->set_update_via_cache_called_count()); + EXPECT_EQ(blink::mojom::ServiceWorkerUpdateViaCache::kNone, + mock_registration_object->update_via_cache()); + + registration->SetUpdateViaCache( + blink::mojom::ServiceWorkerUpdateViaCache::kImports); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(3, mock_registration_object->set_update_via_cache_called_count()); + EXPECT_EQ(blink::mojom::ServiceWorkerUpdateViaCache::kImports, + mock_registration_object->update_via_cache()); +} + TEST_F(ServiceWorkerRegistrationObjectHostTest, UpdateFound) { const GURL kScope("https://www.example.com/"); const GURL kScriptUrl("https://www.example.com/sw.js"); diff --git a/chromium/content/browser/service_worker/service_worker_request_handler.cc b/chromium/content/browser/service_worker/service_worker_request_handler.cc index 2ca37e938e9..379a90df55e 100644 --- a/chromium/content/browser/service_worker/service_worker_request_handler.cc +++ b/chromium/content/browser/service_worker/service_worker_request_handler.cc @@ -71,7 +71,6 @@ bool SchemeMaySupportRedirectingToHTTPS(const GURL& url) { // static int ServiceWorkerRequestHandler::user_data_key_; -// PlzNavigate: // static void ServiceWorkerRequestHandler::InitializeForNavigation( net::URLRequest* request, @@ -108,16 +107,17 @@ void ServiceWorkerRequestHandler::InitializeForNavigation( return; } - if (!navigation_handle_core->context_wrapper() || - !navigation_handle_core->context_wrapper()->context()) { + if (!navigation_handle_core->context_wrapper()) + return; + ServiceWorkerContextCore* context = + navigation_handle_core->context_wrapper()->context(); + if (!context) return; - } // Initialize the SWProviderHost. - std::unique_ptr provider_host = + base::WeakPtr provider_host = ServiceWorkerProviderHost::PreCreateNavigationHost( - navigation_handle_core->context_wrapper()->context()->AsWeakPtr(), - is_parent_frame_secure, web_contents_getter); + context->AsWeakPtr(), is_parent_frame_secure, web_contents_getter); std::unique_ptr handler( provider_host->CreateRequestHandler( @@ -130,12 +130,8 @@ void ServiceWorkerRequestHandler::InitializeForNavigation( if (handler) request->SetUserData(&user_data_key_, std::move(handler)); - // Transfer ownership to the ServiceWorkerNavigationHandleCore. - // In the case of a successful navigation, the SWProviderHost will be - // transferred to its "final" destination in the OnProviderCreated handler. If - // the navigation fails, it will be destroyed along with the - // ServiceWorkerNavigationHandleCore. - navigation_handle_core->DidPreCreateProviderHost(std::move(provider_host)); + navigation_handle_core->DidPreCreateProviderHost( + provider_host->provider_id()); } // S13nServiceWorker: @@ -163,13 +159,15 @@ ServiceWorkerRequestHandler::InitializeForNavigationNetworkService( return nullptr; } - if (!navigation_handle_core->context_wrapper() || - !navigation_handle_core->context_wrapper()->context()) { + if (!navigation_handle_core->context_wrapper()) + return nullptr; + ServiceWorkerContextCore* context = + navigation_handle_core->context_wrapper()->context(); + if (!context) return nullptr; - } // Initialize the SWProviderHost. - std::unique_ptr provider_host = + base::WeakPtr provider_host = ServiceWorkerProviderHost::PreCreateNavigationHost( navigation_handle_core->context_wrapper()->context()->AsWeakPtr(), is_parent_frame_secure, web_contents_getter); @@ -183,12 +181,8 @@ ServiceWorkerRequestHandler::InitializeForNavigationNetworkService( request_context_type, frame_type, blob_storage_context->AsWeakPtr(), body, skip_service_worker)); - // Transfer ownership to the ServiceWorkerNavigationHandleCore. - // In the case of a successful navigation, the SWProviderHost will be - // transferred to its "final" destination in the OnProviderCreated handler. If - // the navigation fails, it will be destroyed along with the - // ServiceWorkerNavigationHandleCore. - navigation_handle_core->DidPreCreateProviderHost(std::move(provider_host)); + navigation_handle_core->DidPreCreateProviderHost( + provider_host->provider_id()); return base::WrapUnique(handler.release()); } @@ -238,6 +232,14 @@ void ServiceWorkerRequestHandler::InitializeHandler( RequestContextType request_context_type, network::mojom::RequestContextFrameType frame_type, scoped_refptr body) { + // S13nServiceWorker enabled, NetworkService disabled: + // for subresource requests, subresource loader should be used, but when that + // request handler falls back to network, InitializeHandler() is called. + // Since we already determined to fall back to network, don't create another + // handler. + if (ServiceWorkerUtils::IsServicificationEnabled()) + return; + // Create the handler even for insecure HTTP since it's used in the // case of redirect to HTTPS. if (!request->url().SchemeIsHTTPOrHTTPS() && @@ -304,28 +306,6 @@ void ServiceWorkerRequestHandler::MaybeCreateLoader( std::move(callback).Run({}); } -void ServiceWorkerRequestHandler::PrepareForCrossSiteTransfer( - int old_process_id) { - CHECK(!IsBrowserSideNavigationEnabled()); -} - -void ServiceWorkerRequestHandler::CompleteCrossSiteTransfer( - int new_process_id, int new_provider_id) { - CHECK(!IsBrowserSideNavigationEnabled()); -} - -void ServiceWorkerRequestHandler::MaybeCompleteCrossSiteTransferInOldProcess( - int old_process_id) { - CHECK(!IsBrowserSideNavigationEnabled()); -} - -bool ServiceWorkerRequestHandler::SanityCheckIsSameContext( - ServiceWorkerContextWrapper* wrapper) { - if (!wrapper) - return !context_; - return context_.get() == wrapper->context(); -} - ServiceWorkerRequestHandler::~ServiceWorkerRequestHandler() { } diff --git a/chromium/content/browser/service_worker/service_worker_request_handler.h b/chromium/content/browser/service_worker/service_worker_request_handler.h index da73b702058..1dc6fb71f4e 100644 --- a/chromium/content/browser/service_worker/service_worker_request_handler.h +++ b/chromium/content/browser/service_worker/service_worker_request_handler.h @@ -143,18 +143,6 @@ class CONTENT_EXPORT ServiceWorkerRequestHandler ResourceContext* resource_context, LoaderCallback callback) override; - // These are obsolete, needed for non-PlzNavigate. - // TODO(falken): Remove these completely. - void PrepareForCrossSiteTransfer(int old_process_id); - void CompleteCrossSiteTransfer(int new_process_id, - int new_provider_id); - void MaybeCompleteCrossSiteTransferInOldProcess( - int old_process_id); - - // Useful for detecting storage partition mismatches in the context of cross - // site transfer navigations. - bool SanityCheckIsSameContext(ServiceWorkerContextWrapper* wrapper); - protected: ServiceWorkerRequestHandler( base::WeakPtr context, diff --git a/chromium/content/browser/service_worker/service_worker_script_loader_factory.cc b/chromium/content/browser/service_worker/service_worker_script_loader_factory.cc index a61fc1f14fe..344db0e12f9 100644 --- a/chromium/content/browser/service_worker/service_worker_script_loader_factory.cc +++ b/chromium/content/browser/service_worker/service_worker_script_loader_factory.cc @@ -10,21 +10,22 @@ #include "content/browser/service_worker/service_worker_new_script_loader.h" #include "content/browser/service_worker/service_worker_provider_host.h" #include "content/browser/service_worker/service_worker_version.h" -#include "content/browser/url_loader_factory_getter.h" #include "content/common/service_worker/service_worker_utils.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "services/network/public/cpp/resource_response.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" namespace content { ServiceWorkerScriptLoaderFactory::ServiceWorkerScriptLoaderFactory( base::WeakPtr context, base::WeakPtr provider_host, - scoped_refptr loader_factory_getter) + scoped_refptr loader_factory) : context_(context), provider_host_(provider_host), - loader_factory_getter_(loader_factory_getter) { + loader_factory_(std::move(loader_factory)) { DCHECK(provider_host_->IsProviderForServiceWorker()); + DCHECK(loader_factory_); } ServiceWorkerScriptLoaderFactory::~ServiceWorkerScriptLoaderFactory() = default; @@ -38,13 +39,12 @@ void ServiceWorkerScriptLoaderFactory::CreateLoaderAndStart( network::mojom::URLLoaderClientPtr client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { DCHECK(ServiceWorkerUtils::IsServicificationEnabled()); - DCHECK(loader_factory_getter_); if (!ShouldHandleScriptRequest(resource_request)) { - // If the request should not be handled, just fallback to the network. - // This needs a relaying as we use different associated message pipes. + // If the request should not be handled, just do a passthrough load. This + // needs a relaying as we use different associated message pipes. // TODO(kinuko): Record the reason like what we do with netlog in // ServiceWorkerContextRequestHandler. - loader_factory_getter_->GetNetworkFactory()->CreateLoaderAndStart( + loader_factory_->CreateLoaderAndStart( std::move(request), routing_id, request_id, options, resource_request, std::move(client), traffic_annotation); return; @@ -70,11 +70,11 @@ void ServiceWorkerScriptLoaderFactory::CreateLoaderAndStart( return; } - // The common case: load the script from network and install it. + // The common case: load the script and install it. mojo::MakeStrongBinding( std::make_unique( routing_id, request_id, options, resource_request, std::move(client), - provider_host_->running_hosted_version(), loader_factory_getter_, + provider_host_->running_hosted_version(), loader_factory_, traffic_annotation), std::move(request)); } diff --git a/chromium/content/browser/service_worker/service_worker_script_loader_factory.h b/chromium/content/browser/service_worker/service_worker_script_loader_factory.h index 8463f047bbe..995bb61f499 100644 --- a/chromium/content/browser/service_worker/service_worker_script_loader_factory.h +++ b/chromium/content/browser/service_worker/service_worker_script_loader_factory.h @@ -8,30 +8,38 @@ #include "base/macros.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" +namespace network { +class SharedURLLoaderFactory; +} // namespace network + namespace content { class ServiceWorkerContextCore; class ServiceWorkerProviderHost; -class URLLoaderFactoryGetter; // S13nServiceWorker: -// Created per one running service worker for loading its scripts (only during -// installation, eventually). This is kept alive while -// ServiceWorkerNetworkProvider in the renderer process is alive. +// Created per one running service worker for loading its scripts. This is kept +// alive while ServiceWorkerNetworkProvider in the renderer process is alive. // // This factory handles requests for the scripts of a new (installing) // service worker. For installed workers, service worker script streaming -// (ServiceWorkerInstalledScriptsSender) is used instead. +// (ServiceWorkerInstalledScriptsSender) is typically used instead. However, +// this factory can still be used when an installed worker imports a +// non-installed script (https://crbug.com/719052). // // This factory creates either a ServiceWorkerNewScriptLoader or a // ServiceWorkerInstalledScriptLoader to load a script. class ServiceWorkerScriptLoaderFactory : public network::mojom::URLLoaderFactory { public: + // |loader_factory| is used to load scripts. Typically + // a new script will be loaded from the NetworkService. However, + // |loader_factory| may internally contain non-NetworkService + // factories used for non-http(s) URLs, e.g., a chrome-extension:// URL. ServiceWorkerScriptLoaderFactory( base::WeakPtr context, base::WeakPtr provider_host, - scoped_refptr loader_factory_getter); + scoped_refptr loader_factory); ~ServiceWorkerScriptLoaderFactory() override; // network::mojom::URLLoaderFactory: @@ -51,7 +59,7 @@ class ServiceWorkerScriptLoaderFactory base::WeakPtr context_; base::WeakPtr provider_host_; - scoped_refptr loader_factory_getter_; + scoped_refptr loader_factory_; DISALLOW_COPY_AND_ASSIGN(ServiceWorkerScriptLoaderFactory); }; diff --git a/chromium/content/browser/service_worker/service_worker_storage.cc b/chromium/content/browser/service_worker/service_worker_storage.cc index 08f439d6e4c..81676aaacaf 100644 --- a/chromium/content/browser/service_worker/service_worker_storage.cc +++ b/chromium/content/browser/service_worker/service_worker_storage.cc @@ -11,7 +11,6 @@ #include "base/bind_helpers.h" #include "base/files/file_util.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" #include "base/sequenced_task_runner.h" #include "base/task_runner_util.h" #include "base/task_scheduler/post_task.h" @@ -121,24 +120,25 @@ ServiceWorkerStorage::~ServiceWorkerStorage() { // static std::unique_ptr ServiceWorkerStorage::Create( - const base::FilePath& path, + const base::FilePath& user_data_directory, const base::WeakPtr& context, scoped_refptr database_task_runner, storage::QuotaManagerProxy* quota_manager_proxy, storage::SpecialStoragePolicy* special_storage_policy) { - return base::WrapUnique( - new ServiceWorkerStorage(path, context, std::move(database_task_runner), - quota_manager_proxy, special_storage_policy)); + return base::WrapUnique(new ServiceWorkerStorage( + user_data_directory, context, std::move(database_task_runner), + quota_manager_proxy, special_storage_policy)); } // static std::unique_ptr ServiceWorkerStorage::Create( const base::WeakPtr& context, ServiceWorkerStorage* old_storage) { - return base::WrapUnique(new ServiceWorkerStorage( - old_storage->path_, context, old_storage->database_task_runner_, - old_storage->quota_manager_proxy_.get(), - old_storage->special_storage_policy_.get())); + return base::WrapUnique( + new ServiceWorkerStorage(old_storage->user_data_directory_, context, + old_storage->database_task_runner_, + old_storage->quota_manager_proxy_.get(), + old_storage->special_storage_policy_.get())); } void ServiceWorkerStorage::FindRegistrationForDocument( @@ -1052,7 +1052,7 @@ void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) { } ServiceWorkerStorage::ServiceWorkerStorage( - const base::FilePath& path, + const base::FilePath& user_data_directory, base::WeakPtr context, scoped_refptr database_task_runner, storage::QuotaManagerProxy* quota_manager_proxy, @@ -1062,7 +1062,7 @@ ServiceWorkerStorage::ServiceWorkerStorage( next_resource_id_(kInvalidServiceWorkerResourceId), state_(UNINITIALIZED), expecting_done_with_disk_on_disable_(false), - path_(path), + user_data_directory_(user_data_directory), context_(context), database_task_runner_(std::move(database_task_runner)), quota_manager_proxy_(quota_manager_proxy), @@ -1075,16 +1075,18 @@ ServiceWorkerStorage::ServiceWorkerStorage( } base::FilePath ServiceWorkerStorage::GetDatabasePath() { - if (path_.empty()) + if (user_data_directory_.empty()) return base::FilePath(); - return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory) + return user_data_directory_ + .Append(ServiceWorkerContextCore::kServiceWorkerDirectory) .Append(kDatabaseName); } base::FilePath ServiceWorkerStorage::GetDiskCachePath() { - if (path_.empty()) + if (user_data_directory_.empty()) return base::FilePath(); - return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory) + return user_data_directory_ + .Append(ServiceWorkerContextCore::kServiceWorkerDirectory) .Append(kDiskCacheName); } @@ -1389,6 +1391,8 @@ void ServiceWorkerStorage::DidStoreRegistration( deleted_version.resources_total_size_bytes); } + context_->NotifyRegistrationStored(new_version.registration_id, + new_version.scope); std::move(callback).Run(SERVICE_WORKER_OK); if (!context_->GetLiveVersion(deleted_version.version_id)) diff --git a/chromium/content/browser/service_worker/service_worker_storage.h b/chromium/content/browser/service_worker/service_worker_storage.h index 3daafe9cb55..88a1c291efb 100644 --- a/chromium/content/browser/service_worker/service_worker_storage.h +++ b/chromium/content/browser/service_worker/service_worker_storage.h @@ -97,7 +97,7 @@ class CONTENT_EXPORT ServiceWorkerStorage ~ServiceWorkerStorage() override; static std::unique_ptr Create( - const base::FilePath& path, + const base::FilePath& user_data_directory, const base::WeakPtr& context, scoped_refptr database_task_runner, storage::QuotaManagerProxy* quota_manager_proxy, @@ -356,7 +356,7 @@ class CONTENT_EXPORT ServiceWorkerStorage ServiceWorkerDatabase::Status status)>; ServiceWorkerStorage( - const base::FilePath& path, + const base::FilePath& user_data_directory, base::WeakPtr context, scoped_refptr database_task_runner, storage::QuotaManagerProxy* quota_manager_proxy, @@ -583,7 +583,7 @@ class CONTENT_EXPORT ServiceWorkerStorage // ... so it's easier to keep track of the case when it will happen. bool expecting_done_with_disk_on_disable_; - base::FilePath path_; + base::FilePath user_data_directory_; // The context should be valid while the storage is alive. base::WeakPtr context_; 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 8ab6e03831c..1c69543d47c 100644 --- a/chromium/content/browser/service_worker/service_worker_storage_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_storage_unittest.cc @@ -26,7 +26,7 @@ #include "content/common/service_worker/service_worker_status_code.h" #include "content/common/service_worker/service_worker_utils.h" #include "content/public/common/content_client.h" -#include "content/public/common/origin_trial_policy.h" +#include "content/public/common/origin_util.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "ipc/ipc_message.h" @@ -39,6 +39,7 @@ #include "net/test/cert_test_util.h" #include "net/test/test_data_directory.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/origin_trials/origin_trial_policy.h" #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" @@ -1449,7 +1450,7 @@ TEST_F(ServiceWorkerResourceStorageTest, DeleteRegistration_ActiveVersion) { EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id2_, true)); // Removing the controllee should cause the resources to be deleted. - registration_->active_version()->RemoveControllee(host.get()); + registration_->active_version()->RemoveControllee(host.get()->client_uuid()); registration_->active_version()->Doom(); base::RunLoop().RunUntilIdle(); verify_ids.clear(); @@ -1666,7 +1667,7 @@ TEST_F(ServiceWorkerResourceStorageTest, UpdateRegistration) { // Removing the controllee should cause the old version's resources to be // deleted. - registration_->active_version()->RemoveControllee(host.get()); + registration_->active_version()->RemoveControllee(host.get()->client_uuid()); registration_->active_version()->Doom(); base::RunLoop().RunUntilIdle(); verify_ids.clear(); @@ -1798,10 +1799,12 @@ class ServiceWorkerStorageOriginTrialsDiskTest : public ServiceWorkerStorageTest { public: ServiceWorkerStorageOriginTrialsDiskTest() { - SetContentClient(&test_content_client_); + blink::TrialTokenValidator::SetOriginTrialPolicyGetter(base::BindRepeating( + [](blink::OriginTrialPolicy* policy) { return policy; }, + base::Unretained(&origin_trial_policy_))); } ~ServiceWorkerStorageOriginTrialsDiskTest() override { - SetContentClient(nullptr); + blink::TrialTokenValidator::ResetOriginTrialPolicyGetter(); } void SetUp() override { ASSERT_TRUE(InitUserDataDirectory()); @@ -1809,24 +1812,18 @@ class ServiceWorkerStorageOriginTrialsDiskTest } private: - class TestOriginTrialPolicy : public OriginTrialPolicy { + class TestOriginTrialPolicy : public blink::OriginTrialPolicy { public: + bool IsOriginTrialsSupported() const override { return true; } base::StringPiece GetPublicKey() const override { return base::StringPiece(reinterpret_cast(kTestPublicKey), arraysize(kTestPublicKey)); } - }; - class TestContentClient : public ContentClient { - public: - // ContentRendererClient methods - OriginTrialPolicy* GetOriginTrialPolicy() override { - return &origin_trial_policy_; + bool IsOriginSecure(const GURL& url) const override { + return content::IsOriginSecure(url); } - - private: - TestOriginTrialPolicy origin_trial_policy_; }; - TestContentClient test_content_client_; + TestOriginTrialPolicy origin_trial_policy_; }; TEST_F(ServiceWorkerStorageOriginTrialsDiskTest, FromMainScript) { diff --git a/chromium/content/browser/service_worker/service_worker_test_utils.cc b/chromium/content/browser/service_worker/service_worker_test_utils.cc index 67e56a81a2b..d01073ccaeb 100644 --- a/chromium/content/browser/service_worker/service_worker_test_utils.cc +++ b/chromium/content/browser/service_worker/service_worker_test_utils.cc @@ -101,11 +101,6 @@ void ServiceWorkerRemoteProviderEndpoint::BindWithProviderInfo( mojom::ServiceWorkerProviderInfoForStartWorkerPtr info) { client_request_ = std::move(info->client_request); host_ptr_.Bind(std::move(info->host_ptr_info)); - registration_object_info_ = std::move(info->registration); - // To enable the caller end point to make calls safely with no need to pass - // |registration_object_info_->request| through a message pipe endpoint. - mojo::AssociateWithDisconnectedPipe( - registration_object_info_->request.PassHandle()); } std::unique_ptr CreateProviderHostForWindow( @@ -136,8 +131,10 @@ CreateProviderHostForServiceWorkerContext( is_parent_frame_secure); std::unique_ptr host = ServiceWorkerProviderHost::PreCreateForController(std::move(context)); + host->SetDocumentUrl(hosted_version->script_url()); mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info = - host->CompleteStartWorkerPreparation(process_id, hosted_version); + host->CompleteStartWorkerPreparation( + process_id, hosted_version, nullptr /* non_network_loader_factory */); output_endpoint->BindWithProviderInfo(std::move(provider_info)); return host; } diff --git a/chromium/content/browser/service_worker/service_worker_test_utils.h b/chromium/content/browser/service_worker/service_worker_test_utils.h index 363387afbde..859b06d488f 100644 --- a/chromium/content/browser/service_worker/service_worker_test_utils.h +++ b/chromium/content/browser/service_worker/service_worker_test_utils.h @@ -29,7 +29,6 @@ namespace content { class ServiceWorkerContextCore; class ServiceWorkerDispatcherHost; class ServiceWorkerProviderHost; -class ServiceWorkerRegistration; class ServiceWorkerStorage; class ServiceWorkerVersion; struct ServiceWorkerProviderHostInfo; @@ -87,9 +86,6 @@ class ServiceWorkerRemoteProviderEndpoint { // This is the other end of ServiceWorkerContainerAssociatedPtr owned by // content::ServiceWorkerProviderHost. mojom::ServiceWorkerContainerAssociatedRequest client_request_; - // This is to keep alive the corresponding content::ServiceWorkerRegistration. - blink::mojom::ServiceWorkerRegistrationObjectInfoPtr - registration_object_info_; DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRemoteProviderEndpoint); }; 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 index 20b8ee246d9..094bfb5e5f4 100644 --- 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 @@ -468,9 +468,8 @@ class DelayHelper : public EmbeddedWorkerTestHelper { EmbeddedWorkerTestHelper::OnStartWorker( embedded_worker_id_, service_worker_version_id_, scope_, script_url_, pause_after_download_, std::move(start_worker_request_), - std::move(controller_request_), std::move(service_worker_host_), - std::move(start_worker_instance_host_), std::move(provider_info_), - std::move(installed_scripts_info_)); + std::move(controller_request_), std::move(start_worker_instance_host_), + std::move(provider_info_), std::move(installed_scripts_info_)); } void Respond() { @@ -500,7 +499,6 @@ class DelayHelper : public EmbeddedWorkerTestHelper { bool pause_after_download, mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, mojom::ControllerServiceWorkerRequest controller_request, - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) @@ -512,7 +510,6 @@ class DelayHelper : public EmbeddedWorkerTestHelper { pause_after_download_ = pause_after_download; start_worker_request_ = std::move(dispatcher_request); controller_request_ = std::move(controller_request); - service_worker_host_ = std::move(service_worker_host); start_worker_instance_host_ = std::move(instance_host); provider_info_ = std::move(provider_info); installed_scripts_info_ = std::move(installed_scripts_info); @@ -538,7 +535,6 @@ class DelayHelper : public EmbeddedWorkerTestHelper { bool pause_after_download_; mojom::ServiceWorkerEventDispatcherRequest start_worker_request_; mojom::ControllerServiceWorkerRequest controller_request_; - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host_; mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo start_worker_instance_host_; mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info_; diff --git a/chromium/content/browser/service_worker/service_worker_version.cc b/chromium/content/browser/service_worker/service_worker_version.cc index 025c6403566..bfe78b055f3 100644 --- a/chromium/content/browser/service_worker/service_worker_version.cc +++ b/chromium/content/browser/service_worker/service_worker_version.cc @@ -31,7 +31,6 @@ #include "content/browser/service_worker/service_worker_installed_scripts_sender.h" #include "content/browser/service_worker/service_worker_registration.h" #include "content/browser/service_worker/service_worker_type_converters.h" -#include "content/common/origin_trials/trial_policy_impl.h" #include "content/common/service_worker/embedded_worker.mojom.h" #include "content/common/service_worker/service_worker_messages.h" #include "content/common/service_worker/service_worker_utils.h" @@ -166,17 +165,20 @@ void OnEventDispatcherConnectionError( } } +// In S13nServiceWorker, |loader_factory| is the factory to use loading new +// scripts from network (or other sources, e.g., for chrome-extension:// URLs). mojom::ServiceWorkerProviderInfoForStartWorkerPtr CompleteProviderHostPreparation( ServiceWorkerVersion* version, std::unique_ptr provider_host, base::WeakPtr context, - int process_id) { + int process_id, + scoped_refptr loader_factory) { // Caller should ensure |context| is alive when completing StartWorker // preparation. DCHECK(context); - auto info = - provider_host->CompleteStartWorkerPreparation(process_id, version); + auto info = provider_host->CompleteStartWorkerPreparation( + process_id, version, std::move(loader_factory)); context->AddProviderHost(std::move(provider_host)); return info; } @@ -329,14 +331,14 @@ ServiceWorkerVersion::ServiceWorkerVersion( tick_clock_(base::DefaultTickClock::GetInstance()), clock_(base::DefaultClock::GetInstance()), ping_controller_(new PingController(this)), - validator_(TrialPolicyImpl::CreateValidatorForPolicy()), + validator_(std::make_unique()), weak_factory_(this) { DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId, version_id); DCHECK(context_); DCHECK(registration); DCHECK(script_url_.is_valid()); embedded_worker_ = context_->embedded_worker_registry()->CreateWorker(this); - embedded_worker_->AddListener(this); + embedded_worker_->AddObserver(this); context_->AddLiveVersion(this); } @@ -359,7 +361,7 @@ ServiceWorkerVersion::~ServiceWorkerVersion() { running_status() == EmbeddedWorkerStatus::RUNNING) { embedded_worker_->Stop(); } - embedded_worker_->RemoveListener(this); + embedded_worker_->RemoveObserver(this); } void ServiceWorkerVersion::SetNavigationPreloadState( @@ -453,9 +455,9 @@ ServiceWorkerVersionInfo ServiceWorkerVersion::GetInfo() { const ServiceWorkerProviderHost* host = controllee.second; info.clients.insert(std::make_pair( host->client_uuid(), - ServiceWorkerVersionInfo::ClientInfo( - host->process_id(), host->route_id(), host->web_contents_getter(), - host->provider_type()))); + ServiceWorkerClientInfo(host->process_id(), host->route_id(), + host->web_contents_getter(), + host->provider_type()))); } if (!main_script_http_info_) return info; @@ -504,15 +506,7 @@ void ServiceWorkerVersion::StartWorker(ServiceWorkerMetrics::EventType purpose, base::BindOnce(std::move(callback), SERVICE_WORKER_ERROR_REDUNDANT)); return; } - - // Check that the worker is allowed to start on the given scope. Since this - // worker might not be used for a specific tab, pass a null callback as - // WebContents getter. - // resource_context() can return null in unit tests. - if (context_->wrapper()->resource_context() && - !GetContentClient()->browser()->AllowServiceWorker( - scope_, scope_, context_->wrapper()->resource_context(), - base::Callback())) { + if (!IsStartWorkerAllowed()) { RecordStartWorkerResult(purpose, status_, kInvalidTraceId, is_browser_startup_complete, SERVICE_WORKER_ERROR_DISALLOWED); @@ -729,6 +723,7 @@ void ServiceWorkerVersion::RunAfterStartWorker( void ServiceWorkerVersion::AddControllee( ServiceWorkerProviderHost* provider_host) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); const std::string& uuid = provider_host->client_uuid(); CHECK(!provider_host->client_uuid().empty()); DCHECK(!base::ContainsKey(controllee_map_, uuid)); @@ -736,22 +731,28 @@ void ServiceWorkerVersion::AddControllee( // Keep the worker alive a bit longer right after a new controllee is added. RestartTick(&idle_time_); ClearTick(&no_controllees_time_); - for (auto& observer : listeners_) - observer.OnControlleeAdded(this, provider_host); + + // Notify observers asynchronously for consistency with RemoveControllee. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&ServiceWorkerVersion::NotifyControlleeAdded, + weak_factory_.GetWeakPtr(), uuid, + ServiceWorkerClientInfo( + provider_host->process_id(), provider_host->route_id(), + provider_host->web_contents_getter(), + provider_host->provider_type()))); } -void ServiceWorkerVersion::RemoveControllee( - ServiceWorkerProviderHost* provider_host) { - const std::string& uuid = provider_host->client_uuid(); - DCHECK(base::ContainsKey(controllee_map_, uuid)); - controllee_map_.erase(uuid); - for (auto& observer : listeners_) - observer.OnControlleeRemoved(this, provider_host); - if (!HasControllee()) { - RestartTick(&no_controllees_time_); - for (auto& observer : listeners_) - observer.OnNoControllees(this); - } +void ServiceWorkerVersion::RemoveControllee(const std::string& client_uuid) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(base::ContainsKey(controllee_map_, client_uuid)); + controllee_map_.erase(client_uuid); + // Notify observers asynchronously since this gets called during + // ServiceWorkerProviderHost's destructor, and we don't want observers to do + // work during that. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&ServiceWorkerVersion::NotifyControlleeRemoved, + weak_factory_.GetWeakPtr(), client_uuid)); } void ServiceWorkerVersion::OnStreamResponseStarted() { @@ -935,9 +936,6 @@ ServiceWorkerVersion::InflightRequest::~InflightRequest() {} void ServiceWorkerVersion::OnThreadStarted() { DCHECK_EQ(EmbeddedWorkerStatus::STARTING, running_status()); - DCHECK(provider_host_); - provider_host_->SetReadyToSendMessagesToWorker( - embedded_worker()->thread_id()); // Activate ping/pong now that JavaScript execution will start. ping_controller_->Activate(); } @@ -1517,9 +1515,19 @@ void ServiceWorkerVersion::StartWorkerInternal() { // TODO(horo): These CHECKs are for debugging crbug.com/759938. CHECK(event_dispatcher_.is_bound()); CHECK(params->dispatcher_request.is_pending()); - + event_dispatcher_.set_connection_error_handler(base::BindOnce( + &OnEventDispatcherConnectionError, embedded_worker_->AsWeakPtr())); + blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host; binding_.Close(); - binding_.Bind(mojo::MakeRequest(¶ms->service_worker_host)); + binding_.Bind(mojo::MakeRequest(&service_worker_host)); + ServiceWorkerRegistration* registration = + context_->GetLiveRegistration(registration_id_); + DCHECK(registration); + provider_host->SetDocumentUrl(script_url()); + event_dispatcher_->InitializeGlobalScope( + std::move(service_worker_host), + provider_host->CreateServiceWorkerRegistrationObjectInfo( + scoped_refptr(registration))); // S13nServiceWorker: if (!controller_request_.is_pending()) { @@ -1536,8 +1544,6 @@ void ServiceWorkerVersion::StartWorkerInternal() { std::move(provider_host), context()), base::BindOnce(&ServiceWorkerVersion::OnStartSentAndScriptEvaluated, weak_factory_.GetWeakPtr())); - event_dispatcher_.set_connection_error_handler(base::BindOnce( - &OnEventDispatcherConnectionError, embedded_worker_->AsWeakPtr())); } void ServiceWorkerVersion::StartTimeoutTimer() { @@ -1610,10 +1616,10 @@ void ServiceWorkerVersion::OnTimeoutTimer() { // EmbeddedWorkerInstance could destroy our ServiceWorkerProviderHost // which could in turn destroy |this|. scoped_refptr protect_this(this); - embedded_worker_->RemoveListener(this); + embedded_worker_->RemoveObserver(this); embedded_worker_->Detach(); embedded_worker_ = context_->embedded_worker_registry()->CreateWorker(this); - embedded_worker_->AddListener(this); + embedded_worker_->AddObserver(this); // Call OnStoppedInternal to fail callbacks and possibly restart. OnStoppedInternal(EmbeddedWorkerStatus::STOPPING); @@ -1994,4 +2000,50 @@ void ServiceWorkerVersion::OnNoWorkInBrowser() { idle_timer_fired_in_renderer_ = false; } +bool ServiceWorkerVersion::IsStartWorkerAllowed() const { + // Check that the worker is allowed on this origin. It's possible a + // worker was previously allowed and installed, but later the embedder's + // policy or binary changed to disallow this origin. + if (!ServiceWorkerUtils::AllOriginsMatchAndCanAccessServiceWorkers( + {script_url_})) { + return false; + } + + // Check that the worker is allowed on the given scope. It's possible a worker + // was previously allowed and installed, but later content settings changed to + // disallow this scope. Since this worker might not be used for a specific + // tab, pass a null callback as WebContents getter. + // resource_context() can return null in unit tests. + if ((context_->wrapper()->resource_context() && + !GetContentClient()->browser()->AllowServiceWorker( + scope_, scope_, context_->wrapper()->resource_context(), + base::Callback()))) { + return false; + } + + return true; +} + +void ServiceWorkerVersion::NotifyControlleeAdded( + const std::string& uuid, + const ServiceWorkerClientInfo& info) { + for (auto& observer : listeners_) + observer.OnControlleeAdded(this, uuid, info); +} + +void ServiceWorkerVersion::NotifyControlleeRemoved(const std::string& uuid) { + // The observers can destroy |this|, so protect it first. + // TODO(falken): Make OnNoControllees an explicit call to our registration + // instead of an observer callback, if it has dangerous side-effects like + // destroying the caller. + auto protect = base::WrapRefCounted(this); + for (auto& observer : listeners_) + observer.OnControlleeRemoved(this, uuid); + if (!HasControllee()) { + RestartTick(&no_controllees_time_); + for (auto& observer : listeners_) + observer.OnNoControllees(this); + } +} + } // 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 c0aba6af7c5..ce8fec41aaa 100644 --- a/chromium/content/browser/service_worker/service_worker_version.h +++ b/chromium/content/browser/service_worker/service_worker_version.h @@ -29,6 +29,7 @@ #include "base/timer/timer.h" #include "content/browser/service_worker/embedded_worker_instance.h" #include "content/browser/service_worker/embedded_worker_status.h" +#include "content/browser/service_worker/service_worker_client_info.h" #include "content/browser/service_worker/service_worker_client_utils.h" #include "content/browser/service_worker/service_worker_context_request_handler.h" #include "content/browser/service_worker/service_worker_metrics.h" @@ -159,11 +160,15 @@ class CONTENT_EXPORT ServiceWorkerVersion const base::string16& message, int line_number, const GURL& source_url) {} + // OnControlleeAdded/Removed are called asynchronously. It is possible the + // provider host identified by |client_uuid| was already destroyed when they + // are called. virtual void OnControlleeAdded(ServiceWorkerVersion* version, - ServiceWorkerProviderHost* provider_host) {} - virtual void OnControlleeRemoved(ServiceWorkerVersion* version, - ServiceWorkerProviderHost* provider_host) { + const std::string& client_uuid, + const ServiceWorkerClientInfo& client_info) { } + virtual void OnControlleeRemoved(ServiceWorkerVersion* version, + const std::string& client_uuid) {} virtual void OnNoControllees(ServiceWorkerVersion* version) {} virtual void OnNoWork(ServiceWorkerVersion* version) {} virtual void OnCachedMetadataUpdated(ServiceWorkerVersion* version, @@ -323,6 +328,8 @@ class CONTENT_EXPORT ServiceWorkerVersion // This must be called when the worker is running. mojom::ServiceWorkerEventDispatcher* event_dispatcher() { + DCHECK(running_status() == EmbeddedWorkerStatus::STARTING || + running_status() == EmbeddedWorkerStatus::RUNNING); // Temporarily CHECK for debugging https://crbug.com/817981. CHECK(event_dispatcher_.is_bound()); CHECK(event_dispatcher_.get()); @@ -344,9 +351,9 @@ class CONTENT_EXPORT ServiceWorkerVersion return controller_ptr_.get(); } - // Adds and removes |provider_host| as a controllee of this ServiceWorker. + // Adds and removes the specified host as a controllee of this service worker. void AddControllee(ServiceWorkerProviderHost* provider_host); - void RemoveControllee(ServiceWorkerProviderHost* provider_host); + void RemoveControllee(const std::string& client_uuid); // Returns if it has controllee. bool HasControllee() const { return !controllee_map_.empty(); } @@ -722,6 +729,12 @@ class CONTENT_EXPORT ServiceWorkerVersion // has been fired or the worker has been stopped. void OnNoWorkInBrowser(); + bool IsStartWorkerAllowed() const; + + void NotifyControlleeAdded(const std::string& uuid, + const ServiceWorkerClientInfo& info); + void NotifyControlleeRemoved(const std::string& uuid); + const int64_t version_id_; const int64_t registration_id_; const GURL script_url_; diff --git a/chromium/content/browser/service_worker/service_worker_version_unittest.cc b/chromium/content/browser/service_worker/service_worker_version_unittest.cc index 8023d7da5fa..dd8493e736a 100644 --- a/chromium/content/browser/service_worker/service_worker_version_unittest.cc +++ b/chromium/content/browser/service_worker/service_worker_version_unittest.cc @@ -45,45 +45,13 @@ class MessageReceiver : public EmbeddedWorkerTestHelper { void SimulateSetCachedMetadata(int embedded_worker_id, const GURL& url, const std::vector& data) { - ASSERT_TRUE(service_worker_host_map_[embedded_worker_id]); - service_worker_host_map_[embedded_worker_id]->SetCachedMetadata(url, data); + GetServiceWorkerHost(embedded_worker_id)->SetCachedMetadata(url, data); } void SimulateClearCachedMetadata(int embedded_worker_id, const GURL& url) { - ASSERT_TRUE(service_worker_host_map_[embedded_worker_id]); - service_worker_host_map_[embedded_worker_id]->ClearCachedMetadata(url); + GetServiceWorkerHost(embedded_worker_id)->ClearCachedMetadata(url); } - protected: - void OnStartWorker( - int embedded_worker_id, - int64_t service_worker_version_id, - const GURL& scope, - const GURL& script_url, - bool pause_after_download, - mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, - mojom::ControllerServiceWorkerRequest controller_request, - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, - mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, - mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, - blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) - override { - service_worker_host_map_[embedded_worker_id].Bind( - std::move(service_worker_host)); - EmbeddedWorkerTestHelper::OnStartWorker( - embedded_worker_id, service_worker_version_id, scope, script_url, - pause_after_download, std::move(dispatcher_request), - std::move(controller_request), nullptr /* service_worker_host */, - std::move(instance_host), std::move(provider_info), - std::move(installed_scripts_info)); - } - - private: - std::map< - int /* embedded_worker_id */, - blink::mojom::ServiceWorkerHostAssociatedPtr /* service_worker_host */> - service_worker_host_map_; - DISALLOW_COPY_AND_ASSIGN(MessageReceiver); }; @@ -177,7 +145,8 @@ class ServiceWorkerVersionTest : public testing::Test { blink::mojom::ServiceWorkerRegistrationOptions options; options.scope = pattern_; registration_ = new ServiceWorkerRegistration( - options, 1L, helper_->context()->AsWeakPtr()); + options, helper_->context()->storage()->NewRegistrationId(), + helper_->context()->AsWeakPtr()); version_ = new ServiceWorkerVersion( registration_.get(), GURL("https://www.example.com/test/service_worker.js"), @@ -266,7 +235,6 @@ class MessageReceiverDisallowStart : public MessageReceiver { bool pause_after_download, mojom::ServiceWorkerEventDispatcherRequest dispatcher_request, mojom::ControllerServiceWorkerRequest controller_request, - blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) @@ -287,16 +255,13 @@ class MessageReceiverDisallowStart : public MessageReceiver { mock_instance_clients()->size()); // Remove the connection by peer mock_instance_clients()->at(current_mock_instance_index_).reset(); - std::move(dispatcher_request); - std::move(controller_request); break; case StartMode::SUCCEED: MessageReceiver::OnStartWorker( embedded_worker_id, service_worker_version_id, scope, script_url, pause_after_download, std::move(dispatcher_request), - std::move(controller_request), std::move(service_worker_host), - std::move(instance_host), std::move(provider_info), - std::move(installed_scripts_info)); + std::move(controller_request), std::move(instance_host), + std::move(provider_info), std::move(installed_scripts_info)); break; } current_mock_instance_index_++; @@ -1266,6 +1231,26 @@ TEST_F(ServiceWorkerVersionTest, RendererCrashDuringEvent) { base::Time::Now())); } +// Test starting a service worker from a disallowed origin. +TEST_F(ServiceWorkerVersionTest, BadOrigin) { + const GURL scope("bad-origin://www.example.com/test/"); + blink::mojom::ServiceWorkerRegistrationOptions options; + options.scope = scope; + auto registration = base::MakeRefCounted( + options, helper_->context()->storage()->NewRegistrationId(), + helper_->context()->AsWeakPtr()); + auto version = base::MakeRefCounted( + registration_.get(), + GURL("bad-origin://www.example.com/test/service_worker.js"), + helper_->context()->storage()->NewVersionId(), + helper_->context()->AsWeakPtr()); + ServiceWorkerStatusCode status = SERVICE_WORKER_OK; + version->StartWorker(ServiceWorkerMetrics::EventType::UNKNOWN, + CreateReceiverOnCurrentThread(&status)); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(SERVICE_WORKER_ERROR_DISALLOWED, status); +} + TEST_F(ServiceWorkerFailToStartTest, FailingWorkerUsesNewRendererProcess) { ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; ServiceWorkerContextCore* context = helper_->context(); 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 index 53b45366cfe..1a3a3f2ad9a 100644 --- 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 @@ -62,11 +62,6 @@ class CONTENT_EXPORT ServiceWorkerWriteToCacheJob private: friend class ServiceWorkerContextRequestHandlerTest; - // TODO(https://crbug.com/675540): Remove the following - // FRIEND_TEST_ALL_PREFIXES directive when the update_via_cache flag is - // shipped to stable. - FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest, - UpdateBefore24HoursWithoutUpdateViaCache); FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest, ServiceWorkerDataRequestAnnotation); diff --git a/chromium/content/browser/shape_detection/shape_detection_browsertest.cc b/chromium/content/browser/shape_detection/shape_detection_browsertest.cc index 8eb87b5f7e3..8b02c0faff0 100644 --- a/chromium/content/browser/shape_detection/shape_detection_browsertest.cc +++ b/chromium/content/browser/shape_detection/shape_detection_browsertest.cc @@ -49,9 +49,9 @@ class ShapeDetectionBrowserTest public ::testing::WithParamInterface { public: void SetUpCommandLine(base::CommandLine* command_line) override { - // Flags to enable ShapeDetection and DOMRect API. + // Flag to enable ShapeDetection API. CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kEnableBlinkFeatures, "ShapeDetection, GeometryInterfaces"); + switches::kEnableBlinkFeatures, "ShapeDetection"); } protected: diff --git a/chromium/content/browser/shared_worker/mock_shared_worker.cc b/chromium/content/browser/shared_worker/mock_shared_worker.cc new file mode 100644 index 00000000000..32ce0577fdc --- /dev/null +++ b/chromium/content/browser/shared_worker/mock_shared_worker.cc @@ -0,0 +1,197 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/shared_worker/mock_shared_worker.h" + +#include "mojo/public/cpp/test_support/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace content { + +namespace { + +template +bool CheckEquality(const T& expected, const T& actual) { + EXPECT_EQ(expected, actual); + return expected == actual; +} + +} // namespace + +MockSharedWorker::MockSharedWorker(mojom::SharedWorkerRequest request) + : binding_(this, std::move(request)) {} + +MockSharedWorker::~MockSharedWorker() = default; + +bool MockSharedWorker::CheckReceivedConnect(int* connection_request_id, + blink::MessagePortChannel* port) { + if (connect_received_.empty()) + return false; + if (connection_request_id) + *connection_request_id = connect_received_.front().first; + if (port) + *port = connect_received_.front().second; + connect_received_.pop(); + return true; +} + +bool MockSharedWorker::CheckNotReceivedConnect() { + return connect_received_.empty(); +} + +bool MockSharedWorker::CheckReceivedTerminate() { + if (!terminate_received_) + return false; + terminate_received_ = false; + return true; +} + +void MockSharedWorker::Connect(int connection_request_id, + mojo::ScopedMessagePipeHandle port) { + connect_received_.emplace(connection_request_id, + blink::MessagePortChannel(std::move(port))); +} + +void MockSharedWorker::Terminate() { + // Allow duplicate events. + terminate_received_ = true; +} + +void MockSharedWorker::BindDevToolsAgent( + blink::mojom::DevToolsAgentAssociatedRequest request) { + NOTREACHED(); +} + +MockSharedWorkerFactory::MockSharedWorkerFactory( + mojom::SharedWorkerFactoryRequest request) + : binding_(this, std::move(request)) {} + +MockSharedWorkerFactory::~MockSharedWorkerFactory() = default; + +bool MockSharedWorkerFactory::CheckReceivedCreateSharedWorker( + const GURL& expected_url, + const std::string& expected_name, + blink::WebContentSecurityPolicyType expected_content_security_policy_type, + mojom::SharedWorkerHostPtr* host, + mojom::SharedWorkerRequest* request) { + std::unique_ptr create_params = std::move(create_params_); + if (!create_params) + return false; + if (!CheckEquality(expected_url, create_params->info->url)) + return false; + if (!CheckEquality(expected_name, create_params->info->name)) + return false; + if (!CheckEquality(expected_content_security_policy_type, + create_params->info->content_security_policy_type)) + return false; + if (!create_params->interface_provider) + return false; + *host = std::move(create_params->host); + *request = std::move(create_params->request); + return true; +} + +void MockSharedWorkerFactory::CreateSharedWorker( + mojom::SharedWorkerInfoPtr info, + bool pause_on_start, + const base::UnguessableToken& devtools_worker_token, + blink::mojom::WorkerContentSettingsProxyPtr content_settings, + mojom::ServiceWorkerProviderInfoForSharedWorkerPtr + service_worker_provider_info, + network::mojom::URLLoaderFactoryAssociatedPtrInfo + script_loader_factory_ptr_info, + mojom::SharedWorkerHostPtr host, + mojom::SharedWorkerRequest request, + service_manager::mojom::InterfaceProviderPtr interface_provider) { + DCHECK(!create_params_); + create_params_ = std::make_unique(); + create_params_->info = std::move(info); + create_params_->pause_on_start = pause_on_start; + create_params_->content_settings = std::move(content_settings); + create_params_->host = std::move(host); + create_params_->request = std::move(request); + create_params_->interface_provider = std::move(interface_provider); +} + +MockSharedWorkerFactory::CreateParams::CreateParams() = default; + +MockSharedWorkerFactory::CreateParams::~CreateParams() = default; + +MockSharedWorkerClient::MockSharedWorkerClient() : binding_(this) {} + +MockSharedWorkerClient::~MockSharedWorkerClient() = default; + +void MockSharedWorkerClient::Bind(mojom::SharedWorkerClientRequest request) { + binding_.Bind(std::move(request)); +} + +void MockSharedWorkerClient::Close() { + binding_.Close(); +} + +bool MockSharedWorkerClient::CheckReceivedOnCreated() { + if (!on_created_received_) + return false; + on_created_received_ = false; + return true; +} + +bool MockSharedWorkerClient::CheckReceivedOnConnected( + std::set expected_used_features) { + if (!on_connected_received_) + return false; + on_connected_received_ = false; + if (!CheckEquality(expected_used_features, on_connected_features_)) + return false; + return true; +} + +bool MockSharedWorkerClient::CheckReceivedOnFeatureUsed( + blink::mojom::WebFeature expected_feature) { + if (!on_feature_used_received_) + return false; + on_feature_used_received_ = false; + if (!CheckEquality(expected_feature, on_feature_used_feature_)) + return false; + return true; +} + +bool MockSharedWorkerClient::CheckNotReceivedOnFeatureUsed() { + return !on_feature_used_received_; +} + +bool MockSharedWorkerClient::CheckReceivedOnScriptLoadFailed() { + if (!on_script_load_failed_) + return false; + on_script_load_failed_ = false; + return true; +} + +void MockSharedWorkerClient::OnCreated( + blink::mojom::SharedWorkerCreationContextType creation_context_type) { + DCHECK(!on_created_received_); + on_created_received_ = true; +} + +void MockSharedWorkerClient::OnConnected( + const std::vector& features_used) { + DCHECK(!on_connected_received_); + on_connected_received_ = true; + for (auto feature : features_used) + on_connected_features_.insert(feature); +} + +void MockSharedWorkerClient::OnScriptLoadFailed() { + DCHECK(!on_script_load_failed_); + on_script_load_failed_ = true; +} + +void MockSharedWorkerClient::OnFeatureUsed(blink::mojom::WebFeature feature) { + DCHECK(!on_feature_used_received_); + on_feature_used_received_ = true; + on_feature_used_feature_ = feature; +} + +} // namespace content diff --git a/chromium/content/browser/shared_worker/mock_shared_worker.h b/chromium/content/browser/shared_worker/mock_shared_worker.h new file mode 100644 index 00000000000..791fdb17060 --- /dev/null +++ b/chromium/content/browser/shared_worker/mock_shared_worker.h @@ -0,0 +1,132 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SHARED_WORKER_MOCK_SHARED_WORKER_H_ +#define CONTENT_BROWSER_SHARED_WORKER_MOCK_SHARED_WORKER_H_ + +#include +#include +#include +#include +#include +#include + +#include "base/macros.h" +#include "content/browser/shared_worker/shared_worker_host.h" +#include "content/common/service_worker/service_worker_provider.mojom.h" +#include "content/common/shared_worker/shared_worker_factory.mojom.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "third_party/blink/public/common/message_port/message_port_channel.h" + +class GURL; + +namespace content { + +class MockSharedWorker : public mojom::SharedWorker { + public: + explicit MockSharedWorker(mojom::SharedWorkerRequest request); + ~MockSharedWorker() override; + + bool CheckReceivedConnect(int* connection_request_id, + blink::MessagePortChannel* port); + bool CheckNotReceivedConnect(); + bool CheckReceivedTerminate(); + + private: + // mojom::SharedWorker methods: + void Connect(int connection_request_id, + mojo::ScopedMessagePipeHandle port) override; + void Terminate() override; + void BindDevToolsAgent( + blink::mojom::DevToolsAgentAssociatedRequest request) override; + + mojo::Binding binding_; + std::queue> connect_received_; + bool terminate_received_ = false; + + DISALLOW_COPY_AND_ASSIGN(MockSharedWorker); +}; + +class MockSharedWorkerFactory : public mojom::SharedWorkerFactory { + public: + explicit MockSharedWorkerFactory(mojom::SharedWorkerFactoryRequest request); + ~MockSharedWorkerFactory() override; + + bool CheckReceivedCreateSharedWorker( + const GURL& expected_url, + const std::string& expected_name, + blink::WebContentSecurityPolicyType expected_content_security_policy_type, + mojom::SharedWorkerHostPtr* host, + mojom::SharedWorkerRequest* request); + + private: + // mojom::SharedWorkerFactory methods: + void CreateSharedWorker( + mojom::SharedWorkerInfoPtr info, + bool pause_on_start, + const base::UnguessableToken& devtools_worker_token, + blink::mojom::WorkerContentSettingsProxyPtr content_settings, + mojom::ServiceWorkerProviderInfoForSharedWorkerPtr + service_worker_provider_info, + network::mojom::URLLoaderFactoryAssociatedPtrInfo + script_loader_factory_ptr_info, + mojom::SharedWorkerHostPtr host, + mojom::SharedWorkerRequest request, + service_manager::mojom::InterfaceProviderPtr interface_provider) override; + + struct CreateParams { + CreateParams(); + ~CreateParams(); + mojom::SharedWorkerInfoPtr info; + bool pause_on_start; + blink::mojom::WorkerContentSettingsProxyPtr content_settings; + mojom::SharedWorkerHostPtr host; + mojom::SharedWorkerRequest request; + service_manager::mojom::InterfaceProviderPtr interface_provider; + }; + + mojo::Binding binding_; + std::unique_ptr create_params_; + + DISALLOW_COPY_AND_ASSIGN(MockSharedWorkerFactory); +}; + +class MockSharedWorkerClient : public mojom::SharedWorkerClient { + public: + MockSharedWorkerClient(); + ~MockSharedWorkerClient() override; + + void Bind(mojom::SharedWorkerClientRequest request); + void Close(); + bool CheckReceivedOnCreated(); + bool CheckReceivedOnConnected( + std::set expected_used_features); + bool CheckReceivedOnFeatureUsed(blink::mojom::WebFeature expected_feature); + bool CheckNotReceivedOnFeatureUsed(); + bool CheckReceivedOnScriptLoadFailed(); + + private: + // mojom::SharedWorkerClient methods: + void OnCreated(blink::mojom::SharedWorkerCreationContextType + creation_context_type) override; + void OnConnected( + const std::vector& features_used) override; + void OnScriptLoadFailed() override; + void OnFeatureUsed(blink::mojom::WebFeature feature) override; + + mojo::Binding binding_; + bool on_created_received_ = false; + bool on_connected_received_ = false; + std::set on_connected_features_; + bool on_feature_used_received_ = false; + blink::mojom::WebFeature on_feature_used_feature_ = + blink::mojom::WebFeature(); + bool on_script_load_failed_ = false; + + DISALLOW_COPY_AND_ASSIGN(MockSharedWorkerClient); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SHARED_WORKER_MOCK_SHARED_WORKER_H_ diff --git a/chromium/content/browser/shared_worker/shared_worker_host.cc b/chromium/content/browser/shared_worker/shared_worker_host.cc index 623c6617174..107d46564b9 100644 --- a/chromium/content/browser/shared_worker/shared_worker_host.cc +++ b/chromium/content/browser/shared_worker/shared_worker_host.cc @@ -54,6 +54,31 @@ bool AllowIndexedDBOnIOThread(const GURL& url, } // namespace +// RAII helper class for talking to SharedWorkerDevToolsManager. +class SharedWorkerHost::ScopedDevToolsHandle { + public: + ScopedDevToolsHandle(SharedWorkerHost* owner, + bool* out_pause_on_start, + base::UnguessableToken* out_devtools_worker_token) + : owner_(owner) { + SharedWorkerDevToolsManager::GetInstance()->WorkerCreated( + owner, out_pause_on_start, out_devtools_worker_token); + } + + ~ScopedDevToolsHandle() { + SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(owner_); + } + + void WorkerReadyForInspection() { + SharedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection( + owner_); + } + + private: + SharedWorkerHost* owner_; + DISALLOW_COPY_AND_ASSIGN(ScopedDevToolsHandle); +}; + SharedWorkerHost::SharedWorkerHost( SharedWorkerServiceImpl* service, std::unique_ptr instance, @@ -67,13 +92,29 @@ SharedWorkerHost::SharedWorkerHost( interface_provider_binding_(this), weak_factory_(this) { DCHECK(instance_); + // Set up the worker interface request. This is needed first in either + // AddClient() or Start(). AddClient() can sometimes be called before Start() + // when two clients call new SharedWorker() at around the same time. + worker_request_ = mojo::MakeRequest(&worker_); } SharedWorkerHost::~SharedWorkerHost() { UMA_HISTOGRAM_LONG_TIMES("SharedWorker.TimeToDeleted", base::TimeTicks::Now() - creation_time_); - if (!closed_ && !termination_message_sent_) - SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(this); + switch (phase_) { + case Phase::kInitial: + // Tell clients that this worker failed to start. This is only needed in + // kInitial. Once in kStarted, the worker in the renderer would alert this + // host if script loading failed. + for (const ClientInfo& info : clients_) + info.client->OnScriptLoadFailed(); + break; + case Phase::kStarted: + case Phase::kClosed: + case Phase::kTerminationSent: + case Phase::kTerminationSentAndClosed: + break; + } } void SharedWorkerHost::Start( @@ -82,6 +123,7 @@ void SharedWorkerHost::Start( service_worker_provider_info, network::mojom::URLLoaderFactoryAssociatedPtrInfo script_loader_factory) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + AdvanceTo(Phase::kStarted); mojom::SharedWorkerInfoPtr info(mojom::SharedWorkerInfo::New( instance_->url(), instance_->name(), instance_->content_security_policy(), @@ -91,7 +133,7 @@ void SharedWorkerHost::Start( // Register with DevTools. bool pause_on_start; base::UnguessableToken devtools_worker_token; - SharedWorkerDevToolsManager::GetInstance()->WorkerCreated( + devtools_handle_ = std::make_unique( this, &pause_on_start, &devtools_worker_token); // Set up content settings interface. @@ -115,7 +157,7 @@ void SharedWorkerHost::Start( std::move(info), pause_on_start, devtools_worker_token, std::move(content_settings), std::move(service_worker_provider_info), std::move(script_loader_factory), std::move(host), - mojo::MakeRequest(&worker_), std::move(interface_provider)); + std::move(worker_request_), std::move(interface_provider)); // Monitor the lifetime of the worker. worker_.set_connection_error_handler(base::BindOnce( @@ -148,16 +190,57 @@ void SharedWorkerHost::AllowIndexedDB(const GURL& url, } void SharedWorkerHost::TerminateWorker() { - // This can be called twice in tests while cleaning up all the workers. - if (termination_message_sent_) - return; - termination_message_sent_ = true; - if (!closed_) - SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(this); + switch (phase_) { + case Phase::kInitial: + // The host is being asked to terminate the worker before it started. + // Tell clients that this worker failed to start. + for (const ClientInfo& info : clients_) + info.client->OnScriptLoadFailed(); + // Tell the caller it terminated, so the caller doesn't wait forever. + AdvanceTo(Phase::kTerminationSentAndClosed); + OnWorkerConnectionLost(); + // |this| is destroyed here. + return; + case Phase::kStarted: + AdvanceTo(Phase::kTerminationSent); + break; + case Phase::kClosed: + AdvanceTo(Phase::kTerminationSentAndClosed); + break; + case Phase::kTerminationSent: + case Phase::kTerminationSentAndClosed: + // Termination was already sent. TerminateWorker can be called twice in + // tests while cleaning up all the workers. + return; + } + + devtools_handle_.reset(); worker_->Terminate(); // Now, we wait to observe OnWorkerConnectionLost. } +void SharedWorkerHost::AdvanceTo(Phase phase) { + switch (phase_) { + case Phase::kInitial: + DCHECK(phase == Phase::kStarted || + phase == Phase::kTerminationSentAndClosed); + break; + case Phase::kStarted: + DCHECK(phase == Phase::kClosed || phase == Phase::kTerminationSent); + break; + case Phase::kClosed: + DCHECK(phase == Phase::kTerminationSentAndClosed); + break; + case Phase::kTerminationSent: + DCHECK(phase == Phase::kTerminationSentAndClosed); + break; + case Phase::kTerminationSentAndClosed: + NOTREACHED(); + break; + } + phase_ = phase; +} + SharedWorkerHost::ClientInfo::ClientInfo(mojom::SharedWorkerClientPtr client, int connection_request_id, int process_id, @@ -182,17 +265,33 @@ void SharedWorkerHost::OnConnected(int connection_request_id) { } void SharedWorkerHost::OnContextClosed() { - // 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; - if (!termination_message_sent_) - SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(this); + devtools_handle_.reset(); + + // Mark as closed - this will stop any further messages from being sent to the + // worker (messages can still be sent from the worker, for exception + // reporting, etc). + switch (phase_) { + case Phase::kInitial: + // Not possible: there is no Mojo connection on which OnContextClosed can + // be called. + NOTREACHED(); + break; + case Phase::kStarted: + AdvanceTo(Phase::kClosed); + break; + case Phase::kTerminationSent: + AdvanceTo(Phase::kTerminationSentAndClosed); + break; + case Phase::kClosed: + case Phase::kTerminationSentAndClosed: + // Already closed, just ignore. + break; + } } void SharedWorkerHost::OnReadyForInspection() { - if (!closed_ && !termination_message_sent_) - SharedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(this); + if (devtools_handle_) + devtools_handle_->WorkerReadyForInspection(); } void SharedWorkerHost::OnScriptLoaded() { @@ -225,7 +324,21 @@ SharedWorkerHost::GetRenderFrameIDsForWorker() { } bool SharedWorkerHost::IsAvailable() const { - return !termination_message_sent_ && !closed_; + switch (phase_) { + case Phase::kInitial: + case Phase::kStarted: + return true; + case Phase::kClosed: + case Phase::kTerminationSent: + case Phase::kTerminationSentAndClosed: + return false; + } + NOTREACHED(); + return false; +} + +base::WeakPtr SharedWorkerHost::AsWeakPtr() { + return weak_factory_.GetWeakPtr(); } void SharedWorkerHost::AddClient(mojom::SharedWorkerClientPtr client, diff --git a/chromium/content/browser/shared_worker/shared_worker_host.h b/chromium/content/browser/shared_worker/shared_worker_host.h index dd95fc35655..e6f1f73f997 100644 --- a/chromium/content/browser/shared_worker/shared_worker_host.h +++ b/chromium/content/browser/shared_worker/shared_worker_host.h @@ -41,8 +41,9 @@ class SharedWorkerServiceImpl; // the browser <-> worker communication channel. This is owned by // SharedWorkerServiceImpl and destructed when a worker context or worker's // message filter is closed. -class SharedWorkerHost : public mojom::SharedWorkerHost, - public service_manager::mojom::InterfaceProvider { +class CONTENT_EXPORT SharedWorkerHost + : public mojom::SharedWorkerHost, + public service_manager::mojom::InterfaceProvider { public: SharedWorkerHost(SharedWorkerServiceImpl* service, std::unique_ptr instance, @@ -87,7 +88,21 @@ class SharedWorkerHost : public mojom::SharedWorkerHost, int process_id() const { return process_id_; } bool IsAvailable() const; + base::WeakPtr AsWeakPtr(); + private: + friend class SharedWorkerHostTest; + + enum class Phase { + kInitial, + kStarted, + kClosed, + kTerminationSent, + kTerminationSentAndClosed + }; + + class ScopedDevToolsHandle; + struct ClientInfo { ClientInfo(mojom::SharedWorkerClientPtr client, int connection_request_id, @@ -122,19 +137,22 @@ class SharedWorkerHost : public mojom::SharedWorkerHost, void GetInterface(const std::string& interface_name, mojo::ScopedMessagePipeHandle interface_pipe) override; + void AdvanceTo(Phase phase); + mojo::Binding binding_; + // |service_| owns |this|. SharedWorkerServiceImpl* service_; std::unique_ptr instance_; ClientList clients_; + mojom::SharedWorkerRequest worker_request_; mojom::SharedWorkerPtr worker_; const int process_id_; int next_connection_request_id_; - bool termination_message_sent_ = false; - bool closed_ = false; const base::TimeTicks creation_time_; + std::unique_ptr devtools_handle_; // This is the set of features that this worker has used. std::set used_features_; @@ -150,6 +168,8 @@ class SharedWorkerHost : public mojom::SharedWorkerHost, mojo::Binding interface_provider_binding_; + Phase phase_ = Phase::kInitial; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(SharedWorkerHost); diff --git a/chromium/content/browser/shared_worker/shared_worker_host_unittest.cc b/chromium/content/browser/shared_worker/shared_worker_host_unittest.cc new file mode 100644 index 00000000000..a00d44ffb4b --- /dev/null +++ b/chromium/content/browser/shared_worker/shared_worker_host_unittest.cc @@ -0,0 +1,263 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/shared_worker/shared_worker_host.h" + +#include +#include + +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/run_loop.h" +#include "content/browser/shared_worker/mock_shared_worker.h" +#include "content/browser/shared_worker/shared_worker_connector_impl.h" +#include "content/browser/shared_worker/shared_worker_instance.h" +#include "content/browser/shared_worker/shared_worker_service_impl.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/test_utils.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/message_port/message_port_channel.h" +#include "url/origin.h" + +using blink::MessagePortChannel; + +namespace content { + +class SharedWorkerHostTest : public testing::Test { + public: + SharedWorkerHostTest() : service_(nullptr) {} + + base::WeakPtr CreateHost() { + GURL url("http://www.example.com/w.js"); + std::string name("name"); + url::Origin origin = url::Origin::Create(url); + std::string content_security_policy; + blink::WebContentSecurityPolicyType content_security_policy_type = blink:: + WebContentSecurityPolicyType::kWebContentSecurityPolicyTypeReport; + blink::mojom::IPAddressSpace creation_address_space = + blink::mojom::IPAddressSpace::kPublic; + blink::mojom::SharedWorkerCreationContextType creation_context_type = + blink::mojom::SharedWorkerCreationContextType::kSecure; + + auto instance = std::make_unique( + url, name, origin, content_security_policy, + content_security_policy_type, creation_address_space, + creation_context_type); + auto host = std::make_unique( + &service_, std::move(instance), 11 /* dummy process_id */); + auto weak_host = host->AsWeakPtr(); + service_.worker_hosts_.insert(std::move(host)); + return weak_host; + } + + void StartWorker(SharedWorkerHost* host, + mojom::SharedWorkerFactoryPtr factory) { + host->Start(std::move(factory), nullptr /* service_worker_provider_info */, + {} /* script_loader_factory_info */); + } + + MessagePortChannel AddClient(SharedWorkerHost* host, + mojom::SharedWorkerClientPtr client) { + mojo::MessagePipe message_pipe; + MessagePortChannel local_port(std::move(message_pipe.handle0)); + MessagePortChannel remote_port(std::move(message_pipe.handle1)); + host->AddClient(std::move(client), host->process_id(), + 22 /* dummy frame_id */, std::move(remote_port)); + return local_port; + } + + protected: + TestBrowserThreadBundle test_browser_thread_bundle_; + SharedWorkerServiceImpl service_; + + DISALLOW_COPY_AND_ASSIGN(SharedWorkerHostTest); +}; + +TEST_F(SharedWorkerHostTest, Normal) { + base::WeakPtr host = CreateHost(); + + // Start the worker. + mojom::SharedWorkerFactoryPtr factory; + MockSharedWorkerFactory factory_impl(mojo::MakeRequest(&factory)); + StartWorker(host.get(), std::move(factory)); + + // Add the initiating client. + MockSharedWorkerClient client; + mojom::SharedWorkerClientPtr client_ptr; + client.Bind(mojo::MakeRequest(&client_ptr)); + MessagePortChannel local_port = AddClient(host.get(), std::move(client_ptr)); + base::RunLoop().RunUntilIdle(); + + // The factory should have gotten the CreateSharedWorker message. + mojom::SharedWorkerHostPtr worker_host; + mojom::SharedWorkerRequest worker_request; + EXPECT_TRUE(factory_impl.CheckReceivedCreateSharedWorker( + host->instance()->url(), host->instance()->name(), + host->instance()->content_security_policy_type(), &worker_host, + &worker_request)); + { + MockSharedWorker worker(std::move(worker_request)); + base::RunLoop().RunUntilIdle(); + + // The worker and client should have gotten initial messages. + int connection_request_id; + MessagePortChannel port; + EXPECT_TRUE(worker.CheckReceivedConnect(&connection_request_id, &port)); + EXPECT_TRUE(client.CheckReceivedOnCreated()); + // Simulate events the shared worker would send. + worker_host->OnReadyForInspection(); + worker_host->OnScriptLoaded(); + worker_host->OnConnected(connection_request_id); + base::RunLoop().RunUntilIdle(); + + // The client should be connected. + EXPECT_TRUE( + client.CheckReceivedOnConnected({} /* expected_used_features */)); + + // Close the client. The host should detect that there are no clients left + // and ask the worker to terminate. + client.Close(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(worker.CheckReceivedTerminate()); + + // Simulate the worker terminating by breaking the Mojo connection when + // |worker| goes out of scope. + } + base::RunLoop().RunUntilIdle(); + + // The host should have self-destructed. + EXPECT_FALSE(host); +} + +TEST_F(SharedWorkerHostTest, DestructBeforeStarting) { + base::WeakPtr host = CreateHost(); + + // Add a client. + MockSharedWorkerClient client; + mojom::SharedWorkerClientPtr client_ptr; + client.Bind(mojo::MakeRequest(&client_ptr)); + MessagePortChannel local_port = AddClient(host.get(), std::move(client_ptr)); + base::RunLoop().RunUntilIdle(); + + // Destroy the host before starting. + service_.DestroyHost(host.get()); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(host); + + // The client should be told startup failed. + EXPECT_TRUE(client.CheckReceivedOnScriptLoadFailed()); +} + +TEST_F(SharedWorkerHostTest, TerminateBeforeStarting) { + base::WeakPtr host = CreateHost(); + + // Add a client. + MockSharedWorkerClient client; + mojom::SharedWorkerClientPtr client_ptr; + client.Bind(mojo::MakeRequest(&client_ptr)); + MessagePortChannel local_port = AddClient(host.get(), std::move(client_ptr)); + base::RunLoop().RunUntilIdle(); + + // Request to terminate the worker before starting. + host->TerminateWorker(); + base::RunLoop().RunUntilIdle(); + + // The client should be told startup failed. + EXPECT_TRUE(client.CheckReceivedOnScriptLoadFailed()); + + // The host should be destroyed. + EXPECT_FALSE(host); +} + +TEST_F(SharedWorkerHostTest, TerminateAfterStarting) { + base::WeakPtr host = CreateHost(); + + // Create the factory. + mojom::SharedWorkerFactoryPtr factory; + MockSharedWorkerFactory factory_impl(mojo::MakeRequest(&factory)); + + // Start the worker. + host->Start(std::move(factory), nullptr /* service_worker_provider_info */, + {} /* script_loader_factory_info */); + + // Add a client. + MockSharedWorkerClient client; + mojom::SharedWorkerClientPtr client_ptr; + client.Bind(mojo::MakeRequest(&client_ptr)); + MessagePortChannel local_port = AddClient(host.get(), std::move(client_ptr)); + base::RunLoop().RunUntilIdle(); + + { + mojom::SharedWorkerHostPtr worker_host; + mojom::SharedWorkerRequest worker_request; + EXPECT_TRUE(factory_impl.CheckReceivedCreateSharedWorker( + host->instance()->url(), host->instance()->name(), + host->instance()->content_security_policy_type(), &worker_host, + &worker_request)); + MockSharedWorker worker(std::move(worker_request)); + + // Terminate after starting. + host->TerminateWorker(); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(worker.CheckReceivedTerminate()); + // We simulate the worker terminating by breaking the Mojo connection when + // it goes out of scope. + } + + // Simulate the worker in the renderer terminating. + base::RunLoop().RunUntilIdle(); + + // The client should not have been told startup failed. + EXPECT_FALSE(client.CheckReceivedOnScriptLoadFailed()); + // The host should be destroyed. + EXPECT_FALSE(host); +} + +TEST_F(SharedWorkerHostTest, OnContextClosed) { + base::WeakPtr host = CreateHost(); + + // Start the worker. + mojom::SharedWorkerFactoryPtr factory; + MockSharedWorkerFactory factory_impl(mojo::MakeRequest(&factory)); + StartWorker(host.get(), std::move(factory)); + + // Add a client. + MockSharedWorkerClient client; + mojom::SharedWorkerClientPtr client_ptr; + client.Bind(mojo::MakeRequest(&client_ptr)); + MessagePortChannel local_port = AddClient(host.get(), std::move(client_ptr)); + base::RunLoop().RunUntilIdle(); + + { + mojom::SharedWorkerHostPtr worker_host; + mojom::SharedWorkerRequest worker_request; + EXPECT_TRUE(factory_impl.CheckReceivedCreateSharedWorker( + host->instance()->url(), host->instance()->name(), + host->instance()->content_security_policy_type(), &worker_host, + &worker_request)); + MockSharedWorker worker(std::move(worker_request)); + + // Simulate the worker calling OnContextClosed(). + worker_host->OnContextClosed(); + base::RunLoop().RunUntilIdle(); + + // Close the client. The host should detect that there are no clients left + // and terminate the worker. + client.Close(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(worker.CheckReceivedTerminate()); + + // Simulate the worker terminating by breaking the Mojo connection when + // |worker| goes out of scope. + } + base::RunLoop().RunUntilIdle(); + + // The host should have self-destructed. + EXPECT_FALSE(host); +} + +} // namespace content diff --git a/chromium/content/browser/shared_worker/shared_worker_script_loader.cc b/chromium/content/browser/shared_worker/shared_worker_script_loader.cc index ff39e068b26..2a540fac395 100644 --- a/chromium/content/browser/shared_worker/shared_worker_script_loader.cc +++ b/chromium/content/browser/shared_worker/shared_worker_script_loader.cc @@ -6,10 +6,10 @@ #include "content/browser/loader/navigation_loader_interceptor.h" #include "content/browser/service_worker/service_worker_provider_host.h" -#include "content/browser/url_loader_factory_getter.h" #include "content/common/service_worker/service_worker_utils.h" #include "content/public/browser/resource_context.h" #include "net/url_request/redirect_util.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" namespace content { @@ -21,7 +21,7 @@ SharedWorkerScriptLoader::SharedWorkerScriptLoader( network::mojom::URLLoaderClientPtr client, base::WeakPtr service_worker_provider_host, ResourceContext* resource_context, - scoped_refptr loader_factory_getter, + scoped_refptr default_loader_factory, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) : routing_id_(routing_id), request_id_(request_id), @@ -30,7 +30,7 @@ SharedWorkerScriptLoader::SharedWorkerScriptLoader( client_(std::move(client)), service_worker_provider_host_(service_worker_provider_host), resource_context_(resource_context), - loader_factory_getter_(std::move(loader_factory_getter)), + default_loader_factory_(std::move(default_loader_factory)), traffic_annotation_(traffic_annotation), url_loader_client_binding_(this), weak_factory_(this) { @@ -84,7 +84,7 @@ void SharedWorkerScriptLoader::MaybeStartLoader( void SharedWorkerScriptLoader::LoadFromNetwork() { network::mojom::URLLoaderClientPtr client; url_loader_client_binding_.Bind(mojo::MakeRequest(&client)); - url_loader_factory_ = loader_factory_getter_->GetNetworkFactory(); + url_loader_factory_ = default_loader_factory_; url_loader_factory_->CreateLoaderAndStart( mojo::MakeRequest(&url_loader_), routing_id_, request_id_, options_, resource_request_, std::move(client), traffic_annotation_); @@ -95,7 +95,11 @@ void SharedWorkerScriptLoader::LoadFromNetwork() { // When this class gets a FollowRedirect IPC from the renderer, it restarts with // the new URL. -void SharedWorkerScriptLoader::FollowRedirect() { +void SharedWorkerScriptLoader::FollowRedirect( + const base::Optional& modified_request_headers) { + DCHECK(!modified_request_headers.has_value()) << "Redirect with modified " + "headers was not supported " + "yet. crbug.com/845683"; DCHECK(redirect_info_); // |should_clear_upload| is unused because there is no body anyway. diff --git a/chromium/content/browser/shared_worker/shared_worker_script_loader.h b/chromium/content/browser/shared_worker/shared_worker_script_loader.h index 7e61d8c6c69..3868aedd721 100644 --- a/chromium/content/browser/shared_worker/shared_worker_script_loader.h +++ b/chromium/content/browser/shared_worker/shared_worker_script_loader.h @@ -12,26 +12,34 @@ #include "services/network/public/cpp/resource_request.h" #include "services/network/public/mojom/url_loader.mojom.h" +namespace network { +class SharedURLLoaderFactory; +} // namespace network + namespace content { class NavigationLoaderInterceptor; class ResourceContext; class ServiceWorkerProviderHost; -class URLLoaderFactoryGetter; // The URLLoader for loading a shared worker script. Only used for the main // script request. // -// This acts much like NavigationURLLoaderNetworkService. It allows a +// This acts much like NavigationURLLoaderImpl. It allows a // NavigationLoaderInterceptor to intercept the request with its own loader, and -// goes to the network loader otherwise. Once a loader is started, this class -// acts as the URLLoaderClient for it, forwarding messages to the outer client. -// On redirects, it starts over with the new request URL, possibly starting a -// new loader and becoming the client of that. +// goes to |default_loader_factory| otherwise. Once a loader is started, this +// class acts as the URLLoaderClient for it, forwarding messages to the outer +// client. On redirects, it starts over with the new request URL, possibly +// starting a new loader and becoming the client of that. // // Lives on the IO thread. class SharedWorkerScriptLoader : public network::mojom::URLLoader, public network::mojom::URLLoaderClient { public: + // |default_loader_factory| is used to load the script if the load is not + // intercepted by a feature like service worker. Typically it will load the + // script from the NetworkService. However, it may internally contain + // non-NetworkService factories used for non-http(s) URLs, e.g., a + // chrome-extension:// URL. SharedWorkerScriptLoader( int32_t routing_id, int32_t request_id, @@ -40,12 +48,13 @@ class SharedWorkerScriptLoader : public network::mojom::URLLoader, network::mojom::URLLoaderClientPtr client, base::WeakPtr service_worker_provider_host, ResourceContext* resource_context, - scoped_refptr loader_factory_getter, + scoped_refptr default_loader_factory, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation); ~SharedWorkerScriptLoader() override; // network::mojom::URLLoader: - void FollowRedirect() override; + void FollowRedirect(const base::Optional& + modified_request_headers) override; void ProceedWithResponse() override; void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override; @@ -76,8 +85,7 @@ class SharedWorkerScriptLoader : public network::mojom::URLLoader, SingleRequestURLLoaderFactory::RequestHandler single_request_handler); void LoadFromNetwork(); - // TODO(falken): Add other interceptors like in - // NavigationURLLoaderNetworkService. + // TODO(falken): Add other interceptors like in NavigationURLLoaderImpl. std::unique_ptr service_worker_interceptor_; const int32_t routing_id_; @@ -87,7 +95,7 @@ class SharedWorkerScriptLoader : public network::mojom::URLLoader, network::mojom::URLLoaderClientPtr client_; base::WeakPtr service_worker_provider_host_; ResourceContext* resource_context_; - scoped_refptr loader_factory_getter_; + scoped_refptr default_loader_factory_; net::MutableNetworkTrafficAnnotationTag traffic_annotation_; base::Optional redirect_info_; @@ -95,6 +103,9 @@ class SharedWorkerScriptLoader : public network::mojom::URLLoader, network::mojom::URLLoaderPtr url_loader_; mojo::Binding url_loader_client_binding_; + // The factory used to request the script. This is the same as + // |default_loader_factory_| if a service worker or other interceptor didn't + // elect to handle the request. scoped_refptr url_loader_factory_; base::WeakPtrFactory weak_factory_; diff --git a/chromium/content/browser/shared_worker/shared_worker_script_loader_factory.cc b/chromium/content/browser/shared_worker/shared_worker_script_loader_factory.cc index f819351042c..08260e2484f 100644 --- a/chromium/content/browser/shared_worker/shared_worker_script_loader_factory.cc +++ b/chromium/content/browser/shared_worker/shared_worker_script_loader_factory.cc @@ -10,10 +10,10 @@ #include "content/browser/service_worker/service_worker_provider_host.h" #include "content/browser/service_worker/service_worker_version.h" #include "content/browser/shared_worker/shared_worker_script_loader.h" -#include "content/browser/url_loader_factory_getter.h" #include "content/common/service_worker/service_worker_utils.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "services/network/public/cpp/resource_response.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" #include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h" namespace content { @@ -22,10 +22,10 @@ SharedWorkerScriptLoaderFactory::SharedWorkerScriptLoaderFactory( ServiceWorkerContextWrapper* context, base::WeakPtr service_worker_provider_host, ResourceContext* resource_context, - scoped_refptr loader_factory_getter) + scoped_refptr loader_factory) : service_worker_provider_host_(service_worker_provider_host), resource_context_(resource_context), - loader_factory_getter_(loader_factory_getter) { + loader_factory_(std::move(loader_factory)) { DCHECK(ServiceWorkerUtils::IsServicificationEnabled()); DCHECK_EQ(service_worker_provider_host_->provider_type(), blink::mojom::ServiceWorkerProviderType::kForSharedWorker); @@ -54,8 +54,8 @@ void SharedWorkerScriptLoaderFactory::CreateLoaderAndStart( mojo::MakeStrongBinding( std::make_unique( routing_id, request_id, options, resource_request, std::move(client), - service_worker_provider_host_, resource_context_, - loader_factory_getter_, traffic_annotation), + service_worker_provider_host_, resource_context_, loader_factory_, + traffic_annotation), std::move(request)); } diff --git a/chromium/content/browser/shared_worker/shared_worker_script_loader_factory.h b/chromium/content/browser/shared_worker/shared_worker_script_loader_factory.h index 7e0e4c5ceae..2e42cfaafa6 100644 --- a/chromium/content/browser/shared_worker/shared_worker_script_loader_factory.h +++ b/chromium/content/browser/shared_worker/shared_worker_script_loader_factory.h @@ -8,11 +8,14 @@ #include "base/macros.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" +namespace network { +class SharedURLLoaderFactory; +} // namespace network + namespace content { class ServiceWorkerContextWrapper; class ServiceWorkerProviderHost; -class URLLoaderFactoryGetter; class ResourceContext; // S13nServiceWorker: @@ -28,11 +31,15 @@ class ResourceContext; class SharedWorkerScriptLoaderFactory : public network::mojom::URLLoaderFactory { public: + // |loader_factory| is used to load the script if the load is not intercepted + // by a feature like service worker. Typically it will load the script from + // the NetworkService. However, it may internally contain non-NetworkService + // factories used for non-http(s) URLs, e.g., a chrome-extension:// URL. SharedWorkerScriptLoaderFactory( ServiceWorkerContextWrapper* context, base::WeakPtr provider_host, ResourceContext* resource_context, - scoped_refptr loader_factory_getter); + scoped_refptr loader_factory); ~SharedWorkerScriptLoaderFactory() override; // network::mojom::URLLoaderFactory: @@ -49,7 +56,7 @@ class SharedWorkerScriptLoaderFactory private: base::WeakPtr service_worker_provider_host_; ResourceContext* resource_context_ = nullptr; - scoped_refptr loader_factory_getter_; + scoped_refptr loader_factory_; DISALLOW_COPY_AND_ASSIGN(SharedWorkerScriptLoaderFactory); }; diff --git a/chromium/content/browser/shared_worker/shared_worker_service_impl.cc b/chromium/content/browser/shared_worker/shared_worker_service_impl.cc index 5589e34dec3..8140ae678d3 100644 --- a/chromium/content/browser/shared_worker/shared_worker_service_impl.cc +++ b/chromium/content/browser/shared_worker/shared_worker_service_impl.cc @@ -27,6 +27,7 @@ #include "content/public/browser/render_view_host.h" #include "content/public/common/bind_interface_helpers.h" #include "mojo/public/cpp/bindings/strong_associated_binding.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "third_party/blink/public/common/message_port/message_port_channel.h" #include "url/origin.h" @@ -40,24 +41,48 @@ bool IsShuttingDown(RenderProcessHost* host) { void CreateScriptLoaderOnIO( scoped_refptr loader_factory_getter, + std::unique_ptr factory_bundle_info, scoped_refptr context, int process_id, base::OnceCallback callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + // Set up for service worker. auto provider_info = mojom::ServiceWorkerProviderInfoForSharedWorker::New(); base::WeakPtr host = context->PreCreateHostForSharedWorker(process_id, &provider_info); + // Create the factory bundle for loading the script. + scoped_refptr factory_bundle = + base::MakeRefCounted( + std::move(factory_bundle_info)); + + // Add the network factory to the bundle. The factory from + // CloneNetworkFactory() doesn't support reconnection to the network service + // after a crash, but it's OK since it's used for a single shared worker + // startup. + network::mojom::URLLoaderFactoryPtr network_factory_ptr; + loader_factory_getter->CloneNetworkFactory( + mojo::MakeRequest(&network_factory_ptr)); + factory_bundle->SetDefaultFactory(std::move(network_factory_ptr)); + // Create the SharedWorkerScriptLoaderFactory. network::mojom::URLLoaderFactoryAssociatedPtrInfo script_loader_factory; mojo::MakeStrongAssociatedBinding( std::make_unique( context.get(), host->AsWeakPtr(), context->resource_context(), - std::move(loader_factory_getter)), + std::move(factory_bundle)), mojo::MakeRequest(&script_loader_factory)); + // TODO(falken): Also send the factory bundle to the renderer like + // CommitNavigation does, to be used for subresource requests from the shared + // worker (SharedWorkerScriptLoaderFactory is only used for the main resource + // request). However, the restartable network factory should be used in this + // case. + + // We continue in StartWorker. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::BindOnce(std::move(callback), std::move(provider_info), @@ -77,6 +102,7 @@ bool SharedWorkerServiceImpl::TerminateWorker( const GURL& url, const std::string& name, const url::Origin& constructor_origin) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); for (auto& host : worker_hosts_) { if (host->IsAvailable() && host->instance()->Matches(url, name, constructor_origin)) { @@ -160,29 +186,12 @@ void SharedWorkerServiceImpl::ConnectToWorker( DestroyHost(host); } - // Bounce to the IO thread to setup service worker support in case the request - // for the worker script will need to be intercepted by service workers. - // TODO(falken): Move service worker to the UI thread. - if (ServiceWorkerUtils::IsServicificationEnabled()) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&CreateScriptLoaderOnIO, - service_worker_context_->storage_partition() - ->url_loader_factory_getter(), - service_worker_context_, process_id, - base::BindOnce(&SharedWorkerServiceImpl::CreateWorker, - weak_factory_.GetWeakPtr(), - std::move(instance), std::move(client), - process_id, frame_id, message_port))); - return; - } - CreateWorker(std::move(instance), std::move(client), process_id, frame_id, - message_port, nullptr /* service_worker_provider_info */, - {} /* script_loader_factory */); + message_port); } void SharedWorkerServiceImpl::DestroyHost(SharedWorkerHost* host) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); RenderProcessHost* process_host = RenderProcessHost::FromID(host->process_id()); worker_hosts_.erase(worker_hosts_.find(host)); @@ -201,6 +210,69 @@ void SharedWorkerServiceImpl::CreateWorker( mojom::SharedWorkerClientPtr client, int process_id, int frame_id, + const blink::MessagePortChannel& message_port) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + // Create the host. We need to do this even before starting the worker, + // because we are about to bounce to the IO thread. If another ConnectToWorker + // request arrives in the meantime, it finds and reuses the host instead of + // creating a new host and therefore new SharedWorker thread. + auto host = + std::make_unique(this, std::move(instance), process_id); + auto weak_host = host->AsWeakPtr(); + worker_hosts_.insert(std::move(host)); + + // Bounce to the IO thread to setup service worker support in case the request + // for the worker script will need to be intercepted by service workers. + if (ServiceWorkerUtils::IsServicificationEnabled()) { + // Set up the factory bundle for non-NetworkService URLs, e.g., + // chrome-extension:// URLs. + ContentBrowserClient::NonNetworkURLLoaderFactoryMap factories; + GetContentClient() + ->browser() + ->RegisterNonNetworkSubresourceURLLoaderFactories( + process_id, MSG_ROUTING_NONE, &factories); + + // TODO(falken): Add FileURLLoaderFactory if the requesting frame is a file + // resource. + + auto factory_bundle = std::make_unique(); + for (auto& pair : factories) { + const std::string& scheme = pair.first; + std::unique_ptr factory = + std::move(pair.second); + + network::mojom::URLLoaderFactoryPtr factory_ptr; + mojo::MakeStrongBinding(std::move(factory), + mojo::MakeRequest(&factory_ptr)); + factory_bundle->factories_info().emplace(scheme, + factory_ptr.PassInterface()); + } + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &CreateScriptLoaderOnIO, + service_worker_context_->storage_partition() + ->url_loader_factory_getter(), + std::move(factory_bundle), service_worker_context_, process_id, + base::BindOnce(&SharedWorkerServiceImpl::StartWorker, + weak_factory_.GetWeakPtr(), std::move(instance), + weak_host, std::move(client), process_id, frame_id, + message_port))); + return; + } + + StartWorker(std::move(instance), weak_host, std::move(client), process_id, + frame_id, message_port, nullptr, {}); +} + +void SharedWorkerServiceImpl::StartWorker( + std::unique_ptr instance, + base::WeakPtr host, + mojom::SharedWorkerClientPtr client, + int process_id, + int frame_id, const blink::MessagePortChannel& message_port, mojom::ServiceWorkerProviderInfoForSharedWorkerPtr service_worker_provider_info, @@ -208,17 +280,23 @@ void SharedWorkerServiceImpl::CreateWorker( script_loader_factory_info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + // The host may already be gone if something forcibly terminated the worker + // before it could start (e.g., in tests or a UI action). Just fail. + if (!host) + return; + RenderProcessHost* process_host = RenderProcessHost::FromID(process_id); - // If the requesting process is shutting down, then just drop this request on - // the floor. The client is not going to be around much longer anyways. - if (IsShuttingDown(process_host)) + // If the target process is shutting down, then just drop this request and + // tell the host to destruct. This also means clients that were still waiting + // for the shared worker to start will fail. + if (!process_host || IsShuttingDown(process_host)) { + host->TerminateWorker(); return; + } // Keep the renderer process alive that will be hosting the shared worker. process_host->IncrementKeepAliveRefCount( RenderProcessHost::KeepAliveClientType::kSharedWorker); - auto host = - std::make_unique(this, std::move(instance), process_id); // Get the factory used to instantiate the new shared worker instance in // the target process. @@ -228,8 +306,6 @@ void SharedWorkerServiceImpl::CreateWorker( host->Start(std::move(factory), std::move(service_worker_provider_info), std::move(script_loader_factory_info)); host->AddClient(std::move(client), process_id, frame_id, message_port); - - worker_hosts_.insert(std::move(host)); } SharedWorkerHost* SharedWorkerServiceImpl::FindAvailableSharedWorkerHost( diff --git a/chromium/content/browser/shared_worker/shared_worker_service_impl.h b/chromium/content/browser/shared_worker/shared_worker_service_impl.h index c4075f85b8b..b41bcc2d352 100644 --- a/chromium/content/browser/shared_worker/shared_worker_service_impl.h +++ b/chromium/content/browser/shared_worker/shared_worker_service_impl.h @@ -56,16 +56,23 @@ class CONTENT_EXPORT SharedWorkerServiceImpl : public SharedWorkerService { private: friend class SharedWorkerServiceImplTest; + friend class SharedWorkerHostTest; void CreateWorker(std::unique_ptr instance, mojom::SharedWorkerClientPtr client, int process_id, int frame_id, - const blink::MessagePortChannel& message_port, - mojom::ServiceWorkerProviderInfoForSharedWorkerPtr - service_worker_provider_info, - network::mojom::URLLoaderFactoryAssociatedPtrInfo - script_loader_factory_info); + const blink::MessagePortChannel& message_port); + void StartWorker(std::unique_ptr instance, + base::WeakPtr host, + mojom::SharedWorkerClientPtr client, + int process_id, + int frame_id, + const blink::MessagePortChannel& message_port, + mojom::ServiceWorkerProviderInfoForSharedWorkerPtr + service_worker_provider_info, + network::mojom::URLLoaderFactoryAssociatedPtrInfo + script_loader_factory_info); // Returns nullptr if there is no such host. SharedWorkerHost* FindSharedWorkerHost(int process_id, int route_id); 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 index d3ac80d6a79..2afa11622eb 100644 --- a/chromium/content/browser/shared_worker/shared_worker_service_impl_unittest.cc +++ b/chromium/content/browser/shared_worker/shared_worker_service_impl_unittest.cc @@ -4,23 +4,16 @@ #include "content/browser/shared_worker/shared_worker_service_impl.h" -#include - -#include #include +#include #include #include -#include -#include "base/atomic_sequence_num.h" #include "base/macros.h" #include "base/run_loop.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "base/synchronization/lock.h" +#include "content/browser/shared_worker/mock_shared_worker.h" #include "content/browser/shared_worker/shared_worker_connector_impl.h" #include "content/browser/site_instance_impl.h" -#include "content/public/browser/storage_partition.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_utils.h" @@ -80,7 +73,7 @@ class SharedWorkerServiceImplTest : public RenderViewHostImplTestHarness { RenderViewHostImplTestHarness::SetUp(); render_process_host_factory_ = std::make_unique(); - RenderProcessHostImpl::set_render_process_host_factory( + RenderProcessHostImpl::set_render_process_host_factory_for_testing( render_process_host_factory_.get()); } @@ -104,202 +97,14 @@ std::queue namespace { -template -static bool CheckEquality(const T& expected, const T& actual) { - EXPECT_EQ(expected, actual); - return expected == actual; -} - -class MockSharedWorker : public mojom::SharedWorker { - public: - explicit MockSharedWorker(mojom::SharedWorkerRequest request) - : binding_(this, std::move(request)) {} - - bool CheckReceivedConnect(int* connection_request_id, - MessagePortChannel* port) { - if (connect_received_.empty()) - return false; - if (connection_request_id) - *connection_request_id = connect_received_.front().first; - if (port) - *port = connect_received_.front().second; - connect_received_.pop(); - return true; - } - - bool CheckNotReceivedConnect() { return connect_received_.empty(); } - - bool CheckReceivedTerminate() { - if (!terminate_received_) - return false; - terminate_received_ = false; - return true; - } - - private: - // mojom::SharedWorker methods: - void Connect(int connection_request_id, - mojo::ScopedMessagePipeHandle port) override { - connect_received_.emplace(connection_request_id, - MessagePortChannel(std::move(port))); - } - void Terminate() override { - // Allow duplicate events. - terminate_received_ = true; - } - void BindDevToolsAgent( - blink::mojom::DevToolsAgentAssociatedRequest request) override { - NOTREACHED(); - } - - mojo::Binding binding_; - std::queue> connect_received_; - bool terminate_received_ = false; -}; - -class MockSharedWorkerFactory : public mojom::SharedWorkerFactory { - public: - explicit MockSharedWorkerFactory(mojom::SharedWorkerFactoryRequest request) - : binding_(this, std::move(request)) {} - - bool CheckReceivedCreateSharedWorker( - const std::string& expected_url, - const std::string& expected_name, - blink::WebContentSecurityPolicyType expected_content_security_policy_type, - mojom::SharedWorkerHostPtr* host, - mojom::SharedWorkerRequest* request) { - std::unique_ptr create_params = std::move(create_params_); - if (!create_params) - return false; - if (!CheckEquality(GURL(expected_url), create_params->info->url)) - return false; - if (!CheckEquality(expected_name, create_params->info->name)) - return false; - if (!CheckEquality(expected_content_security_policy_type, - create_params->info->content_security_policy_type)) - return false; - if (!create_params->interface_provider) - return false; - *host = std::move(create_params->host); - *request = std::move(create_params->request); - return true; - } - - private: - // mojom::SharedWorkerFactory methods: - void CreateSharedWorker( - mojom::SharedWorkerInfoPtr info, - bool pause_on_start, - const base::UnguessableToken& devtools_worker_token, - blink::mojom::WorkerContentSettingsProxyPtr content_settings, - mojom::ServiceWorkerProviderInfoForSharedWorkerPtr - service_worker_provider_info, - network::mojom::URLLoaderFactoryAssociatedPtrInfo - script_loader_factory_ptr_info, - mojom::SharedWorkerHostPtr host, - mojom::SharedWorkerRequest request, - service_manager::mojom::InterfaceProviderPtr interface_provider) - override { - CHECK(!create_params_); - create_params_ = std::make_unique(); - create_params_->info = std::move(info); - create_params_->pause_on_start = pause_on_start; - create_params_->content_settings = std::move(content_settings); - create_params_->host = std::move(host); - create_params_->request = std::move(request); - create_params_->interface_provider = std::move(interface_provider); - } - - struct CreateParams { - mojom::SharedWorkerInfoPtr info; - bool pause_on_start; - blink::mojom::WorkerContentSettingsProxyPtr content_settings; - mojom::SharedWorkerHostPtr host; - mojom::SharedWorkerRequest request; - service_manager::mojom::InterfaceProviderPtr interface_provider; - }; - - mojo::Binding binding_; - std::unique_ptr create_params_; -}; - -class MockSharedWorkerClient : public mojom::SharedWorkerClient { - public: - MockSharedWorkerClient() : binding_(this) {} - - void Bind(mojom::SharedWorkerClientRequest request) { - binding_.Bind(std::move(request)); - } - - void Close() { binding_.Close(); } - - bool CheckReceivedOnCreated() { - if (!on_created_received_) - return false; - on_created_received_ = false; - return true; - } - - bool CheckReceivedOnConnected( - std::set expected_used_features) { - if (!on_connected_received_) - return false; - on_connected_received_ = false; - if (!CheckEquality(expected_used_features, on_connected_features_)) - return false; - return true; - } - - bool CheckReceivedOnFeatureUsed(blink::mojom::WebFeature expected_feature) { - if (!on_feature_used_received_) - return false; - on_feature_used_received_ = false; - if (!CheckEquality(expected_feature, on_feature_used_feature_)) - return false; - return true; - } - - bool CheckNotReceivedOnFeatureUsed() { return !on_feature_used_received_; } - - private: - // mojom::SharedWorkerClient methods: - void OnCreated(blink::mojom::SharedWorkerCreationContextType - creation_context_type) override { - CHECK(!on_created_received_); - on_created_received_ = true; - } - void OnConnected( - const std::vector& features_used) override { - CHECK(!on_connected_received_); - on_connected_received_ = true; - for (auto feature : features_used) - on_connected_features_.insert(feature); - } - void OnScriptLoadFailed() override { NOTREACHED(); } - void OnFeatureUsed(blink::mojom::WebFeature feature) override { - CHECK(!on_feature_used_received_); - on_feature_used_received_ = true; - on_feature_used_feature_ = feature; - } - - mojo::Binding binding_; - bool on_created_received_ = false; - bool on_connected_received_ = false; - std::set on_connected_features_; - bool on_feature_used_received_ = false; - blink::mojom::WebFeature on_feature_used_feature_ = - blink::mojom::WebFeature(); -}; - void ConnectToSharedWorker(mojom::SharedWorkerConnectorPtr connector, - const std::string& url, + const GURL& url, const std::string& name, MockSharedWorkerClient* client, MessagePortChannel* local_port) { - mojom::SharedWorkerInfoPtr info( - mojom::SharedWorkerInfo::New(GURL(url), name, std::string(), - blink::kWebContentSecurityPolicyTypeReport, - blink::mojom::IPAddressSpace::kPublic)); + mojom::SharedWorkerInfoPtr info(mojom::SharedWorkerInfo::New( + url, name, std::string(), blink::kWebContentSecurityPolicyTypeReport, + blink::mojom::IPAddressSpace::kPublic)); mojo::MessagePipe message_pipe; *local_port = MessagePortChannel(std::move(message_pipe.handle0)); @@ -325,39 +130,38 @@ TEST_F(SharedWorkerServiceImplTest, BasicTest) { MockSharedWorkerClient client; MessagePortChannel local_port; + const GURL kUrl("http://example.com/w.js"); ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host, render_frame_host->GetRoutingID()), - "http://example.com/w.js", "name", &client, - &local_port); + kUrl, "name", &client, &local_port); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerFactoryRequest factory_request; EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request)); MockSharedWorkerFactory factory(std::move(factory_request)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host; mojom::SharedWorkerRequest worker_request; EXPECT_TRUE(factory.CheckReceivedCreateSharedWorker( - "http://example.com/w.js", "name", - blink::kWebContentSecurityPolicyTypeReport, &worker_host, + kUrl, "name", blink::kWebContentSecurityPolicyTypeReport, &worker_host, &worker_request)); MockSharedWorker worker(std::move(worker_request)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); int connection_request_id; MessagePortChannel port; EXPECT_TRUE(worker.CheckReceivedConnect(&connection_request_id, &port)); - client.CheckReceivedOnCreated(); + EXPECT_TRUE(client.CheckReceivedOnCreated()); // Simulate events the shared worker would send. worker_host->OnReadyForInspection(); worker_host->OnScriptLoaded(); worker_host->OnConnected(connection_request_id); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE( client.CheckReceivedOnConnected(std::set())); @@ -374,18 +178,18 @@ TEST_F(SharedWorkerServiceImplTest, BasicTest) { // Send feature from shared worker to host. auto feature1 = static_cast(124); worker_host->OnFeatureUsed(feature1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(client.CheckReceivedOnFeatureUsed(feature1)); // A message should be sent only one time per feature. worker_host->OnFeatureUsed(feature1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(client.CheckNotReceivedOnFeatureUsed()); // Send another feature. auto feature2 = static_cast(901); worker_host->OnFeatureUsed(feature2); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(client.CheckReceivedOnFeatureUsed(feature2)); } @@ -401,39 +205,38 @@ TEST_F(SharedWorkerServiceImplTest, TwoRendererTest) { MockSharedWorkerClient client0; MessagePortChannel local_port0; + const GURL kUrl("http://example.com/w.js"); ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host0, render_frame_host0->GetRoutingID()), - "http://example.com/w.js", "name", &client0, - &local_port0); + kUrl, "name", &client0, &local_port0); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerFactoryRequest factory_request; EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request)); MockSharedWorkerFactory factory(std::move(factory_request)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host; mojom::SharedWorkerRequest worker_request; EXPECT_TRUE(factory.CheckReceivedCreateSharedWorker( - "http://example.com/w.js", "name", - blink::kWebContentSecurityPolicyTypeReport, &worker_host, + kUrl, "name", blink::kWebContentSecurityPolicyTypeReport, &worker_host, &worker_request)); MockSharedWorker worker(std::move(worker_request)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); int connection_request_id0; MessagePortChannel port0; EXPECT_TRUE(worker.CheckReceivedConnect(&connection_request_id0, &port0)); - client0.CheckReceivedOnCreated(); + EXPECT_TRUE(client0.CheckReceivedOnCreated()); // Simulate events the shared worker would send. worker_host->OnReadyForInspection(); worker_host->OnScriptLoaded(); worker_host->OnConnected(connection_request_id0); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE( client0.CheckReceivedOnConnected(std::set())); @@ -449,11 +252,11 @@ TEST_F(SharedWorkerServiceImplTest, TwoRendererTest) { auto feature1 = static_cast(124); worker_host->OnFeatureUsed(feature1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(client0.CheckReceivedOnFeatureUsed(feature1)); auto feature2 = static_cast(901); worker_host->OnFeatureUsed(feature2); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(client0.CheckReceivedOnFeatureUsed(feature2)); // Only a single worker instance in process 0. @@ -472,10 +275,9 @@ TEST_F(SharedWorkerServiceImplTest, TwoRendererTest) { MessagePortChannel local_port1; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host1, render_frame_host1->GetRoutingID()), - "http://example.com/w.js", "name", &client1, - &local_port1); + kUrl, "name", &client1, &local_port1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); // Should not have tried to create a new shared worker. EXPECT_TRUE(CheckNotReceivedFactoryRequest()); @@ -484,7 +286,7 @@ TEST_F(SharedWorkerServiceImplTest, TwoRendererTest) { MessagePortChannel port1; EXPECT_TRUE(worker.CheckReceivedConnect(&connection_request_id1, &port1)); - client1.CheckReceivedOnCreated(); + EXPECT_TRUE(client1.CheckReceivedOnCreated()); // Only a single worker instance in process 0. EXPECT_EQ(1u, renderer_host0->GetKeepAliveRefCount()); @@ -492,7 +294,7 @@ TEST_F(SharedWorkerServiceImplTest, TwoRendererTest) { worker_host->OnConnected(connection_request_id1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(client1.CheckReceivedOnConnected({feature1, feature2})); @@ -506,19 +308,19 @@ TEST_F(SharedWorkerServiceImplTest, TwoRendererTest) { EXPECT_EQ(expected_message1, received_message1); worker_host->OnFeatureUsed(feature1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(client0.CheckNotReceivedOnFeatureUsed()); EXPECT_TRUE(client1.CheckNotReceivedOnFeatureUsed()); auto feature3 = static_cast(1019); worker_host->OnFeatureUsed(feature3); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(client0.CheckReceivedOnFeatureUsed(feature3)); EXPECT_TRUE(client1.CheckReceivedOnFeatureUsed(feature3)); } TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_NormalCase) { - const char kURL[] = "http://example.com/w.js"; + const GURL kUrl("http://example.com/w.js"); const char kName[] = "name"; // The first renderer host. @@ -545,24 +347,24 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_NormalCase) { MessagePortChannel local_port0; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host0, render_frame_host0->GetRoutingID()), - kURL, kName, &client0, &local_port0); - RunAllPendingInMessageLoop(); + kUrl, kName, &client0, &local_port0); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerFactoryRequest factory_request; EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request)); MockSharedWorkerFactory factory(std::move(factory_request)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host; mojom::SharedWorkerRequest worker_request; EXPECT_TRUE(factory.CheckReceivedCreateSharedWorker( - kURL, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host, + kUrl, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host, &worker_request)); MockSharedWorker worker(std::move(worker_request)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker.CheckReceivedConnect(nullptr, nullptr)); - client0.CheckReceivedOnCreated(); + EXPECT_TRUE(client0.CheckReceivedOnCreated()); // Second client, same worker. @@ -570,26 +372,26 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_NormalCase) { MessagePortChannel local_port1; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host1, render_frame_host1->GetRoutingID()), - kURL, kName, &client1, &local_port1); - RunAllPendingInMessageLoop(); + kUrl, kName, &client1, &local_port1); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(CheckNotReceivedFactoryRequest()); EXPECT_TRUE(worker.CheckReceivedConnect(nullptr, nullptr)); - client1.CheckReceivedOnCreated(); + EXPECT_TRUE(client1.CheckReceivedOnCreated()); // Cleanup client0.Close(); client1.Close(); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker.CheckReceivedTerminate()); } TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_NormalCase_URLMismatch) { - const char kURL0[] = "http://example.com/w0.js"; - const char kURL1[] = "http://example.com/w1.js"; + const GURL kUrl0("http://example.com/w0.js"); + const GURL kUrl1("http://example.com/w1.js"); const char kName[] = "name"; // The first renderer host. @@ -616,24 +418,24 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_NormalCase_URLMismatch) { MessagePortChannel local_port0; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host0, render_frame_host0->GetRoutingID()), - kURL0, kName, &client0, &local_port0); - RunAllPendingInMessageLoop(); + kUrl0, kName, &client0, &local_port0); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerFactoryRequest factory_request0; EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request0)); MockSharedWorkerFactory factory0(std::move(factory_request0)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host0; mojom::SharedWorkerRequest worker_request0; EXPECT_TRUE(factory0.CheckReceivedCreateSharedWorker( - kURL0, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host0, + kUrl0, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host0, &worker_request0)); MockSharedWorker worker0(std::move(worker_request0)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker0.CheckReceivedConnect(nullptr, nullptr)); - client0.CheckReceivedOnCreated(); + EXPECT_TRUE(client0.CheckReceivedOnCreated()); // Second client, creates worker. @@ -641,37 +443,37 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_NormalCase_URLMismatch) { MessagePortChannel local_port1; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host1, render_frame_host1->GetRoutingID()), - kURL1, kName, &client1, &local_port1); - RunAllPendingInMessageLoop(); + kUrl1, kName, &client1, &local_port1); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerFactoryRequest factory_request1; EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1)); MockSharedWorkerFactory factory1(std::move(factory_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host1; mojom::SharedWorkerRequest worker_request1; EXPECT_TRUE(factory1.CheckReceivedCreateSharedWorker( - kURL1, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, + kUrl1, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, &worker_request1)); MockSharedWorker worker1(std::move(worker_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker1.CheckReceivedConnect(nullptr, nullptr)); - client1.CheckReceivedOnCreated(); + EXPECT_TRUE(client1.CheckReceivedOnCreated()); // Cleanup client0.Close(); client1.Close(); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker0.CheckReceivedTerminate()); EXPECT_TRUE(worker1.CheckReceivedTerminate()); } TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_NormalCase_NameMismatch) { - const char kURL[] = "http://example.com/w.js"; + const GURL kUrl("http://example.com/w.js"); const char kName0[] = "name0"; const char kName1[] = "name1"; @@ -699,24 +501,24 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_NormalCase_NameMismatch) { MessagePortChannel local_port0; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host0, render_frame_host0->GetRoutingID()), - kURL, kName0, &client0, &local_port0); - RunAllPendingInMessageLoop(); + kUrl, kName0, &client0, &local_port0); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerFactoryRequest factory_request0; EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request0)); MockSharedWorkerFactory factory0(std::move(factory_request0)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host0; mojom::SharedWorkerRequest worker_request0; EXPECT_TRUE(factory0.CheckReceivedCreateSharedWorker( - kURL, kName0, blink::kWebContentSecurityPolicyTypeReport, &worker_host0, + kUrl, kName0, blink::kWebContentSecurityPolicyTypeReport, &worker_host0, &worker_request0)); MockSharedWorker worker0(std::move(worker_request0)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker0.CheckReceivedConnect(nullptr, nullptr)); - client0.CheckReceivedOnCreated(); + EXPECT_TRUE(client0.CheckReceivedOnCreated()); // Second client, creates worker. @@ -724,37 +526,37 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_NormalCase_NameMismatch) { MessagePortChannel local_port1; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host1, render_frame_host1->GetRoutingID()), - kURL, kName1, &client1, &local_port1); - RunAllPendingInMessageLoop(); + kUrl, kName1, &client1, &local_port1); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerFactoryRequest factory_request1; EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1)); MockSharedWorkerFactory factory1(std::move(factory_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host1; mojom::SharedWorkerRequest worker_request1; EXPECT_TRUE(factory1.CheckReceivedCreateSharedWorker( - kURL, kName1, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, + kUrl, kName1, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, &worker_request1)); MockSharedWorker worker1(std::move(worker_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker1.CheckReceivedConnect(nullptr, nullptr)); - client1.CheckReceivedOnCreated(); + EXPECT_TRUE(client1.CheckReceivedOnCreated()); // Cleanup client0.Close(); client1.Close(); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker0.CheckReceivedTerminate()); EXPECT_TRUE(worker1.CheckReceivedTerminate()); } TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase) { - const char kURL[] = "http://example.com/w.js"; + const GURL kUrl("http://example.com/w.js"); const char kName[] = "name"; // The first renderer host. @@ -781,15 +583,15 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase) { MessagePortChannel local_port0; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host0, render_frame_host0->GetRoutingID()), - kURL, kName, &client0, &local_port0); + kUrl, kName, &client0, &local_port0); MockSharedWorkerClient client1; MessagePortChannel local_port1; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host1, render_frame_host1->GetRoutingID()), - kURL, kName, &client1, &local_port1); + kUrl, kName, &client1, &local_port1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); // Check that the worker was created. @@ -797,37 +599,37 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase) { EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request)); MockSharedWorkerFactory factory(std::move(factory_request)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host; mojom::SharedWorkerRequest worker_request; EXPECT_TRUE(factory.CheckReceivedCreateSharedWorker( - kURL, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host, + kUrl, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host, &worker_request)); MockSharedWorker worker(std::move(worker_request)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); // Check that the worker received two connections. EXPECT_TRUE(worker.CheckReceivedConnect(nullptr, nullptr)); - client0.CheckReceivedOnCreated(); + EXPECT_TRUE(client0.CheckReceivedOnCreated()); EXPECT_TRUE(worker.CheckReceivedConnect(nullptr, nullptr)); - client1.CheckReceivedOnCreated(); + EXPECT_TRUE(client1.CheckReceivedOnCreated()); // Cleanup client0.Close(); client1.Close(); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker.CheckReceivedTerminate()); } TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase_URLMismatch) { - const char kURL0[] = "http://example.com/w0.js"; - const char kURL1[] = "http://example.com/w1.js"; + const GURL kUrl0("http://example.com/w0.js"); + const GURL kUrl1("http://example.com/w1.js"); const char kName[] = "name"; // The first renderer host. @@ -854,15 +656,15 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase_URLMismatch) { MessagePortChannel local_port0; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host0, render_frame_host0->GetRoutingID()), - kURL0, kName, &client0, &local_port0); + kUrl0, kName, &client0, &local_port0); MockSharedWorkerClient client1; MessagePortChannel local_port1; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host1, render_frame_host1->GetRoutingID()), - kURL1, kName, &client1, &local_port1); + kUrl1, kName, &client1, &local_port1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); // Check that both workers were created. @@ -874,46 +676,46 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase_URLMismatch) { EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1)); MockSharedWorkerFactory factory1(std::move(factory_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host0; mojom::SharedWorkerRequest worker_request0; EXPECT_TRUE(factory0.CheckReceivedCreateSharedWorker( - kURL0, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host0, + kUrl0, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host0, &worker_request0)); MockSharedWorker worker0(std::move(worker_request0)); mojom::SharedWorkerHostPtr worker_host1; mojom::SharedWorkerRequest worker_request1; EXPECT_TRUE(factory1.CheckReceivedCreateSharedWorker( - kURL1, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, + kUrl1, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, &worker_request1)); MockSharedWorker worker1(std::move(worker_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); // Check that the workers each received a connection. EXPECT_TRUE(worker0.CheckReceivedConnect(nullptr, nullptr)); EXPECT_TRUE(worker0.CheckNotReceivedConnect()); - client0.CheckReceivedOnCreated(); + EXPECT_TRUE(client0.CheckReceivedOnCreated()); EXPECT_TRUE(worker1.CheckReceivedConnect(nullptr, nullptr)); EXPECT_TRUE(worker1.CheckNotReceivedConnect()); - client1.CheckReceivedOnCreated(); + EXPECT_TRUE(client1.CheckReceivedOnCreated()); // Cleanup client0.Close(); client1.Close(); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker0.CheckReceivedTerminate()); EXPECT_TRUE(worker1.CheckReceivedTerminate()); } TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase_NameMismatch) { - const char kURL[] = "http://example.com/w.js"; + const GURL kUrl("http://example.com/w.js"); const char kName0[] = "name0"; const char kName1[] = "name1"; @@ -941,15 +743,15 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase_NameMismatch) { MessagePortChannel local_port0; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host0, render_frame_host0->GetRoutingID()), - kURL, kName0, &client0, &local_port0); + kUrl, kName0, &client0, &local_port0); MockSharedWorkerClient client1; MessagePortChannel local_port1; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host1, render_frame_host1->GetRoutingID()), - kURL, kName1, &client1, &local_port1); + kUrl, kName1, &client1, &local_port1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); // Check that both workers were created. @@ -961,46 +763,46 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerTest_PendingCase_NameMismatch) { EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1)); MockSharedWorkerFactory factory1(std::move(factory_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host0; mojom::SharedWorkerRequest worker_request0; EXPECT_TRUE(factory0.CheckReceivedCreateSharedWorker( - kURL, kName0, blink::kWebContentSecurityPolicyTypeReport, &worker_host0, + kUrl, kName0, blink::kWebContentSecurityPolicyTypeReport, &worker_host0, &worker_request0)); MockSharedWorker worker0(std::move(worker_request0)); mojom::SharedWorkerHostPtr worker_host1; mojom::SharedWorkerRequest worker_request1; EXPECT_TRUE(factory1.CheckReceivedCreateSharedWorker( - kURL, kName1, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, + kUrl, kName1, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, &worker_request1)); MockSharedWorker worker1(std::move(worker_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); // Check that the workers each received a connection. EXPECT_TRUE(worker0.CheckReceivedConnect(nullptr, nullptr)); EXPECT_TRUE(worker0.CheckNotReceivedConnect()); - client0.CheckReceivedOnCreated(); + EXPECT_TRUE(client0.CheckReceivedOnCreated()); EXPECT_TRUE(worker1.CheckReceivedConnect(nullptr, nullptr)); EXPECT_TRUE(worker1.CheckNotReceivedConnect()); - client1.CheckReceivedOnCreated(); + EXPECT_TRUE(client1.CheckReceivedOnCreated()); // Cleanup client0.Close(); client1.Close(); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker0.CheckReceivedTerminate()); EXPECT_TRUE(worker1.CheckReceivedTerminate()); } TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest) { - const char kURL[] = "http://example.com/w.js"; + const GURL kUrl("http://example.com/w.js"); const char kName[] = "name"; // Create three renderer hosts. @@ -1033,9 +835,9 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest) { MessagePortChannel local_port0; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host0, render_frame_host0->GetRoutingID()), - kURL, kName, &client0, &local_port0); + kUrl, kName, &client0, &local_port0); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); // Starts a worker. @@ -1043,19 +845,19 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest) { EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request0)); MockSharedWorkerFactory factory0(std::move(factory_request0)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host0; mojom::SharedWorkerRequest worker_request0; EXPECT_TRUE(factory0.CheckReceivedCreateSharedWorker( - kURL, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host0, + kUrl, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host0, &worker_request0)); MockSharedWorker worker0(std::move(worker_request0)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker0.CheckReceivedConnect(nullptr, nullptr)); - client0.CheckReceivedOnCreated(); + EXPECT_TRUE(client0.CheckReceivedOnCreated()); // Kill this process, which should make worker0 unavailable. web_contents0.reset(); @@ -1067,9 +869,9 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest) { MessagePortChannel local_port1; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host1, render_frame_host1->GetRoutingID()), - kURL, kName, &client1, &local_port1); + kUrl, kName, &client1, &local_port1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); // The previous worker is unavailable, so a new worker is created. @@ -1077,38 +879,38 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest) { EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request1)); MockSharedWorkerFactory factory1(std::move(factory_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host1; mojom::SharedWorkerRequest worker_request1; EXPECT_TRUE(factory1.CheckReceivedCreateSharedWorker( - kURL, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, + kUrl, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, &worker_request1)); MockSharedWorker worker1(std::move(worker_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker0.CheckNotReceivedConnect()); EXPECT_TRUE(worker1.CheckReceivedConnect(nullptr, nullptr)); - client1.CheckReceivedOnCreated(); + EXPECT_TRUE(client1.CheckReceivedOnCreated()); // Start another client to confirm that it can connect to the same worker. MockSharedWorkerClient client2; MessagePortChannel local_port2; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host2, render_frame_host2->GetRoutingID()), - kURL, kName, &client2, &local_port2); + kUrl, kName, &client2, &local_port2); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(CheckNotReceivedFactoryRequest()); EXPECT_TRUE(worker1.CheckReceivedConnect(nullptr, nullptr)); - client2.CheckReceivedOnCreated(); + EXPECT_TRUE(client2.CheckReceivedOnCreated()); } TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest2) { - const char kURL[] = "http://example.com/w.js"; + const GURL kUrl("http://example.com/w.js"); const char kName[] = "name"; // Create three renderer hosts. @@ -1141,7 +943,7 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest2) { MessagePortChannel local_port0; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host0, render_frame_host0->GetRoutingID()), - kURL, kName, &client0, &local_port0); + kUrl, kName, &client0, &local_port0); // Kill this process, which should make worker0 unavailable. renderer_host0->FastShutdownIfPossible(0, true); @@ -1151,9 +953,9 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest2) { MessagePortChannel local_port1; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host1, render_frame_host1->GetRoutingID()), - kURL, kName, &client1, &local_port1); + kUrl, kName, &client1, &local_port1); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); // The previous worker is unavailable, so a new worker is created. @@ -1163,33 +965,104 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest2) { EXPECT_TRUE(CheckNotReceivedFactoryRequest()); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); mojom::SharedWorkerHostPtr worker_host1; mojom::SharedWorkerRequest worker_request1; EXPECT_TRUE(factory1.CheckReceivedCreateSharedWorker( - kURL, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, + kUrl, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host1, &worker_request1)); MockSharedWorker worker1(std::move(worker_request1)); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(worker1.CheckReceivedConnect(nullptr, nullptr)); - client1.CheckReceivedOnCreated(); + EXPECT_TRUE(client1.CheckReceivedOnCreated()); // Start another client to confirm that it can connect to the same worker. MockSharedWorkerClient client2; MessagePortChannel local_port2; ConnectToSharedWorker(MakeSharedWorkerConnector( renderer_host2, render_frame_host2->GetRoutingID()), - kURL, kName, &client2, &local_port2); + kUrl, kName, &client2, &local_port2); - RunAllPendingInMessageLoop(); + base::RunLoop().RunUntilIdle(); EXPECT_TRUE(CheckNotReceivedFactoryRequest()); EXPECT_TRUE(worker1.CheckReceivedConnect(nullptr, nullptr)); - client2.CheckReceivedOnCreated(); + EXPECT_TRUE(client2.CheckReceivedOnCreated()); +} + +TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest3) { + const GURL kURL("http://example.com/w.js"); + const char kName[] = "name"; + + // The first renderer host. + std::unique_ptr web_contents0 = + CreateWebContents(GURL("http://example.com/")); + TestRenderFrameHost* render_frame_host0 = web_contents0->GetMainFrame(); + MockRenderProcessHost* renderer_host0 = render_frame_host0->GetProcess(); + renderer_host0->OverrideBinderForTesting( + mojom::SharedWorkerFactory::Name_, + base::BindRepeating( + &SharedWorkerServiceImplTest::BindSharedWorkerFactory)); + + // The second renderer host. + std::unique_ptr web_contents1 = + CreateWebContents(GURL("http://example.com/")); + TestRenderFrameHost* render_frame_host1 = web_contents1->GetMainFrame(); + MockRenderProcessHost* renderer_host1 = render_frame_host1->GetProcess(); + renderer_host1->OverrideBinderForTesting( + mojom::SharedWorkerFactory::Name_, + base::BindRepeating( + &SharedWorkerServiceImplTest::BindSharedWorkerFactory)); + + // Both clients try to connect/create a worker. + + MockSharedWorkerClient client0; + MessagePortChannel local_port0; + ConnectToSharedWorker(MakeSharedWorkerConnector( + renderer_host0, render_frame_host0->GetRoutingID()), + kURL, kName, &client0, &local_port0); + + MockSharedWorkerClient client1; + MessagePortChannel local_port1; + ConnectToSharedWorker(MakeSharedWorkerConnector( + renderer_host1, render_frame_host1->GetRoutingID()), + kURL, kName, &client1, &local_port1); + base::RunLoop().RunUntilIdle(); + + // Expect a factory request. + mojom::SharedWorkerFactoryRequest factory_request; + EXPECT_TRUE(CheckReceivedFactoryRequest(&factory_request)); + MockSharedWorkerFactory factory(std::move(factory_request)); + base::RunLoop().RunUntilIdle(); + + // Expect a create shared worker. + mojom::SharedWorkerHostPtr worker_host; + mojom::SharedWorkerRequest worker_request; + EXPECT_TRUE(factory.CheckReceivedCreateSharedWorker( + kURL, kName, blink::kWebContentSecurityPolicyTypeReport, &worker_host, + &worker_request)); + MockSharedWorker worker(std::move(worker_request)); + base::RunLoop().RunUntilIdle(); + + // Expect one connect for the first client. + EXPECT_TRUE(worker.CheckReceivedConnect(nullptr, nullptr)); + client0.CheckReceivedOnCreated(); + + // Expect one connect for the second client. + EXPECT_TRUE(worker.CheckReceivedConnect(nullptr, nullptr)); + client1.CheckReceivedOnCreated(); + + // Cleanup + + client0.Close(); + client1.Close(); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(worker.CheckReceivedTerminate()); } } // namespace content diff --git a/chromium/content/browser/shared_worker/worker_browsertest.cc b/chromium/content/browser/shared_worker/worker_browsertest.cc index 6c9a3d21a54..179376232af 100644 --- a/chromium/content/browser/shared_worker/worker_browsertest.cc +++ b/chromium/content/browser/shared_worker/worker_browsertest.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "base/bind.h" -#include "base/files/file_path.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -55,6 +54,7 @@ class WorkerTest : public ContentBrowserTest { ShellContentBrowserClient::Get()->set_select_client_certificate_callback( base::Bind(&WorkerTest::OnSelectClientCertificate, base::Unretained(this))); + ASSERT_TRUE(embedded_test_server()->Start()); } void TearDownOnMainThread() override { @@ -65,9 +65,8 @@ class WorkerTest : public ContentBrowserTest { int select_certificate_count() const { return select_certificate_count_; } GURL GetTestURL(const std::string& test_case, const std::string& query) { - base::FilePath test_file_path = GetTestFilePath( - "workers", test_case.c_str()); - return GetFileUrlWithQuery(test_file_path, query); + std::string url_string = "/workers/" + test_case + "?" + query; + return embedded_test_server()->GetURL(url_string); } void RunTest(Shell* window, const GURL& url) { @@ -129,11 +128,6 @@ IN_PROC_BROWSER_TEST_F(WorkerTest, IncognitoSharedWorkers) { if (!SupportsSharedWorker()) return; - // Launch the server to host a shared worker on http environment because a - // local file (file://) is treated like it has an opaque origin and the - // shared worker on the origin cannot be shared. - ASSERT_TRUE(embedded_test_server()->Start()); - // Load a non-incognito tab and have it create a shared worker RunTest(embedded_test_server()->GetURL("/workers/incognito_worker.html")); @@ -145,28 +139,11 @@ IN_PROC_BROWSER_TEST_F(WorkerTest, IncognitoSharedWorkers) { // Make sure that auth dialog is displayed from worker context. // http://crbug.com/33344 IN_PROC_BROWSER_TEST_F(WorkerTest, WorkerHttpAuth) { - ASSERT_TRUE(embedded_test_server()->Start()); GURL url = embedded_test_server()->GetURL("/workers/worker_auth.html"); NavigateAndWaitForAuth(url); } -// Make sure that HTTP auth dialog is displayed from shared worker context. -// http://crbug.com/33344 -// -// TODO(davidben): HTTP auth dialogs are no longer displayed on shared workers, -// but this test only tests that the delegate is called. Move handling the -// WebContentsless case from chrome/ to content/ and adjust the test -// accordingly. -IN_PROC_BROWSER_TEST_F(WorkerTest, SharedWorkerHttpAuth) { - if (!SupportsSharedWorker()) - return; - - ASSERT_TRUE(embedded_test_server()->Start()); - GURL url = embedded_test_server()->GetURL("/workers/shared_worker_auth.html"); - NavigateAndWaitForAuth(url); -} - // Tests that TLS client auth prompts for normal workers's importScripts. IN_PROC_BROWSER_TEST_F(WorkerTest, WorkerTlsClientAuthImportScripts) { // Launch HTTPS server. diff --git a/chromium/content/browser/site_instance_impl.cc b/chromium/content/browser/site_instance_impl.cc index d1ec2e7afab..b34db8034e1 100644 --- a/chromium/content/browser/site_instance_impl.cc +++ b/chromium/content/browser/site_instance_impl.cc @@ -36,6 +36,7 @@ SiteInstanceImpl::SiteInstanceImpl(BrowsingInstance* browsing_instance) active_frame_count_(0), browsing_instance_(browsing_instance), process_(nullptr), + can_associate_with_spare_process_(true), has_site_(false), process_reuse_policy_(ProcessReusePolicy::DEFAULT), is_for_service_worker_(false) { @@ -154,6 +155,14 @@ RenderProcessHost* SiteInstanceImpl::GetProcess() { return process_; } +bool SiteInstanceImpl::CanAssociateWithSpareProcess() { + return can_associate_with_spare_process_; +} + +void SiteInstanceImpl::PreventAssociationWithSpareProcess() { + can_associate_with_spare_process_ = false; +} + void SiteInstanceImpl::SetSite(const GURL& url) { TRACE_EVENT2("navigation", "SiteInstanceImpl::SetSite", "site id", id_, "url", url.possibly_invalid_spec()); @@ -468,6 +477,12 @@ bool SiteInstanceImpl::DoesSiteRequireDedicatedProcess( if (SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) return true; + // Error pages in main frames do require isolation, however since this is + // missing the context whether this is for a main frame or not, that part + // is enforced in RenderFrameHostManager. + if (url.SchemeIs(kChromeErrorScheme)) + return true; + // Always require a dedicated process for isolated origins. GURL site_url = GetSiteForURL(browser_context, url); auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); @@ -536,9 +551,9 @@ void SiteInstanceImpl::RenderProcessWillExit(RenderProcessHost* host) { observer.RenderProcessGone(this); } -void SiteInstanceImpl::RenderProcessExited(RenderProcessHost* host, - base::TerminationStatus status, - int exit_code) { +void SiteInstanceImpl::RenderProcessExited( + RenderProcessHost* host, + const ChildProcessTerminationInfo& info) { for (auto& observer : observers_) observer.RenderProcessGone(this); } diff --git a/chromium/content/browser/site_instance_impl.h b/chromium/content/browser/site_instance_impl.h index 7548752f7d8..4e96a2383e2 100644 --- a/chromium/content/browser/site_instance_impl.h +++ b/chromium/content/browser/site_instance_impl.h @@ -165,6 +165,22 @@ class CONTENT_EXPORT SiteInstanceImpl final : public SiteInstance, void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); + // Whether GetProcess() method (when it needs to find a new process to + // associate with the current SiteInstanceImpl) can return a spare process. + bool CanAssociateWithSpareProcess(); + + // Has no effect if the SiteInstanceImpl already has a |process_|. + // Otherwise, prevents GetProcess() from associating this SiteInstanceImpl + // with the spare RenderProcessHost - instead GetProcess will either need to + // create a new, not-yet-initialized/spawned RenderProcessHost or will need to + // reuse one of existing RenderProcessHosts. + // + // See also: + // - https://crbug.com/840409. + // - WebContents::CreateParams::desired_renderer_state + // - SiteInstanceImpl::CanAssociateWithSpareProcess(). + void PreventAssociationWithSpareProcess(); + // Get the effective URL for the given actual URL. This allows the // ContentBrowserClient to override the SiteInstance's site for certain URLs. // For example, Chrome uses this to replace hosted app URLs with extension @@ -209,8 +225,7 @@ class CONTENT_EXPORT SiteInstanceImpl final : public SiteInstance, void RenderProcessHostDestroyed(RenderProcessHost* host) override; void RenderProcessWillExit(RenderProcessHost* host) override; void RenderProcessExited(RenderProcessHost* host, - base::TerminationStatus status, - int exit_code) override; + const ChildProcessTerminationInfo& info) override; // Used to restrict a process' origin access rights. void LockToOriginIfNeeded(); @@ -241,6 +256,12 @@ class CONTENT_EXPORT SiteInstanceImpl final : public SiteInstance, // scenario the RenderProcessHost remains the same. RenderProcessHost* process_; + // Describes the desired behavior when GetProcess() method needs to find a new + // process to associate with the current SiteInstanceImpl. If |false|, then + // prevents the spare RenderProcessHost from being taken and stored in + // |process_|. + bool can_associate_with_spare_process_; + // The web site that this SiteInstance is rendering pages for. GURL site_; diff --git a/chromium/content/browser/site_instance_impl_unittest.cc b/chromium/content/browser/site_instance_impl_unittest.cc index 95595ca8c75..effcb936cb2 100644 --- a/chromium/content/browser/site_instance_impl_unittest.cc +++ b/chromium/content/browser/site_instance_impl_unittest.cc @@ -107,7 +107,8 @@ class SiteInstanceTest : public testing::Test { url::AddStandardScheme(kPrivilegedScheme, url::SCHEME_WITH_HOST); url::AddStandardScheme(kChromeUIScheme, url::SCHEME_WITH_HOST); - RenderProcessHostImpl::set_render_process_host_factory(&rph_factory_); + RenderProcessHostImpl::set_render_process_host_factory_for_testing( + &rph_factory_); } void TearDown() override { @@ -115,7 +116,7 @@ class SiteInstanceTest : public testing::Test { EXPECT_TRUE(RenderProcessHost::AllHostsIterator().IsAtEnd()); SetBrowserClientForTesting(old_browser_client_); - RenderProcessHostImpl::set_render_process_host_factory(nullptr); + RenderProcessHostImpl::set_render_process_host_factory_for_testing(nullptr); // http://crbug.com/143565 found SiteInstanceTest leaking an // AppCacheDatabase. This happens because some part of the test indirectly @@ -165,7 +166,7 @@ TEST_F(SiteInstanceTest, SiteInstanceDestructor) { NavigationEntryImpl* e1 = new NavigationEntryImpl( instance, url, Referrer(), base::string16(), ui::PAGE_TRANSITION_LINK, - false); + false, nullptr /* blob_url_loader_factory */); // Redundantly setting e1's SiteInstance shouldn't affect the ref count. e1->set_site_instance(instance); @@ -175,7 +176,7 @@ TEST_F(SiteInstanceTest, SiteInstanceDestructor) { // Add a second reference NavigationEntryImpl* e2 = new NavigationEntryImpl( instance, url, Referrer(), base::string16(), ui::PAGE_TRANSITION_LINK, - false); + false, nullptr /* blob_url_loader_factory */); instance = nullptr; EXPECT_EQ(0, browser_client()->GetAndClearSiteInstanceDeleteCount()); @@ -194,10 +195,10 @@ TEST_F(SiteInstanceTest, SiteInstanceDestructor) { // Ensure that instances are deleted when their RenderViewHosts are gone. std::unique_ptr browser_context(new TestBrowserContext()); { - std::unique_ptr web_contents(static_cast( + std::unique_ptr web_contents( WebContents::Create(WebContents::CreateParams( browser_context.get(), - SiteInstance::Create(browser_context.get()))))); + SiteInstance::Create(browser_context.get())))); EXPECT_EQ(0, browser_client()->GetAndClearSiteInstanceDeleteCount()); EXPECT_EQ(0, browser_client()->GetAndClearBrowsingInstanceDeleteCount()); } @@ -219,8 +220,9 @@ TEST_F(SiteInstanceTest, CloneNavigationEntry) { std::unique_ptr e1 = base::WrapUnique(new NavigationEntryImpl( - SiteInstanceImpl::Create(nullptr), url, Referrer(), - base::string16(), ui::PAGE_TRANSITION_LINK, false)); + SiteInstanceImpl::Create(nullptr), url, Referrer(), base::string16(), + ui::PAGE_TRANSITION_LINK, false, + nullptr /* blob_url_loader_factory */)); // Clone the entry. std::unique_ptr e2 = e1->Clone(); diff --git a/chromium/content/browser/site_per_process_browsertest.cc b/chromium/content/browser/site_per_process_browsertest.cc index 35f13184f84..569aa71cfe3 100644 --- a/chromium/content/browser/site_per_process_browsertest.cc +++ b/chromium/content/browser/site_per_process_browsertest.cc @@ -22,9 +22,9 @@ #include "base/json/json_reader.h" #include "base/location.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/run_loop.h" +#include "base/scoped_observer.h" #include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" #include "base/strings/pattern.h" @@ -33,6 +33,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/bind_test_util.h" +#include "base/test/histogram_tester.h" #include "base/test/test_timeouts.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h" @@ -69,6 +70,7 @@ #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/render_widget_host_observer.h" #include "content/public/browser/resource_dispatcher_host.h" #include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/content_features.h" @@ -108,6 +110,10 @@ #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/gesture_detection/gesture_configuration.h" +#include "ui/events/keycodes/dom/dom_code.h" +#include "ui/events/keycodes/dom/dom_key.h" +#include "ui/events/keycodes/dom/keycode_converter.h" +#include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/latency/latency_info.h" @@ -415,44 +421,46 @@ class SwapoutACKMessageFilter : public BrowserMessageFilter { DISALLOW_COPY_AND_ASSIGN(SwapoutACKMessageFilter); }; -class RenderWidgetHostVisibilityObserver : public NotificationObserver { +class RenderWidgetHostVisibilityObserver : public RenderWidgetHostObserver { public: explicit RenderWidgetHostVisibilityObserver(RenderWidgetHostImpl* rwhi, bool expected_visibility_state) : expected_visibility_state_(expected_visibility_state), + observer_(this), was_observed_(false), did_fail_(false), - source_(rwhi) { - registrar_.Add(this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, - source_); + render_widget_(rwhi) { + observer_.Add(render_widget_); message_loop_runner_ = new MessageLoopRunner; } bool WaitUntilSatisfied() { if (!was_observed_) message_loop_runner_->Run(); - registrar_.Remove(this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, - source_); + if (observer_.IsObserving(render_widget_)) + observer_.Remove(render_widget_); return !did_fail_; } private: - void Observe(int type, - const NotificationSource& source, - const NotificationDetails& details) override { + void RenderWidgetHostVisibilityChanged(RenderWidgetHost* widget_host, + bool became_visible) override { was_observed_ = true; - did_fail_ = expected_visibility_state_ != - (*static_cast&>(details).ptr()); + did_fail_ = expected_visibility_state_ != became_visible; if (message_loop_runner_->loop_running()) message_loop_runner_->Quit(); } + void RenderWidgetHostDestroyed(RenderWidgetHost* widget_host) override { + observer_.Remove(widget_host); + } + bool expected_visibility_state_; scoped_refptr message_loop_runner_; - NotificationRegistrar registrar_; + ScopedObserver observer_; bool was_observed_; bool did_fail_; - Source source_; + RenderWidgetHost* render_widget_; DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostVisibilityObserver); }; @@ -527,9 +535,8 @@ void CheckFrameDepth(unsigned int expected_depth, FrameTreeNode* node) { RenderProcessHost::Priority priority = node->current_frame_host()->GetRenderWidgetHost()->GetPriority(); EXPECT_EQ(expected_depth, priority.frame_depth); - EXPECT_EQ( - expected_depth, - node->current_frame_host()->GetProcess()->GetFrameDepthForTesting()); + EXPECT_EQ(expected_depth, + node->current_frame_host()->GetProcess()->GetFrameDepth()); } } // namespace @@ -1133,8 +1140,8 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ViewBoundsInNestedFrameTest) { // relative offset of its direct parent within the root frame. gfx::Rect bounds = rwhv_nested->GetViewBounds(); - scoped_refptr filter = - new UpdateResizeParamsMessageFilter(); + scoped_refptr filter = + new SynchronizeVisualPropertiesMessageFilter(); root->current_frame_host()->GetProcess()->AddFilter(filter.get()); // Scroll the parent frame downward to verify that the child rect gets updated @@ -1254,11 +1261,12 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ScrollBubblingFromOOPIFTest) { FrameTreeNode* parent_iframe_node = root->child_at(0); // This test uses the position of the nested iframe within the parent iframe - // to infer the scroll position of the parent. UpdateResizeParamsMessageFilter - // catches updates to the position in order to avoid busy waiting. - // It gets created early to catch the initial rects from the navigation. - scoped_refptr filter = - new UpdateResizeParamsMessageFilter(); + // to infer the scroll position of the parent. + // SynchronizeVisualPropertiesMessageFilter catches updates to the position in + // order to avoid busy waiting. It gets created early to catch the initial + // rects from the navigation. + scoped_refptr filter = + new SynchronizeVisualPropertiesMessageFilter(); parent_iframe_node->current_frame_host()->GetProcess()->AddFilter( filter.get()); @@ -1472,8 +1480,74 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ScrollBubblingFromOOPIFTest) { DCHECK_EQ(filter->last_rect().y(), 0); } +// Tests that scrolling with the keyboard will bubble unused scroll to the +// OOPIF's parent. +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + KeyboardScrollBubblingFromOOPIF) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/frame_tree/page_with_iframe_in_scrollable_div.html")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + // It is safe to obtain the root frame tree node here, as it doesn't change. + FrameTreeNode* root = static_cast(shell()->web_contents()) + ->GetFrameTree() + ->root(); + ASSERT_EQ(1U, root->child_count()); + + FrameTreeNode* iframe_node = root->child_at(0); + + EXPECT_EQ( + " Site A ------------ proxies for B\n" + " +--Site B ------- proxies for A\n" + "Where A = http://a.com/\n" + " B = http://b.com/", + DepictFrameTree(root)); + + RenderWidgetHostViewBase* rwhv_child = static_cast( + iframe_node->current_frame_host()->GetRenderWidgetHost()->GetView()); + + double initial_y = 0.0; + ASSERT_TRUE(content::ExecuteScriptAndExtractDouble( + root, + "var wrapperDiv = document.getElementById('wrapper-div');" + "var initial_y = wrapperDiv.scrollTop;" + "var waitForScrollDownPromise = new Promise(function(resolve) {" + " wrapperDiv.addEventListener('scroll', () => {" + " if (wrapperDiv.scrollTop > initial_y)" + " resolve(wrapperDiv.scrollTop);" + " });" + "});" + "window.domAutomationController.send(initial_y);", + &initial_y)); + EXPECT_DOUBLE_EQ(0.0, initial_y); + + NativeWebKeyboardEvent key_event( + blink::WebKeyboardEvent::kRawKeyDown, blink::WebInputEvent::kNoModifiers, + blink::WebInputEvent::GetStaticTimeStampForTests()); + key_event.windows_key_code = ui::VKEY_DOWN; + key_event.native_key_code = + ui::KeycodeConverter::DomCodeToNativeKeycode(ui::DomCode::ARROW_DOWN); + key_event.dom_code = static_cast(ui::DomCode::ARROW_DOWN); + key_event.dom_key = ui::DomKey::ARROW_DOWN; + + rwhv_child->GetRenderWidgetHost()->ForwardKeyboardEvent(key_event); + + key_event.SetType(blink::WebKeyboardEvent::kKeyUp); + rwhv_child->GetRenderWidgetHost()->ForwardKeyboardEvent(key_event); + + double scrolled_y = 0.0; + ASSERT_TRUE(content::ExecuteScriptAndExtractDouble( + root, + "waitForScrollDownPromise.then((scrolled_y) => {" + " window.domAutomationController.send(scrolled_y);" + "});", + &scrolled_y)); + EXPECT_GT(scrolled_y, 0.0); +} + // Test that fling on an out-of-process iframe progresses properly. -IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, GestureFlingStart) { +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + TouchscreenGestureFlingStart) { GURL main_url(embedded_test_server()->GetURL( "a.com", "/cross_site_iframe_factory.html?a(b)")); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -1518,6 +1592,53 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, GestureFlingStart) { gesture_scroll_update_ack_observer.Wait(); } +// Test that fling on an out-of-process iframe progresses properly. +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, TouchpadGestureFlingStart) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + FrameTreeNode* root = static_cast(shell()->web_contents()) + ->GetFrameTree() + ->root(); + ASSERT_EQ(1U, root->child_count()); + + FrameTreeNode* child_iframe_node = root->child_at(0); + + RenderWidgetHost* child_rwh = + child_iframe_node->current_frame_host()->GetRenderWidgetHost(); + + // Send a wheel event with phaseBegan to start scrolling sequence. + InputEventAckWaiter gesture_scroll_begin_ack_observer( + child_rwh, blink::WebInputEvent::kGestureScrollBegin); + blink::WebMouseWheelEvent scroll_event( + blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers, + blink::WebInputEvent::GetStaticTimeStampForTests()); + scroll_event.delta_x = 0.0f; + scroll_event.delta_y = 5.0f; + scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan; + scroll_event.has_precise_scrolling_deltas = true; + child_rwh->ForwardWheelEvent(scroll_event); + gesture_scroll_begin_ack_observer.Wait(); + + // Send a GFS and wait for the ack of the first GSU generated from progressing + // the fling on the browser. + InputEventAckWaiter gesture_scroll_update_ack_observer( + child_rwh, blink::WebInputEvent::kGestureScrollUpdate); + gesture_scroll_update_ack_observer.Reset(); + blink::WebGestureEvent gesture_fling_start( + blink::WebGestureEvent::kGestureFlingStart, + blink::WebInputEvent::kNoModifiers, + blink::WebInputEvent::GetStaticTimeStampForTests()); + gesture_fling_start.SetSourceDevice(blink::kWebGestureDeviceTouchpad); + gesture_fling_start.data.fling_start.velocity_x = 0.f; + gesture_fling_start.data.fling_start.velocity_y = 50.f; + child_rwh->ForwardGestureEvent(gesture_fling_start); + // The test will pass when the GSU ack arrives, since it shows that the fling + // controller has properly generated a GSU event from progressing the fling. + gesture_scroll_update_ack_observer.Wait(); +} + class ScrollObserver : public RenderWidgetHost::InputEventObserver { public: ScrollObserver(double delta_x, double delta_y) { Reset(delta_x, delta_y); } @@ -1656,10 +1777,18 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, } } +#if defined(OS_CHROMEOS) +// Flaky: https://crbug.com/836200. +#define MAYBE_ScrollBubblingFromOOPIFWithBodyOverflowHidden \ + DISABLED_ScrollBubblingFromOOPIFWithBodyOverflowHidden +#else +#define MAYBE_ScrollBubblingFromOOPIFWithBodyOverflowHidden \ + ScrollBubblingFromOOPIFWithBodyOverflowHidden +#endif // Tests that scrolling bubbles from an oopif if its source body has // "overflow:hidden" style. IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, - ScrollBubblingFromOOPIFWithBodyOverflowHidden) { + MAYBE_ScrollBubblingFromOOPIFWithBodyOverflowHidden) { GURL url_domain_a(embedded_test_server()->GetURL( "a.com", "/scrollable_page_with_iframe.html")); EXPECT_TRUE(NavigateToURL(shell(), url_domain_a)); @@ -2673,20 +2802,14 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ProcessTransferAfterError) { // Disable host resolution in the test server and try to navigate the subframe // cross-site, which will lead to a committed net error. GURL url_b = embedded_test_server()->GetURL("b.com", "/title3.html"); - bool network_service = - base::FeatureList::IsEnabled(network::features::kNetworkService); std::unique_ptr url_loader_interceptor; - if (network_service) { - url_loader_interceptor = std::make_unique( - base::BindRepeating([](URLLoaderInterceptor::RequestParams* params) { - network::URLLoaderCompletionStatus status; - status.error_code = net::ERR_NOT_IMPLEMENTED; - params->client->OnComplete(status); - return true; - })); - } else { - host_resolver()->ClearRules(); - } + url_loader_interceptor = std::make_unique( + base::BindRepeating([](URLLoaderInterceptor::RequestParams* params) { + network::URLLoaderCompletionStatus status; + status.error_code = net::ERR_NOT_IMPLEMENTED; + params->client->OnComplete(status); + return true; + })); TestNavigationObserver observer(shell()->web_contents()); NavigateIframeToURL(shell()->web_contents(), "child-0", url_b); @@ -2711,11 +2834,7 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ProcessTransferAfterError) { EXPECT_EQ("null", child->current_origin().Serialize()); // Try again after re-enabling host resolution. - if (network_service) { - url_loader_interceptor.reset(); - } else { - host_resolver()->AddRule("*", "127.0.0.1"); - } + url_loader_interceptor.reset(); NavigateIframeToURL(shell()->web_contents(), "child-0", url_b); EXPECT_TRUE(observer.last_navigation_succeeded()); @@ -7746,7 +7865,7 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FileChooserInSubframe) { // Use FileChooserDelegate to avoid showing the actual dialog and to respond // back to the renderer process with predefined file. base::FilePath file; - EXPECT_TRUE(PathService::Get(base::DIR_TEMP, &file)); + EXPECT_TRUE(base::PathService::Get(base::DIR_TEMP, &file)); file = file.AppendASCII("bar"); std::unique_ptr delegate(new FileChooserDelegate(file)); shell()->web_contents()->SetDelegate(delegate.get()); @@ -8929,6 +9048,64 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, EXPECT_EQ(0UL, final_effective_policy[0].origins.size()); } +// Test that creating a new remote frame at the same origin as its parent +// results in the correct feature policy in the RemoteSecurityContext. +// https://crbug.com/852102 +IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyJavaScriptBrowserTest, + FeaturePolicyConstructionInExistingProxy) { + WebContentsImpl* contents = web_contents(); + FrameTreeNode* root = contents->GetFrameTree()->root(); + + // Navigate to a page (1) with a cross-origin iframe (2). After load, the + // frame tree should look like: + // + // a.com(1) + // / + // b.com(2) + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)"))); + + // Programmatically create a new same-origin frame (3) under the root, with a + // cross-origin child (4). Since two SiteInstances already exist at this + // point, a proxy for frame 3 will be created in the renderer for frames 2 and + // 4. The frame tree should look like: + // + // a.com(1) + // / \ + // b.com(2) a.com(3) + // \ + // b.com(4) + std::string create_subframe_script = + "var f = document.createElement('iframe'); f.src='" + + embedded_test_server() + ->GetURL("a.com", + "/cross_site_iframe_factory.html?a(b{allow-autoplay})") + .spec() + + "'; document.body.appendChild(f);"; + EXPECT_TRUE(ExecuteScript(root, create_subframe_script)); + EXPECT_TRUE(WaitForLoadStop(contents)); + + // Verify the shape of the frame tree + EXPECT_EQ(2UL, root->child_count()); + EXPECT_EQ(1UL, root->child_at(1)->child_count()); + + // Ask frame 4 to report the enabled state of the autoplay feature. Frame 3's + // policy should allow autoplay if created correctly, as it is same-origin + // with the root, where the feature is enabled by default, and therefore + // should be able to delegate it to frame 4. + // This indirectly tests the replicated policy in frame 3: Because frame 4 is + // cross-origin to frame 3, it will use the proxy's replicated policy as the + // parent policy; otherwise we would just ask frame 3 to report its own state. + bool success = false; + EXPECT_TRUE( + ExecuteScriptAndExtractBool(root->child_at(1)->child_at(0), + "window.domAutomationController.send(" + "document.policy.allowsFeature('autoplay'));", + &success)); + EXPECT_TRUE(success); +} + // Test harness that allows for "barrier" style delaying of requests matching // certain paths. Call SetDelayedRequestsForPath to delay requests, then // SetUpEmbeddedTestServer to register handlers and start the server. @@ -9945,7 +10122,7 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, #if defined(OS_ANDROID) IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, TestChildProcessImportance) { - web_contents()->SetImportance(ChildProcessImportance::MODERATE); + web_contents()->SetMainFrameImportance(ChildProcessImportance::MODERATE); // Construct root page with one child in different domain. GURL main_url(embedded_test_server()->GetURL( @@ -9955,25 +10132,26 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, TestChildProcessImportance) { ASSERT_EQ(1u, root->child_count()); FrameTreeNode* child = root->child_at(0); - // Importance should survive initial navigation. + // Importance should survive initial navigation. Note importance only affect + // main frame, so sub frame process should remain NORMAL throughout. EXPECT_EQ(ChildProcessImportance::MODERATE, root->current_frame_host()->GetProcess()->GetEffectiveImportance()); EXPECT_EQ( - ChildProcessImportance::MODERATE, + ChildProcessImportance::NORMAL, child->current_frame_host()->GetProcess()->GetEffectiveImportance()); // Check setting importance. - web_contents()->SetImportance(ChildProcessImportance::NORMAL); + web_contents()->SetMainFrameImportance(ChildProcessImportance::NORMAL); EXPECT_EQ(ChildProcessImportance::NORMAL, root->current_frame_host()->GetProcess()->GetEffectiveImportance()); EXPECT_EQ( ChildProcessImportance::NORMAL, child->current_frame_host()->GetProcess()->GetEffectiveImportance()); - web_contents()->SetImportance(ChildProcessImportance::IMPORTANT); + web_contents()->SetMainFrameImportance(ChildProcessImportance::IMPORTANT); EXPECT_EQ(ChildProcessImportance::IMPORTANT, root->current_frame_host()->GetProcess()->GetEffectiveImportance()); EXPECT_EQ( - ChildProcessImportance::IMPORTANT, + ChildProcessImportance::NORMAL, child->current_frame_host()->GetProcess()->GetEffectiveImportance()); // Check importance is maintained if child navigates to new domain. @@ -9987,8 +10165,10 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, TestChildProcessImportance) { int new_child_process_id = child->current_frame_host()->GetProcess()->GetID(); EXPECT_NE(old_child_process_id, new_child_process_id); EXPECT_EQ( - ChildProcessImportance::IMPORTANT, + ChildProcessImportance::NORMAL, child->current_frame_host()->GetProcess()->GetEffectiveImportance()); + EXPECT_EQ(ChildProcessImportance::IMPORTANT, + root->current_frame_host()->GetProcess()->GetEffectiveImportance()); // Check importance is maintained if root navigates to new domain. int old_root_process_id = root->current_frame_host()->GetProcess()->GetID(); @@ -10018,7 +10198,7 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, TestChildProcessImportance) { EXPECT_EQ(ChildProcessImportance::IMPORTANT, interstitial_process->GetEffectiveImportance()); - web_contents()->SetImportance(ChildProcessImportance::MODERATE); + web_contents()->SetMainFrameImportance(ChildProcessImportance::MODERATE); EXPECT_EQ(ChildProcessImportance::MODERATE, interstitial_process->GetEffectiveImportance()); } @@ -10893,9 +11073,7 @@ class CommitMessageOrderReverser : public DidCommitProvisionalLoadInterceptor { if (params->url == deferred_url_) { std::move(deferred_url_triggered_action_).Run(render_frame_host); - base::MessageLoop::ScopedNestableTaskAllower allow( - base::MessageLoop::current()); - base::RunLoop nested_run_loop; + base::RunLoop nested_run_loop(base::RunLoop::Type::kNestableTasksAllowed); nested_loop_quit_ = nested_run_loop.QuitClosure(); nested_run_loop.Run(); outer_run_loop.Quit(); @@ -11300,9 +11478,7 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FrameDepthTest) { child0->current_frame_host()->GetRenderWidgetHost()->GetPriority(); // Same site instance as root. EXPECT_EQ(0u, priority.frame_depth); - EXPECT_EQ( - 0u, - child0->current_frame_host()->GetProcess()->GetFrameDepthForTesting()); + EXPECT_EQ(0u, child0->current_frame_host()->GetProcess()->GetFrameDepth()); } FrameTreeNode* child1 = root->child_at(1); @@ -11323,9 +11499,8 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FrameDepthTest) { grand_child->current_frame_host()->GetRenderWidgetHost()->GetPriority(); EXPECT_EQ(2u, priority.frame_depth); // Same process as root - EXPECT_EQ(0u, grand_child->current_frame_host() - ->GetProcess() - ->GetFrameDepthForTesting()); + EXPECT_EQ(0u, + grand_child->current_frame_host()->GetProcess()->GetFrameDepth()); } } @@ -11349,13 +11524,13 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, VisibilityFrameDepthTest) { popup_root->current_frame_host()->GetProcess(); EXPECT_EQ(subframe_process, popup_process); EXPECT_EQ(2, popup_process->VisibleClientCount()); - EXPECT_EQ(0u, popup_process->GetFrameDepthForTesting()); + EXPECT_EQ(0u, popup_process->GetFrameDepth()); // Hide popup. Process should have one visible client and depth should be 1, // since depth 0 popup is hidden. new_shell->web_contents()->WasHidden(); EXPECT_EQ(1, popup_process->VisibleClientCount()); - EXPECT_EQ(1u, popup_process->GetFrameDepthForTesting()); + EXPECT_EQ(1u, popup_process->GetFrameDepth()); // Navigate main page to same origin as popup in same BrowsingInstance, // s main page should run in the same process as the popup. The depth on the @@ -11369,19 +11544,19 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, VisibilityFrameDepthTest) { root->current_frame_host()->GetProcess(); EXPECT_EQ(new_root_process, popup_process); EXPECT_EQ(1, popup_process->VisibleClientCount()); - EXPECT_EQ(0u, popup_process->GetFrameDepthForTesting()); + EXPECT_EQ(0u, popup_process->GetFrameDepth()); // Go back on main page. Should go back to same state as before navigation. TestNavigationObserver back_load_observer(shell()->web_contents()); shell()->web_contents()->GetController().GoBack(); back_load_observer.Wait(); EXPECT_EQ(1, popup_process->VisibleClientCount()); - EXPECT_EQ(1u, popup_process->GetFrameDepthForTesting()); + EXPECT_EQ(1u, popup_process->GetFrameDepth()); // Unhide popup. Should go back to same state as before hide. new_shell->web_contents()->WasShown(); EXPECT_EQ(2, popup_process->VisibleClientCount()); - EXPECT_EQ(0u, popup_process->GetFrameDepthForTesting()); + EXPECT_EQ(0u, popup_process->GetFrameDepth()); } // Ensure that after a main frame with an OOPIF is navigated cross-site, the @@ -11495,6 +11670,38 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, EXPECT_TRUE(success); } +// Tests that the last committed URL is preserved on an RFH even after the RFH +// goes into the pending deletion state. +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + LastCommittedURLRetainedAfterSwapOut) { + // Navigate to a.com. + GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), start_url)); + RenderFrameHostImpl* rfh = web_contents()->GetMainFrame(); + EXPECT_EQ(start_url, rfh->GetLastCommittedURL()); + + // Disable the swapout ACK and the swapout timer. + scoped_refptr filter = new SwapoutACKMessageFilter(); + rfh->GetProcess()->AddFilter(filter.get()); + rfh->DisableSwapOutTimerForTesting(); + + // Open a popup on a.com to keep the process alive. + OpenPopup(shell(), embedded_test_server()->GetURL("a.com", "/title2.html"), + "foo"); + + // Navigate cross-process to b.com. + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("b.com", "/title3.html"))); + + // The old RFH should be pending deletion. + EXPECT_FALSE(rfh->is_active()); + EXPECT_FALSE(rfh->IsCurrent()); + EXPECT_NE(rfh, web_contents()->GetMainFrame()); + + // Check that it still has a valid last committed URL. + EXPECT_EQ(start_url, rfh->GetLastCommittedURL()); +} + // Class to monitor incoming FrameHostMsg_UpdateViewportIntersection messages. class UpdateViewportIntersectionMessageFilter : public content::BrowserMessageFilter { @@ -11549,9 +11756,10 @@ class UpdateViewportIntersectionMessageFilter // Tests that when a large OOPIF has been scaled, the compositor raster area // sent from the embedder is correct. -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_MACOSX) // Temporarily disabled on Android because this doesn't account for browser // control height or page scale factor. +// Flaky on Mac. https://crbug.com/840314 #define MAYBE_ScaledIframeRasterSize DISABLED_ScaledframeRasterSize #else #define MAYBE_ScaledIframeRasterSize ScaledIframeRasterSize @@ -11823,4 +12031,664 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, EXPECT_TRUE(success); } +class SitePerProcessBrowserTouchActionTest : public SitePerProcessBrowserTest { + public: + SitePerProcessBrowserTouchActionTest() = default; + + // Returns the effective touch action for |rwhv_child| by dispatching a + // touch to it through |rwhv_root|. |rwhv_root| is the root frame containing + // |rwhv_child|. |rwhv_child| is the child (or indirect descendent) of + // |rwhv_root| to get the touch action of. |event_position| should be within + // |rwhv_child| in |rwhv_root|'s coordinate space. + cc::TouchAction GetEffectiveTouchActionForChild( + RenderWidgetHostInputEventRouter* router, + RenderWidgetHostViewBase* rwhv_root, + RenderWidgetHostViewBase* rwhv_child, + const gfx::Point& event_position) { + InputEventAckWaiter ack_observer( + rwhv_child->GetRenderWidgetHost(), + base::BindRepeating([](content::InputEventAckSource source, + content::InputEventAckState state, + const blink::WebInputEvent& event) { + return event.GetType() == blink::WebGestureEvent::kTouchStart || + event.GetType() == blink::WebGestureEvent::kTouchMove || + event.GetType() == blink::WebGestureEvent::kTouchEnd; + })); + + // Send a touch start event to child to get the TAF filled with child + // frame's touch action. + ack_observer.Reset(); + SyntheticWebTouchEvent touch_event; + int index = touch_event.PressPoint(event_position.x(), event_position.y()); + router->RouteTouchEvent(rwhv_root, &touch_event, + ui::LatencyInfo(ui::SourceEventType::TOUCH)); + ack_observer.Wait(); + cc::TouchAction touch_action = + static_cast(rwhv_child->GetRenderWidgetHost()) + ->input_router() + ->AllowedTouchAction(); + + // Send a touch move and touch end to complete the sequence, this also + // avoids triggering DCHECKs when sending followup events. + ack_observer.Reset(); + touch_event.MovePoint(index, 1, 1); + router->RouteTouchEvent(rwhv_root, &touch_event, + ui::LatencyInfo(ui::SourceEventType::TOUCH)); + ack_observer.Wait(); + + ack_observer.Reset(); + touch_event.ReleasePoint(index); + router->RouteTouchEvent(rwhv_root, &touch_event, + ui::LatencyInfo(ui::SourceEventType::TOUCH)); + ack_observer.Wait(); + return touch_action; + } + + // Waits until the parent frame has had enough time to propagate the effective + // touch action to the child frame and the child frame has had enough time to + // process it. + void WaitForTouchActionUpdated(MainThreadFrameObserver* observer, + content::RenderFrameHost* child_frame) { + // Ensures that main frame has calculated the new effective touch action for + // child frames. + observer->Wait(); + // Ensures that if a child frame is in progress we will wait until the next + // one. + WaitForChildFrameSurfaceReady(child_frame); + // This child frame should receive the effective touch action from parent + // (if the previous one didn't) and propagates it. + WaitForChildFrameSurfaceReady(child_frame); + observer->Wait(); + } +}; + +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTouchActionTest, + EffectiveTouchActionPropagatesAcrossFrames) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + FrameTreeNode* child = root->child_at(0); + RenderWidgetHostViewBase* rwhv_root = static_cast( + root->current_frame_host()->GetRenderWidgetHost()->GetView()); + RenderWidgetHostViewBase* rwhv_child = static_cast( + child->current_frame_host()->GetRenderWidgetHost()->GetView()); + std::unique_ptr observer(new MainThreadFrameObserver( + root->current_frame_host()->GetRenderWidgetHost())); + observer->Wait(); + + GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html")); + NavigateFrameToURL(child, b_url); + + // Force the renderer to generate a new frame. + EXPECT_TRUE( + ExecuteScript(shell(), "document.body.style.touchAction = 'none'")); + // Waits for the next frame. + WaitForChildFrameSurfaceReady(child->current_frame_host()); + + RenderWidgetHostViewChildFrame* child_view = + static_cast( + child->current_frame_host()->GetRenderWidgetHost()->GetView()); + gfx::Point point_inside_child = ToFlooredPoint( + child_view->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f))); + + RenderWidgetHostInputEventRouter* router = + static_cast(web_contents())->GetInputEventRouter(); + + WaitForTouchActionUpdated(observer.get(), child->current_frame_host()); + // Gestures are filtered by the intersection of touch-action values of the + // touched element and all its ancestors up to the one that implements the + // gesture. Since iframe allows scrolling, touch action pan restrictions will + // not affect iframe's descendants, so we expect kTouchActionPan instead of + // kTouchActionAuto in iframe's child. + EXPECT_EQ(cc::TouchAction::kTouchActionPan, + GetEffectiveTouchActionForChild(router, rwhv_root, rwhv_child, + point_inside_child)); + + EXPECT_TRUE( + ExecuteScript(shell(), "document.body.style.touchAction = 'auto'")); + WaitForTouchActionUpdated(observer.get(), child->current_frame_host()); + EXPECT_EQ(cc::TouchAction::kTouchActionAuto, + GetEffectiveTouchActionForChild(router, rwhv_root, rwhv_child, + point_inside_child)); +} + +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTouchActionTest, + EffectiveTouchActionPropagatesAcrossNestedFrames) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b(c))")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + FrameTreeNode* parent = root->child_at(0); + GURL b_url(embedded_test_server()->GetURL( + "b.com", "/frame_tree/page_with_iframe_in_div.html")); + NavigateFrameToURL(parent, b_url); + + ASSERT_EQ(1U, parent->child_count()); + EXPECT_EQ( + " Site A ------------ proxies for B C\n" + " +--Site B ------- proxies for A C\n" + " +--Site C -- proxies for A B\n" + "Where A = http://a.com/\n" + " B = http://b.com/\n" + " C = http://bar.com/", + DepictFrameTree(root)); + + FrameTreeNode* child = root->child_at(0)->child_at(0); + RenderWidgetHostViewBase* rwhv_root = static_cast( + root->current_frame_host()->GetRenderWidgetHost()->GetView()); + RenderWidgetHostViewBase* rwhv_child = static_cast( + child->current_frame_host()->GetRenderWidgetHost()->GetView()); + std::unique_ptr observer(new MainThreadFrameObserver( + root->current_frame_host()->GetRenderWidgetHost())); + observer->Wait(); + + EXPECT_TRUE( + ExecuteScript(shell(), "document.body.style.touchAction = 'none'")); + + // Wait for child frame ready in order to get the correct point inside child. + WaitForChildFrameSurfaceReady(child->current_frame_host()); + RenderWidgetHostViewChildFrame* child_view = + static_cast( + child->current_frame_host()->GetRenderWidgetHost()->GetView()); + gfx::Point point_inside_child = ToFlooredPoint( + child_view->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f))); + + RenderWidgetHostInputEventRouter* router = + static_cast(web_contents())->GetInputEventRouter(); + + // Child should inherit effective touch action none from root. + WaitForTouchActionUpdated(observer.get(), child->current_frame_host()); + EXPECT_EQ(cc::TouchAction::kTouchActionPan, + GetEffectiveTouchActionForChild(router, rwhv_root, rwhv_child, + point_inside_child)); + + // Child should inherit effective touch action none from parent. + EXPECT_TRUE( + ExecuteScript(shell(), "document.body.style.touchAction = 'auto'")); + EXPECT_TRUE(ExecuteScript( + parent, + "document.getElementById('parent-div').style.touchAction = 'none';")); + WaitForTouchActionUpdated(observer.get(), child->current_frame_host()); + EXPECT_EQ(cc::TouchAction::kTouchActionPan, + GetEffectiveTouchActionForChild(router, rwhv_root, rwhv_child, + point_inside_child)); + + // Child should inherit effective touch action auto from root and parent. + EXPECT_TRUE(ExecuteScript( + parent, + "document.getElementById('parent-div').style.touchAction = 'auto'")); + WaitForTouchActionUpdated(observer.get(), child->current_frame_host()); + EXPECT_EQ(cc::TouchAction::kTouchActionAuto, + GetEffectiveTouchActionForChild(router, rwhv_root, rwhv_child, + point_inside_child)); +} + +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTouchActionTest, + EffectiveTouchActionPropagatesWhenChildFrameNavigates) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + FrameTreeNode* child = root->child_at(0); + GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html")); + NavigateFrameToURL(child, b_url); + + EXPECT_EQ( + " Site A ------------ proxies for B\n" + " +--Site B ------- proxies for A\n" + "Where A = http://a.com/\n" + " B = http://b.com/", + DepictFrameTree(root)); + + RenderWidgetHostViewBase* rwhv_root = static_cast( + root->current_frame_host()->GetRenderWidgetHost()->GetView()); + RenderWidgetHostViewBase* rwhv_child = static_cast( + child->current_frame_host()->GetRenderWidgetHost()->GetView()); + std::unique_ptr observer(new MainThreadFrameObserver( + root->current_frame_host()->GetRenderWidgetHost())); + observer->Wait(); + + EXPECT_TRUE( + ExecuteScript(shell(), "document.body.style.touchAction = 'none'")); + + // Wait for child frame ready in order to get the correct point inside child. + WaitForChildFrameSurfaceReady(child->current_frame_host()); + RenderWidgetHostViewChildFrame* child_view = + static_cast( + child->current_frame_host()->GetRenderWidgetHost()->GetView()); + gfx::Point point_inside_child = gfx::ToFlooredPoint( + child_view->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f))); + + RenderWidgetHostInputEventRouter* router = + static_cast(web_contents())->GetInputEventRouter(); + // Child should inherit effective touch action none from root. + WaitForTouchActionUpdated(observer.get(), child->current_frame_host()); + EXPECT_EQ(cc::TouchAction::kTouchActionPan, + GetEffectiveTouchActionForChild(router, rwhv_root, rwhv_child, + point_inside_child)); + + // After navigation, child should still inherit effective touch action none + // from parent. + GURL new_url(embedded_test_server()->GetURL("c.com", "/title2.html")); + NavigateFrameToURL(child, new_url); + WaitForChildFrameSurfaceReady(child->current_frame_host()); + rwhv_child = static_cast( + child->current_frame_host()->GetRenderWidgetHost()->GetView()); + + WaitForTouchActionUpdated(observer.get(), child->current_frame_host()); + EXPECT_EQ(cc::TouchAction::kTouchActionPan, + GetEffectiveTouchActionForChild(router, rwhv_root, rwhv_child, + point_inside_child)); +} + +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + ChildFrameCrashMetrics_KilledWhileVisible) { + // Set-up a frame tree that helps verify what the metrics tracks: + // 1) frames (12 frames are affected if B process gets killed) or + // 2) crashes (simply 1 crash if B process gets killed)? + // 3) widgets (10 b widgets and 1 c widget are affected if B is killed, + // but a sad frame will appear only in 9 widgets - this excludes + // widgets for the b,c(b) part of the frame tree) or + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b(b,c(b)),b,b,b,b,b,b,b,b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + + // Kill the child frame. + base::HistogramTester histograms; + RenderProcessHost* child_process = + root->child_at(0)->current_frame_host()->GetProcess(); + RenderProcessHostWatcher crash_observer( + child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); + child_process->Shutdown(0); + crash_observer.Wait(); + + // Verify that the expected metrics got logged. + histograms.ExpectUniqueSample( + "Stability.ChildFrameCrash.Visibility", + CrossProcessFrameConnector::CrashVisibility::kCrashedWhileVisible, 9); + + // Hide and show the web contents and verify that no more metrics got logged. + web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN); + web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); + histograms.ExpectUniqueSample( + "Stability.ChildFrameCrash.Visibility", + CrossProcessFrameConnector::CrashVisibility::kCrashedWhileVisible, 9); +} + +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + ChildFrameCrashMetrics_KilledMainFrame) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(a(b(b,c)))")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + + // Kill the main frame. + base::HistogramTester histograms; + RenderProcessHost* child_process = root->current_frame_host()->GetProcess(); + RenderProcessHostWatcher crash_observer( + child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); + child_process->Shutdown(0); + crash_observer.Wait(); + + // Verify that no child frame metrics got logged. + histograms.ExpectTotalCount("Stability.ChildFrameCrash.Visibility", 0); +} + +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + ChildFrameCrashMetrics_KilledWhileHiddenThenShown) { + // Set-up a frame tree that helps verify what the metrics tracks: + // 1) frames (12 frames are affected if B process gets killed) or + // 2) widgets (10 b widgets and 1 c widget are affected if B is killed) or + // 3) crashes (1 crash if B process gets killed)? + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b(b,c),b,b,b,b,b,b,b,b,b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + + // Hide the web contents (UpdateWebContentsVisibility is called twice to avoid + // hitting the |!did_first_set_visible_| case). + web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); + web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN); + + // Kill the subframe. + base::HistogramTester histograms; + RenderProcessHost* child_process = + root->child_at(0)->current_frame_host()->GetProcess(); + RenderProcessHostWatcher crash_observer( + child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); + child_process->Shutdown(0); + crash_observer.Wait(); + + // Verify that no child frame metrics got logged (yet - while WebContents are + // hidden). + histograms.ExpectTotalCount("Stability.ChildFrameCrash.Visibility", 0); + + // Show the web contents. + // and verify that the expected metrics got logged. + web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); + histograms.ExpectUniqueSample( + "Stability.ChildFrameCrash.Visibility", + CrossProcessFrameConnector::CrashVisibility::kShownAfterCrashing, 10); + + // Hide and show the web contents again and verify that no more metrics got + // logged. + web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN); + web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); + histograms.ExpectUniqueSample( + "Stability.ChildFrameCrash.Visibility", + CrossProcessFrameConnector::CrashVisibility::kShownAfterCrashing, 10); +} + +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + ChildFrameCrashMetrics_NeverShown) { + // Set-up a frame tree that helps verify what the metrics tracks: + // 1) frames (12 frames are affected if B process gets killed) or + // 2) widgets (10 b widgets and 1 c widget are affected if B is killed) or + // 3) crashes (1 crash if B process gets killed)? + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b(b,c),b,b,b,b,b,b,b,b,b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + + // Hide the web contents (UpdateWebContentsVisibility is called twice to avoid + // hitting the |!did_first_set_visible_| case). + web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE); + web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN); + + // Kill the subframe. + base::HistogramTester histograms; + RenderProcessHost* child_process = + root->child_at(0)->current_frame_host()->GetProcess(); + RenderProcessHostWatcher crash_observer( + child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); + child_process->Shutdown(0); + crash_observer.Wait(); + + // Navigate away - this will trigger logging of the UMA. + EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank"))); + histograms.ExpectUniqueSample( + "Stability.ChildFrameCrash.Visibility", + CrossProcessFrameConnector::CrashVisibility::kNeverVisibleAfterCrash, 10); +} + +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + ChildFrameCrashMetrics_ScrolledIntoView) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + + // Fill the main frame so that the subframe is pushed below the fold (is + // scrolled outside of the current view) and wait until the main frame redraws + // itself (i.e. making sure CPFC::OnUpdateViewportIntersection has arrived). + std::string filling_script = R"( + var frame = document.body.querySelectorAll("iframe")[0]; + for (var i = 0; i < 100; i++) { + var p = document.createElement("p"); + p.innerText = "blah"; + document.body.insertBefore(p, frame); + } + )"; + EXPECT_TRUE(ExecuteScript(root, filling_script)); + MainThreadFrameObserver main_widget_observer( + root->current_frame_host()->GetRenderWidgetHost()); + main_widget_observer.Wait(); + + // Kill the child frame. + base::HistogramTester histograms; + RenderProcessHost* child_process = + root->child_at(0)->current_frame_host()->GetProcess(); + RenderProcessHostWatcher crash_observer( + child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); + child_process->Shutdown(0); + crash_observer.Wait(); + + // Verify that no child frame metrics got logged (yet - while the subframe is + // below the fold / is not scrolled into view). + histograms.ExpectTotalCount("Stability.ChildFrameCrash.Visibility", 0); + + // Scroll the subframe into view and wait until the scrolled frame draws + // itself. + std::string scrolling_script = R"( + var frame = document.body.querySelectorAll("iframe")[0]; + frame.scrollIntoView(); + )"; + EXPECT_TRUE(ExecuteScript(root, scrolling_script)); + main_widget_observer.Wait(); + + // Verify that the expected metrics got logged. + histograms.ExpectUniqueSample( + "Stability.ChildFrameCrash.Visibility", + CrossProcessFrameConnector::CrashVisibility::kShownAfterCrashing, 1); +} + +// Check that when a frame changes a subframe's size twice and then sends a +// postMessage to the subframe, the subframe's onmessage handler sees the new +// size. In particular, ensure that the postMessage won't get reordered with +// the second resize, which might be throttled if the first resize is still in +// progress. See https://crbug.com/828529. +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + ResizeAndCrossProcessPostMessagePreserveOrder) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + + // Add an onmessage handler to the subframe to send back its width. + EXPECT_TRUE(ExecuteScript(root->child_at(0), R"( + window.addEventListener('message', function(event) { + domAutomationController.send(document.body.clientWidth); + });)")); + + // Drop the visual properties ACKs from the child renderer. To do this, + // unsubscribe the child's RenderWidgetHost from its + // RenderFrameMetadataProvider, which ensures that + // DidUpdateVisualProperties() won't be called on it, and the ACK won't be + // reset. This simulates that the ACK for the first resize below does not + // arrive before the second resize IPC arrives from the + // parent, and that the second resize IPC early-exits in + // SynchronizeVisualProperties() due to the pending visual properties ACK. + RenderWidgetHostImpl* rwh = + root->child_at(0)->current_frame_host()->GetRenderWidgetHost(); + rwh->render_frame_metadata_provider_.RemoveObserver(rwh); + + // Now, resize the subframe twice from the main frame and send it a + // postMessage. The postMessage handler should see the second updated size. + int width = 0; + EXPECT_TRUE(ExecuteScriptAndExtractInt(root, R"( + var f = document.querySelector('iframe'); + f.width = 500; + f.offsetTop; // force layout; this sends a resize IPC for width of 500. + f.width = 700; + f.offsetTop; // force layout; this sends a resize IPC for width of 700. + f.contentWindow.postMessage('foo', '*');)", &width)); + EXPECT_EQ(width, 700); +} + +class SitePerProcessAndProcessPerSiteBrowserTest + : public SitePerProcessBrowserTest { + public: + SitePerProcessAndProcessPerSiteBrowserTest() {} + + protected: + void SetUpCommandLine(base::CommandLine* command_line) override { + SitePerProcessBrowserTest::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kProcessPerSite); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SitePerProcessAndProcessPerSiteBrowserTest); +}; + +// Verify that when --site-per-process is combined with --process-per-site, a +// cross-site, browser-initiated navigation with a generated page transition +// does not stay in the old SiteInstance. See https://crbug.com/825411. +IN_PROC_BROWSER_TEST_F(SitePerProcessAndProcessPerSiteBrowserTest, + GeneratedTransitionsSwapProcesses) { + EXPECT_TRUE(NavigateToURL( + shell(), embedded_test_server()->GetURL("foo.com", "/title1.html"))); + scoped_refptr foo_site_instance( + web_contents()->GetSiteInstance()); + + // Navigate cross-site via a generated transition. This would normally + // happen for search queries. + TestNavigationObserver observer(web_contents()); + NavigationController::LoadURLParams params( + embedded_test_server()->GetURL("bar.com", "/title2.html")); + params.transition_type = ui::PAGE_TRANSITION_GENERATED; + web_contents()->GetController().LoadURLWithParams(params); + observer.Wait(); + + // Ensure the original SiteInstance wasn't reused. + EXPECT_NE(foo_site_instance, web_contents()->GetSiteInstance()); + + // Ensure the new page can access cookies without getting killed. + EXPECT_TRUE(ExecuteScript(web_contents(), "document.cookie = 'foo=bar';")); + std::string cookie; + EXPECT_TRUE(ExecuteScriptAndExtractString( + web_contents(), "window.domAutomationController.send(document.cookie);", + &cookie)); + EXPECT_EQ("foo=bar", cookie); +} + +namespace { + +// Helper for waiting until next same-document navigation commits in +// |web_contents|. +class SameDocumentCommitObserver : public WebContentsObserver { + public: + SameDocumentCommitObserver(WebContents* web_contents) + : WebContentsObserver(web_contents), + message_loop_runner_(new MessageLoopRunner) { + EXPECT_TRUE(web_contents); + } + + void Wait() { message_loop_runner_->Run(); } + + const GURL& last_committed_url() { return last_committed_url_; } + + private: + void DidFinishNavigation(NavigationHandle* navigation_handle) override { + if (navigation_handle->IsSameDocument()) { + last_committed_url_ = navigation_handle->GetURL(); + message_loop_runner_->Quit(); + } + } + + GURL last_committed_url_; + scoped_refptr message_loop_runner_; + + DISALLOW_COPY_AND_ASSIGN(SameDocumentCommitObserver); +}; + +} // namespace + +// Ensure that a same-document navigation does not cancel an ongoing +// cross-process navigation. See https://crbug.com/825677. +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + ReplaceStateDoesNotCancelCrossSiteNavigation) { + GURL url(embedded_test_server()->GetURL("a.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + + // Give the page a beforeunload handler that does a replaceState. Do this + // from setTimeout so that the navigation that triggers beforeunload is + // already started when the replaceState happens. + EXPECT_TRUE(ExecuteScript(root, + "window.onbeforeunload = function (e) {" + " setTimeout(() => {" + " history.replaceState({}, 'footitle', 'foo');" + " }, 0);" + "};\n")); + + GURL url2 = embedded_test_server()->GetURL("b.com", "/title1.html"); + TestNavigationManager cross_site_navigation(web_contents(), url2); + SameDocumentCommitObserver replace_state_observer(web_contents()); + + // Start a cross-site navigation. Using a renderer-initiated navigation + // rather than a browser-initiated one is important here, since + // https://crbug.com/825677 was triggered only when replaceState ran while + // having a user gesture, which will be the case here since ExecuteScript + // runs with a user gesture. + EXPECT_TRUE(ExecuteScript(root, "location.href = '" + url2.spec() + "';")); + EXPECT_TRUE(cross_site_navigation.WaitForRequestStart()); + + // Now wait for the replaceState to commit while the cross-process navigation + // is paused. + replace_state_observer.Wait(); + GURL replace_state_url = embedded_test_server()->GetURL("a.com", "/foo"); + EXPECT_EQ(replace_state_url, replace_state_observer.last_committed_url()); + + // The cross-process navigation should not be canceled after the + // replaceState. + ASSERT_TRUE(root->IsLoading()); + ASSERT_TRUE(root->navigation_request()); + + // Resume and finish the cross-process navigation. + cross_site_navigation.ResumeNavigation(); + cross_site_navigation.WaitForNavigationFinished(); + EXPECT_TRUE(cross_site_navigation.was_successful()); + EXPECT_EQ(url2, web_contents()->GetLastCommittedURL()); +} + +// Test that a pending frame policy, such as an updated sandbox attribute, does +// not take effect after a same-document navigation. See +// https://crbug.com/849311. +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, + SameDocumentNavigationDoesNotCommitPendingFramePolicy) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + FrameTreeNode* subframe = root->child_at(0); + + // The subframe should not be sandboxed. + EXPECT_EQ(blink::WebSandboxFlags::kNone, + subframe->pending_frame_policy().sandbox_flags); + EXPECT_EQ(blink::WebSandboxFlags::kNone, + subframe->effective_frame_policy().sandbox_flags); + + // Set the "sandbox" attribute on the subframe; pending policy should update. + EXPECT_TRUE(ExecuteScript( + root, "document.querySelector('iframe').sandbox = 'allow-scripts';")); + // "allow-scripts" resets both SandboxFlags::Scripts and + // SandboxFlags::AutomaticFeatures bits per blink::ParseSandboxPolicy(). + blink::WebSandboxFlags expected_flags = + blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts & + ~blink::WebSandboxFlags::kAutomaticFeatures; + EXPECT_EQ(expected_flags, subframe->pending_frame_policy().sandbox_flags); + EXPECT_EQ(blink::WebSandboxFlags::kNone, + subframe->effective_frame_policy().sandbox_flags); + + // Commit a same-document navigation with replaceState. The new sandbox + // flags should still be pending but not effective. + SameDocumentCommitObserver replace_state_observer(web_contents()); + EXPECT_TRUE( + ExecuteScript(subframe, "history.replaceState({}, 'footitle', 'foo');")); + replace_state_observer.Wait(); + + EXPECT_EQ(expected_flags, subframe->pending_frame_policy().sandbox_flags); + EXPECT_EQ(blink::WebSandboxFlags::kNone, + subframe->effective_frame_policy().sandbox_flags); + + // Also try a same-document navigation to a fragment, which also shouldn't + // commit the pending sandbox flags. + GURL fragment_url = GURL(subframe->current_url().spec() + "#foo"); + { + SameDocumentCommitObserver fragment_observer(web_contents()); + EXPECT_TRUE(ExecuteScript( + subframe, "location.href=\"" + fragment_url.spec() + "\";")); + fragment_observer.Wait(); + EXPECT_EQ(fragment_url, subframe->current_url()); + } + + EXPECT_EQ(expected_flags, subframe->pending_frame_policy().sandbox_flags); + EXPECT_EQ(blink::WebSandboxFlags::kNone, + subframe->effective_frame_policy().sandbox_flags); +} + } // namespace content diff --git a/chromium/content/browser/site_per_process_hit_test_browsertest.cc b/chromium/content/browser/site_per_process_hit_test_browsertest.cc index f758815ac57..9884d624e54 100644 --- a/chromium/content/browser/site_per_process_hit_test_browsertest.cc +++ b/chromium/content/browser/site_per_process_hit_test_browsertest.cc @@ -16,6 +16,7 @@ #include "components/viz/common/features.h" #include "content/browser/renderer_host/cursor_manager.h" #include "content/browser/renderer_host/input/synthetic_tap_gesture.h" +#include "content/browser/renderer_host/input/touch_emulator.h" #include "content/browser/renderer_host/render_widget_host_input_event_router.h" #include "content/browser/renderer_host/render_widget_host_view_child_frame.h" #include "content/common/frame_messages.h" @@ -34,6 +35,7 @@ #include "ui/display/display_switches.h" #include "ui/events/base_event_utils.h" #include "ui/events/gesture_detection/gesture_configuration.h" +#include "ui/events/gesture_detection/gesture_provider_config_helper.h" #if defined(USE_AURA) #include "content/browser/renderer_host/render_widget_host_view_aura.h" @@ -469,6 +471,68 @@ bool ConvertJSONToRect(const std::string& str, gfx::Rect* rect) { } #endif // defined(USE_AURA) +// Class for intercepting SetMouseCapture messages being sent to a +// RenderWidgetHost. Note that this only works for RenderWidgetHosts that +// are attached to RenderFrameHosts, and not those for page popups, which +// use different bindings. +class SetMouseCaptureInterceptor + : public base::RefCountedThreadSafe, + public mojom::WidgetInputHandlerHostInterceptorForTesting { + public: + SetMouseCaptureInterceptor(RenderWidgetHostImpl* host) + : msg_received_(false), + capturing_(false), + host_(host), + impl_(binding().SwapImplForTesting(this)) {} + + bool Capturing() const { return capturing_; } + + void Wait() { + DCHECK(!run_loop_); + if (msg_received_) { + msg_received_ = false; + return; + } + run_loop_.reset(new base::RunLoop()); + run_loop_->Run(); + run_loop_.reset(); + msg_received_ = false; + } + + protected: + // mojom::WidgetInputHandlerHostInterceptorForTesting: + mojom::WidgetInputHandlerHost* GetForwardingInterface() override { + return impl_; + } + void SetMouseCapture(bool capturing) override { + capturing_ = capturing; + msg_received_ = true; + if (run_loop_) + run_loop_->Quit(); + GetForwardingInterface()->SetMouseCapture(capturing); + } + + private: + friend class base::RefCountedThreadSafe; + + ~SetMouseCaptureInterceptor() override { + binding().SwapImplForTesting(impl_); + } + + mojo::Binding& binding() { + return static_cast(host_->input_router()) + ->frame_host_binding_for_testing(); + } + + std::unique_ptr run_loop_; + bool msg_received_; + bool capturing_; + RenderWidgetHostImpl* host_; + mojom::WidgetInputHandlerHost* impl_; + + DISALLOW_COPY_AND_ASSIGN(SetMouseCaptureInterceptor); +}; + } // namespace class SitePerProcessHitTestBrowserTest @@ -485,6 +549,8 @@ class SitePerProcessHitTestBrowserTest } else if (std::get<0>(GetParam()) == 2) { feature_list_.InitAndEnableFeature( features::kEnableVizHitTestSurfaceLayer); + } else { + feature_list_.InitAndDisableFeature(features::kEnableVizHitTestDrawQuad); } } @@ -628,7 +694,7 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessInternalsHitTestBrowserTest, parent_iframe_node->current_frame_host() ->GetRenderWidgetHost() ->GetView(), - &nested_in_parent); + &nested_in_parent, viz::EventSource::MOUSE); // Get original scroll position. double div_scroll_top_start; @@ -993,6 +1059,157 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, root_scroll_begin_observer.Wait(); } +class SitePerProcessEmulatedTouchBrowserTest + : public SitePerProcessHitTestBrowserTest { + public: + enum TestType { ScrollBubbling, PinchGoesToMainFrame }; + + ~SitePerProcessEmulatedTouchBrowserTest() override {} + + void RunTest(TestType test_type) { + GURL main_url(embedded_test_server()->GetURL( + "/frame_tree/page_with_positioned_frame.html")); + ASSERT_TRUE(NavigateToURL(shell(), main_url)); + + // It is safe to obtain the root frame tree node here, as it doesn't change. + FrameTreeNode* root = web_contents()->GetFrameTree()->root(); + ASSERT_EQ(1U, root->child_count()); + + FrameTreeNode* iframe_node = root->child_at(0); + GURL site_url(embedded_test_server()->GetURL("baz.com", "/title1.html")); + EXPECT_EQ(site_url, iframe_node->current_url()); + + RenderWidgetHostViewBase* root_rwhv = + static_cast( + root->current_frame_host()->GetRenderWidgetHost()->GetView()); + RenderWidgetHostViewBase* child_rwhv = + static_cast(iframe_node->current_frame_host() + ->GetRenderWidgetHost() + ->GetView()); + + RenderWidgetHostInputEventRouter* router = + static_cast(shell()->web_contents()) + ->GetInputEventRouter(); + + WaitForChildFrameSurfaceReady(iframe_node->current_frame_host()); + + auto expect_gesture_with_position = base::BindRepeating( + [](blink::WebInputEvent::Type expected_type, + const gfx::Point& expected_position, content::InputEventAckSource, + content::InputEventAckState, const blink::WebInputEvent& event) { + if (event.GetType() != expected_type) + return false; + + const blink::WebGestureEvent& gesture_event = + static_cast(event); + EXPECT_NEAR(expected_position.x(), gesture_event.PositionInWidget().x, + 1); + EXPECT_NEAR(expected_position.y(), gesture_event.PositionInWidget().y, + 1); + EXPECT_EQ(blink::kWebGestureDeviceTouchscreen, + gesture_event.SourceDevice()); + return true; + }); + + blink::WebInputEvent::Type expected_gesture_type; + switch (test_type) { + case ScrollBubbling: + expected_gesture_type = blink::WebInputEvent::kGestureScrollBegin; + break; + case PinchGoesToMainFrame: + expected_gesture_type = blink::WebInputEvent::kGesturePinchBegin; + break; + default: + ASSERT_TRUE(false); + } + + gfx::Point position_in_child(5, 5); + InputEventAckWaiter child_gesture_event_observer( + child_rwhv->GetRenderWidgetHost(), + base::BindRepeating(expect_gesture_with_position, expected_gesture_type, + position_in_child)); + + gfx::Point position_in_root = + child_rwhv->TransformPointToRootCoordSpace(position_in_child); + InputEventAckWaiter root_gesture_event_observer( + root_rwhv->GetRenderWidgetHost(), + base::BindRepeating(expect_gesture_with_position, expected_gesture_type, + position_in_root)); + + // Enable touch emulation. + auto* touch_emulator = router->GetTouchEmulator(); + ASSERT_TRUE(touch_emulator); + touch_emulator->Enable(TouchEmulator::Mode::kEmulatingTouchFromMouse, + ui::GestureProviderConfigType::CURRENT_PLATFORM); + + // Create mouse events to emulate touch scroll. Since the page has no touch + // handlers, these events will be converted into a gesture scroll sequence. + base::TimeTicks simulated_event_time = ui::EventTimeForNow(); + base::TimeDelta simulated_event_time_delta = + base::TimeDelta::FromMilliseconds(100); + blink::WebMouseEvent mouse_move_event = + SyntheticWebMouseEventBuilder::Build(blink::WebInputEvent::kMouseMove, + position_in_root.x(), + position_in_root.y(), 0); + mouse_move_event.SetTimeStamp(simulated_event_time); + + int mouse_modifier = (test_type == PinchGoesToMainFrame) + ? blink::WebInputEvent::kShiftKey + : 0; + mouse_modifier |= blink::WebInputEvent::kLeftButtonDown; + blink::WebMouseEvent mouse_down_event = + SyntheticWebMouseEventBuilder::Build( + blink::WebInputEvent::kMouseDown, position_in_root.x(), + position_in_root.y(), mouse_modifier); + mouse_down_event.button = blink::WebMouseEvent::Button::kLeft; + simulated_event_time += simulated_event_time_delta; + mouse_down_event.SetTimeStamp(simulated_event_time); + + blink::WebMouseEvent mouse_drag_event = + SyntheticWebMouseEventBuilder::Build( + blink::WebInputEvent::kMouseMove, position_in_root.x(), + position_in_root.y() + 20, mouse_modifier); + simulated_event_time += simulated_event_time_delta; + mouse_drag_event.SetTimeStamp(simulated_event_time); + mouse_drag_event.button = blink::WebMouseEvent::Button::kLeft; + + blink::WebMouseEvent mouse_up_event = SyntheticWebMouseEventBuilder::Build( + blink::WebInputEvent::kMouseUp, position_in_root.x(), + position_in_root.y() + 20, mouse_modifier); + mouse_up_event.button = blink::WebMouseEvent::Button::kLeft; + simulated_event_time += simulated_event_time_delta; + mouse_up_event.SetTimeStamp(simulated_event_time); + + // Send mouse events and wait for GesturePinchBegin. + router->RouteMouseEvent(root_rwhv, &mouse_move_event, ui::LatencyInfo()); + router->RouteMouseEvent(root_rwhv, &mouse_down_event, ui::LatencyInfo()); + router->RouteMouseEvent(root_rwhv, &mouse_drag_event, ui::LatencyInfo()); + router->RouteMouseEvent(root_rwhv, &mouse_up_event, ui::LatencyInfo()); + + if (test_type == ScrollBubbling) { + // Verify child receives GestureScrollBegin. + child_gesture_event_observer.Wait(); + } + + // Verify the root receives the GesturePinchBegin or GestureScrollBegin, + // depending on |test_type|. + root_gesture_event_observer.Wait(); + + // Shut down. + touch_emulator->Disable(); + } +}; + +IN_PROC_BROWSER_TEST_P(SitePerProcessEmulatedTouchBrowserTest, + EmulatedTouchScrollBubbles) { + RunTest(ScrollBubbling); +} + +IN_PROC_BROWSER_TEST_P(SitePerProcessEmulatedTouchBrowserTest, + EmulatedTouchPinchGoesToMainFrame) { + RunTest(PinchGoesToMainFrame); +} + #if defined(USE_AURA) || defined(OS_ANDROID) // When unconsumed scrolls in a child bubble to the root and start an @@ -1001,6 +1218,14 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, // overscroll gesture. IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, RootConsumesScrollDuringOverscrollGesture) { +#if defined(OS_ANDROID) + // TODO(835058): Fix flakiness on android with viz hit testing. + if (features::IsVizHitTestingEnabled()) { + LOG(INFO) << "Skipping test due to https://crbug.com/835058"; + return; + } +#endif + GURL main_url(embedded_test_server()->GetURL( "a.com", "/cross_site_iframe_factory.html?a(b)")); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -1207,7 +1432,15 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, // results in a scroll. This is only handled by RenderWidgetHostViewAura // and is needed for trackpad scrolling on Chromebooks. #if defined(USE_AURA) -IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, ScrollEventToOOPIF) { + +#if defined(THREAD_SANITIZER) +// Flaky: https://crbug.com/833380 +#define MAYBE_ScrollEventToOOPIF DISABLED_ScrollEventToOOPIF +#else +#define MAYBE_ScrollEventToOOPIF ScrollEventToOOPIF +#endif +IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, + MAYBE_ScrollEventToOOPIF) { GURL main_url(embedded_test_server()->GetURL( "/frame_tree/page_with_positioned_frame.html")); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -1252,8 +1485,17 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, ScrollEventToOOPIF) { EXPECT_EQ(child_frame_monitor.EventType(), blink::WebInputEvent::kMouseWheel); } +#if defined(THREAD_SANITIZER) +// Flaky: https://crbug.com/833380 +#define MAYBE_InputEventRouterWheelCoalesceTest \ + DISABLED_InputEventRouterWheelCoalesceTest +#else +#define MAYBE_InputEventRouterWheelCoalesceTest \ + InputEventRouterWheelCoalesceTest +#endif + IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, - InputEventRouterWheelCoalesceTest) { + MAYBE_InputEventRouterWheelCoalesceTest) { GURL main_url(embedded_test_server()->GetURL( "/frame_tree/page_with_positioned_frame.html")); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -1351,13 +1593,19 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, } // Same test as above, but runs in high-dpi mode. +// NOTE: This has to be renamed from SurfaceHitTestTest to +// HighDPISurfaceHitTestTest. Otherwise MAYBE_SurfaceHitTestTest gets #defined +// twice. #if defined(OS_ANDROID) || defined(OS_WIN) // High DPI browser tests are not needed on Android, and confuse some of the // coordinate calculations. Android uses fixed device scale factor. // Windows is disabled because of https://crbug.com/545547. -#define MAYBE_HighDPISurfaceHitTestTest DISABLED_SurfaceHitTestTest +#define MAYBE_HighDPISurfaceHitTestTest DISABLED_HighDPISurfaceHitTestTest +#elif defined(THREAD_SANITIZER) +// Flaky: https://crbug.com/833380 +#define MAYBE_HighDPISurfaceHitTestTest DISABLED_HighDPISurfaceHitTestTest #else -#define MAYBE_HighDPISurfaceHitTestTest SurfaceHitTestTest +#define MAYBE_HighDPISurfaceHitTestTest HighDPISurfaceHitTestTest #endif IN_PROC_BROWSER_TEST_P(SitePerProcessHighDPIHitTestBrowserTest, MAYBE_HighDPISurfaceHitTestTest) { @@ -1381,27 +1629,46 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, OverlapSurfaceHitTestHelper(shell(), embedded_test_server()); } +#if defined(OS_LINUX) +// Flaky timeouts and failures: https://crbug.com/833380 +#define MAYBE_OverlapSurfaceHitTestTest DISABLED_OverlapSurfaceHitTestTest +#else +#define MAYBE_OverlapSurfaceHitTestTest OverlapSurfaceHitTestTest +#endif IN_PROC_BROWSER_TEST_P(SitePerProcessHighDPIHitTestBrowserTest, - OverlapSurfaceHitTestTest) { + MAYBE_OverlapSurfaceHitTestTest) { OverlapSurfaceHitTestHelper(shell(), embedded_test_server()); } +#if defined(OS_LINUX) +// Flaky timeouts and failures: https://crbug.com/833380 +#define MAYBE_HitTestLayerSquashing DISABLED_HitTestLayerSquashing +#else +#define MAYBE_HitTestLayerSquashing HitTestLayerSquashing +#endif IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, - HitTestLayerSquashing) { + MAYBE_HitTestLayerSquashing) { HitTestLayerSquashing(shell(), embedded_test_server()); } IN_PROC_BROWSER_TEST_P(SitePerProcessHighDPIHitTestBrowserTest, - HitTestLayerSquashing) { + MAYBE_HitTestLayerSquashing) { HitTestLayerSquashing(shell(), embedded_test_server()); } -IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, HitTestWatermark) { +#if defined(OS_LINUX) +// Flaky timeouts and failures: https://crbug.com/833380 +#define MAYBE_HitTestWatermark DISABLED_HitTestWatermark +#else +#define MAYBE_HitTestWatermark HitTestWatermark +#endif +IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, + MAYBE_HitTestWatermark) { HitTestWatermark(shell(), embedded_test_server()); } IN_PROC_BROWSER_TEST_P(SitePerProcessHighDPIHitTestBrowserTest, - HitTestWatermark) { + MAYBE_HitTestWatermark) { HitTestWatermark(shell(), embedded_test_server()); } @@ -1478,10 +1745,18 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, EXPECT_EQ(result.target_location.value(), parent_location); } +#if defined(THREAD_SANITIZER) +// Flaky: https://crbug.com/833380 +#define MAYBE_SurfaceHitTestPointerEventsNone \ + DISABLED_SurfaceHitTestPointerEventsNone +#else +#define MAYBE_SurfaceHitTestPointerEventsNone SurfaceHitTestPointerEventsNone +#endif + // This test tests that browser process hittesting ignores frames with // pointer-events: none. IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, - SurfaceHitTestPointerEventsNone) { + MAYBE_SurfaceHitTestPointerEventsNone) { GURL main_url(embedded_test_server()->GetURL( "/frame_tree/page_with_positioned_frame_pointer-events_none.html")); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -1718,6 +1993,109 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, } #endif // !defined(OS_ANDROID) +#if defined(OS_ANDROID) +// The following test ensures that we don't get a crash if a tooltip is +// triggered on Android. This test is nearly identical to +// SitePerProcessHitTestBrowserTest.CrossProcessTooltipTestAndroid, except +// it omits the tooltip monitor, and all dereferences of GetCursorManager(). +IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, + CrossProcessTooltipTestAndroid) { + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + FrameTreeNode* root = static_cast(shell()->web_contents()) + ->GetFrameTree() + ->root(); + + EXPECT_EQ( + " Site A ------------ proxies for B\n" + " +--Site B ------- proxies for A\n" + "Where A = http://a.com/\n" + " B = http://b.com/", + DepictFrameTree(root)); + + FrameTreeNode* b_node = root->child_at(0); + + RenderWidgetHostViewBase* rwhv_a = static_cast( + root->current_frame_host()->GetRenderWidgetHost()->GetView()); + RenderWidgetHostViewBase* rwhv_b = static_cast( + b_node->current_frame_host()->GetRenderWidgetHost()->GetView()); + + // On Android we don't expect GetCursorManager() to return anything other + // than nullptr. If it did, this test would be unnecessary. + DCHECK(!rwhv_a->GetCursorManager()); + + WaitForChildFrameSurfaceReady(b_node->current_frame_host()); + + // Make sure the point_in_a_frame value is outside the default 8px margin + // for the body element. + gfx::Point point_in_a_frame(10, 10); + gfx::Point point_in_b_frame = + rwhv_b->TransformPointToRootCoordSpace(gfx::Point(25, 25)); + + // Create listeners for mouse events. These are used to verify that the + // RenderWidgetHostInputEventRouter is generating MouseLeave, etc for + // the right renderers. + RenderWidgetHostMouseEventMonitor a_frame_monitor( + root->current_frame_host()->GetRenderWidgetHost()); + RenderWidgetHostMouseEventMonitor b_frame_monitor( + b_node->current_frame_host()->GetRenderWidgetHost()); + + // Add tooltip text to both the body and the iframe in A. + std::string script_a = + "body = document.body.setAttribute('title', 'body_a_tooltip');\n" + "iframe = document.getElementsByTagName('iframe')[0];\n" + "iframe.setAttribute('title','iframe_for_b');"; + EXPECT_TRUE(ExecuteScript(root->current_frame_host(), script_a)); + std::string script_b = + "body = document.body.setAttribute('title', 'body_b_tooltip');"; + EXPECT_TRUE(ExecuteScript(b_node->current_frame_host(), script_b)); + + // Send mouse events to both A and B. + blink::WebMouseEvent mouse_event( + blink::WebInputEvent::kMouseMove, blink::WebInputEvent::kNoModifiers, + blink::WebInputEvent::GetStaticTimeStampForTests()); + auto* router = web_contents()->GetInputEventRouter(); + + // Alternate mouse moves between main frame and the cross-process iframe to + // test that the tool tip in the iframe can override the one set by the main + // frame renderer, even on a second entry into the iframe. + gfx::Point current_point; + for (int iteration = 0; iteration < 2; ++iteration) { + // The following is a bit of a hack to prevent hitting the same + // position/node check in ChromeClient::SetToolTip(). + current_point = point_in_a_frame; + current_point.Offset(iteration, iteration); + SetWebEventPositions(&mouse_event, current_point, rwhv_a); + RouteMouseEventAndWaitUntilDispatch(router, rwhv_a, rwhv_a, &mouse_event); + EXPECT_TRUE(a_frame_monitor.EventWasReceived()); + a_frame_monitor.ResetEventReceived(); + // B will receive a mouseLeave on all but the first iteration. + EXPECT_EQ(iteration != 0, b_frame_monitor.EventWasReceived()); + b_frame_monitor.ResetEventReceived(); + + // Next send a MouseMove to B frame, and A should receive a MouseMove event. + current_point = point_in_b_frame; + current_point.Offset(iteration, iteration); + SetWebEventPositions(&mouse_event, current_point, rwhv_a); + RouteMouseEventAndWaitUntilDispatch(router, rwhv_a, rwhv_b, &mouse_event); + EXPECT_TRUE(a_frame_monitor.EventWasReceived()); + EXPECT_EQ(a_frame_monitor.event().GetType(), + blink::WebInputEvent::kMouseMove); + a_frame_monitor.ResetEventReceived(); + EXPECT_TRUE(b_frame_monitor.EventWasReceived()); + b_frame_monitor.ResetEventReceived(); + } + + // This is an (arbitrary) delay to allow the test to crash if it's going to. + base::RunLoop run_loop; + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, run_loop.QuitClosure(), TestTimeouts::action_max_timeout()); + run_loop.Run(); +} +#endif // defined(OS_ANDROID) + // This test verifies that MouseEnter and MouseLeave events fire correctly // when the mouse cursor moves between processes. IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, @@ -1833,13 +2211,22 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, EXPECT_TRUE(d_frame_monitor.EventWasReceived()); } +#if defined(OS_LINUX) +// Flaky timeouts and failures: https://crbug.com/833380 +#define MAYBE_CrossProcessMouseCapture DISABLED_CrossProcessMouseCapture +#else +#define MAYBE_CrossProcessMouseCapture CrossProcessMouseCapture +#endif // Verify that mouse capture works on a RenderWidgetHostView level, so that // dragging scroll bars and selecting text continues even when the mouse // cursor crosses over cross-process frame boundaries. +// TODO(kenrb): This currently only works for scrollbar dragging. +// Other reasons for a node to capture mouse input need to be addressed. See +// https://crbug.com/647378. IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, - CrossProcessMouseCapture) { + MAYBE_CrossProcessMouseCapture) { GURL main_url(embedded_test_server()->GetURL( - "/frame_tree/page_with_positioned_frame.html")); + "/frame_tree/page_with_large_scrollable_frame.html")); EXPECT_TRUE(NavigateToURL(shell(), main_url)); // It is safe to obtain the root frame tree node here, as it doesn't change. @@ -1847,10 +2234,12 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, ASSERT_EQ(1U, root->child_count()); FrameTreeNode* child_node = root->child_at(0); - GURL site_url(embedded_test_server()->GetURL("baz.com", "/title1.html")); - EXPECT_EQ(site_url, child_node->current_url()); - EXPECT_NE(shell()->web_contents()->GetSiteInstance(), - child_node->current_frame_host()->GetSiteInstance()); + ASSERT_EQ( + " Site A ------------ proxies for B\n" + " +--Site B ------- proxies for A\n" + "Where A = http://127.0.0.1/\n" + " B = http://baz.com/", + DepictFrameTree(root)); // Create listeners for mouse events. RenderWidgetHostMouseEventMonitor main_frame_monitor( @@ -1896,76 +2285,78 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, EXPECT_FALSE(main_frame_monitor.EventWasReceived()); EXPECT_TRUE(child_frame_monitor.EventWasReceived()); - // Target MouseMove to main frame. This should still be routed to the - // child frame because it is now capturing mouse input. + // Target MouseMove to main frame. This should be routed to the main frame + // because the child frame is not capturing input. mouse_event.SetType(blink::WebInputEvent::kMouseMove); mouse_event.SetModifiers(blink::WebInputEvent::kLeftButtonDown); SetWebEventPositions(&mouse_event, gfx::Point(1, 1), root_view); - // Note that this event is sent twice, with the monitors cleared after - // the first time, because the first MouseMove to the child frame - // causes a MouseMove to be sent to the main frame also, which we - // need to ignore. - router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo()); - main_frame_monitor.ResetEventReceived(); - child_frame_monitor.ResetEventReceived(); - SetWebEventPositions(&mouse_event, gfx::Point(1, 5), root_view); - RouteMouseEventAndWaitUntilDispatch(router, root_view, rwhv_child, + RouteMouseEventAndWaitUntilDispatch(router, root_view, root_view, &mouse_event); - EXPECT_FALSE(main_frame_monitor.EventWasReceived()); - EXPECT_TRUE(child_frame_monitor.EventWasReceived()); - - // A MouseUp to the child frame should cancel the mouse capture. - mouse_event.SetType(blink::WebInputEvent::kMouseUp); - mouse_event.SetModifiers(blink::WebInputEvent::kNoModifiers); - SetWebEventPositions(&mouse_event, - gfx::Point(child_frame_target_x, child_frame_target_y), - root_view); + // Dispatch twice because the router generates an extra MouseLeave for the + // child frame. main_frame_monitor.ResetEventReceived(); child_frame_monitor.ResetEventReceived(); - RouteMouseEventAndWaitUntilDispatch(router, root_view, rwhv_child, + RouteMouseEventAndWaitUntilDispatch(router, root_view, root_view, &mouse_event); - EXPECT_FALSE(main_frame_monitor.EventWasReceived()); - EXPECT_TRUE(child_frame_monitor.EventWasReceived()); - - // Subsequent MouseMove events targeted to the main frame should be routed - // to that frame. - mouse_event.SetType(blink::WebInputEvent::kMouseMove); - SetWebEventPositions(&mouse_event, gfx::Point(1, 10), root_view); - // Sending the MouseMove twice for the same reason as above. - router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo()); - main_frame_monitor.ResetEventReceived(); - child_frame_monitor.ResetEventReceived(); - SetWebEventPositions(&mouse_event, gfx::Point(1, 15), root_view); - router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo()); - EXPECT_TRUE(main_frame_monitor.EventWasReceived()); EXPECT_FALSE(child_frame_monitor.EventWasReceived()); - // Target MouseDown to the main frame to cause it to capture input. +// Targeting a scrollbar with a click doesn't work on Mac or Android. +#if !defined(OS_MACOSX) && !defined(OS_ANDROID) + scoped_refptr interceptor = + new SetMouseCaptureInterceptor(static_cast( + root->current_frame_host()->GetRenderWidgetHost())); + + // Now send a MouseDown to target the thumb part of the scroll bar, which + // should initiate mouse capture for the main frame. mouse_event.SetType(blink::WebInputEvent::kMouseDown); - SetWebEventPositions(&mouse_event, gfx::Point(1, 20), root_view); - main_frame_monitor.ResetEventReceived(); - child_frame_monitor.ResetEventReceived(); + SetWebEventPositions(&mouse_event, gfx::Point(100, 25), root_view); router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo()); EXPECT_TRUE(main_frame_monitor.EventWasReceived()); - EXPECT_FALSE(child_frame_monitor.EventWasReceived()); - // Sending a MouseMove to the child frame should still result in the main - // frame receiving the event. + // Wait for the mouse capture message. + interceptor->Wait(); + EXPECT_TRUE(interceptor->Capturing()); + + // Yield the thread, in order to let the capture message be processed by its + // actual handler. + { + base::RunLoop loop; + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + loop.QuitClosure()); + loop.Run(); + } + + main_frame_monitor.ResetEventReceived(); + child_frame_monitor.ResetEventReceived(); + + // Now that the main frame is capturing, a MouseMove targeted to the child + // frame should be received by the main frame. mouse_event.SetType(blink::WebInputEvent::kMouseMove); - mouse_event.SetModifiers(blink::WebInputEvent::kLeftButtonDown); SetWebEventPositions(&mouse_event, gfx::Point(child_frame_target_x, child_frame_target_y), root_view); + RouteMouseEventAndWaitUntilDispatch(router, root_view, root_view, + &mouse_event); + EXPECT_TRUE(main_frame_monitor.EventWasReceived()); + EXPECT_FALSE(child_frame_monitor.EventWasReceived()); main_frame_monitor.ResetEventReceived(); child_frame_monitor.ResetEventReceived(); - router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo()); - EXPECT_TRUE(main_frame_monitor.EventWasReceived()); - EXPECT_FALSE(child_frame_monitor.EventWasReceived()); + // A MouseUp sent anywhere should cancel the mouse capture. + mouse_event.SetType(blink::WebInputEvent::kMouseUp); + SetWebEventPositions(&mouse_event, + gfx::Point(child_frame_target_x, child_frame_target_y), + root_view); + RouteMouseEventAndWaitUntilDispatch(router, root_view, root_view, + &mouse_event); + + interceptor->Wait(); + EXPECT_FALSE(interceptor->Capturing()); +#endif // !defined(OS_MACOSX) && !defined(OS_ANDROID) } // There are no cursors on Android. @@ -2102,14 +2493,16 @@ void CursorUpdateReceivedFromCrossSiteIframeHelper( } // namespace +// Flaky: https://crbug.com/833380 IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, - CursorUpdateReceivedFromCrossSiteIframe) { + DISABLED_CursorUpdateReceivedFromCrossSiteIframe) { CursorUpdateReceivedFromCrossSiteIframeHelper(shell(), embedded_test_server()); } +// Flaky: https://crbug.com/833380 IN_PROC_BROWSER_TEST_P(SitePerProcessHighDPIHitTestBrowserTest, - CursorUpdateReceivedFromCrossSiteIframe) { + DISABLED_CursorUpdateReceivedFromCrossSiteIframe) { CursorUpdateReceivedFromCrossSiteIframeHelper(shell(), embedded_test_server()); } @@ -2784,8 +3177,9 @@ void SendTouchpadFlingSequenceWithExpectedTarget( ui::EventTimeForNow(), 0, 1, 0, 1, 0, 1); UpdateEventRootLocation(&fling_start, root_view_aura); TestInputEventObserver target_monitor(expected_target->GetRenderWidgetHost()); - InputEventAckWaiter waiter(expected_target->GetRenderWidgetHost(), - blink::WebInputEvent::kGestureFlingStart); + InputEventAckWaiter fling_start_waiter( + expected_target->GetRenderWidgetHost(), + blink::WebInputEvent::kGestureFlingStart); root_view_aura->OnScrollEvent(&fling_start); // If the expected target is not the root, then we should be doing async // targeting first. So event dispatch should not happen synchronously. @@ -2793,11 +3187,17 @@ void SendTouchpadFlingSequenceWithExpectedTarget( // such cases. if (root_view != expected_target) EXPECT_FALSE(target_monitor.EventWasReceived()); - waiter.Wait(); + fling_start_waiter.Wait(); EXPECT_TRUE(target_monitor.EventWasReceived()); EXPECT_EQ(expected_target, router_touchpad_gesture_target); target_monitor.ResetEventsReceived(); + // Send a GFC event, the fling_controller will process the GFC and stop the + // fling by generating a wheel event with phaseEnded. The + // mouse_wheel_event_queue will process the wheel event and generate a GSE. + InputEventAckWaiter gestrue_scroll_end_waiter( + expected_target->GetRenderWidgetHost(), + blink::WebInputEvent::kGestureScrollEnd); ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL, gesture_point, ui::EventTimeForNow(), 0, 1, 0, 1, 0, 1); UpdateEventRootLocation(&fling_cancel, root_view_aura); @@ -2806,24 +3206,22 @@ void SendTouchpadFlingSequenceWithExpectedTarget( EXPECT_TRUE(target_monitor.EventWasReceived()); EXPECT_EQ(target_monitor.EventType(), blink::WebInputEvent::kGestureFlingCancel); - - if (root_view_aura->wheel_scroll_latching_enabled()) { - blink::WebGestureEvent gesture_event( - blink::WebGestureEvent::kGestureScrollEnd, - blink::WebInputEvent::kNoModifiers, - blink::WebInputEvent::GetStaticTimeStampForTests(), - blink::kWebGestureDeviceTouchpad); - gesture_event.SetPositionInWidget(gfx::PointF(gesture_point)); - expected_target->GetRenderWidgetHost()->ForwardGestureEvent(gesture_event); - } + gestrue_scroll_end_waiter.Wait(); } #endif // !defined(OS_WIN) } // anonymous namespace // Flaky, see https://crbug.com/823578 +#if defined(OS_WIN) +#define MAYBE_InputEventRouterGestureTargetMapTest \ + DISABLED_InputEventRouterGestureTargetMapTest +#else +#define MAYBE_InputEventRouterGestureTargetMapTest \ + InputEventRouterGestureTargetMapTest +#endif IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, - DISABLED_InputEventRouterGestureTargetMapTest) { + MAYBE_InputEventRouterGestureTargetMapTest) { GURL main_url(embedded_test_server()->GetURL( "/frame_tree/page_with_positioned_nested_frames.html")); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -2996,6 +3394,14 @@ IN_PROC_BROWSER_TEST_P( IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, InputEventRouterTouchpadGestureTargetTest) { +#if defined(OS_WIN) + // TODO(838835): Flaky with viz hit testing + if (features::IsVizHitTestingEnabled()) { + LOG(INFO) << "Skipping test due to https://crbug.com/838835"; + return; + } +#endif + GURL main_url(embedded_test_server()->GetURL( "/frame_tree/page_with_positioned_nested_frames.html")); EXPECT_TRUE(NavigateToURL(shell(), main_url)); @@ -3166,25 +3572,29 @@ void CreateContextMenuTestHelper( EXPECT_NEAR(point.y(), params.y, 2); } +#if defined(OS_ANDROID) || defined(OS_WIN) +// High DPI tests don't work properly on Android, which has fixed scale factor. +// Windows is disabled because of https://crbug.com/545547. +#define MAYBE_CreateContextMenuTest DISABLED_CreateContextMenuTest +#elif defined(THREAD_SANITIZER) +// TSAN is flaky on both standard and High DPI: https://crbug.com/833380 +#define MAYBE_CreateContextMenuTest DISABLED_CreateContextMenuTest +#else +#define MAYBE_CreateContextMenuTest CreateContextMenuTest +#endif + // Test that a mouse right-click to an out-of-process iframe causes a context // menu to be generated with the correct screen position. IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, - CreateContextMenuTest) { + MAYBE_CreateContextMenuTest) { CreateContextMenuTestHelper(shell(), embedded_test_server()); } // Test that a mouse right-click to an out-of-process iframe causes a context // menu to be generated with the correct screen position on a screen with // non-default scale factor. -#if defined(OS_ANDROID) || defined(OS_WIN) -// High DPI tests don't work properly on Android, which has fixed scale factor. -// Windows is disabled because of https://crbug.com/545547. -#define MAYBE_HighDPICreateContextMenuTest DISABLED_HighDPICreateContextMenuTest -#else -#define MAYBE_HighDPICreateContextMenuTest HighDPICreateContextMenuTest -#endif IN_PROC_BROWSER_TEST_P(SitePerProcessHighDPIHitTestBrowserTest, - MAYBE_HighDPICreateContextMenuTest) { + MAYBE_CreateContextMenuTest) { CreateContextMenuTestHelper(shell(), embedded_test_server()); } @@ -3469,6 +3879,7 @@ class SitePerProcessGestureHitTestBrowserTest ui::ET_GESTURE_SCROLL_BEGIN); gesture_scroll_begin_details.set_device_type( ui::GestureDeviceType::DEVICE_TOUCHSCREEN); + gesture_scroll_begin_details.set_touch_points(2); ui::GestureEvent gesture_scroll_begin( position.x(), position.y(), 0, ui::EventTimeForNow(), gesture_scroll_begin_details, touch_pressed.unique_event_id()); @@ -3859,6 +4270,10 @@ INSTANTIATE_TEST_CASE_P(/* no prefix */, SitePerProcessNonIntegerScaleFactorHitTestBrowserTest, testing::Combine(testing::ValuesIn(kHitTestOption), testing::ValuesIn(kOneScale))); +INSTANTIATE_TEST_CASE_P(/* no prefix */, + SitePerProcessEmulatedTouchBrowserTest, + testing::Combine(testing::ValuesIn(kHitTestOption), + testing::ValuesIn(kOneScale))); #if defined(USE_AURA) static const float kMultiScale[] = {1.f, 1.5f, 2.f}; diff --git a/chromium/content/browser/speech/speech_recognition_browsertest.cc b/chromium/content/browser/speech/speech_recognition_browsertest.cc index fc367e3d119..90bf96296dc 100644 --- a/chromium/content/browser/speech/speech_recognition_browsertest.cc +++ b/chromium/content/browser/speech/speech_recognition_browsertest.cc @@ -32,20 +32,98 @@ #include "content/public/test/test_navigation_observer.h" #include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" -#include "media/audio/audio_system_impl.h" -#include "media/audio/audio_thread_impl.h" -#include "media/audio/mock_audio_manager.h" -#include "media/audio/test_audio_input_controller_factory.h" +#include "media/audio/audio_system.h" +#include "media/base/audio_capturer_source.h" #include "net/test/embedded_test_server/controllable_http_response.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using base::RunLoop; +using CaptureCallback = media::AudioCapturerSource::CaptureCallback; namespace content { namespace { +// TODO(https://crbug.com/841818) Use FakeSystemInfo instead. +class MockAudioSystem : public media::AudioSystem { + public: + MockAudioSystem() = default; + + // AudioSystem implementation. + void GetInputStreamParameters(const std::string& device_id, + OnAudioParamsCallback on_params_cb) override { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + // Posting callback to allow current SpeechRecognizerImpl dispatching event + // to complete before transitioning to the next FSM state. + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::BindOnce(std::move(on_params_cb), + media::AudioParameters::UnavailableDeviceParams())); + } + + MOCK_METHOD2(GetOutputStreamParameters, + void(const std::string& device_id, + OnAudioParamsCallback on_params_cb)); + MOCK_METHOD1(HasInputDevices, void(OnBoolCallback on_has_devices_cb)); + MOCK_METHOD1(HasOutputDevices, void(OnBoolCallback on_has_devices_cb)); + MOCK_METHOD2(GetDeviceDescriptions, + void(bool for_input, + OnDeviceDescriptionsCallback on_descriptions_cp)); + MOCK_METHOD2(GetAssociatedOutputDeviceID, + void(const std::string& input_device_id, + OnDeviceIdCallback on_device_id_cb)); + MOCK_METHOD2(GetInputDeviceInfo, + void(const std::string& input_device_id, + OnInputDeviceInfoCallback on_input_device_info_cb)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockAudioSystem); +}; + +class MockCapturerSource : public media::AudioCapturerSource { + public: + using StartCallback = + base::OnceCallback; + using StopCallback = base::OnceCallback; + + MockCapturerSource(StartCallback start_callback, StopCallback stop_callback) { + start_callback_ = std::move(start_callback); + stop_callback_ = std::move(stop_callback); + } + + void Initialize(const media::AudioParameters& params, + CaptureCallback* callback) { + audio_parameters_ = params; + capture_callback_ = callback; + } + + void Start() override { + std::move(start_callback_).Run(audio_parameters_, capture_callback_); + } + + void Stop() override { std::move(stop_callback_).Run(); } + + MOCK_METHOD1(SetAutomaticGainControl, void(bool enable)); + MOCK_METHOD1(SetVolume, void(double volume)); + MOCK_METHOD1(SetOutputDeviceForAec, + void(const std::string& output_device_id)); + + protected: + ~MockCapturerSource() override = default; + + private: + StartCallback start_callback_; + StopCallback stop_callback_; + CaptureCallback* capture_callback_; + media::AudioParameters audio_parameters_; + + DISALLOW_COPY_AND_ASSIGN(MockCapturerSource); +}; + std::string MakeGoodResponse() { proto::SpeechRecognitionEvent proto_event; proto_event.set_status(proto::SpeechRecognitionEvent::STATUS_SUCCESS); @@ -75,46 +153,14 @@ std::string MakeGoodResponse() { } // namespace -class SpeechRecognitionBrowserTest - : public ContentBrowserTest, - public media::TestAudioInputControllerDelegate { +class SpeechRecognitionBrowserTest : public ContentBrowserTest { public: enum StreamingServerState { kIdle, - kTestAudioControllerOpened, - kTestAudioControllerClosed, + kTestAudioCapturerSourceOpened, + kTestAudioCapturerSourceClosed, }; - // media::TestAudioInputControllerDelegate methods. - void TestAudioControllerOpened( - media::TestAudioInputController* controller) override { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - ASSERT_EQ(kIdle, streaming_server_state_); - streaming_server_state_ = kTestAudioControllerOpened; - const int capture_packet_interval_ms = - (1000 * controller->audio_parameters().frames_per_buffer()) / - controller->audio_parameters().sample_rate(); - ASSERT_EQ(SpeechRecognitionEngine::kAudioPacketIntervalMs, - capture_packet_interval_ms); - FeedAudioController(500 /* ms */, /*noise=*/ false); - FeedAudioController(1000 /* ms */, /*noise=*/ true); - FeedAudioController(1000 /* ms */, /*noise=*/ false); - } - - void TestAudioControllerClosed( - media::TestAudioInputController* controller) override { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - ASSERT_EQ(kTestAudioControllerOpened, streaming_server_state_); - streaming_server_state_ = kTestAudioControllerClosed; - - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::BindOnce(&SpeechRecognitionBrowserTest::SendResponse, - base::Unretained(this))); - } - - void SendResponse() {} - // Helper methods used by test fixtures. GURL GetTestUrlFromFragment(const std::string& fragment) { return GURL(GetTestUrl("speech", "web_speech_recognition.html").spec() + @@ -132,39 +178,67 @@ class SpeechRecognitionBrowserTest protected: // ContentBrowserTest methods. void SetUpOnMainThread() override { - test_audio_input_controller_factory_.set_delegate(this); - media::AudioInputController::set_factory_for_testing( - &test_audio_input_controller_factory_); streaming_server_state_ = kIdle; ASSERT_TRUE(SpeechRecognitionManagerImpl::GetInstance()); - media::AudioManager::StartHangMonitorIfNeeded( - BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)); - audio_manager_ = std::make_unique( - std::make_unique()); - audio_manager_->SetInputStreamParameters( - media::AudioParameters::UnavailableDeviceParams()); - audio_system_ = - std::make_unique(audio_manager_.get()); - SpeechRecognizerImpl::SetAudioEnvironmentForTesting(audio_system_.get(), - audio_manager_.get()); + audio_system_ = std::make_unique(); + audio_capturer_source_ = base::MakeRefCounted( + base::BindOnce(&SpeechRecognitionBrowserTest::OnCapturerSourceStart, + base::Unretained(this)), + base::BindOnce(&SpeechRecognitionBrowserTest::OnCapturerSourceStop, + base::Unretained(this))); + SpeechRecognizerImpl::SetAudioEnvironmentForTesting( + audio_system_.get(), + static_cast(audio_capturer_source_.get())); } void TearDownOnMainThread() override { SpeechRecognizerImpl::SetAudioEnvironmentForTesting(nullptr, nullptr); + } - audio_manager_->Shutdown(); + private: + void OnCapturerSourceStart(const media::AudioParameters& audio_parameters, + CaptureCallback* capture_callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + ASSERT_EQ(kIdle, streaming_server_state_); + streaming_server_state_ = kTestAudioCapturerSourceOpened; - test_audio_input_controller_factory_.set_delegate(nullptr); + const int capture_packet_interval_ms = + (1000 * audio_parameters.frames_per_buffer()) / + audio_parameters.sample_rate(); + ASSERT_EQ(SpeechRecognitionEngine::kAudioPacketIntervalMs, + capture_packet_interval_ms); + FeedAudioCapturerSource(audio_parameters, capture_callback, 500 /* ms */, + /*noise=*/false); + FeedAudioCapturerSource(audio_parameters, capture_callback, 1000 /* ms */, + /*noise=*/true); + FeedAudioCapturerSource(audio_parameters, capture_callback, 1000 /* ms */, + /*noise=*/false); } - private: - static void FeedSingleBufferToAudioController( - scoped_refptr controller, + void OnCapturerSourceStop() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + ASSERT_EQ(kTestAudioCapturerSourceOpened, streaming_server_state_); + streaming_server_state_ = kTestAudioCapturerSourceClosed; + + // Reset capturer source so SpeechRecognizerImpl destructor doesn't call + // AudioCaptureSourcer::Stop() again. + SpeechRecognizerImpl::SetAudioEnvironmentForTesting(nullptr, nullptr); + + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::BindOnce(&SpeechRecognitionBrowserTest::SendResponse, + base::Unretained(this))); + } + + void SendResponse() {} + + static void FeedSingleBufferToAudioCapturerSource( + const media::AudioParameters& audio_params, + CaptureCallback* capture_callback, size_t buffer_size, bool fill_with_noise) { - DCHECK(controller.get()); - const media::AudioParameters& audio_params = controller->audio_parameters(); + DCHECK(capture_callback); std::unique_ptr audio_buffer(new uint8_t[buffer_size]); if (fill_with_noise) { for (size_t i = 0; i < buffer_size; ++i) @@ -176,21 +250,19 @@ class SpeechRecognitionBrowserTest std::unique_ptr audio_bus = media::AudioBus::Create(audio_params); - audio_bus->FromInterleaved(&audio_buffer.get()[0], - audio_bus->frames(), - audio_params.bits_per_sample() / 8); - controller->sync_writer()->Write(audio_bus.get(), 0.0, false, - base::TimeTicks::Now()); + audio_bus->FromInterleaved( + reinterpret_cast(&audio_buffer.get()[0]), + audio_bus->frames()); + capture_callback->Capture(audio_bus.get(), 0, 0.0, false); } - void FeedAudioController(int duration_ms, bool feed_with_noise) { - media::TestAudioInputController* controller = - test_audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); - const media::AudioParameters& audio_params = controller->audio_parameters(); - const size_t buffer_size = audio_params.GetBytesPerBuffer(); - const int ms_per_buffer = audio_params.frames_per_buffer() * 1000 / - audio_params.sample_rate(); + void FeedAudioCapturerSource(const media::AudioParameters& audio_params, + CaptureCallback* capture_callback, + int duration_ms, + bool feed_with_noise) { + const size_t buffer_size = + audio_params.GetBytesPerBuffer(media::kSampleFormatS16); + const int ms_per_buffer = audio_params.GetBufferDuration().InMilliseconds(); // We can only simulate durations that are integer multiples of the // buffer size. In this regard see // SpeechRecognitionEngine::GetDesiredAudioChunkDurationMs(). @@ -200,18 +272,14 @@ class SpeechRecognitionBrowserTest for (int i = 0; i < n_buffers; ++i) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::BindOnce( - &FeedSingleBufferToAudioController, - scoped_refptr(controller), - buffer_size, feed_with_noise)); + base::BindOnce(&FeedSingleBufferToAudioCapturerSource, audio_params, + capture_callback, buffer_size, feed_with_noise)); } } - std::unique_ptr audio_manager_; std::unique_ptr audio_system_; + scoped_refptr audio_capturer_source_; StreamingServerState streaming_server_state_; - - media::TestAudioInputControllerFactory test_audio_input_controller_factory_; }; // Simply loads the test page and checks if it was able to create a Speech @@ -274,7 +342,7 @@ IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, MAYBE_OneShotRecognition) { navigation_observer.Wait(); - EXPECT_EQ(kTestAudioControllerClosed, streaming_server_state()); + EXPECT_EQ(kTestAudioCapturerSourceClosed, streaming_server_state()); EXPECT_EQ("goodresult1", GetPageFragment()); // Remove reference to URL string that's on the stack. diff --git a/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc b/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc index 1e34625f16b..410717dee0f 100644 --- a/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc +++ b/chromium/content/browser/speech/speech_recognition_dispatcher_host.cc @@ -15,88 +15,85 @@ #include "content/browser/frame_host/render_frame_host_manager.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/browser_context.h" #include "content/public/browser/render_frame_host.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" #include "content/public/common/content_switches.h" +#include "mojo/public/cpp/bindings/strong_binding.h" namespace content { SpeechRecognitionDispatcherHost::SpeechRecognitionDispatcherHost( int render_process_id, - net::URLRequestContextGetter* context_getter) - : BrowserMessageFilter(SpeechRecognitionMsgStart), - render_process_id_(render_process_id), - context_getter_(context_getter), + int render_frame_id, + scoped_refptr context_getter) + : render_process_id_(render_process_id), + render_frame_id_(render_frame_id), + context_getter_(std::move(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. } -SpeechRecognitionDispatcherHost::~SpeechRecognitionDispatcherHost() { +// static +void SpeechRecognitionDispatcherHost::Create( + int render_process_id, + int render_frame_id, + scoped_refptr context_getter, + mojom::SpeechRecognizerRequest request) { + mojo::MakeStrongBinding( + std::make_unique( + render_process_id, render_frame_id, std::move(context_getter)), + std::move(request)); } +SpeechRecognitionDispatcherHost::~SpeechRecognitionDispatcherHost() {} + base::WeakPtr SpeechRecognitionDispatcherHost::AsWeakPtr() { return weak_factory_.GetWeakPtr(); } -void SpeechRecognitionDispatcherHost::OnDestruct() const { - BrowserThread::DeleteOnIOThread::Destruct(this); -} - -bool SpeechRecognitionDispatcherHost::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - 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; -} - -void SpeechRecognitionDispatcherHost::OverrideThreadForMessage( - const IPC::Message& message, - BrowserThread::ID* thread) { - if (message.type() == SpeechRecognitionHostMsg_StartRequest::ID) - *thread = BrowserThread::UI; -} +// -------- mojom::SpeechRecognizer interface implementation ------------------ -void SpeechRecognitionDispatcherHost::OnChannelClosing() { - weak_factory_.InvalidateWeakPtrs(); -} - -void SpeechRecognitionDispatcherHost::OnStartRequest( - const SpeechRecognitionHostMsg_StartRequest_Params& params) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); +void SpeechRecognitionDispatcherHost::Start( + mojom::StartSpeechRecognitionRequestParamsPtr params) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); // Check that the origin specified by the renderer process is one // that it is allowed to access. - if (params.origin_url != "null" && + if (!params->origin.unique() && !ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( - render_process_id_, GURL(params.origin_url))) { + render_process_id_, params->origin.GetURL())) { LOG(ERROR) << "SRDH::OnStartRequest, disallowed origin: " - << params.origin_url; + << params->origin.Serialize(); return; } + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&SpeechRecognitionDispatcherHost::StartRequestOnUI, + AsWeakPtr(), render_process_id_, render_frame_id_, + std::move(params))); +} + +// static +void SpeechRecognitionDispatcherHost::StartRequestOnUI( + base::WeakPtr + speech_recognition_dispatcher_host, + int render_process_id, + int render_frame_id, + mojom::StartSpeechRecognitionRequestParamsPtr params) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); int embedder_render_process_id = 0; int embedder_render_frame_id = MSG_ROUTING_NONE; WebContentsImpl* web_contents = static_cast(WebContentsImpl::FromRenderFrameHostID( - render_process_id_, params.render_frame_id)); + render_process_id, render_frame_id)); if (!web_contents) { // The render frame id is renderer-provided. If it's invalid, don't crash. DLOG(ERROR) << "SRDH::OnStartRequest, invalid frame"; @@ -132,153 +129,127 @@ void SpeechRecognitionDispatcherHost::OnStartRequest( bool filter_profanities = SpeechRecognitionManagerImpl::GetInstance() && SpeechRecognitionManagerImpl::GetInstance()->delegate() && - SpeechRecognitionManagerImpl::GetInstance()->delegate()-> - FilterProfanities(render_process_id_); - - SpeechRecognitionSessionContext context; - context.context_name = params.origin_url; - context.render_process_id = render_process_id_; - context.render_frame_id = params.render_frame_id; - context.embedder_render_process_id = embedder_render_process_id; - context.embedder_render_frame_id = embedder_render_frame_id; - context.request_id = params.request_id; + SpeechRecognitionManagerImpl::GetInstance() + ->delegate() + ->FilterProfanities(embedder_render_process_id); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::BindOnce(&SpeechRecognitionDispatcherHost::StartSession, - base::Unretained(this), params, context, + base::BindOnce(&SpeechRecognitionDispatcherHost::StartSessionOnIO, + speech_recognition_dispatcher_host, std::move(params), + embedder_render_process_id, embedder_render_frame_id, filter_profanities)); } -void SpeechRecognitionDispatcherHost::StartSession( - const SpeechRecognitionHostMsg_StartRequest_Params& params, - const SpeechRecognitionSessionContext& context, +void SpeechRecognitionDispatcherHost::StartSessionOnIO( + mojom::StartSpeechRecognitionRequestParamsPtr params, + int embedder_render_process_id, + int embedder_render_frame_id, bool filter_profanities) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + SpeechRecognitionSessionContext context; + context.security_origin = params->origin; + context.render_process_id = render_process_id_; + context.render_frame_id = render_frame_id_; + context.embedder_render_process_id = embedder_render_process_id; + context.embedder_render_frame_id = embedder_render_frame_id; + + auto session = + std::make_unique(std::move(params->client)); + SpeechRecognitionSessionConfig config; - config.language = params.language; - config.grammars = params.grammars; - config.max_hypotheses = params.max_hypotheses; - config.origin_url = params.origin_url; + config.language = params->language; + config.max_hypotheses = params->max_hypotheses; + config.origin = params->origin; config.initial_context = context; config.url_request_context_getter = context_getter_.get(); config.filter_profanities = filter_profanities; - config.continuous = params.continuous; - config.interim_results = params.interim_results; - config.event_listener = AsWeakPtr(); + config.continuous = params->continuous; + config.interim_results = params->interim_results; + config.event_listener = session->AsWeakPtr(); - int session_id = SpeechRecognitionManager::GetInstance()->CreateSession( - config); + for (mojom::SpeechRecognitionGrammarPtr& grammar_ptr : params->grammars) { + config.grammars.push_back(*grammar_ptr); + } + + int session_id = + SpeechRecognitionManager::GetInstance()->CreateSession(config); DCHECK_NE(session_id, SpeechRecognitionManager::kSessionIDInvalid); + session->SetSessionId(session_id); + mojo::MakeStrongBinding(std::move(session), + std::move(params->session_request)); + SpeechRecognitionManager::GetInstance()->StartSession(session_id); } -void SpeechRecognitionDispatcherHost::OnAbortRequest(int render_frame_id, - int request_id) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); +// ---------------------- SpeechRecognizerSession ----------------------------- - int session_id = SpeechRecognitionManager::GetInstance()->GetSession( - render_process_id_, render_frame_id, request_id); +SpeechRecognitionSession::SpeechRecognitionSession( + mojom::SpeechRecognitionSessionClientPtrInfo client_ptr_info) + : session_id_(SpeechRecognitionManager::kSessionIDInvalid), + client_(std::move(client_ptr_info)), + weak_factory_(this) {} - // 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); -} +SpeechRecognitionSession::~SpeechRecognitionSession() = default; -void SpeechRecognitionDispatcherHost::OnAbortAllRequests(int render_frame_id) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderFrame( - render_process_id_, render_frame_id); +base::WeakPtr SpeechRecognitionSession::AsWeakPtr() { + return weak_factory_.GetWeakPtr(); } -void SpeechRecognitionDispatcherHost::OnStopCaptureRequest(int render_frame_id, - int request_id) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - int session_id = SpeechRecognitionManager::GetInstance()->GetSession( - render_process_id_, render_frame_id, request_id); +void SpeechRecognitionSession::Abort() { + SpeechRecognitionManager::GetInstance()->AbortSession(session_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); - } +void SpeechRecognitionSession::StopCapture() { + SpeechRecognitionManager::GetInstance()->StopAudioCaptureForSession( + session_id_); } // -------- SpeechRecognitionEventListener interface implementation ----------- -void SpeechRecognitionDispatcherHost::OnRecognitionStart(int session_id) { - const SpeechRecognitionSessionContext& context = - SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - Send(new SpeechRecognitionMsg_Started(context.render_frame_id, - context.request_id)); +void SpeechRecognitionSession::OnRecognitionStart(int session_id) { + client_->Started(); } -void SpeechRecognitionDispatcherHost::OnAudioStart(int session_id) { - const SpeechRecognitionSessionContext& context = - SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - Send(new SpeechRecognitionMsg_AudioStarted(context.render_frame_id, - context.request_id)); +void SpeechRecognitionSession::OnAudioStart(int session_id) { + client_->AudioStarted(); } -void SpeechRecognitionDispatcherHost::OnSoundStart(int session_id) { - const SpeechRecognitionSessionContext& context = - SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - Send(new SpeechRecognitionMsg_SoundStarted(context.render_frame_id, - context.request_id)); +void SpeechRecognitionSession::OnSoundStart(int session_id) { + client_->SoundStarted(); } -void SpeechRecognitionDispatcherHost::OnSoundEnd(int session_id) { - const SpeechRecognitionSessionContext& context = - SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - Send(new SpeechRecognitionMsg_SoundEnded(context.render_frame_id, - context.request_id)); +void SpeechRecognitionSession::OnSoundEnd(int session_id) { + client_->SoundEnded(); } -void SpeechRecognitionDispatcherHost::OnAudioEnd(int session_id) { - const SpeechRecognitionSessionContext& context = - SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - Send(new SpeechRecognitionMsg_AudioEnded(context.render_frame_id, - context.request_id)); +void SpeechRecognitionSession::OnAudioEnd(int session_id) { + client_->AudioEnded(); } -void SpeechRecognitionDispatcherHost::OnRecognitionEnd(int session_id) { - const SpeechRecognitionSessionContext& context = - SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - Send(new SpeechRecognitionMsg_Ended(context.render_frame_id, - context.request_id)); +void SpeechRecognitionSession::OnRecognitionEnd(int session_id) { + client_->Ended(); } -void SpeechRecognitionDispatcherHost::OnRecognitionResults( +void SpeechRecognitionSession::OnRecognitionResults( int session_id, const SpeechRecognitionResults& results) { - const SpeechRecognitionSessionContext& context = - SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - Send(new SpeechRecognitionMsg_ResultRetrieved(context.render_frame_id, - context.request_id, results)); + client_->ResultRetrieved(results); } -void SpeechRecognitionDispatcherHost::OnRecognitionError( +void SpeechRecognitionSession::OnRecognitionError( int session_id, const SpeechRecognitionError& error) { - const SpeechRecognitionSessionContext& context = - SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); - Send(new SpeechRecognitionMsg_ErrorOccurred(context.render_frame_id, - context.request_id, error)); + client_->ErrorOccurred(error); } // The events below are currently not used by speech JS APIs implementation. -void SpeechRecognitionDispatcherHost::OnAudioLevelsChange(int session_id, - float volume, - float noise_volume) { -} +void SpeechRecognitionSession::OnAudioLevelsChange(int session_id, + float volume, + float noise_volume) {} -void SpeechRecognitionDispatcherHost::OnEnvironmentEstimationComplete( - int session_id) { +void SpeechRecognitionSession::OnEnvironmentEstimationComplete(int session_id) { } } // namespace content diff --git a/chromium/content/browser/speech/speech_recognition_dispatcher_host.h b/chromium/content/browser/speech/speech_recognition_dispatcher_host.h index babf35dc9b4..bce40c3ad6d 100644 --- a/chromium/content/browser/speech/speech_recognition_dispatcher_host.h +++ b/chromium/content/browser/speech/speech_recognition_dispatcher_host.h @@ -10,32 +10,80 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" +#include "content/common/speech_recognizer.mojom.h" #include "content/public/browser/browser_message_filter.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/speech_recognition_event_listener.h" +#include "mojo/public/cpp/bindings/binding.h" #include "net/url_request/url_request_context_getter.h" -struct SpeechRecognitionHostMsg_StartRequest_Params; - namespace content { +class SpeechRecognitionSession; class SpeechRecognitionManager; -struct SpeechRecognitionSessionContext; -// SpeechRecognitionDispatcherHost 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 RenderFrame). +// SpeechRecognitionDispatcherHost is an implementation of the SpeechRecognizer +// interface that allows a RenderFrame to start a speech recognition session +// in the browser process, by communicating with SpeechRecognitionManager. class CONTENT_EXPORT SpeechRecognitionDispatcherHost - : public BrowserMessageFilter, - public SpeechRecognitionEventListener { + : public mojom::SpeechRecognizer { public: SpeechRecognitionDispatcherHost( int render_process_id, - net::URLRequestContextGetter* context_getter); - + int render_frame_id, + scoped_refptr context_getter); + ~SpeechRecognitionDispatcherHost() override; + static void Create(int render_process_id, + int render_frame_id, + scoped_refptr context_getter, + mojom::SpeechRecognizerRequest request); base::WeakPtr AsWeakPtr(); + // mojom::SpeechRecognizer implementation + void Start(mojom::StartSpeechRecognitionRequestParamsPtr params) override; + + private: + static void StartRequestOnUI( + base::WeakPtr + speech_recognition_dispatcher_host, + int render_process_id, + int render_frame_id, + mojom::StartSpeechRecognitionRequestParamsPtr params); + void StartSessionOnIO(mojom::StartSpeechRecognitionRequestParamsPtr params, + int embedder_render_process_id, + int embedder_render_frame_id, + bool filter_profanities); + + const int render_process_id_; + const int render_frame_id_; + scoped_refptr 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 weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionDispatcherHost); +}; + +// SpeechRecognitionSession implements the mojom::SpeechRecognitionSession +// interface for a particular session. It also acts as a proxy for events sent +// from SpeechRecognitionManager, and forwards the events to the renderer using +// a SpeechRecognitionSessionClientPtr (that is passed from the render process). +class SpeechRecognitionSession : public mojom::SpeechRecognitionSession, + public SpeechRecognitionEventListener { + public: + explicit SpeechRecognitionSession( + mojom::SpeechRecognitionSessionClientPtrInfo client_ptr_info); + ~SpeechRecognitionSession() override; + base::WeakPtr AsWeakPtr(); + + void SetSessionId(int session_id) { session_id_ = session_id; } + + // mojom::SpeechRecognitionSession implementation. + void Abort() override; + void StopCapture() override; + // SpeechRecognitionEventListener methods. void OnRecognitionStart(int session_id) override; void OnAudioStart(int session_id) override; @@ -52,38 +100,11 @@ class CONTENT_EXPORT SpeechRecognitionDispatcherHost float volume, float noise_volume) override; - // BrowserMessageFilter implementation. - void OnDestruct() const override; - bool OnMessageReceived(const IPC::Message& message) override; - void OverrideThreadForMessage(const IPC::Message& message, - BrowserThread::ID* thread) override; - - void OnChannelClosing() override; - private: - friend class base::DeleteHelper; - friend class BrowserThread; - - ~SpeechRecognitionDispatcherHost() override; - - void OnStartRequest( - const SpeechRecognitionHostMsg_StartRequest_Params& params); - void StartSession(const SpeechRecognitionHostMsg_StartRequest_Params& params, - const SpeechRecognitionSessionContext& context, - bool filter_profanities); - void OnAbortRequest(int render_frame_id, int request_id); - void OnStopCaptureRequest(int render_frame_id, int request_id); - void OnAbortAllRequests(int render_frame_id); + int session_id_; + mojom::SpeechRecognitionSessionClientPtr client_; - int render_process_id_; - scoped_refptr 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 weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionDispatcherHost); + base::WeakPtrFactory weak_factory_; }; } // namespace content diff --git a/chromium/content/browser/speech/speech_recognition_engine.cc b/chromium/content/browser/speech/speech_recognition_engine.cc index 856f9a1df55..e05de0f8484 100644 --- a/chromium/content/browser/speech/speech_recognition_engine.cc +++ b/chromium/content/browser/speech/speech_recognition_engine.cc @@ -418,9 +418,9 @@ SpeechRecognitionEngine::ConnectBothStreams(const FSMEventArgs&) { base::UintToString(max_alternatives)); } upstream_args.push_back("app=chromium"); - for (const SpeechRecognitionGrammar& grammar : config_.grammars) { + for (const mojom::SpeechRecognitionGrammar& grammar : config_.grammars) { std::string grammar_value(base::NumberToString(grammar.weight) + ":" + - grammar.url); + grammar.url.spec()); upstream_args.push_back( "grammar=" + net::EscapeQueryParamValue(grammar_value, true)); } diff --git a/chromium/content/browser/speech/speech_recognition_engine.h b/chromium/content/browser/speech/speech_recognition_engine.h index 6ac7f2528b4..8a28cb6f86a 100644 --- a/chromium/content/browser/speech/speech_recognition_engine.h +++ b/chromium/content/browser/speech/speech_recognition_engine.h @@ -18,7 +18,7 @@ #include "content/common/content_export.h" #include "content/public/browser/speech_recognition_session_preamble.h" #include "content/public/common/speech_recognition_error.h" -#include "content/public/common/speech_recognition_grammar.h" +#include "content/public/common/speech_recognition_grammar.mojom.h" #include "content/public/common/speech_recognition_result.h" #include "net/url_request/url_fetcher_delegate.h" @@ -78,7 +78,7 @@ class CONTENT_EXPORT SpeechRecognitionEngine : public net::URLFetcherDelegate { ~Config(); std::string language; - SpeechRecognitionGrammarArray grammars; + std::vector grammars; bool filter_profanities; bool continuous; bool interim_results; diff --git a/chromium/content/browser/speech/speech_recognition_manager_impl.cc b/chromium/content/browser/speech/speech_recognition_manager_impl.cc index b197c9c5535..e04810d1d03 100644 --- a/chromium/content/browser/speech/speech_recognition_manager_impl.cc +++ b/chromium/content/browser/speech/speech_recognition_manager_impl.cc @@ -218,10 +218,8 @@ SpeechRecognitionManagerImpl* SpeechRecognitionManagerImpl::GetInstance() { SpeechRecognitionManagerImpl::SpeechRecognitionManagerImpl( media::AudioSystem* audio_system, - media::AudioManager* audio_manager, MediaStreamManager* media_stream_manager) : audio_system_(audio_system), - audio_manager_(audio_manager), media_stream_manager_(media_stream_manager), primary_session_id_(kSessionIDInvalid), last_session_id_(kSessionIDInvalid), @@ -277,7 +275,7 @@ int SpeechRecognitionManagerImpl::CreateSession( remote_engine_config.continuous = config.continuous; remote_engine_config.interim_results = config.interim_results; remote_engine_config.max_hypotheses = config.max_hypotheses; - remote_engine_config.origin_url = config.origin_url; + remote_engine_config.origin_url = config.origin.Serialize(); remote_engine_config.auth_token = config.auth_token; remote_engine_config.auth_scope = config.auth_scope; remote_engine_config.preamble = config.preamble; @@ -287,7 +285,7 @@ int SpeechRecognitionManagerImpl::CreateSession( google_remote_engine->SetConfig(remote_engine_config); session->recognizer = new SpeechRecognizerImpl( - this, audio_system_, audio_manager_, session_id, config.continuous, + this, audio_system_, session_id, config.continuous, config.interim_results, google_remote_engine); #else session->recognizer = new SpeechRecognizerImplAndroid(this, session_id); @@ -348,9 +346,8 @@ void SpeechRecognitionManagerImpl::RecognitionAllowedCallback(int session_id, if (ask_user) { SpeechRecognitionSessionContext& context = session->context; context.label = media_stream_manager_->MakeMediaAccessRequest( - context.render_process_id, context.render_frame_id, context.request_id, - StreamControls(true, false), - url::Origin::Create(GURL(context.context_name)), + context.render_process_id, context.render_frame_id, session_id, + StreamControls(true, false), context.security_origin, base::BindOnce( &SpeechRecognitionManagerImpl::MediaRequestPermissionCallback, weak_factory_.GetWeakPtr(), session_id)); @@ -592,26 +589,6 @@ void SpeechRecognitionManagerImpl::OnRecognitionEnd(int session_id) { EVENT_RECOGNITION_ENDED)); } -int SpeechRecognitionManagerImpl::GetSession(int render_process_id, - int render_frame_id, - int request_id) const { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - auto iter = std::find_if( - sessions_.begin(), sessions_.end(), - [render_process_id, render_frame_id, request_id]( - const std::pair>& session_pair) { - const SpeechRecognitionSessionContext& context = - session_pair.second->context; - return context.render_process_id == render_process_id && - context.render_frame_id == render_frame_id && - context.request_id == request_id; - }); - if (iter == sessions_.end()) - return kSessionIDInvalid; - - return iter->first; -} - SpeechRecognitionSessionContext SpeechRecognitionManagerImpl::GetSessionContext(int session_id) const { return GetSession(session_id)->context; diff --git a/chromium/content/browser/speech/speech_recognition_manager_impl.h b/chromium/content/browser/speech/speech_recognition_manager_impl.h index 89a8f048a54..cf4468d3bef 100644 --- a/chromium/content/browser/speech/speech_recognition_manager_impl.h +++ b/chromium/content/browser/speech/speech_recognition_manager_impl.h @@ -20,7 +20,6 @@ namespace media { class AudioSystem; -class AudioManager; } namespace content { @@ -68,9 +67,6 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl int session_id) const override; SpeechRecognitionSessionContext GetSessionContext( int session_id) const override; - int GetSession(int render_process_id, - int render_frame_id, - int request_id) const override; // SpeechRecognitionEventListener methods. void OnRecognitionStart(int session_id) override; @@ -99,7 +95,6 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl friend class base::DeleteHelper; SpeechRecognitionManagerImpl(media::AudioSystem* audio_system, - media::AudioManager* audio_manager, MediaStreamManager* media_stream_manager); ~SpeechRecognitionManagerImpl() override; @@ -183,7 +178,6 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl frame_deletion_observer_; media::AudioSystem* audio_system_; - media::AudioManager* audio_manager_; MediaStreamManager* media_stream_manager_; base::flat_map> sessions_; int primary_session_id_; diff --git a/chromium/content/browser/speech/speech_recognizer_impl.cc b/chromium/content/browser/speech/speech_recognizer_impl.cc index 7cb388ad30a..e05b95b8d48 100644 --- a/chromium/content/browser/speech/speech_recognizer_impl.cc +++ b/chromium/content/browser/speech/speech_recognizer_impl.cc @@ -14,12 +14,16 @@ #include "build/build_config.h" #include "content/browser/browser_main_loop.h" #include "content/browser/media/media_internals.h" +#include "content/browser/service_manager/service_manager_context.h" #include "content/browser/speech/audio_buffer.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/speech_recognition_event_listener.h" -#include "media/audio/audio_manager.h" #include "media/audio/audio_system.h" #include "media/base/audio_converter.h" +#include "media/mojo/interfaces/audio_logging.mojom.h" +#include "services/audio/public/cpp/audio_system_factory.h" +#include "services/audio/public/cpp/device_factory.h" +#include "services/service_manager/public/mojom/connector.mojom.h" #if defined(OS_WIN) #include "media/audio/win/core_audio_util_win.h" @@ -27,8 +31,6 @@ using media::AudioBus; using media::AudioConverter; -using media::AudioInputController; -using media::AudioManager; using media::AudioParameters; using media::ChannelLayout; @@ -103,9 +105,6 @@ bool DetectClipping(const AudioChunk& chunk) { return false; } -void KeepAudioControllerRefcountedForDtor(scoped_refptr) { -} - } // namespace const int SpeechRecognizerImpl::kAudioSampleRate = 16000; @@ -115,10 +114,8 @@ const int SpeechRecognizerImpl::kNumBitsPerAudioSample = 16; const int SpeechRecognizerImpl::kNoSpeechTimeoutMs = 8000; const int SpeechRecognizerImpl::kEndpointerEstimationTimeMs = 300; media::AudioSystem* SpeechRecognizerImpl::audio_system_for_tests_ = nullptr; -media::AudioManager* SpeechRecognizerImpl::audio_manager_for_tests_ = nullptr; - -static_assert(SpeechRecognizerImpl::kNumBitsPerAudioSample % 8 == 0, - "kNumBitsPerAudioSample must be a multiple of 8"); +media::AudioCapturerSource* + SpeechRecognizerImpl::audio_capturer_source_for_tests_ = nullptr; // SpeechRecognizerImpl::OnDataConverter implementation @@ -156,12 +153,15 @@ scoped_refptr SpeechRecognizerImpl::OnDataConverter::Convert( // See http://crbug.com/506051 for details. audio_converter_.Convert(output_bus_.get()); // Create an audio chunk based on the converted result. - scoped_refptr chunk( - new AudioChunk(output_parameters_.GetBytesPerBuffer(), - output_parameters_.bits_per_sample() / 8)); - output_bus_->ToInterleaved(output_bus_->frames(), - output_parameters_.bits_per_sample() / 8, - chunk->writable_data()); + scoped_refptr chunk(new AudioChunk( + output_parameters_.GetBytesPerBuffer(media::kSampleFormatS16), + kNumBitsPerAudioSample / 8)); + + static_assert(SpeechRecognizerImpl::kNumBitsPerAudioSample == 16, + "kNumBitsPerAudioSample must match interleaving type."); + output_bus_->ToInterleaved( + output_bus_->frames(), + reinterpret_cast(chunk->writable_data())); return chunk; } @@ -180,26 +180,23 @@ double SpeechRecognizerImpl::OnDataConverter::ProvideInput( SpeechRecognizerImpl::SpeechRecognizerImpl( SpeechRecognitionEventListener* listener, media::AudioSystem* audio_system, - media::AudioManager* audio_manager, int session_id, bool continuous, bool provisional_results, SpeechRecognitionEngine* engine) : SpeechRecognizer(listener, session_id), audio_system_(audio_system), - audio_manager_(audio_manager), recognition_engine_(engine), endpointer_(kAudioSampleRate), - audio_log_(MediaInternals::GetInstance()->CreateMojoAudioLog( - media::AudioLogFactory::AUDIO_INPUT_CONTROLLER, - 0 /* component_id */)), is_dispatching_event_(false), provisional_results_(provisional_results), end_of_utterance_(false), state_(STATE_IDLE), weak_ptr_factory_(this) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(recognition_engine_ != nullptr); DCHECK(audio_system_ != nullptr); + if (!continuous) { // In single shot (non-continous) recognition, // the session is automatically ended after: @@ -261,8 +258,6 @@ bool SpeechRecognizerImpl::IsCapturingAudio() const { DCHECK_CURRENTLY_ON(BrowserThread::IO); // See IsActive(). const bool is_capturing_audio = state_ >= STATE_STARTING && state_ <= STATE_RECOGNIZING; - DCHECK((is_capturing_audio && (audio_controller_.get() != nullptr)) || - (!is_capturing_audio && audio_controller_.get() == nullptr)); return is_capturing_audio; } @@ -274,26 +269,16 @@ SpeechRecognizerImpl::recognition_engine() const { SpeechRecognizerImpl::~SpeechRecognizerImpl() { DCHECK_CURRENTLY_ON(BrowserThread::IO); endpointer_.EndSession(); - if (audio_controller_.get()) { - audio_controller_->Close(base::BindOnce( - &KeepAudioControllerRefcountedForDtor, audio_controller_)); - audio_log_->OnClosed(); + if (GetAudioCapturerSource()) { + GetAudioCapturerSource()->Stop(); + audio_capturer_source_ = nullptr; } } -// Invoked in the audio thread. -void SpeechRecognizerImpl::OnError( - media::AudioInputController::ErrorCode error_code) { - FSMEventArgs event_args(EVENT_AUDIO_ERROR); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&SpeechRecognizerImpl::DispatchEvent, this, event_args)); -} - -void SpeechRecognizerImpl::Write(const AudioBus* data, - double volume, - bool key_pressed, - base::TimeTicks capture_time) { +void SpeechRecognizerImpl::Capture(const AudioBus* data, + int audio_delay_milliseconds, + double volume, + bool key_pressed) { // 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); @@ -313,9 +298,12 @@ void SpeechRecognizerImpl::Write(const AudioBus* data, CHECK(audio_converter_->data_was_converted()); } -void SpeechRecognizerImpl::Close() {} - -void SpeechRecognizerImpl::OnAudioClosed(AudioInputController*) {} +void SpeechRecognizerImpl::OnCaptureError(const std::string& message) { + FSMEventArgs event_args(EVENT_AUDIO_ERROR); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&SpeechRecognizerImpl::DispatchEvent, this, event_args)); +} void SpeechRecognizerImpl::OnSpeechRecognitionEngineResults( const SpeechRecognitionResults& results) { @@ -344,13 +332,10 @@ void SpeechRecognizerImpl::OnSpeechRecognitionEngineError( // TODO(primiano): After the changes in the media package (r129173), this class // slightly violates the SpeechRecognitionEventListener interface contract. In // particular, it is not true anymore that this class can be freed after the -// OnRecognitionEnd event, since the audio_controller_.Close() asynchronous +// OnRecognitionEnd event, since the audio_capturer_source_->Stop() asynchronous // call can be still in progress after the end event. Currently, it does not // represent a problem for the browser itself, since refcounting protects us // against such race conditions. However, we should fix this in the next CLs. -// For instance, tests are currently working just because the -// TestAudioInputController is not closing asynchronously as the real controller -// does, but they will become flaky if TestAudioInputController will be fixed. void SpeechRecognizerImpl::DispatchEvent(const FSMEventArgs& event_args) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -526,7 +511,6 @@ SpeechRecognizerImpl::ExecuteTransitionAndGetNextState( // - Are guaranteed to be not reentrant (themselves and each other); // - event_args members are guaranteed to be stable during the call; // - The class won't be freed in the meanwhile due to callbacks; -// - IsCapturingAudio() returns true if and only if audio_controller_ != NULL. // TODO(primiano): the audio pipeline is currently serial. However, the // clipper->endpointer->vumeter chain and the sr_engine could be parallelized. @@ -568,6 +552,7 @@ SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::PrepareRecognition( DCHECK(state_ == STATE_IDLE); DCHECK(recognition_engine_.get() != nullptr); DCHECK(!IsCapturingAudio()); + GetAudioSystem()->GetInputStreamParameters( device_id_, base::BindOnce(&SpeechRecognizerImpl::OnDeviceInfo, weak_ptr_factory_.GetWeakPtr())); @@ -598,9 +583,9 @@ SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) { // Audio converter shall provide audio based on these parameters as output. // Hard coded, WebSpeech specific parameters are utilized here. int frames_per_buffer = (kAudioSampleRate * chunk_duration_ms) / 1000; - AudioParameters output_parameters = AudioParameters( - AudioParameters::AUDIO_PCM_LOW_LATENCY, kChannelLayout, kAudioSampleRate, - kNumBitsPerAudioSample, frames_per_buffer); + AudioParameters output_parameters = + AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, kChannelLayout, + kAudioSampleRate, frames_per_buffer); DVLOG(1) << "SRI::output_parameters: " << output_parameters.AsHumanReadableString(); @@ -639,24 +624,16 @@ SpeechRecognizerImpl::StartRecording(const FSMEventArgs&) { audio_converter_.reset( new OnDataConverter(input_parameters, output_parameters)); - audio_controller_ = AudioInputController::Create( - GetAudioManager(), this, this, nullptr, input_parameters, device_id_, - /*agc_is_enabled*/ false); - - if (!audio_controller_.get()) { - return Abort( - SpeechRecognitionError(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE)); - } - - audio_log_->OnCreated(input_parameters, device_id_); - // The endpointer needs to estimate the environment/background noise before // starting to treat the audio as user input. We wait in the state // ESTIMATING_ENVIRONMENT until such interval has elapsed before switching // to user input mode. endpointer_.SetEnvironmentEstimationMode(); - audio_controller_->Record(); - audio_log_->OnStarted(); + + CreateAudioCapturerSource(); + GetAudioCapturerSource()->Initialize(input_parameters, this); + GetAudioCapturerSource()->Start(); + return STATE_STARTING; } @@ -710,7 +687,7 @@ SpeechRecognizerImpl::StopCaptureAndWaitForResult(const FSMEventArgs&) { DCHECK(state_ >= STATE_ESTIMATING_ENVIRONMENT && state_ <= STATE_RECOGNIZING); DVLOG(1) << "Concluding recognition"; - CloseAudioControllerAsynchronously(); + CloseAudioCapturerSource(); recognition_engine_->AudioChunksEnded(); if (state_ > STATE_WAITING_FOR_SPEECH) @@ -743,7 +720,7 @@ SpeechRecognizerImpl::FSMState SpeechRecognizerImpl::Abort( DCHECK_CURRENTLY_ON(BrowserThread::IO); if (IsCapturingAudio()) - CloseAudioControllerAsynchronously(); + CloseAudioCapturerSource(); DVLOG(1) << "SpeechRecognizerImpl canceling recognition. "; @@ -848,18 +825,11 @@ SpeechRecognizerImpl::NotFeasible(const FSMEventArgs& event_args) { return state_; } -void SpeechRecognizerImpl::CloseAudioControllerAsynchronously() { +void SpeechRecognizerImpl::CloseAudioCapturerSource() { DCHECK(IsCapturingAudio()); - DVLOG(1) << "SpeechRecognizerImpl closing audio controller."; - // Issues a Close on the audio controller, passing an empty callback. The only - // purpose of such callback is to keep the audio controller refcounted until - // Close has completed (in the audio thread) and automatically destroy it - // afterwards (upon return from OnAudioClosed). - audio_controller_->Close( - base::BindOnce(&SpeechRecognizerImpl::OnAudioClosed, this, - base::RetainedRef(audio_controller_))); - audio_controller_ = nullptr; // The controller is still refcounted by Bind. - audio_log_->OnClosed(); + DVLOG(1) << "SpeechRecognizerImpl closing audio capturer source."; + GetAudioCapturerSource()->Stop(); + audio_capturer_source_ = nullptr; } int SpeechRecognizerImpl::GetElapsedTimeMs() const { @@ -890,17 +860,30 @@ void SpeechRecognizerImpl::UpdateSignalAndNoiseLevels(const float& rms, void SpeechRecognizerImpl::SetAudioEnvironmentForTesting( media::AudioSystem* audio_system, - media::AudioManager* audio_manager) { + media::AudioCapturerSource* audio_capturer_source) { audio_system_for_tests_ = audio_system; - audio_manager_for_tests_ = audio_manager; + audio_capturer_source_for_tests_ = audio_capturer_source; } media::AudioSystem* SpeechRecognizerImpl::GetAudioSystem() { return audio_system_for_tests_ ? audio_system_for_tests_ : audio_system_; } -media::AudioManager* SpeechRecognizerImpl::GetAudioManager() { - return audio_manager_for_tests_ ? audio_manager_for_tests_ : audio_manager_; +void SpeechRecognizerImpl::CreateAudioCapturerSource() { + service_manager::Connector* connector = + ServiceManagerContext::GetConnectorForIOThread(); + if (connector) { + audio_capturer_source_ = audio::CreateInputDevice( + connector->Clone(), device_id_, + MediaInternals::GetInstance()->CreateMojoAudioLog( + media::AudioLogFactory::AUDIO_INPUT_CONTROLLER, + 0 /* component_id */)); + } +} + +media::AudioCapturerSource* SpeechRecognizerImpl::GetAudioCapturerSource() { + return audio_capturer_source_for_tests_ ? audio_capturer_source_for_tests_ + : audio_capturer_source_.get(); } SpeechRecognizerImpl::FSMEventArgs::FSMEventArgs(FSMEvent event_value) diff --git a/chromium/content/browser/speech/speech_recognizer_impl.h b/chromium/content/browser/speech/speech_recognizer_impl.h index 9fed72910dd..d489585409f 100644 --- a/chromium/content/browser/speech/speech_recognizer_impl.h +++ b/chromium/content/browser/speech/speech_recognizer_impl.h @@ -16,8 +16,7 @@ #include "content/browser/speech/speech_recognizer.h" #include "content/public/common/speech_recognition_error.h" #include "content/public/common/speech_recognition_result.h" -#include "media/audio/audio_input_controller.h" -#include "media/mojo/interfaces/audio_logging.mojom.h" +#include "media/base/audio_capturer_source.h" #include "net/url_request/url_request_context_getter.h" namespace media { @@ -34,8 +33,7 @@ class SpeechRecognitionEventListener; // SpeechRecognitionEngine. class CONTENT_EXPORT SpeechRecognizerImpl : public SpeechRecognizer, - public media::AudioInputController::EventHandler, - public media::AudioInputController::SyncWriter, + public media::AudioCapturerSource::CaptureCallback, public SpeechRecognitionEngine::Delegate { public: static const int kAudioSampleRate; @@ -44,22 +42,24 @@ class CONTENT_EXPORT SpeechRecognizerImpl static const int kNoSpeechTimeoutMs; static const int kEndpointerEstimationTimeMs; - static void SetAudioEnvironmentForTesting(media::AudioSystem* audio_system, - media::AudioManager* audio_manager); + static void SetAudioEnvironmentForTesting( + media::AudioSystem* audio_system, + media::AudioCapturerSource* capturer_source); SpeechRecognizerImpl(SpeechRecognitionEventListener* listener, media::AudioSystem* audio_system, - media::AudioManager* audio_manager, int session_id, bool continuous, bool provisional_results, SpeechRecognitionEngine* engine); + // SpeechRecognizer methods. void StartRecognition(const std::string& device_id) override; void AbortRecognition() override; void StopAudioCapture() override; bool IsActive() const override; bool IsCapturingAudio() const override; + const SpeechRecognitionEngine& recognition_engine() const; private: @@ -138,23 +138,16 @@ class CONTENT_EXPORT SpeechRecognizerImpl // OnAudioLevelsChange event accordingly. void UpdateSignalAndNoiseLevels(const float& rms, bool clip_detected); - void CloseAudioControllerAsynchronously(); - - // Callback called on IO thread by audio_controller->Close(). - void OnAudioClosed(media::AudioInputController*); + void CloseAudioCapturerSource(); - // AudioInputController::EventHandler methods. - void OnCreated(bool initially_muted) override {} - void OnError(media::AudioInputController::ErrorCode error_code) override; - void OnLog(base::StringPiece) override {} - void OnMuted(bool is_muted) override {} - - // AudioInputController::SyncWriter methods. - void Write(const media::AudioBus* data, - double volume, - bool key_pressed, - base::TimeTicks capture_time) override; - void Close() override; + // media::AudioCapturerSource::CaptureCallback methods. + void OnCaptureStarted() final {} + void Capture(const media::AudioBus* audio_bus, + int audio_delay_milliseconds, + double volume, + bool key_pressed) final; + void OnCaptureError(const std::string& message) final; + void OnCaptureMuted(bool is_muted) final {} // SpeechRecognitionEngineDelegate methods. void OnSpeechRecognitionEngineResults( @@ -164,17 +157,17 @@ class CONTENT_EXPORT SpeechRecognizerImpl const SpeechRecognitionError& error) override; media::AudioSystem* GetAudioSystem(); - media::AudioManager* GetAudioManager(); + void CreateAudioCapturerSource(); + media::AudioCapturerSource* GetAudioCapturerSource(); - // Substitutes the real audio system in browser tests. + // Substitute the real audio system and capturer source in browser tests. static media::AudioSystem* audio_system_for_tests_; - static media::AudioManager* audio_manager_for_tests_; + static media::AudioCapturerSource* audio_capturer_source_for_tests_; + media::AudioSystem* audio_system_; - media::AudioManager* audio_manager_; std::unique_ptr recognition_engine_; Endpointer endpointer_; - scoped_refptr audio_controller_; - media::mojom::AudioLogPtr audio_log_; + scoped_refptr audio_capturer_source_; int num_samples_recorded_; float audio_level_; bool is_dispatching_event_; diff --git a/chromium/content/browser/speech/speech_recognizer_impl_android.cc b/chromium/content/browser/speech/speech_recognizer_impl_android.cc index 19bec767ab1..5ca639cd1fd 100644 --- a/chromium/content/browser/speech/speech_recognizer_impl_android.cc +++ b/chromium/content/browser/speech/speech_recognizer_impl_android.cc @@ -16,9 +16,8 @@ #include "content/public/browser/speech_recognition_event_listener.h" #include "content/public/browser/speech_recognition_manager.h" #include "content/public/browser/speech_recognition_session_config.h" -#include "content/public/common/speech_recognition_grammar.h" #include "content/public/common/speech_recognition_result.h" -#include "jni/SpeechRecognition_jni.h" +#include "jni/SpeechRecognitionImpl_jni.h" using base::android::AppendJavaStringArrayToStringVector; using base::android::AttachCurrentThread; @@ -41,15 +40,17 @@ void SpeechRecognizerImplAndroid::StartRecognition( const std::string& device_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // TODO(xians): Open the correct device for speech on Android. - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( - &SpeechRecognitionEventListener::OnRecognitionStart, - base::Unretained(listener()), - session_id())); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&SpeechRecognitionEventListener::OnRecognitionStart, + base::Unretained(listener()), session_id())); SpeechRecognitionSessionConfig config = SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id()); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( - &content::SpeechRecognizerImplAndroid::StartRecognitionOnUIThread, this, - config.language, config.continuous, config.interim_results)); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce( + &content::SpeechRecognizerImplAndroid::StartRecognitionOnUIThread, + this, config.language, config.continuous, config.interim_results)); } void SpeechRecognizerImplAndroid::StartRecognitionOnUIThread( @@ -58,9 +59,9 @@ void SpeechRecognizerImplAndroid::StartRecognitionOnUIThread( bool interim_results) { DCHECK_CURRENTLY_ON(BrowserThread::UI); JNIEnv* env = AttachCurrentThread(); - j_recognition_.Reset(Java_SpeechRecognition_createSpeechRecognition( + j_recognition_.Reset(Java_SpeechRecognitionImpl_createSpeechRecognition( env, reinterpret_cast(this))); - Java_SpeechRecognition_startRecognition( + Java_SpeechRecognitionImpl_startRecognition( env, j_recognition_, ConvertUTF8ToJavaString(env, language), continuous, interim_results); } @@ -68,26 +69,30 @@ void SpeechRecognizerImplAndroid::StartRecognitionOnUIThread( void SpeechRecognizerImplAndroid::AbortRecognition() { if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { state_ = STATE_IDLE; - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( - &content::SpeechRecognizerImplAndroid::AbortRecognition, this)); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&content::SpeechRecognizerImplAndroid::AbortRecognition, + this)); return; } DCHECK_CURRENTLY_ON(BrowserThread::UI); JNIEnv* env = AttachCurrentThread(); if (!j_recognition_.is_null()) - Java_SpeechRecognition_abortRecognition(env, j_recognition_); + Java_SpeechRecognitionImpl_abortRecognition(env, j_recognition_); } void SpeechRecognizerImplAndroid::StopAudioCapture() { if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( - &content::SpeechRecognizerImplAndroid::StopAudioCapture, this)); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&content::SpeechRecognizerImplAndroid::StopAudioCapture, + this)); return; } DCHECK_CURRENTLY_ON(BrowserThread::UI); JNIEnv* env = AttachCurrentThread(); if (!j_recognition_.is_null()) - Java_SpeechRecognition_stopRecognition(env, j_recognition_); + Java_SpeechRecognitionImpl_stopRecognition(env, j_recognition_); } bool SpeechRecognizerImplAndroid::IsActive() const { @@ -106,8 +111,8 @@ void SpeechRecognizerImplAndroid::OnAudioStart( if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&SpeechRecognizerImplAndroid::OnAudioStart, this, nullptr, - nullptr)); + base::BindOnce(&SpeechRecognizerImplAndroid::OnAudioStart, this, + nullptr, nullptr)); return; } DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -121,8 +126,8 @@ void SpeechRecognizerImplAndroid::OnSoundStart( if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&SpeechRecognizerImplAndroid::OnSoundStart, this, nullptr, - nullptr)); + base::BindOnce(&SpeechRecognizerImplAndroid::OnSoundStart, this, + nullptr, nullptr)); return; } DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -132,9 +137,10 @@ void SpeechRecognizerImplAndroid::OnSoundStart( void SpeechRecognizerImplAndroid::OnSoundEnd(JNIEnv* env, const JavaParamRef& obj) { if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&SpeechRecognizerImplAndroid::OnSoundEnd, - this, nullptr, nullptr)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&SpeechRecognizerImplAndroid::OnSoundEnd, this, nullptr, + nullptr)); return; } DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -144,9 +150,10 @@ void SpeechRecognizerImplAndroid::OnSoundEnd(JNIEnv* env, void SpeechRecognizerImplAndroid::OnAudioEnd(JNIEnv* env, const JavaParamRef& obj) { if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&SpeechRecognizerImplAndroid::OnAudioEnd, - this, nullptr, nullptr)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&SpeechRecognizerImplAndroid::OnAudioEnd, this, nullptr, + nullptr)); return; } DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -176,9 +183,11 @@ void SpeechRecognizerImplAndroid::OnRecognitionResults( options[i], static_cast(scores[i]))); } result.is_provisional = provisional; - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( - &SpeechRecognizerImplAndroid::OnRecognitionResultsOnIOThread, - this, results)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &SpeechRecognizerImplAndroid::OnRecognitionResultsOnIOThread, this, + results)); } void SpeechRecognizerImplAndroid::OnRecognitionResultsOnIOThread( @@ -194,8 +203,8 @@ void SpeechRecognizerImplAndroid::OnRecognitionError( if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&SpeechRecognizerImplAndroid::OnRecognitionError, this, - nullptr, nullptr, error)); + base::BindOnce(&SpeechRecognizerImplAndroid::OnRecognitionError, this, + nullptr, nullptr, error)); return; } DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -210,8 +219,8 @@ void SpeechRecognizerImplAndroid::OnRecognitionEnd( if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&SpeechRecognizerImplAndroid::OnRecognitionEnd, this, - nullptr, nullptr)); + base::BindOnce(&SpeechRecognizerImplAndroid::OnRecognitionEnd, this, + nullptr, nullptr)); return; } DCHECK_CURRENTLY_ON(BrowserThread::IO); diff --git a/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc b/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc index 2c5928d61f5..21c79cb7445 100644 --- a/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc +++ b/chromium/content/browser/speech/speech_recognizer_impl_unittest.cc @@ -7,6 +7,7 @@ #include +#include "base/bind_helpers.h" #include "base/run_loop.h" #include "base/synchronization/waitable_event.h" #include "base/sys_byteorder.h" @@ -22,7 +23,6 @@ #include "media/audio/fake_audio_input_stream.h" #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/audio/test_audio_thread.h" #include "media/base/audio_bus.h" #include "media/base/test_helpers.h" @@ -31,20 +31,39 @@ #include "net/url_request/url_request_status.h" #include "testing/gtest/include/gtest/gtest.h" -using media::AudioInputController; using media::AudioInputStream; using media::AudioOutputStream; using media::AudioParameters; -using media::TestAudioInputController; -using media::TestAudioInputControllerFactory; namespace content { +namespace { + +class MockCapturerSource : public media::AudioCapturerSource { + public: + MockCapturerSource() = default; + MOCK_METHOD2(Initialize, + void(const media::AudioParameters& params, + CaptureCallback* callback)); + MOCK_METHOD0(Start, void()); + MOCK_METHOD0(Stop, void()); + MOCK_METHOD1(SetAutomaticGainControl, void(bool enable)); + MOCK_METHOD1(SetVolume, void(double volume)); + MOCK_METHOD1(SetOutputDeviceForAec, + void(const std::string& output_device_id)); + + protected: + ~MockCapturerSource() override = default; +}; + +} // namespace + class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, public testing::Test { public: SpeechRecognizerImplTest() - : recognition_started_(false), + : audio_capturer_source_(new testing::NiceMock()), + recognition_started_(false), recognition_ended_(false), result_received_(false), audio_started_(false), @@ -71,9 +90,10 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, media::AudioParameters::UnavailableDeviceParams()); audio_system_ = std::make_unique(audio_manager_.get()); + SpeechRecognizerImpl::SetAudioEnvironmentForTesting( + audio_system_.get(), audio_capturer_source_.get()); recognizer_ = new SpeechRecognizerImpl( - this, audio_system_.get(), audio_manager_.get(), kTestingSessionId, - false, false, sr_engine); + this, audio_system_.get(), kTestingSessionId, false, false, sr_engine); int audio_packet_length_bytes = (SpeechRecognizerImpl::kAudioSampleRate * @@ -90,7 +110,10 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, audio_bus_->Zero(); } - ~SpeechRecognizerImplTest() override { audio_manager_->Shutdown(); } + ~SpeechRecognizerImplTest() override { + SpeechRecognizerImpl::SetAudioEnvironmentForTesting(nullptr, nullptr); + audio_manager_->Shutdown(); + } void CheckEventsConsistency() { // Note: "!x || y" == "x implies y". @@ -162,16 +185,6 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, CheckEventsConsistency(); } - // testing::Test methods. - void SetUp() override { - AudioInputController::set_factory_for_testing( - &audio_input_controller_factory_); - } - - void TearDown() override { - AudioInputController::set_factory_for_testing(nullptr); - } - void CopyPacketToAudioBus() { // Copy the created signal into an audio bus in a deinterleaved format. audio_bus_->FromInterleaved( @@ -195,10 +208,18 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, CopyPacketToAudioBus(); } - void OnData(media::AudioBus* data) { - auto* writer = - static_cast(recognizer_.get()); - writer->Write(data, 0.0, false, base::TimeTicks::Now()); + void Capture(media::AudioBus* data) { + auto* capture_callback = + static_cast( + recognizer_.get()); + capture_callback->Capture(data, 0, 0.0, false); + } + + void OnCaptureError() { + auto* capture_callback = + static_cast( + recognizer_.get()); + capture_callback->OnCaptureError(""); } void WaitForAudioThreadToPostDeviceInfo() { @@ -216,6 +237,7 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, scoped_refptr recognizer_; std::unique_ptr audio_manager_; std::unique_ptr audio_system_; + scoped_refptr audio_capturer_source_; bool recognition_started_; bool recognition_ended_; bool result_received_; @@ -225,7 +247,6 @@ class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, bool sound_ended_; SpeechRecognitionErrorCode error_; net::TestURLFetcherFactory url_fetcher_factory_; - TestAudioInputControllerFactory audio_input_controller_factory_; std::vector audio_packet_; std::unique_ptr audio_bus_; int bytes_per_sample_; @@ -343,9 +364,6 @@ TEST_F(SpeechRecognizerImplTest, StopWithData) { base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. WaitForAudioThreadToPostDeviceInfo(); base::RunLoop().RunUntilIdle(); // EVENT_START processing. - TestAudioInputController* controller = - audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); // Try sending 5 chunks of mock audio data and verify that each of them // resulted immediately in a packet sent out via the network. This verifies @@ -353,7 +371,7 @@ TEST_F(SpeechRecognizerImplTest, StopWithData) { // full recording to complete. const size_t kNumChunks = 5; for (size_t i = 0; i < kNumChunks; ++i) { - OnData(audio_bus_.get()); + Capture(audio_bus_.get()); base::RunLoop().RunUntilIdle(); net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); @@ -407,10 +425,7 @@ TEST_F(SpeechRecognizerImplTest, CancelWithData) { base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. WaitForAudioThreadToPostDeviceInfo(); base::RunLoop().RunUntilIdle(); // EVENT_START processing. - TestAudioInputController* controller = - audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); - OnData(audio_bus_.get()); + Capture(audio_bus_.get()); base::RunLoop().RunUntilIdle(); recognizer_->AbortRecognition(); base::RunLoop().RunUntilIdle(); @@ -430,10 +445,7 @@ TEST_F(SpeechRecognizerImplTest, ConnectionError) { base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. WaitForAudioThreadToPostDeviceInfo(); base::RunLoop().RunUntilIdle(); // EVENT_START processing. - TestAudioInputController* controller = - audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); - OnData(audio_bus_.get()); + Capture(audio_bus_.get()); base::RunLoop().RunUntilIdle(); net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); @@ -468,10 +480,7 @@ TEST_F(SpeechRecognizerImplTest, ServerError) { base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. WaitForAudioThreadToPostDeviceInfo(); base::RunLoop().RunUntilIdle(); // EVENT_START processing. - TestAudioInputController* controller = - audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); - OnData(audio_bus_.get()); + Capture(audio_bus_.get()); base::RunLoop().RunUntilIdle(); net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); @@ -497,42 +506,18 @@ TEST_F(SpeechRecognizerImplTest, ServerError) { CheckFinalEventsConsistency(); } -TEST_F(SpeechRecognizerImplTest, AudioControllerErrorNoData) { +TEST_F(SpeechRecognizerImplTest, OnCaptureError_PropagatesError) { // Check if things tear down properly if AudioInputController threw an error. recognizer_->StartRecognition( media::AudioDeviceDescription::kDefaultDeviceId); base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. WaitForAudioThreadToPostDeviceInfo(); base::RunLoop().RunUntilIdle(); // EVENT_START processing. - TestAudioInputController* controller = - audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); - controller->event_handler()->OnError(AudioInputController::UNKNOWN_ERROR); - base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(recognition_started_); - EXPECT_FALSE(audio_started_); - EXPECT_FALSE(result_received_); - EXPECT_EQ(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE, error_); - CheckFinalEventsConsistency(); -} -TEST_F(SpeechRecognizerImplTest, AudioControllerErrorWithData) { - // Check if things tear down properly if AudioInputController threw an error - // after giving some audio data. - recognizer_->StartRecognition( - media::AudioDeviceDescription::kDefaultDeviceId); - base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. - WaitForAudioThreadToPostDeviceInfo(); - base::RunLoop().RunUntilIdle(); // EVENT_START processing. - TestAudioInputController* controller = - audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); - OnData(audio_bus_.get()); - controller->event_handler()->OnError(AudioInputController::UNKNOWN_ERROR); + OnCaptureError(); base::RunLoop().RunUntilIdle(); - ASSERT_TRUE(url_fetcher_factory_.GetFetcherByID(0)); EXPECT_TRUE(recognition_started_); - EXPECT_TRUE(audio_started_); + EXPECT_FALSE(audio_started_); EXPECT_FALSE(result_received_); EXPECT_EQ(SPEECH_RECOGNITION_ERROR_AUDIO_CAPTURE, error_); CheckFinalEventsConsistency(); @@ -546,15 +531,12 @@ TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackIssued) { base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. WaitForAudioThreadToPostDeviceInfo(); base::RunLoop().RunUntilIdle(); // EVENT_START processing. - TestAudioInputController* controller = - audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); int num_packets = (SpeechRecognizerImpl::kNoSpeechTimeoutMs) / SpeechRecognitionEngine::kAudioPacketIntervalMs + 1; // The vector is already filled with zero value samples on create. for (int i = 0; i < num_packets; ++i) { - OnData(audio_bus_.get()); + Capture(audio_bus_.get()); } base::RunLoop().RunUntilIdle(); EXPECT_TRUE(recognition_started_); @@ -574,23 +556,18 @@ TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackNotIssued) { base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. WaitForAudioThreadToPostDeviceInfo(); base::RunLoop().RunUntilIdle(); // EVENT_START processing. - TestAudioInputController* controller = - audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); - controller = audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); int num_packets = (SpeechRecognizerImpl::kNoSpeechTimeoutMs) / SpeechRecognitionEngine::kAudioPacketIntervalMs; // The vector is already filled with zero value samples on create. for (int i = 0; i < num_packets / 2; ++i) { - OnData(audio_bus_.get()); + Capture(audio_bus_.get()); } FillPacketWithTestWaveform(); for (int i = 0; i < num_packets / 2; ++i) { - OnData(audio_bus_.get()); + Capture(audio_bus_.get()); } base::RunLoop().RunUntilIdle(); @@ -613,29 +590,24 @@ TEST_F(SpeechRecognizerImplTest, SetInputVolumeCallback) { base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. WaitForAudioThreadToPostDeviceInfo(); base::RunLoop().RunUntilIdle(); // EVENT_START processing. - TestAudioInputController* controller = - audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); - controller = audio_input_controller_factory_.controller(); - ASSERT_TRUE(controller); // Feed some samples to begin with for the endpointer to do noise estimation. int num_packets = SpeechRecognizerImpl::kEndpointerEstimationTimeMs / SpeechRecognitionEngine::kAudioPacketIntervalMs; FillPacketWithNoise(); for (int i = 0; i < num_packets; ++i) { - OnData(audio_bus_.get()); + Capture(audio_bus_.get()); } base::RunLoop().RunUntilIdle(); EXPECT_EQ(-1.0f, volume_); // No audio volume set yet. // The vector is already filled with zero value samples on create. - OnData(audio_bus_.get()); + Capture(audio_bus_.get()); base::RunLoop().RunUntilIdle(); EXPECT_FLOAT_EQ(0.74939233f, volume_); FillPacketWithTestWaveform(); - OnData(audio_bus_.get()); + Capture(audio_bus_.get()); base::RunLoop().RunUntilIdle(); EXPECT_NEAR(0.89926866f, volume_, 0.00001f); EXPECT_FLOAT_EQ(0.75071919f, noise_volume_); diff --git a/chromium/content/browser/startup_task_runner.cc b/chromium/content/browser/startup_task_runner.cc index 5adeb6ae6e1..36259dd2ec7 100644 --- a/chromium/content/browser/startup_task_runner.cc +++ b/chromium/content/browser/startup_task_runner.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/location.h" -#include "base/message_loop/message_loop.h" namespace content { diff --git a/chromium/content/browser/storage_partition_impl.cc b/chromium/content/browser/storage_partition_impl.cc index 9f0423752c5..b9165dd4ab7 100644 --- a/chromium/content/browser/storage_partition_impl.cc +++ b/chromium/content/browser/storage_partition_impl.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include "base/barrier_closure.h" @@ -17,12 +18,14 @@ #include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" #include "base/strings/utf_string_conversions.h" +#include "base/syslog_logging.h" #include "content/browser/background_fetch/background_fetch_context.h" #include "content/browser/blob_storage/blob_registry_wrapper.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/browser_main_loop.h" #include "content/browser/browsing_data/storage_partition_http_cache_data_remover.h" #include "content/browser/child_process_security_policy_impl.h" +#include "content/browser/cookie_store/cookie_store_context.h" #include "content/browser/fileapi/browser_file_system_helper.h" #include "content/browser/gpu/shader_cache_factory.h" #include "content/browser/loader/prefetch_url_loader_service.h" @@ -44,10 +47,10 @@ #include "net/base/completion_callback.h" #include "net/base/net_errors.h" #include "net/cookies/canonical_cookie.h" -#include "net/cookies/cookie_monster.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "ppapi/buildflags/buildflags.h" +#include "services/network/cookie_manager.h" #include "services/network/network_context.h" #include "services/network/network_service.h" #include "services/network/public/cpp/features.h" @@ -66,6 +69,9 @@ #include "content/browser/plugin_private_storage_helper.h" #endif // BUILDFLAG(ENABLE_PLUGINS) +using CookieDeletionFilter = network::mojom::CookieDeletionFilter; +using CookieDeletionFilterPtr = network::mojom::CookieDeletionFilterPtr; + namespace content { namespace { @@ -73,11 +79,6 @@ namespace { base::LazyInstance::Leaky g_url_loader_factory_callback_for_test = LAZY_INSTANCE_INITIALIZER; -bool DoesCookieMatchHost(const std::string& host, - const net::CanonicalCookie& cookie) { - return cookie.IsHostCookie() && cookie.IsDomainMatch(host); -} - void OnClearedCookies(base::OnceClosure callback, uint32_t num_deleted) { // The final callback needs to happen from UI thread. if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { @@ -90,38 +91,6 @@ void OnClearedCookies(base::OnceClosure callback, uint32_t num_deleted) { std::move(callback).Run(); } -// Cookie matcher and storage_origin are never both populated. -void ClearCookiesOnIOThread( - const scoped_refptr& rq_context, - const base::Time begin, - const base::Time end, - const GURL& storage_origin, - const StoragePartition::CookieMatcherFunction& cookie_matcher, - base::OnceClosure callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(cookie_matcher.is_null() || storage_origin.is_empty()); - net::CookieStore* cookie_store = - rq_context->GetURLRequestContext()->cookie_store(); - if (!cookie_matcher.is_null()) { - cookie_store->DeleteAllCreatedBetweenWithPredicateAsync( - begin, end, cookie_matcher, - base::BindOnce(&OnClearedCookies, std::move(callback))); - return; - } - if (!storage_origin.is_empty()) { - // TODO(mkwst): It's not clear whether removing host cookies is the correct - // behavior. We might want to remove all domain-matching cookies instead. - // Also, this code path may be dead anyways. - cookie_store->DeleteAllCreatedBetweenWithPredicateAsync( - begin, end, - StoragePartitionImpl::CreatePredicateForHostCookies(storage_origin), - base::BindOnce(&OnClearedCookies, std::move(callback))); - return; - } - cookie_store->DeleteAllCreatedBetweenAsync( - begin, end, base::BindOnce(&OnClearedCookies, std::move(callback))); -} - void CheckQuotaManagedDataDeletionStatus(size_t* deletion_task_count, const base::Closure& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -278,7 +247,7 @@ class StoragePartitionImpl::NetworkContextOwner { context_getter_ = std::move(context_getter); network_context_ = std::make_unique( GetNetworkServiceImpl(), std::move(network_context_request), - context_getter_); + context_getter_->GetURLRequestContext()); } private: @@ -300,6 +269,7 @@ class StoragePartitionImpl::URLLoaderFactoryForBrowserProcess : storage_partition_(storage_partition) {} // mojom::URLLoaderFactory implementation: + void CreateLoaderAndStart(network::mojom::URLLoaderRequest request, int32_t routing_id, int32_t request_id, @@ -317,6 +287,13 @@ class StoragePartitionImpl::URLLoaderFactoryForBrowserProcess traffic_annotation); } + void Clone(network::mojom::URLLoaderFactoryRequest request) override { + if (!storage_partition_) + return; + storage_partition_->GetURLLoaderFactoryForBrowserProcessInternal()->Clone( + std::move(request)); + } + // SharedURLLoaderFactory implementation: std::unique_ptr Clone() override { NOTREACHED() << "This isn't supported. If you need a SharedURLLoaderFactory" @@ -351,16 +328,12 @@ int StoragePartitionImpl::GenerateQuotaClientMask(uint32_t remove_mask) { quota_client_mask |= storage::QuotaClient::kServiceWorker; if (remove_mask & StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE) quota_client_mask |= storage::QuotaClient::kServiceWorkerCache; + if (remove_mask & StoragePartition::REMOVE_DATA_MASK_BACKGROUND_FETCH) + quota_client_mask |= storage::QuotaClient::kBackgroundFetch; return quota_client_mask; } -// static -net::CookieStore::CookiePredicate -StoragePartitionImpl::CreatePredicateForHostCookies(const GURL& url) { - return base::Bind(&DoesCookieMatchHost, url.host()); -} - // static void StoragePartitionImpl:: SetGetURLLoaderFactoryForBrowserProcessCallbackForTesting( @@ -443,7 +416,7 @@ class StoragePartitionImpl::DataDeletionHelper { // to the DataDeletionHelper. class OwnsReference { public: - OwnsReference(DataDeletionHelper* helper) : helper_(helper) { + explicit OwnsReference(DataDeletionHelper* helper) : helper_(helper) { DCHECK_CURRENTLY_ON(BrowserThread::UI); helper->IncrementTaskCountOnUI(); } @@ -471,13 +444,14 @@ class StoragePartitionImpl::DataDeletionHelper { void ClearDataOnUIThread( const GURL& storage_origin, const OriginMatcherFunction& origin_matcher, - const CookieMatcherFunction& cookie_matcher, + CookieDeletionFilterPtr cookie_deletion_filter, const base::FilePath& path, net::URLRequestContextGetter* rq_context, DOMStorageContextWrapper* dom_storage_context, storage::QuotaManager* quota_manager, storage::SpecialStoragePolicy* special_storage_policy, storage::FileSystemContext* filesystem_context, + network::mojom::CookieManager* cookie_manager, const base::Time begin, const base::Time end); @@ -658,7 +632,8 @@ std::unique_ptr StoragePartitionImpl::Create( partition->background_fetch_context_ = new BackgroundFetchContext(context, partition->service_worker_context_); - partition->background_sync_context_ = new BackgroundSyncContext(); + partition->background_sync_context_ = + base::MakeRefCounted(); partition->background_sync_context_->Init(partition->service_worker_context_); partition->payment_app_context_ = new PaymentAppContextImpl(); @@ -677,11 +652,11 @@ std::unique_ptr StoragePartitionImpl::Create( base::BindOnce(&BlobStorageContextGetterForStorage, blob_context); partition->blob_url_loader_factory_ = BlobURLLoaderFactory::Create(std::move(blob_getter)); - - partition->url_loader_factory_getter_ = new URLLoaderFactoryGetter(); - partition->url_loader_factory_getter_->Initialize(partition.get()); } + partition->url_loader_factory_getter_ = new URLLoaderFactoryGetter(); + partition->url_loader_factory_getter_->Initialize(partition.get()); + partition->service_worker_context_->Init( path, quota_manager_proxy.get(), context->GetSpecialStoragePolicy(), blob_context.get(), partition->url_loader_factory_getter_.get()); @@ -696,6 +671,14 @@ std::unique_ptr StoragePartitionImpl::Create( base::MakeRefCounted( partition->url_loader_factory_getter_); + partition->cookie_store_context_ = base::MakeRefCounted(); + // Unit tests use the Initialize() callback to crash early if restoring the + // CookieManagerStore's state from ServiceWorkerStorage fails. Production and + // browser tests rely on CookieStoreManager's well-defined behavior when + // restoring the state fails. + partition->cookie_store_context_->Initialize( + partition->service_worker_context_, base::DoNothing()); + return partition; } @@ -856,12 +839,17 @@ PrefetchURLLoaderService* StoragePartitionImpl::GetPrefetchURLLoaderService() { return prefetch_url_loader_service_.get(); } +CookieStoreContext* StoragePartitionImpl::GetCookieStoreContext() { + return cookie_store_context_.get(); +} + void StoragePartitionImpl::OpenLocalStorage( const url::Origin& origin, mojom::LevelDBWrapperRequest request) { int process_id = bindings_.dispatch_context(); if (!ChildProcessSecurityPolicy::GetInstance()->CanAccessDataForOrigin( process_id, origin.GetURL())) { + SYSLOG(WARNING) << "Killing renderer: illegal localStorage request."; bindings_.ReportBadMessage("Access denied for localStorage request"); return; } @@ -881,7 +869,7 @@ void StoragePartitionImpl::ClearDataImpl( uint32_t quota_storage_remove_mask, const GURL& storage_origin, const OriginMatcherFunction& origin_matcher, - const CookieMatcherFunction& cookie_matcher, + CookieDeletionFilterPtr cookie_deletion_filter, const base::Time begin, const base::Time end, base::OnceClosure callback) { @@ -894,9 +882,11 @@ void StoragePartitionImpl::ClearDataImpl( // DataDeletionHelper::DecrementTaskCount(). deletion_helpers_running_++; helper->ClearDataOnUIThread( - storage_origin, origin_matcher, cookie_matcher, GetPath(), - GetURLRequestContext(), dom_storage_context_.get(), quota_manager_.get(), - special_storage_policy_.get(), filesystem_context_.get(), begin, end); + storage_origin, origin_matcher, std::move(cookie_deletion_filter), + GetPath(), GetURLRequestContext(), dom_storage_context_.get(), + quota_manager_.get(), special_storage_policy_.get(), + filesystem_context_.get(), GetCookieManagerForBrowserProcess(), begin, + end); } void StoragePartitionImpl::DeletionHelperDone(base::OnceClosure callback) { @@ -1041,13 +1031,14 @@ void StoragePartitionImpl::DataDeletionHelper::DecrementTaskCount() { void StoragePartitionImpl::DataDeletionHelper::ClearDataOnUIThread( const GURL& storage_origin, const OriginMatcherFunction& origin_matcher, - const CookieMatcherFunction& cookie_matcher, + CookieDeletionFilterPtr cookie_deletion_filter, const base::FilePath& path, net::URLRequestContextGetter* rq_context, DOMStorageContextWrapper* dom_storage_context, storage::QuotaManager* quota_manager, storage::SpecialStoragePolicy* special_storage_policy, storage::FileSystemContext* filesystem_context, + network::mojom::CookieManager* cookie_manager, const base::Time begin, const base::Time end) { DCHECK_NE(remove_mask_, 0u); @@ -1058,12 +1049,21 @@ void StoragePartitionImpl::DataDeletionHelper::ClearDataOnUIThread( &DataDeletionHelper::DecrementTaskCount, base::Unretained(this)); if (remove_mask_ & REMOVE_DATA_MASK_COOKIES) { - // Handle the cookies. - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, + // The CookieDeletionFilter has a redundant time interval to |begin| and + // |end|. Ensure that the filter has no time interval specified to help + // callers detect when they are using the wrong interval values. + DCHECK(!cookie_deletion_filter->created_after_time.has_value()); + DCHECK(!cookie_deletion_filter->created_before_time.has_value()); + + if (!begin.is_null()) + cookie_deletion_filter->created_after_time = begin; + if (!end.is_null()) + cookie_deletion_filter->created_before_time = end; + + cookie_manager->DeleteCookies( + std::move(cookie_deletion_filter), base::BindOnce( - &ClearCookiesOnIOThread, base::WrapRefCounted(rq_context), begin, - end, storage_origin, cookie_matcher, + &OnClearedCookies, // Use OwnsReference instead of Increment/DecrementTaskCount* // to handle the cookie store being destroyed and the callback // thus not being called. @@ -1132,9 +1132,12 @@ void StoragePartitionImpl::ClearDataForOrigin( uint32_t quota_storage_remove_mask, const GURL& storage_origin) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + CookieDeletionFilterPtr deletion_filter = CookieDeletionFilter::New(); + if (!storage_origin.host().empty()) + deletion_filter->host_name = storage_origin.host(); ClearDataImpl(remove_mask, quota_storage_remove_mask, storage_origin, - OriginMatcherFunction(), CookieMatcherFunction(), base::Time(), - base::Time::Max(), base::DoNothing()); + OriginMatcherFunction(), std::move(deletion_filter), + base::Time(), base::Time::Max(), base::DoNothing()); } void StoragePartitionImpl::ClearData( @@ -1145,8 +1148,11 @@ void StoragePartitionImpl::ClearData( const base::Time begin, const base::Time end, base::OnceClosure callback) { + CookieDeletionFilterPtr deletion_filter = CookieDeletionFilter::New(); + if (!storage_origin.host().empty()) + deletion_filter->host_name = storage_origin.host(); ClearDataImpl(remove_mask, quota_storage_remove_mask, storage_origin, - origin_matcher, CookieMatcherFunction(), begin, end, + origin_matcher, std::move(deletion_filter), begin, end, std::move(callback)); } @@ -1154,12 +1160,13 @@ void StoragePartitionImpl::ClearData( uint32_t remove_mask, uint32_t quota_storage_remove_mask, const OriginMatcherFunction& origin_matcher, - const CookieMatcherFunction& cookie_matcher, + network::mojom::CookieDeletionFilterPtr cookie_deletion_filter, const base::Time begin, const base::Time end, base::OnceClosure callback) { ClearDataImpl(remove_mask, quota_storage_remove_mask, GURL(), origin_matcher, - cookie_matcher, begin, end, std::move(callback)); + std::move(cookie_deletion_filter), begin, end, + std::move(callback)); } void StoragePartitionImpl::ClearHttpAndMediaCaches( @@ -1247,15 +1254,20 @@ StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcessInternal() { return url_loader_factory_for_browser_process_.get(); } + network::mojom::URLLoaderFactoryParamsPtr params = + network::mojom::URLLoaderFactoryParams::New(); + params->process_id = network::mojom::kBrowserProcessId; + params->is_corb_enabled = false; if (g_url_loader_factory_callback_for_test.Get().is_null()) { GetNetworkContext()->CreateURLLoaderFactory( - mojo::MakeRequest(&url_loader_factory_for_browser_process_), 0); + mojo::MakeRequest(&url_loader_factory_for_browser_process_), + std::move(params)); return url_loader_factory_for_browser_process_.get(); } network::mojom::URLLoaderFactoryPtr original_factory; GetNetworkContext()->CreateURLLoaderFactory( - mojo::MakeRequest(&original_factory), 0); + mojo::MakeRequest(&original_factory), std::move(params)); url_loader_factory_for_browser_process_ = g_url_loader_factory_callback_for_test.Get().Run( std::move(original_factory)); diff --git a/chromium/content/browser/storage_partition_impl.h b/chromium/content/browser/storage_partition_impl.h index 38d0aadaad6..102e3ff8e47 100644 --- a/chromium/content/browser/storage_partition_impl.h +++ b/chromium/content/browser/storage_partition_impl.h @@ -35,7 +35,6 @@ #include "content/common/storage_partition_service.mojom.h" #include "content/public/browser/storage_partition.h" #include "mojo/public/cpp/bindings/binding_set.h" -#include "net/cookies/cookie_store.h" #include "services/network/public/mojom/cookie_manager.mojom.h" #include "services/network/public/mojom/network_service.mojom.h" #include "storage/browser/quota/special_storage_policy.h" @@ -47,6 +46,7 @@ namespace content { class BackgroundFetchContext; +class CookieStoreContext; class BlobRegistryWrapper; class BlobURLLoaderFactory; class PrefetchURLLoaderService; @@ -65,12 +65,6 @@ class CONTENT_EXPORT StoragePartitionImpl // StoragePartition uses. This method generates that mask. static int GenerateQuotaClientMask(uint32_t remove_mask); - // This creates a CookiePredicate that matches all host (NOT domain) cookies - // that match the host of |url|. This is intended to be used with - // DeleteAllCreatedBetweenWithPredicateAsync. - static net::CookieStore::CookiePredicate - CreatePredicateForHostCookies(const GURL& url); - // Allows overriding the URLLoaderFactory creation for // GetURLLoaderFactoryForBrowserProcess. // Passing a null callback will restore the default behavior. @@ -127,7 +121,7 @@ class CONTENT_EXPORT StoragePartitionImpl void ClearData(uint32_t remove_mask, uint32_t quota_storage_remove_mask, const OriginMatcherFunction& origin_matcher, - const CookieMatcherFunction& cookie_matcher, + network::mojom::CookieDeletionFilterPtr cookie_deletion_filter, const base::Time begin, const base::Time end, base::OnceClosure callback) override; @@ -149,6 +143,7 @@ class CONTENT_EXPORT StoragePartitionImpl BlobURLLoaderFactory* GetBlobURLLoaderFactory(); BlobRegistryWrapper* GetBlobRegistry(); PrefetchURLLoaderService* GetPrefetchURLLoaderService(); + CookieStoreContext* GetCookieStoreContext(); // mojom::StoragePartitionService interface. void OpenLocalStorage(const url::Origin& origin, @@ -194,6 +189,7 @@ class CONTENT_EXPORT StoragePartitionImpl friend class BackgroundSyncManagerTest; friend class BackgroundSyncServiceImplTest; + friend class CookieStoreManagerTest; friend class PaymentAppContentUnitTestBase; friend class StoragePartitionImplMap; friend class URLLoaderFactoryForBrowserProcess; @@ -222,7 +218,8 @@ class CONTENT_EXPORT StoragePartitionImpl RemoveQuotaManagedIgnoreDevTools); FRIEND_TEST_ALL_PREFIXES(StoragePartitionImplTest, RemoveCookieForever); FRIEND_TEST_ALL_PREFIXES(StoragePartitionImplTest, RemoveCookieLastHour); - FRIEND_TEST_ALL_PREFIXES(StoragePartitionImplTest, RemoveCookieWithMatcher); + FRIEND_TEST_ALL_PREFIXES(StoragePartitionImplTest, + RemoveCookieWithDeleteInfo); FRIEND_TEST_ALL_PREFIXES(StoragePartitionImplTest, RemoveUnprotectedLocalStorageForever); FRIEND_TEST_ALL_PREFIXES(StoragePartitionImplTest, @@ -246,14 +243,15 @@ class CONTENT_EXPORT StoragePartitionImpl storage::SpecialStoragePolicy* special_storage_policy); // We will never have both remove_origin be populated and a cookie_matcher. - void ClearDataImpl(uint32_t remove_mask, - uint32_t quota_storage_remove_mask, - const GURL& remove_origin, - const OriginMatcherFunction& origin_matcher, - const CookieMatcherFunction& cookie_matcher, - const base::Time begin, - const base::Time end, - base::OnceClosure callback); + void ClearDataImpl( + uint32_t remove_mask, + uint32_t quota_storage_remove_mask, + const GURL& remove_origin, + const OriginMatcherFunction& origin_matcher, + network::mojom::CookieDeletionFilterPtr cookie_deletion_filter, + const base::Time begin, + const base::Time end, + base::OnceClosure callback); void DeletionHelperDone(base::OnceClosure callback); @@ -314,6 +312,7 @@ class CONTENT_EXPORT StoragePartitionImpl scoped_refptr blob_url_loader_factory_; scoped_refptr blob_registry_; scoped_refptr prefetch_url_loader_service_; + scoped_refptr cookie_store_context_; // BindingSet for StoragePartitionService, using the process id as the // binding context type. The process id can subsequently be used during diff --git a/chromium/content/browser/storage_partition_impl_browsertest.cc b/chromium/content/browser/storage_partition_impl_browsertest.cc index 07f74ad1900..e2d5a2c13db 100644 --- a/chromium/content/browser/storage_partition_impl_browsertest.cc +++ b/chromium/content/browser/storage_partition_impl_browsertest.cc @@ -12,6 +12,8 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/content_browser_test.h" #include "content/shell/browser/shell.h" +#include "content/shell/browser/shell_browser_context.h" +#include "content/test/storage_partition_test_utils.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "net/http/http_response_headers.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -42,6 +44,12 @@ class StoragePartititionImplBrowsertest } ~StoragePartititionImplBrowsertest() override {} + GURL GetTestURL() const { + // Use '/echoheader' instead of '/echo' to avoid a disk_cache bug. + // See https://crbug.com/792255. + return embedded_test_server()->GetURL("/echoheader"); + } + private: base::test::ScopedFeatureList feature_list_; }; @@ -52,11 +60,16 @@ class StoragePartititionImplBrowsertest IN_PROC_BROWSER_TEST_P(StoragePartititionImplBrowsertest, NetworkContext) { ASSERT_TRUE(embedded_test_server()->Start()); + network::mojom::URLLoaderFactoryParamsPtr params = + network::mojom::URLLoaderFactoryParams::New(); + params->process_id = network::mojom::kBrowserProcessId; + params->is_corb_enabled = false; network::mojom::URLLoaderFactoryPtr loader_factory; BrowserContext::GetDefaultStoragePartition( shell()->web_contents()->GetBrowserContext()) ->GetNetworkContext() - ->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory), 0); + ->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory), + std::move(params)); network::ResourceRequest request; network::TestURLLoaderClient client; @@ -80,6 +93,71 @@ IN_PROC_BROWSER_TEST_P(StoragePartititionImplBrowsertest, NetworkContext) { EXPECT_EQ("bar", foo_header_value); } +// Make sure the factory info returned from +// |StoragePartition::GetURLLoaderFactoryForBrowserProcessIOThread()| works. +IN_PROC_BROWSER_TEST_P(StoragePartititionImplBrowsertest, + GetURLLoaderFactoryForBrowserProcessIOThread) { + ASSERT_TRUE(embedded_test_server()->Start()); + + base::ScopedAllowBlockingForTesting allow_blocking; + auto shared_url_loader_factory_info = + BrowserContext::GetDefaultStoragePartition( + shell()->web_contents()->GetBrowserContext()) + ->GetURLLoaderFactoryForBrowserProcessIOThread(); + + auto factory_owner = IOThreadSharedURLLoaderFactoryOwner::Create( + std::move(shared_url_loader_factory_info)); + + EXPECT_EQ(net::OK, factory_owner->LoadBasicRequestOnIOThread(GetTestURL())); +} + +// Make sure the factory info returned from +// |StoragePartition::GetURLLoaderFactoryForBrowserProcessIOThread()| doesn't +// crash if it's called after the StoragePartition is deleted. +IN_PROC_BROWSER_TEST_P(StoragePartititionImplBrowsertest, + BrowserIOFactoryInfoAfterStoragePartitionGone) { + ASSERT_TRUE(embedded_test_server()->Start()); + + base::ScopedAllowBlockingForTesting allow_blocking; + std::unique_ptr browser_context = + std::make_unique(true, nullptr); + auto* partition = + BrowserContext::GetDefaultStoragePartition(browser_context.get()); + auto shared_url_loader_factory_info = + partition->GetURLLoaderFactoryForBrowserProcessIOThread(); + + browser_context.reset(); + + auto factory_owner = IOThreadSharedURLLoaderFactoryOwner::Create( + std::move(shared_url_loader_factory_info)); + + EXPECT_EQ(net::ERR_FAILED, + factory_owner->LoadBasicRequestOnIOThread(GetTestURL())); +} + +// Make sure the factory constructed from +// |StoragePartition::GetURLLoaderFactoryForBrowserProcessIOThread()| doesn't +// crash if it's called after the StoragePartition is deleted. +IN_PROC_BROWSER_TEST_P(StoragePartititionImplBrowsertest, + BrowserIOFactoryAfterStoragePartitionGone) { + ASSERT_TRUE(embedded_test_server()->Start()); + + base::ScopedAllowBlockingForTesting allow_blocking; + std::unique_ptr browser_context = + std::make_unique(true, nullptr); + auto* partition = + BrowserContext::GetDefaultStoragePartition(browser_context.get()); + auto factory_owner = IOThreadSharedURLLoaderFactoryOwner::Create( + partition->GetURLLoaderFactoryForBrowserProcessIOThread()); + + EXPECT_EQ(net::OK, factory_owner->LoadBasicRequestOnIOThread(GetTestURL())); + + browser_context.reset(); + + EXPECT_EQ(net::ERR_FAILED, + factory_owner->LoadBasicRequestOnIOThread(GetTestURL())); +} + // NetworkServiceState::kEnabled currently DCHECKs on Android, as Android isn't // expected to create extra processes. #if defined(OS_ANDROID) diff --git a/chromium/content/browser/storage_partition_impl_map.cc b/chromium/content/browser/storage_partition_impl_map.cc index 7225a2a55d2..3be6979bde8 100644 --- a/chromium/content/browser/storage_partition_impl_map.cc +++ b/chromium/content/browser/storage_partition_impl_map.cc @@ -25,6 +25,7 @@ #include "content/browser/appcache/chrome_appcache_service.h" #include "content/browser/background_fetch/background_fetch_context.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" +#include "content/browser/cookie_store/cookie_store_context.h" #include "content/browser/devtools/devtools_url_request_interceptor.h" #include "content/browser/fileapi/browser_file_system_helper.h" #include "content/browser/loader/prefetch_url_loader_service.h" @@ -37,17 +38,18 @@ #include "content/browser/streams/stream_registry.h" #include "content/browser/streams/stream_url_request_job.h" #include "content/browser/webui/url_data_manager_backend.h" +#include "content/common/service_worker/service_worker_utils.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/content_constants.h" #include "content/public/common/content_switches.h" -#include "content/public/common/origin_trial_policy.h" #include "content/public/common/url_constants.h" #include "crypto/sha2.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" +#include "services/network/public/cpp/features.h" #include "storage/browser/blob/blob_storage_context.h" #include "storage/browser/blob/blob_url_request_job_factory.h" #include "storage/browser/fileapi/file_system_url_request_job_factory.h" @@ -434,6 +436,18 @@ StoragePartitionImpl* StoragePartitionImplMap::Get( browser_context_->CreateMediaRequestContext() : browser_context_->CreateMediaRequestContextForStoragePartition( partition->GetPath(), in_memory)); + partition->GetCookieStoreContext()->ListenToCookieChanges( + partition->GetNetworkContext(), base::DoNothing()); + + if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { + // This needs to happen after SetURLRequestContext() since we need this + // code path only for non-NetworkService cases where NetworkContext needs to + // be initialized using |url_request_context_|, which is initialized by + // SetURLRequestContext(). + DCHECK(partition->url_loader_factory_getter()); + DCHECK(partition->url_request_context_); + partition->url_loader_factory_getter()->HandleFactoryRequests(); + } PostCreateInitialization(partition, in_memory); diff --git a/chromium/content/browser/storage_partition_impl_unittest.cc b/chromium/content/browser/storage_partition_impl_unittest.cc index b51d8011af8..b151ad3c097 100644 --- a/chromium/content/browser/storage_partition_impl_unittest.cc +++ b/chromium/content/browser/storage_partition_impl_unittest.cc @@ -8,7 +8,6 @@ #include "base/files/file_util.h" #include "base/location.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread.h" @@ -29,6 +28,7 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "ppapi/buildflags/buildflags.h" +#include "services/network/cookie_manager.h" #include "storage/browser/quota/quota_manager.h" #include "storage/browser/test/mock_quota_manager.h" #include "storage/browser/test/mock_special_storage_policy.h" @@ -44,6 +44,8 @@ #endif // BUILDFLAG(ENABLE_PLUGINS) using net::CanonicalCookie; +using CookieDeletionFilter = network::mojom::CookieDeletionFilter; +using CookieDeletionFilterPtr = network::mojom::CookieDeletionFilterPtr; namespace content { namespace { @@ -80,14 +82,6 @@ const uint32_t kAllQuotaRemoveMask = StoragePartition::REMOVE_DATA_MASK_INDEXEDDB | StoragePartition::REMOVE_DATA_MASK_WEBSQL; -bool AlwaysTrueCookiePredicate(const net::CanonicalCookie& cookie) { - return true; -} - -bool AlwaysFalseCookiePredicate(const net::CanonicalCookie& cookie) { - return false; -} - class AwaitCompletionHelper { public: AwaitCompletionHelper() : start_(false), already_quit_(false) {} @@ -573,16 +567,19 @@ void ClearCookies(content::StoragePartition* partition, delete_begin, delete_end, run_loop->QuitClosure()); } -void ClearCookiesWithMatcher( - content::StoragePartition* partition, - const base::Time delete_begin, - const base::Time delete_end, - const StoragePartition::CookieMatcherFunction& cookie_matcher, - base::RunLoop* run_loop) { +void ClearCookiesMatchingInfo(content::StoragePartition* partition, + CookieDeletionFilterPtr delete_filter, + base::RunLoop* run_loop) { + base::Time delete_begin; + if (delete_filter->created_after_time.has_value()) + delete_begin = delete_filter->created_after_time.value(); + base::Time delete_end; + if (delete_filter->created_before_time.has_value()) + delete_end = delete_filter->created_before_time.value(); partition->ClearData(StoragePartition::REMOVE_DATA_MASK_COOKIES, StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, StoragePartition::OriginMatcherFunction(), - cookie_matcher, delete_begin, delete_end, + std::move(delete_filter), delete_begin, delete_end, run_loop->QuitClosure()); } @@ -622,6 +619,11 @@ void ClearPluginPrivateData(content::StoragePartition* partition, } #endif // BUILDFLAG(ENABLE_PLUGINS) +bool FilterMatchesCookie(const CookieDeletionFilterPtr& filter, + const net::CanonicalCookie& cookie) { + return network::DeletionFilterToInfo(filter.Clone()).Matches(cookie); +} + } // namespace class StoragePartitionImplTest : public testing::Test { @@ -1130,13 +1132,8 @@ TEST_F(StoragePartitionImplTest, RemoveCookieLastHour) { EXPECT_FALSE(tester.ContainsCookie()); } -TEST_F(StoragePartitionImplTest, RemoveCookieWithMatcher) { +TEST_F(StoragePartitionImplTest, RemoveCookieWithDeleteInfo) { RemoveCookieTester tester(browser_context()); - StoragePartition::CookieMatcherFunction true_predicate = - base::Bind(&AlwaysTrueCookiePredicate); - - StoragePartition::CookieMatcherFunction false_predicate = - base::Bind(&AlwaysFalseCookiePredicate); tester.AddCookie(); ASSERT_TRUE(tester.ContainsCookie()); @@ -1145,21 +1142,10 @@ TEST_F(StoragePartitionImplTest, RemoveCookieWithMatcher) { BrowserContext::GetDefaultStoragePartition(browser_context())); partition->SetURLRequestContext(browser_context()->GetRequestContext()); - // Return false from our predicate, and make sure the cookies is still around. - base::RunLoop run_loop; - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&ClearCookiesWithMatcher, partition, base::Time(), - base::Time::Max(), std::move(false_predicate), &run_loop)); - run_loop.RunUntilIdle(); - EXPECT_TRUE(tester.ContainsCookie()); - - // Now we return true from our predicate. base::RunLoop run_loop2; base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&ClearCookiesWithMatcher, partition, base::Time(), - base::Time::Max(), std::move(true_predicate), &run_loop2)); + FROM_HERE, base::BindOnce(&ClearCookiesMatchingInfo, partition, + CookieDeletionFilter::New(), &run_loop2)); run_loop2.RunUntilIdle(); EXPECT_FALSE(tester.ContainsCookie()); } @@ -1397,8 +1383,8 @@ TEST(StoragePartitionImplStaticTest, CreatePredicateForHostCookies) { GURL url3("https://www.google.com/"); net::CookieOptions options; - net::CookieStore::CookiePredicate predicate = - StoragePartitionImpl::CreatePredicateForHostCookies(url); + CookieDeletionFilterPtr deletion_filter = CookieDeletionFilter::New(); + deletion_filter->host_name = url.host(); base::Time now = base::Time::Now(); std::vector> valid_cookies; @@ -1413,10 +1399,14 @@ TEST(StoragePartitionImplStaticTest, CreatePredicateForHostCookies) { CanonicalCookie::Create(url2, "A=B;domain=.example.com", now, options)); invalid_cookies.push_back(CanonicalCookie::Create(url3, "A=B", now, options)); - for (const auto& cookie : valid_cookies) - EXPECT_TRUE(predicate.Run(*cookie)) << cookie->DebugString(); - for (const auto& cookie : invalid_cookies) - EXPECT_FALSE(predicate.Run(*cookie)) << cookie->DebugString(); + for (const auto& cookie : valid_cookies) { + EXPECT_TRUE(FilterMatchesCookie(deletion_filter, *cookie)) + << cookie->DebugString(); + } + for (const auto& cookie : invalid_cookies) { + EXPECT_FALSE(FilterMatchesCookie(deletion_filter, *cookie)) + << cookie->DebugString(); + } } } // namespace content diff --git a/chromium/content/browser/streams/stream.cc b/chromium/content/browser/streams/stream.cc index 8a018a7895f..8ce10de0d94 100644 --- a/chromium/content/browser/streams/stream.cc +++ b/chromium/content/browser/streams/stream.cc @@ -42,10 +42,10 @@ Stream::Stream(StreamRegistry* registry, &writer_, &reader_); // Setup callback for writing. - writer_->RegisterCallback(base::Bind(&Stream::OnSpaceAvailable, - weak_ptr_factory_.GetWeakPtr())); - reader_->RegisterCallback(base::Bind(&Stream::OnDataAvailable, - weak_ptr_factory_.GetWeakPtr())); + writer_->RegisterCallback(base::BindRepeating( + &Stream::OnSpaceAvailable, weak_ptr_factory_.GetWeakPtr())); + reader_->RegisterCallback(base::BindRepeating( + &Stream::OnDataAvailable, weak_ptr_factory_.GetWeakPtr())); registry_->RegisterStream(this); } diff --git a/chromium/content/browser/tracing/background_tracing_manager_browsertest.cc b/chromium/content/browser/tracing/background_tracing_manager_browsertest.cc index ead569f8522..374da5bb307 100644 --- a/chromium/content/browser/tracing/background_tracing_manager_browsertest.cc +++ b/chromium/content/browser/tracing/background_tracing_manager_browsertest.cc @@ -107,16 +107,13 @@ class BackgroundTracingManagerBrowserTest : public ContentBrowserTest { class BackgroundTracingManagerUploadConfigWrapper { public: - BackgroundTracingManagerUploadConfigWrapper(const base::Closure& callback) - : callback_(callback), receive_count_(0) { - receive_callback_ = - base::Bind(&BackgroundTracingManagerUploadConfigWrapper::Upload, - base::Unretained(this)); - } + BackgroundTracingManagerUploadConfigWrapper(base::OnceClosure callback) + : callback_(std::move(callback)), receive_count_(0) {} - void Upload(const scoped_refptr& file_contents, - std::unique_ptr metadata, - base::Callback done_callback) { + void Upload( + const scoped_refptr& file_contents, + std::unique_ptr metadata, + BackgroundTracingManager::FinishedProcessingCallback done_callback) { receive_count_ += 1; EXPECT_TRUE(file_contents); @@ -142,8 +139,9 @@ class BackgroundTracingManagerUploadConfigWrapper { last_file_contents_.assign(output_str.data(), bytes_written); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - std::move(done_callback)); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback_); + base::BindOnce(std::move(done_callback), true)); + CHECK(callback_); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(callback_)); } bool TraceHasMatchingString(const char* str) { @@ -152,14 +150,13 @@ class BackgroundTracingManagerUploadConfigWrapper { int get_receive_count() const { return receive_count_; } - const BackgroundTracingManager::ReceiveCallback& get_receive_callback() - const { - return receive_callback_; + BackgroundTracingManager::ReceiveCallback get_receive_callback() { + return base::BindOnce(&BackgroundTracingManagerUploadConfigWrapper::Upload, + base::Unretained(this)); } private: - BackgroundTracingManager::ReceiveCallback receive_callback_; - base::Closure callback_; + base::OnceClosure callback_; int receive_count_; std::string last_file_contents_; }; diff --git a/chromium/content/browser/tracing/background_tracing_manager_impl.cc b/chromium/content/browser/tracing/background_tracing_manager_impl.cc index 6fd8e9c19e2..c6aefd65be1 100644 --- a/chromium/content/browser/tracing/background_tracing_manager_impl.cc +++ b/chromium/content/browser/tracing/background_tracing_manager_impl.cc @@ -25,7 +25,7 @@ #include "content/public/browser/tracing_delegate.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" -#include "services/tracing/public/cpp/chrome_trace_event_agent.h" +#include "services/tracing/public/cpp/trace_event_agent.h" namespace content { @@ -44,8 +44,10 @@ enum BackgroundTracingMetrics { FINALIZATION_ALLOWED = 5, FINALIZATION_DISALLOWED = 6, FINALIZATION_STARTED = 7, - FINALIZATION_COMPLETE = 8, + OBSOLETE_FINALIZATION_COMPLETE = 8, SCENARIO_ACTION_FAILED_LOWRES_CLOCK = 9, + UPLOAD_FAILED = 10, + UPLOAD_SUCCEEDED = 11, NUMBER_OF_BACKGROUND_TRACING_METRICS, }; @@ -105,9 +107,11 @@ BackgroundTracingManagerImpl::~BackgroundTracingManagerImpl() { } void BackgroundTracingManagerImpl::AddMetadataGeneratorFunction() { - tracing::ChromeTraceEventAgent::GetInstance()->AddMetadataGeneratorFunction( - base::BindRepeating(&BackgroundTracingManagerImpl::GenerateMetadataDict, - base::Unretained(this))); + TracingControllerImpl::GetInstance() + ->GetTraceEventAgent() + ->AddMetadataGeneratorFunction(base::BindRepeating( + &BackgroundTracingManagerImpl::GenerateMetadataDict, + base::Unretained(this))); } void BackgroundTracingManagerImpl::WhenIdle( @@ -118,7 +122,7 @@ void BackgroundTracingManagerImpl::WhenIdle( bool BackgroundTracingManagerImpl::SetActiveScenario( std::unique_ptr config, - const BackgroundTracingManager::ReceiveCallback& receive_callback, + BackgroundTracingManager::ReceiveCallback receive_callback, DataFiltering data_filtering) { CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); RecordBackgroundTracingMetric(SCENARIO_ACTIVATION_REQUESTED); @@ -178,7 +182,7 @@ bool BackgroundTracingManagerImpl::SetActiveScenario( } config_ = std::move(config_impl); - receive_callback_ = receive_callback; + receive_callback_ = std::move(receive_callback); requires_anonymized_data_ = requires_anonymized_data; if (config_) { @@ -503,21 +507,21 @@ void BackgroundTracingManagerImpl::OnFinalizeStarted( file_contents->size() / 1024); if (!receive_callback_.is_null()) { - receive_callback_.Run( - file_contents, std::move(metadata), - base::Bind(&BackgroundTracingManagerImpl::OnFinalizeComplete, - base::Unretained(this))); + std::move(receive_callback_) + .Run(file_contents, std::move(metadata), + base::BindOnce(&BackgroundTracingManagerImpl::OnFinalizeComplete, + base::Unretained(this))); } if (!started_finalizing_closure.is_null()) std::move(started_finalizing_closure).Run(); } -void BackgroundTracingManagerImpl::OnFinalizeComplete() { +void BackgroundTracingManagerImpl::OnFinalizeComplete(bool success) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::BindOnce(&BackgroundTracingManagerImpl::OnFinalizeComplete, - base::Unretained(this))); + base::Unretained(this), success)); return; } @@ -541,7 +545,11 @@ void BackgroundTracingManagerImpl::OnFinalizeComplete() { AbortScenario(); } - RecordBackgroundTracingMetric(FINALIZATION_COMPLETE); + if (success) { + RecordBackgroundTracingMetric(UPLOAD_SUCCEEDED); + } else { + RecordBackgroundTracingMetric(UPLOAD_FAILED); + } } bool BackgroundTracingManagerImpl::IsAllowedFinalization() const { @@ -560,9 +568,10 @@ BackgroundTracingManagerImpl::GenerateMetadataDict() { auto config_dict = std::make_unique(); config_->IntoDict(config_dict.get()); metadata_dict->Set("config", std::move(config_dict)); + metadata_dict->SetString("scenario_name", config_->scenario_name()); } if (last_triggered_rule_) - metadata_dict->Set("last_triggered_rule_", std::move(last_triggered_rule_)); + metadata_dict->Set("last_triggered_rule", std::move(last_triggered_rule_)); return metadata_dict; } diff --git a/chromium/content/browser/tracing/background_tracing_manager_impl.h b/chromium/content/browser/tracing/background_tracing_manager_impl.h index d99219b37db..d5c8fe92c1b 100644 --- a/chromium/content/browser/tracing/background_tracing_manager_impl.h +++ b/chromium/content/browser/tracing/background_tracing_manager_impl.h @@ -58,7 +58,7 @@ class BackgroundTracingManagerImpl : public BackgroundTracingManager { CONTENT_EXPORT static BackgroundTracingManagerImpl* GetInstance(); bool SetActiveScenario(std::unique_ptr, - const ReceiveCallback&, + ReceiveCallback, DataFiltering data_filtering) override; void WhenIdle(IdleCallback idle_callback) override; @@ -104,7 +104,7 @@ class BackgroundTracingManagerImpl : public BackgroundTracingManager { void OnFinalizeStarted(base::Closure started_finalizing_closure, std::unique_ptr metadata, base::RefCountedString*); - void OnFinalizeComplete(); + void OnFinalizeComplete(bool success); void BeginFinalizing(StartedFinalizingCallback); void ValidateStartupScenario(); diff --git a/chromium/content/browser/tracing/cast_tracing_agent.cc b/chromium/content/browser/tracing/cast_tracing_agent.cc index a281910d210..41b62141455 100644 --- a/chromium/content/browser/tracing/cast_tracing_agent.cc +++ b/chromium/content/browser/tracing/cast_tracing_agent.cc @@ -55,19 +55,18 @@ CastTracingAgent::CastTracingAgent(service_manager::Connector* connector) CastTracingAgent::~CastTracingAgent() = default; // tracing::mojom::Agent. Called by Mojo internals on the UI thread. -void CastTracingAgent::StartTracing( - const std::string& config, - base::TimeTicks coordinator_time, - const Agent::StartTracingCallback& callback) { +void CastTracingAgent::StartTracing(const std::string& config, + base::TimeTicks coordinator_time, + Agent::StartTracingCallback callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); base::trace_event::TraceConfig trace_config(config); if (!trace_config.IsSystraceEnabled()) { - callback.Run(false /* success */); + std::move(callback).Run(false /* success */); return; } - start_tracing_callback_ = callback; + start_tracing_callback_ = std::move(callback); task_runner_->PostTask(FROM_HERE, base::BindOnce(&CastTracingAgent::StartTracingOnIO, @@ -86,9 +85,8 @@ void CastTracingAgent::StopAndFlush(tracing::mojom::RecorderPtr recorder) { base::ThreadTaskRunnerHandle::Get())); } -void CastTracingAgent::GetCategories( - const Agent::GetCategoriesCallback& callback) { - callback.Run(GetAllTracingCategories()); +void CastTracingAgent::GetCategories(Agent::GetCategoriesCallback callback) { + std::move(callback).Run(GetAllTracingCategories()); } void CastTracingAgent::StartTracingOnIO( @@ -116,7 +114,8 @@ void CastTracingAgent::FinishStartOnIO( void CastTracingAgent::FinishStart(chromecast::SystemTracer::Status status) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - start_tracing_callback_.Run(status == chromecast::SystemTracer::Status::OK); + std::move(start_tracing_callback_) + .Run(status == chromecast::SystemTracer::Status::OK); } void CastTracingAgent::StopAndFlushOnIO( diff --git a/chromium/content/browser/tracing/cast_tracing_agent.h b/chromium/content/browser/tracing/cast_tracing_agent.h index b26a3bde810..1604d951de3 100644 --- a/chromium/content/browser/tracing/cast_tracing_agent.h +++ b/chromium/content/browser/tracing/cast_tracing_agent.h @@ -31,9 +31,9 @@ class CastTracingAgent : public tracing::BaseAgent { // tracing::mojom::Agent. Called by Mojo internals on the UI thread. void StartTracing(const std::string& config, base::TimeTicks coordinator_time, - const Agent::StartTracingCallback& callback) override; + Agent::StartTracingCallback callback) override; void StopAndFlush(tracing::mojom::RecorderPtr recorder) override; - void GetCategories(const Agent::GetCategoriesCallback& callback) override; + void GetCategories(Agent::GetCategoriesCallback callback) override; void StartTracingOnIO(scoped_refptr reply_task_runner, const std::string& categories); diff --git a/chromium/content/browser/tracing/cros_tracing_agent.cc b/chromium/content/browser/tracing/cros_tracing_agent.cc index bb6fd6545d2..30bb8757a81 100644 --- a/chromium/content/browser/tracing/cros_tracing_agent.cc +++ b/chromium/content/browser/tracing/cros_tracing_agent.cc @@ -31,35 +31,35 @@ CrOSTracingAgent::CrOSTracingAgent(service_manager::Connector* connector) CrOSTracingAgent::~CrOSTracingAgent() = default; // tracing::mojom::Agent. Called by Mojo internals on the UI thread. -void CrOSTracingAgent::StartTracing( - const std::string& config, - base::TimeTicks coordinator_time, - const Agent::StartTracingCallback& callback) { +void CrOSTracingAgent::StartTracing(const std::string& config, + base::TimeTicks coordinator_time, + Agent::StartTracingCallback callback) { base::trace_event::TraceConfig trace_config(config); debug_daemon_ = chromeos::DBusThreadManager::Get()->GetDebugDaemonClient(); if (!trace_config.IsSystraceEnabled() || !debug_daemon_) { - callback.Run(false /* success */); + std::move(callback).Run(false /* success */); return; } - start_tracing_callback_ = std::move(callback); debug_daemon_->SetStopAgentTracingTaskRunner( base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})); debug_daemon_->StartAgentTracing( trace_config, - base::BindRepeating(&CrOSTracingAgent::StartTracingCallbackProxy, - base::Unretained(this))); + base::BindOnce(&CrOSTracingAgent::StartTracingCallbackProxy, + base::Unretained(this), std::move(callback))); } void CrOSTracingAgent::StopAndFlush(tracing::mojom::RecorderPtr recorder) { DCHECK(debug_daemon_); recorder_ = std::move(recorder); - debug_daemon_->StopAgentTracing(base::BindRepeating( - &CrOSTracingAgent::RecorderProxy, base::Unretained(this))); + debug_daemon_->StopAgentTracing( + base::BindOnce(&CrOSTracingAgent::RecorderProxy, base::Unretained(this))); } -void CrOSTracingAgent::StartTracingCallbackProxy(const std::string& agent_name, - bool success) { - start_tracing_callback_.Run(success); +void CrOSTracingAgent::StartTracingCallbackProxy( + Agent::StartTracingCallback callback, + const std::string& agent_name, + bool success) { + std::move(callback).Run(success); } void CrOSTracingAgent::RecorderProxy( diff --git a/chromium/content/browser/tracing/cros_tracing_agent.h b/chromium/content/browser/tracing/cros_tracing_agent.h index 2bf56ab1ff8..182163621af 100644 --- a/chromium/content/browser/tracing/cros_tracing_agent.h +++ b/chromium/content/browser/tracing/cros_tracing_agent.h @@ -37,16 +37,17 @@ class CrOSTracingAgent : public tracing::BaseAgent { // tracing::mojom::Agent. Called by Mojo internals on the UI thread. void StartTracing(const std::string& config, base::TimeTicks coordinator_time, - const Agent::StartTracingCallback& callback) override; + Agent::StartTracingCallback callback) override; void StopAndFlush(tracing::mojom::RecorderPtr recorder) override; - void StartTracingCallbackProxy(const std::string& agent_name, bool success); + void StartTracingCallbackProxy(Agent::StartTracingCallback callback, + const std::string& agent_name, + bool success); void RecorderProxy(const std::string& event_name, const std::string& events_label, const scoped_refptr& events); chromeos::DebugDaemonClient* debug_daemon_ = nullptr; - Agent::StartTracingCallback start_tracing_callback_; tracing::mojom::RecorderPtr recorder_; DISALLOW_COPY_AND_ASSIGN(CrOSTracingAgent); diff --git a/chromium/content/browser/tracing/etw_tracing_agent_win.cc b/chromium/content/browser/tracing/etw_tracing_agent_win.cc index adc428b6a9c..f25811775f2 100644 --- a/chromium/content/browser/tracing/etw_tracing_agent_win.cc +++ b/chromium/content/browser/tracing/etw_tracing_agent_win.cc @@ -59,14 +59,13 @@ EtwTracingAgent::~EtwTracingAgent() { g_etw_tracing_agent = nullptr; } -void EtwTracingAgent::StartTracing( - const std::string& config, - base::TimeTicks coordinator_time, - const Agent::StartTracingCallback& callback) { +void EtwTracingAgent::StartTracing(const std::string& config, + base::TimeTicks coordinator_time, + Agent::StartTracingCallback callback) { base::trace_event::TraceConfig trace_config(config); // Activate kernel tracing. if (!trace_config.IsSystraceEnabled() || !StartKernelSessionTracing()) { - callback.Run(false /* success */); + std::move(callback).Run(false /* success */); return; } is_tracing_ = true; @@ -77,9 +76,9 @@ void EtwTracingAgent::StartTracing( // Tracing agents, e.g. this, live as long as BrowserMainLoop lives and so // using base::Unretained here is safe. thread_.task_runner()->PostTask( - FROM_HERE, base::Bind(&EtwTracingAgent::TraceAndConsumeOnThread, - base::Unretained(this))); - callback.Run(true /* success */); + FROM_HERE, base::BindOnce(&EtwTracingAgent::TraceAndConsumeOnThread, + base::Unretained(this))); + std::move(callback).Run(true /* success */); } void EtwTracingAgent::StopAndFlush(tracing::mojom::RecorderPtr recorder) { @@ -93,7 +92,7 @@ void EtwTracingAgent::StopAndFlush(tracing::mojom::RecorderPtr recorder) { // BrowserMainLoop lives and so using base::Unretained here is safe. thread_.task_runner()->PostTask( FROM_HERE, - base::Bind(&EtwTracingAgent::FlushOnThread, base::Unretained(this))); + base::BindOnce(&EtwTracingAgent::FlushOnThread, base::Unretained(this))); } void EtwTracingAgent::OnStopSystemTracingDone(const std::string& output) { diff --git a/chromium/content/browser/tracing/etw_tracing_agent_win.h b/chromium/content/browser/tracing/etw_tracing_agent_win.h index 5136a197539..f9e78372bdb 100644 --- a/chromium/content/browser/tracing/etw_tracing_agent_win.h +++ b/chromium/content/browser/tracing/etw_tracing_agent_win.h @@ -43,7 +43,7 @@ class EtwTracingAgent : public base::win::EtwTraceConsumerBase, // tracing::mojom::Agent. Called by Mojo internals on the UI thread. void StartTracing(const std::string& config, base::TimeTicks coordinator_time, - const Agent::StartTracingCallback& callback) override; + Agent::StartTracingCallback callback) override; void StopAndFlush(tracing::mojom::RecorderPtr recorder) override; // Static override of EtwTraceConsumerBase::ProcessEvent. diff --git a/chromium/content/browser/tracing/memory_instrumentation_browsertest.cc b/chromium/content/browser/tracing/memory_instrumentation_browsertest.cc index 5ebe6aa5eb3..1eeb12aa138 100644 --- a/chromium/content/browser/tracing/memory_instrumentation_browsertest.cc +++ b/chromium/content/browser/tracing/memory_instrumentation_browsertest.cc @@ -27,7 +27,7 @@ namespace content { class MemoryInstrumentationTest : public ContentBrowserTest { protected: void Navigate(Shell* shell) { - NavigateToURL(shell, GetTestUrl("", "title.html")); + EXPECT_TRUE(NavigateToURL(shell, GetTestUrl("", "title1.html"))); } }; @@ -72,7 +72,7 @@ std::unique_ptr DoGlobalDump() { defined(THREAD_SANITIZER) #define MAYBE_PrivateFootprintComputation DISABLED_PrivateFootprintComputation #else -#define MAYBE_PrivateFootprintComputation PrivateFootprintComputatio +#define MAYBE_PrivateFootprintComputation PrivateFootprintComputation #endif // Despite the location, this test is not tracing related. @@ -96,7 +96,7 @@ IN_PROC_BROWSER_TEST_F(MemoryInstrumentationTest, content::WebContents* web_contents = shell()->web_contents(); base::ProcessId renderer_pid = - base::GetProcId(web_contents->GetMainFrame()->GetProcess()->GetHandle()); + web_contents->GetMainFrame()->GetProcess()->GetProcess().Pid(); // Should allocate at least 4*10^6 / 1024 = 4000kb. EXPECT_TRUE(content::ExecuteScript(web_contents, diff --git a/chromium/content/browser/tracing/power_tracing_agent.cc b/chromium/content/browser/tracing/power_tracing_agent.cc index af496fb0d81..2ae2a4d50da 100644 --- a/chromium/content/browser/tracing/power_tracing_agent.cc +++ b/chromium/content/browser/tracing/power_tracing_agent.cc @@ -51,46 +51,46 @@ PowerTracingAgent::PowerTracingAgent(service_manager::Connector* connector) PowerTracingAgent::PowerTracingAgent() : binding_(this) {} PowerTracingAgent::~PowerTracingAgent() = default; -void PowerTracingAgent::StartTracing( - const std::string& config, - base::TimeTicks coordinator_time, - const Agent::StartTracingCallback& callback) { +void PowerTracingAgent::StartTracing(const std::string& config, + base::TimeTicks coordinator_time, + Agent::StartTracingCallback callback) { base::trace_event::TraceConfig trace_config(config); if (!trace_config.IsSystraceEnabled()) { - callback.Run(false /* success */); + std::move(callback).Run(false /* success */); return; } base::PostTaskWithTraits( FROM_HERE, {base::TaskPriority::BACKGROUND, base::MayBlock()}, base::BindOnce(&PowerTracingAgent::FindBattOrOnBackgroundThread, - base::Unretained(this), callback)); + base::Unretained(this), std::move(callback))); } void PowerTracingAgent::FindBattOrOnBackgroundThread( - const Agent::StartTracingCallback& callback) { + Agent::StartTracingCallback callback) { std::string path = battor::BattOrFinder::FindBattOr(); if (path.empty()) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::BindOnce(callback, false /* success */)); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(std::move(callback), false /* success */)); return; } BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce(&PowerTracingAgent::StartTracingOnIOThread, - base::Unretained(this), path, callback)); + base::Unretained(this), path, std::move(callback))); } void PowerTracingAgent::StartTracingOnIOThread( const std::string& path, - const Agent::StartTracingCallback& callback) { + Agent::StartTracingCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); battor_agent_.reset(new battor::BattOrAgent( path, this, BrowserThread::GetTaskRunnerForThread(BrowserThread::UI))); - start_tracing_callback_ = callback; + start_tracing_callback_ = std::move(callback); battor_agent_->StartTracing(); } @@ -101,9 +101,9 @@ void PowerTracingAgent::OnStartTracingComplete(battor::BattOrError error) { if (!success) battor_agent_.reset(); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::BindOnce(start_tracing_callback_, success)); - start_tracing_callback_.Reset(); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(std::move(start_tracing_callback_), success)); } void PowerTracingAgent::StopAndFlush(tracing::mojom::RecorderPtr recorder) { @@ -137,27 +137,28 @@ void PowerTracingAgent::OnStopTracingComplete( void PowerTracingAgent::RequestClockSyncMarker( const std::string& sync_id, - const Agent::RequestClockSyncMarkerCallback& callback) { + Agent::RequestClockSyncMarkerCallback callback) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce(&PowerTracingAgent::RequestClockSyncMarkerOnIOThread, - base::Unretained(this), sync_id, callback)); + base::Unretained(this), sync_id, std::move(callback))); } void PowerTracingAgent::RequestClockSyncMarkerOnIOThread( const std::string& sync_id, - const Agent::RequestClockSyncMarkerCallback& callback) { + Agent::RequestClockSyncMarkerCallback callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); // This makes sense only when the battor agent exists. if (!battor_agent_) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::BindOnce(callback, base::TimeTicks(), base::TimeTicks())); + base::BindOnce(std::move(callback), base::TimeTicks(), + base::TimeTicks())); return; } - request_clock_sync_marker_callback_ = callback; + request_clock_sync_marker_callback_ = std::move(callback); request_clock_sync_marker_start_time_ = TRACE_TIME_TICKS_NOW(); battor_agent_->RecordClockSyncMarker(sync_id); } @@ -172,11 +173,10 @@ void PowerTracingAgent::OnRecordClockSyncMarkerComplete( if (error != battor::BATTOR_ERROR_NONE) issue_start_ts = issue_end_ts = base::TimeTicks(); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::BindOnce(request_clock_sync_marker_callback_, - issue_start_ts, issue_end_ts)); - - request_clock_sync_marker_callback_.Reset(); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(std::move(request_clock_sync_marker_callback_), + issue_start_ts, issue_end_ts)); request_clock_sync_marker_start_time_ = base::TimeTicks(); } @@ -185,14 +185,13 @@ void PowerTracingAgent::OnGetFirmwareGitHashComplete( return; } -void PowerTracingAgent::GetCategories( - const Agent::GetCategoriesCallback& callback) { - callback.Run(""); +void PowerTracingAgent::GetCategories(Agent::GetCategoriesCallback callback) { + std::move(callback).Run(""); } void PowerTracingAgent::RequestBufferStatus( - const Agent::RequestBufferStatusCallback& callback) { - callback.Run(0, 0); + Agent::RequestBufferStatusCallback callback) { + std::move(callback).Run(0, 0); } } // namespace content diff --git a/chromium/content/browser/tracing/power_tracing_agent.h b/chromium/content/browser/tracing/power_tracing_agent.h index 5e7f9a33440..6623bc121d5 100644 --- a/chromium/content/browser/tracing/power_tracing_agent.h +++ b/chromium/content/browser/tracing/power_tracing_agent.h @@ -57,23 +57,22 @@ class PowerTracingAgent : public Agent, public battor::BattOrAgent::Listener { // tracing::mojom::Agent. Called by Mojo internals on the UI thread. void StartTracing(const std::string& config, base::TimeTicks coordinator_time, - const Agent::StartTracingCallback& callback) override; + Agent::StartTracingCallback callback) override; void StopAndFlush(tracing::mojom::RecorderPtr recorder) override; void RequestClockSyncMarker( const std::string& sync_id, - const Agent::RequestClockSyncMarkerCallback& callback) override; - void GetCategories(const Agent::GetCategoriesCallback& callback) override; + Agent::RequestClockSyncMarkerCallback callback) override; + void GetCategories(Agent::GetCategoriesCallback callback) override; void RequestBufferStatus( - const Agent::RequestBufferStatusCallback& callback) override; + Agent::RequestBufferStatusCallback callback) override; - void FindBattOrOnBackgroundThread( - const Agent::StartTracingCallback& callback); + void FindBattOrOnBackgroundThread(Agent::StartTracingCallback callback); void StartTracingOnIOThread(const std::string& path, - const Agent::StartTracingCallback& callback); + Agent::StartTracingCallback callback); void StopAndFlushOnIOThread(tracing::mojom::RecorderPtr recorder); void RequestClockSyncMarkerOnIOThread( const std::string& sync_id, - const Agent::RequestClockSyncMarkerCallback& callback); + Agent::RequestClockSyncMarkerCallback callback); // Returns the path of a BattOr (e.g. /dev/ttyUSB0), or an empty string if // none are found. diff --git a/chromium/content/browser/tracing/tracing_controller_browsertest.cc b/chromium/content/browser/tracing/tracing_controller_browsertest.cc index ba83d7cfed8..680c54845a5 100644 --- a/chromium/content/browser/tracing/tracing_controller_browsertest.cc +++ b/chromium/content/browser/tracing/tracing_controller_browsertest.cc @@ -23,7 +23,7 @@ #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" #include "content/test/test_content_browser_client.h" -#include "services/tracing/public/cpp/chrome_trace_event_agent.h" +#include "services/tracing/public/cpp/trace_event_agent.h" using base::trace_event::RECORD_CONTINUOUSLY; using base::trace_event::RECORD_UNTIL_FULL; @@ -236,10 +236,9 @@ class TracingControllerTest : public ContentBrowserTest { base::trace_event::TraceLog::GetInstance()->SetArgumentFilterPredicate( base::Bind(&IsTraceEventArgsWhitelisted)); - TracingController* controller = TracingController::GetInstance(); - tracing::ChromeTraceEventAgent::GetInstance()->AddMetadataGeneratorFunction( - base::Bind(&TracingControllerTest::GenerateMetadataDict, - base::Unretained(this))); + TracingControllerImpl* controller = TracingControllerImpl::GetInstance(); + controller->GetTraceEventAgent()->AddMetadataGeneratorFunction(base::Bind( + &TracingControllerTest::GenerateMetadataDict, base::Unretained(this))); { base::RunLoop run_loop; @@ -391,9 +390,6 @@ IN_PROC_BROWSER_TEST_F(TracingControllerTest, DisableRecordingStoresMetadata) { std::string os_name; last_metadata()->GetString("os-name", &os_name); EXPECT_TRUE(os_name.length() > 0); - std::string cpu_brand; - last_metadata()->GetString("cpu-brand", &cpu_brand); - EXPECT_TRUE(cpu_brand.length() > 0); std::string command_line; last_metadata()->GetString("command_line", &command_line); EXPECT_TRUE(command_line.length() > 0); @@ -408,10 +404,6 @@ IN_PROC_BROWSER_TEST_F(TracingControllerTest, TestStartAndStopTracingStringWithFilter(); // Check that a number of important keys exist in the metadata dictionary. EXPECT_TRUE(last_metadata() != nullptr); - std::string cpu_brand; - last_metadata()->GetString("cpu-brand", &cpu_brand); - EXPECT_TRUE(cpu_brand.length() > 0); - EXPECT_TRUE(cpu_brand != "__stripped__"); std::string network_type; last_metadata()->GetString("network-type", &network_type); EXPECT_TRUE(network_type.length() > 0); diff --git a/chromium/content/browser/tracing/tracing_controller_impl.cc b/chromium/content/browser/tracing/tracing_controller_impl.cc index 14eff00db8c..0ae496ae61e 100644 --- a/chromium/content/browser/tracing/tracing_controller_impl.cc +++ b/chromium/content/browser/tracing/tracing_controller_impl.cc @@ -20,7 +20,7 @@ #include "base/trace_event/trace_config.h" #include "base/values.h" #include "build/build_config.h" -#include "components/tracing/common/trace_config_file.h" +#include "components/tracing/common/trace_startup_config.h" #include "content/browser/tracing/file_tracing_provider_impl.h" #include "content/browser/tracing/tracing_ui.h" #include "content/public/browser/browser_thread.h" @@ -34,7 +34,7 @@ #include "mojo/public/cpp/system/data_pipe.h" #include "net/base/network_change_notifier.h" #include "services/service_manager/public/cpp/connector.h" -#include "services/tracing/public/cpp/chrome_trace_event_agent.h" +#include "services/tracing/public/cpp/trace_event_agent.h" #include "services/tracing/public/mojom/constants.mojom.h" #include "v8/include/v8-version-string.h" @@ -61,6 +61,13 @@ #include "content/browser/tracing/etw_tracing_agent_win.h" #endif +#if defined(OS_ANDROID) +#include "base/debug/elf_reader_linux.h" + +// Symbol with virtual address of the start of ELF header of the current binary. +extern char __ehdr_start; +#endif // defined(OS_ANDROID) + namespace content { namespace { @@ -146,18 +153,24 @@ void TracingControllerImpl::AddAgents() { agents_.push_back(std::make_unique(connector)); #endif - auto chrome_agent = std::make_unique( + auto trace_event_agent = tracing::TraceEventAgent::Create( connector, true /* request_clock_sync_marker_on_android */); + // For adding general CPU, network, OS, and other system information to the // metadata. - chrome_agent->AddMetadataGeneratorFunction(base::BindRepeating( + trace_event_agent->AddMetadataGeneratorFunction(base::BindRepeating( &TracingControllerImpl::GenerateMetadataDict, base::Unretained(this))); if (delegate_) { - chrome_agent->AddMetadataGeneratorFunction( + trace_event_agent->AddMetadataGeneratorFunction( base::BindRepeating(&TracingDelegate::GenerateMetadataDict, base::Unretained(delegate_.get()))); } - agents_.push_back(std::move(chrome_agent)); + trace_event_agent_ = std::move(trace_event_agent); +} + +tracing::TraceEventAgent* TracingControllerImpl::GetTraceEventAgent() const { + DCHECK(trace_event_agent_); + return trace_event_agent_.get(); } std::unique_ptr @@ -179,6 +192,17 @@ TracingControllerImpl::GenerateMetadataDict() const { metadata_dict->SetString("v8-version", V8_VERSION_STRING); metadata_dict->SetString("user-agent", GetContentClient()->GetUserAgent()); +#if defined(OS_ANDROID) + // The library name is used for symbolizing heap profiles. This cannot be + // obtained from process maps since library can be mapped from apk directly. + // This is not added as part of memory-infra os dumps since it is special case + // only for chrome library. + base::Optional soname = + base::debug::ReadElfLibraryName(&__ehdr_start); + if (soname) + metadata_dict->SetString("chrome-library-name", soname.value()); +#endif // defined(OS_ANDROID) + // OS #if defined(OS_CHROMEOS) metadata_dict->SetString("os-name", "CrOS"); @@ -326,7 +350,7 @@ bool TracingControllerImpl::StopTracing( return false; DCHECK_CURRENTLY_ON(BrowserThread::UI); - tracing::TraceConfigFile::GetInstance()->SetDisabled(); + tracing::TraceStartupConfig::GetInstance()->SetDisabled(); trace_data_endpoint_ = std::move(trace_data_endpoint); is_data_complete_ = false; is_metadata_available_ = false; @@ -406,8 +430,7 @@ void TracingControllerImpl::OnDataComplete() { CompleteFlush(); } -void TracingControllerImpl::OnMetadataAvailable( - std::unique_ptr metadata) { +void TracingControllerImpl::OnMetadataAvailable(base::Value metadata) { DCHECK(!filtered_metadata_); is_metadata_available_ = true; MetadataFilterPredicate metadata_filter; @@ -416,16 +439,15 @@ void TracingControllerImpl::OnMetadataAvailable( metadata_filter = delegate_->GetMetadataFilterPredicate(); } if (metadata_filter.is_null()) { - filtered_metadata_ = std::move(metadata); + filtered_metadata_ = base::DictionaryValue::From( + base::Value::ToUniquePtrValue(std::move(metadata))); } else { filtered_metadata_ = std::make_unique(); - for (base::DictionaryValue::Iterator it(*metadata); !it.IsAtEnd(); - it.Advance()) { - if (metadata_filter.Run(it.key())) { - filtered_metadata_->Set( - it.key(), std::make_unique(it.value().Clone())); + for (auto it : metadata.DictItems()) { + if (metadata_filter.Run(it.first)) { + filtered_metadata_->SetKey(it.first, std::move(it.second)); } else { - filtered_metadata_->SetString(it.key(), "__stripped__"); + filtered_metadata_->SetKey(it.first, base::Value("__stripped__")); } } } diff --git a/chromium/content/browser/tracing/tracing_controller_impl.h b/chromium/content/browser/tracing/tracing_controller_impl.h index ea35ddd324c..356253a594b 100644 --- a/chromium/content/browser/tracing/tracing_controller_impl.h +++ b/chromium/content/browser/tracing/tracing_controller_impl.h @@ -26,6 +26,10 @@ class DictionaryValue; class RefCountedString; } // namespace base +namespace tracing { +class TraceEventAgent; +} // namespace tracing + namespace content { class TracingDelegate; @@ -43,7 +47,7 @@ class TracingControllerImpl : public TracingController, CreateCompressedStringEndpoint(scoped_refptr endpoint, bool compress_with_background_priority); - static TracingControllerImpl* GetInstance(); + CONTENT_EXPORT static TracingControllerImpl* GetInstance(); // Should be called on the UI thread. TracingControllerImpl(); @@ -62,6 +66,8 @@ class TracingControllerImpl : public TracingController, void RegisterTracingUI(TracingUI* tracing_ui); void UnregisterTracingUI(TracingUI* tracing_ui); + CONTENT_EXPORT tracing::TraceEventAgent* GetTraceEventAgent() const; + private: friend std::default_delete; @@ -73,13 +79,14 @@ class TracingControllerImpl : public TracingController, void OnDataAvailable(const void* data, size_t num_bytes) override; void OnDataComplete() override; - void OnMetadataAvailable(std::unique_ptr metadata); + void OnMetadataAvailable(base::Value metadata); void CompleteFlush(); tracing::mojom::AgentRegistryPtr agent_registry_; tracing::mojom::CoordinatorPtr coordinator_; std::vector> agents_; + std::unique_ptr trace_event_agent_; std::unique_ptr delegate_; std::unique_ptr trace_config_; std::unique_ptr drainer_; diff --git a/chromium/content/browser/url_loader_factory_getter.cc b/chromium/content/browser/url_loader_factory_getter.cc index 2a6b4f38ccb..e0593366a2a 100644 --- a/chromium/content/browser/url_loader_factory_getter.cc +++ b/chromium/content/browser/url_loader_factory_getter.cc @@ -4,9 +4,15 @@ #include "content/browser/url_loader_factory_getter.h" +#include +#include + #include "base/bind.h" +#include "base/feature_list.h" #include "base/lazy_instance.h" #include "content/browser/storage_partition_impl.h" +#include "content/common/service_worker/service_worker_utils.h" +#include "services/network/public/cpp/features.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/mojom/network_service.mojom.h" @@ -71,6 +77,12 @@ class URLLoaderFactoryGetter::URLLoaderFactoryForIOThread std::move(client), traffic_annotation); } + void Clone(network::mojom::URLLoaderFactoryRequest request) override { + if (!factory_getter_) + return; + factory_getter_->GetURLLoaderFactory()->Clone(std::move(request)); + } + // SharedURLLoaderFactory implementation: std::unique_ptr Clone() override { NOTREACHED() << "This isn't supported. If you need a SharedURLLoaderFactory" @@ -101,14 +113,19 @@ URLLoaderFactoryGetter::URLLoaderFactoryGetter() {} void URLLoaderFactoryGetter::Initialize(StoragePartitionImpl* partition) { DCHECK(partition); - partition_ = partition; + DCHECK(!pending_network_factory_request_.is_pending()); + DCHECK(!pending_blob_factory_request_.is_pending()); + partition_ = partition; network::mojom::URLLoaderFactoryPtr network_factory; - HandleNetworkFactoryRequestOnUIThread(MakeRequest(&network_factory)); - network::mojom::URLLoaderFactoryPtr blob_factory; - partition_->GetBlobURLLoaderFactory()->HandleRequest( - mojo::MakeRequest(&blob_factory)); + pending_network_factory_request_ = MakeRequest(&network_factory); + pending_blob_factory_request_ = mojo::MakeRequest(&blob_factory); + + // If NetworkService is disabled, HandleFactoryRequests should be called after + // NetworkContext in |partition_| is ready. + if (base::FeatureList::IsEnabled(network::features::kNetworkService)) + HandleFactoryRequests(); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, @@ -117,6 +134,24 @@ void URLLoaderFactoryGetter::Initialize(StoragePartitionImpl* partition) { blob_factory.PassInterface())); } +void URLLoaderFactoryGetter::HandleFactoryRequests() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK(pending_network_factory_request_.is_pending()); + DCHECK(pending_blob_factory_request_.is_pending()); + HandleNetworkFactoryRequestOnUIThread( + std::move(pending_network_factory_request_)); + + // |partition->blob_url_loader_factory_| is not available without the feature. + if (base::FeatureList::IsEnabled(network::features::kNetworkService) || + ServiceWorkerUtils::IsServicificationEnabled()) { + DCHECK(partition_->GetBlobURLLoaderFactory()); + partition_->GetBlobURLLoaderFactory()->HandleRequest( + std::move(pending_blob_factory_request_)); + } else { + pending_blob_factory_request_ = nullptr; + } +} + void URLLoaderFactoryGetter::OnStoragePartitionDestroyed() { DCHECK_CURRENTLY_ON(BrowserThread::UI); partition_ = nullptr; @@ -214,8 +249,12 @@ void URLLoaderFactoryGetter::HandleNetworkFactoryRequestOnUIThread( // still held by consumers. if (!partition_) return; + network::mojom::URLLoaderFactoryParamsPtr params = + network::mojom::URLLoaderFactoryParams::New(); + params->process_id = network::mojom::kBrowserProcessId; + params->is_corb_enabled = false; partition_->GetNetworkContext()->CreateURLLoaderFactory( - std::move(network_factory_request), 0); + std::move(network_factory_request), std::move(params)); } } // namespace content diff --git a/chromium/content/browser/url_loader_factory_getter.h b/chromium/content/browser/url_loader_factory_getter.h index 2386c82bd4c..da31f8cf52e 100644 --- a/chromium/content/browser/url_loader_factory_getter.h +++ b/chromium/content/browser/url_loader_factory_getter.h @@ -31,10 +31,19 @@ class URLLoaderFactoryGetter CONTENT_EXPORT URLLoaderFactoryGetter(); // Initializes this object on the UI thread. The |partition| is used to - // initialize the URLLoaderFactories for the network service and AppCache, and - // will be cached to recover from connection error. + // initialize the URLLoaderFactories for the network service, AppCache, and + // ServiceWorkers, and will be cached to recover from connection error. + // After Initialize(), you can get URLLoaderFactories from this + // getter. However, any messages on it will be queued until + // HandleFactoryRequests() is called. void Initialize(StoragePartitionImpl* partition); + // Called on the UI thread to actually instantiate factories whose pointers + // are to be exposed by this getter. This must be called after NetworkContext + // is initialized. + // TODO(shimazu): Remove this once NetworkService is shipped. + void HandleFactoryRequests(); + // Clear the cached pointer to |StoragePartitionImpl| on the UI thread. Should // be called when the partition is going away. void OnStoragePartitionDestroyed(); @@ -58,7 +67,8 @@ class URLLoaderFactoryGetter network::mojom::URLLoaderFactoryRequest network_factory_request); // Called on the IO thread to get the URLLoaderFactory to the blob service. - // The pointer shouldn't be cached. + // Must be used only if the network service or servicified service worker is + // enabled. The pointer shouldn't be cached. CONTENT_EXPORT network::mojom::URLLoaderFactory* GetBlobFactory(); // Overrides the network URLLoaderFactory for subsequent requests. Passing a @@ -106,6 +116,10 @@ class URLLoaderFactoryGetter // Call |network_factory_.FlushForTesting()|. For test use only. void FlushNetworkInterfaceForTesting(); + // Bound with appropriate URLLoaderFactories at HandleFactoryRequests(). + network::mojom::URLLoaderFactoryRequest pending_network_factory_request_; + network::mojom::URLLoaderFactoryRequest pending_blob_factory_request_; + // Only accessed on IO thread. network::mojom::URLLoaderFactoryPtr network_factory_; network::mojom::URLLoaderFactoryPtr blob_factory_; diff --git a/chromium/content/browser/utility_process_host.cc b/chromium/content/browser/utility_process_host.cc index 39b89cd36c3..3b6dfc16d83 100644 --- a/chromium/content/browser/utility_process_host.cc +++ b/chromium/content/browser/utility_process_host.cc @@ -28,21 +28,24 @@ #include "content/public/common/sandboxed_process_launcher_delegate.h" #include "content/public/common/service_manager_connection.h" #include "content/public/common/service_names.mojom.h" -#include "content/public/common/zygote_buildflags.h" #include "media/base/media_switches.h" #include "services/network/public/cpp/network_switches.h" +#include "services/service_manager/embedder/switches.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "services/service_manager/sandbox/sandbox_type.h" +#include "services/service_manager/sandbox/switches.h" +#include "services/service_manager/zygote/common/zygote_buildflags.h" #include "ui/base/ui_base_switches.h" #include "ui/gl/gl_switches.h" #if defined(OS_WIN) #include "sandbox/win/src/sandbox_policy.h" #include "sandbox/win/src/sandbox_types.h" +#include "services/network/network_sandbox_win.h" #endif #if BUILDFLAG(USE_ZYGOTE_HANDLE) -#include "content/public/common/zygote_handle.h" +#include "services/service_manager/zygote/common/zygote_handle.h" // nogncheck #endif namespace content { @@ -84,16 +87,21 @@ class UtilitySandboxedProcessLauncherDelegate service_manager::SANDBOX_TYPE_NO_SANDBOX_AND_ELEVATED_PRIVILEGES; } - bool PreSpawnTarget(sandbox::TargetPolicy* policy) override { return true; } + bool PreSpawnTarget(sandbox::TargetPolicy* policy) override { + if (sandbox_type_ == service_manager::SANDBOX_TYPE_NETWORK) + return network::NetworkPreSpawnTarget(policy); + + return true; + } #endif // OS_WIN #if BUILDFLAG(USE_ZYGOTE_HANDLE) - ZygoteHandle GetZygote() override { + service_manager::ZygoteHandle GetZygote() override { if (service_manager::IsUnsandboxedSandboxType(sandbox_type_) || sandbox_type_ == service_manager::SANDBOX_TYPE_NETWORK) { return nullptr; } - return GetGenericZygote(); + return service_manager::GetGenericZygote(); } #endif // BUILDFLAG(USE_ZYGOTE_HANDLE) @@ -267,13 +275,14 @@ bool UtilityProcessHost::StartProcess() { network::switches::kIgnoreCertificateErrorsSPKIList, network::switches::kLogNetLog, network::switches::kNoReferrers, + service_manager::switches::kNoSandbox, +#if defined(OS_MACOSX) + service_manager::switches::kEnableSandboxLogging, +#endif switches::kIgnoreCertificateErrors, - switches::kNoSandbox, switches::kOverrideUseSoftwareGLForTests, + switches::kOverrideEnabledCdmInterfaceVersion, switches::kProxyServer, -#if defined(OS_MACOSX) - switches::kEnableSandboxLogging, -#endif switches::kUseFakeDeviceForMediaStream, switches::kUseFileForFakeVideoCapture, switches::kUseMockCertVerifierForTesting, @@ -281,6 +290,23 @@ bool UtilityProcessHost::StartProcess() { switches::kUseGL, #if defined(OS_ANDROID) switches::kOrderfileMemoryOptimization, +#endif + // These flags are used by the audio service: + switches::kAudioBufferSize, + switches::kAudioServiceQuitTimeoutMs, + switches::kDisableAudioOutput, + switches::kFailAudioStreamCreation, + switches::kMuteAudio, + switches::kUseFileForFakeAudioCapture, +#if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_SOLARIS) + switches::kAlsaInputDevice, + switches::kAlsaOutputDevice, +#endif +#if defined(OS_WIN) + switches::kEnableExclusiveAudio, + switches::kForceWaveAudio, + switches::kTrySupportedChannelLayouts, + switches::kWaveOutBuffers, #endif }; cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames, diff --git a/chromium/content/browser/utility_process_host.h b/chromium/content/browser/utility_process_host.h index 2ef900333a2..0185b68cc8a 100644 --- a/chromium/content/browser/utility_process_host.h +++ b/chromium/content/browser/utility_process_host.h @@ -71,6 +71,8 @@ class CONTENT_EXPORT UtilityProcessHost // SANDBOX_TYPE_NO_SANDBOX is specified. void SetSandboxType(service_manager::SandboxType sandbox_type); + service_manager::SandboxType sandbox_type() const { return sandbox_type_; } + // Returns information about the utility child process. const ChildProcessData& GetData(); #if defined(OS_POSIX) diff --git a/chromium/content/browser/wake_lock/wake_lock_browsertest.cc b/chromium/content/browser/wake_lock/wake_lock_browsertest.cc index 0b87944e8da..d63a632d9ef 100644 --- a/chromium/content/browser/wake_lock/wake_lock_browsertest.cc +++ b/chromium/content/browser/wake_lock/wake_lock_browsertest.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "base/command_line.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/test/test_timeouts.h" #include "content/browser/web_contents/web_contents_impl.h" diff --git a/chromium/content/browser/web_contents/aura/gesture_nav_simple_unittest.cc b/chromium/content/browser/web_contents/aura/gesture_nav_simple_unittest.cc index 9864bef84fc..f3da97e61f9 100644 --- a/chromium/content/browser/web_contents/aura/gesture_nav_simple_unittest.cc +++ b/chromium/content/browser/web_contents/aura/gesture_nav_simple_unittest.cc @@ -19,16 +19,24 @@ namespace { // A subclass of TestWebContents that offers a fake content window. class GestureNavTestWebContents : public TestWebContents { public: + explicit GestureNavTestWebContents( + BrowserContext* browser_context, + std::unique_ptr fake_native_view, + std::unique_ptr fake_contents_window) + : TestWebContents(browser_context), + fake_native_view_(std::move(fake_native_view)), + fake_contents_window_(std::move(fake_contents_window)) {} ~GestureNavTestWebContents() override {} - static GestureNavTestWebContents* Create( + static std::unique_ptr Create( BrowserContext* browser_context, scoped_refptr instance, std::unique_ptr fake_native_view, std::unique_ptr fake_contents_window) { - auto* web_contents = new GestureNavTestWebContents( - browser_context, std::move(fake_native_view), - std::move(fake_contents_window)); + std::unique_ptr web_contents = + std::make_unique( + browser_context, std::move(fake_native_view), + std::move(fake_contents_window)); web_contents->Init( WebContents::CreateParams(browser_context, std::move(instance))); return web_contents; @@ -41,15 +49,6 @@ class GestureNavTestWebContents : public TestWebContents { return fake_contents_window_.get(); } - protected: - explicit GestureNavTestWebContents( - BrowserContext* browser_context, - std::unique_ptr fake_native_view, - std::unique_ptr fake_contents_window) - : TestWebContents(browser_context), - fake_native_view_(std::move(fake_native_view)), - fake_contents_window_(std::move(fake_contents_window)) {} - private: std::unique_ptr fake_native_view_; std::unique_ptr fake_contents_window_; 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 index 110658ebc58..559ee9bdcfd 100644 --- a/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc +++ b/chromium/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc @@ -71,16 +71,25 @@ class ImmediateLoadObserver : WebContentsObserver { // A subclass of TestWebContents that offers a fake content window. class OverscrollTestWebContents : public TestWebContents { public: + explicit OverscrollTestWebContents( + BrowserContext* browser_context, + std::unique_ptr fake_native_view, + std::unique_ptr fake_contents_window) + : TestWebContents(browser_context), + fake_native_view_(std::move(fake_native_view)), + fake_contents_window_(std::move(fake_contents_window)), + is_being_destroyed_(false) {} ~OverscrollTestWebContents() override {} - static OverscrollTestWebContents* Create( + static std::unique_ptr Create( BrowserContext* browser_context, scoped_refptr instance, std::unique_ptr fake_native_view, std::unique_ptr fake_contents_window) { - OverscrollTestWebContents* web_contents = new OverscrollTestWebContents( - browser_context, std::move(fake_native_view), - std::move(fake_contents_window)); + std::unique_ptr web_contents = + std::make_unique( + browser_context, std::move(fake_native_view), + std::move(fake_contents_window)); web_contents->Init( WebContents::CreateParams(browser_context, std::move(instance))); return web_contents; @@ -100,16 +109,6 @@ class OverscrollTestWebContents : public TestWebContents { bool IsBeingDestroyed() const override { return is_being_destroyed_; } - protected: - explicit OverscrollTestWebContents( - BrowserContext* browser_context, - std::unique_ptr fake_native_view, - std::unique_ptr fake_contents_window) - : TestWebContents(browser_context), - fake_native_view_(std::move(fake_native_view)), - fake_contents_window_(std::move(fake_contents_window)), - is_being_destroyed_(false) {} - private: std::unique_ptr fake_native_view_; std::unique_ptr fake_contents_window_; @@ -247,13 +246,15 @@ class OverscrollNavigationOverlayTest : public RenderViewHostImplTestHarness { // Receive a paint update. This is necessary to make sure the size is set // correctly in RenderWidgetHostImpl. - ViewHostMsg_ResizeOrRepaint_ACK_Params params; - params.view_size = gfx::Size(10, 10); - ViewHostMsg_ResizeOrRepaint_ACK rect(test_rvh()->GetRoutingID(), params); - RenderViewHostTester::TestOnMessageReceived(test_rvh(), rect); + viz::LocalSurfaceId local_surface_id(10, 10, + base::UnguessableToken::Create()); + cc::RenderFrameMetadata metadata; + metadata.viewport_size_in_pixels = gfx::Size(10, 10); + metadata.local_surface_id = local_surface_id; + test_rvh()->GetWidget()->DidUpdateVisualProperties(metadata); // Reset pending flags for size/paint. - test_rvh()->GetWidget()->ResetSizeAndRepaintPendingFlags(); + test_rvh()->GetWidget()->ResetSentVisualProperties(); // Create the overlay, and set the contents of the overlay window. overlay_.reset(new OverscrollNavigationOverlay(contents(), root_window())); diff --git a/chromium/content/browser/web_contents/web_contents_android.cc b/chromium/content/browser/web_contents/web_contents_android.cc index 5ebf1918903..634d995b762 100644 --- a/chromium/content/browser/web_contents/web_contents_android.cc +++ b/chromium/content/browser/web_contents/web_contents_android.cc @@ -17,11 +17,11 @@ #include "base/json/json_writer.h" #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/metrics/user_metrics.h" #include "base/task_scheduler/post_task.h" #include "content/browser/accessibility/browser_accessibility_android.h" #include "content/browser/accessibility/browser_accessibility_manager_android.h" #include "content/browser/android/content_view_core.h" -#include "content/browser/android/interstitial_page_delegate_android.h" #include "content/browser/android/java/gin_java_bridge_dispatcher_host.h" #include "content/browser/frame_host/interstitial_page_impl.h" #include "content/browser/media/android/browser_media_player_manager.h" @@ -41,8 +41,9 @@ #include "content/public/common/content_switches.h" #include "jni/WebContentsImpl_jni.h" #include "net/android/network_library.h" +#include "ui/accessibility/ax_assistant_structure.h" #include "ui/accessibility/ax_node_data.h" -#include "ui/accessibility/platform/ax_snapshot_node_android_platform.h" +#include "ui/accessibility/mojom/ax_assistant_structure.mojom.h" #include "ui/android/overscroll_refresh_handler.h" #include "ui/android/window_android.h" #include "ui/gfx/android/java_bitmap.h" @@ -81,16 +82,20 @@ void JavaScriptResultCallback(const ScopedJavaGlobalRef& callback, void SmartClipCallback(const ScopedJavaGlobalRef& callback, const base::string16& text, - const base::string16& html) { + const base::string16& html, + const gfx::Rect& clip_rect) { JNIEnv* env = base::android::AttachCurrentThread(); - ScopedJavaLocalRef jtext = ConvertUTF16ToJavaString(env, text); - ScopedJavaLocalRef jhtml = ConvertUTF16ToJavaString(env, html); - Java_WebContentsImpl_onSmartClipDataExtracted(env, jtext, jhtml, callback); + ScopedJavaLocalRef j_text = ConvertUTF16ToJavaString(env, text); + ScopedJavaLocalRef j_html = ConvertUTF16ToJavaString(env, html); + Java_WebContentsImpl_onSmartClipDataExtracted( + env, j_text, j_html, clip_rect.x(), clip_rect.y(), clip_rect.right(), + clip_rect.bottom(), callback); } ScopedJavaLocalRef JNI_WebContentsImpl_CreateJavaAXSnapshot( JNIEnv* env, - const ui::AXSnapshotNodeAndroid* node, + const ui::AssistantTree* tree, + const ui::AssistantNode* node, bool is_root) { ScopedJavaLocalRef j_text = ConvertUTF16ToJavaString(env, node->text); @@ -103,15 +108,16 @@ ScopedJavaLocalRef JNI_WebContentsImpl_CreateJavaAXSnapshot( node->text_size, node->bold, node->italic, node->underline, node->line_through, j_class); - if (node->has_selection) { + if (node->selection.has_value()) { Java_WebContentsImpl_setAccessibilitySnapshotSelection( - env, j_node, node->start_selection, node->end_selection); + env, j_node, node->selection->start(), node->selection->end()); } - for (auto& child : node->children) { + for (int child : node->children_indices) { Java_WebContentsImpl_addAccessibilityNodeAsChild( env, j_node, - JNI_WebContentsImpl_CreateJavaAXSnapshot(env, child.get(), false)); + JNI_WebContentsImpl_CreateJavaAXSnapshot( + env, tree, tree->nodes[child].get(), false)); } return j_node; } @@ -127,10 +133,10 @@ void AXTreeSnapshotCallback(const ScopedJavaGlobalRef& callback, std::unique_ptr manager( static_cast( BrowserAccessibilityManager::Create(result, nullptr))); - auto snapshot = ui::AXSnapshotNodeAndroid::Create( - result, manager->ShouldExposePasswordText()); - ScopedJavaLocalRef j_root = - JNI_WebContentsImpl_CreateJavaAXSnapshot(env, snapshot.get(), true); + std::unique_ptr assistant_tree = + ui::CreateAssistantTree(result, manager->ShouldExposePasswordText()); + ScopedJavaLocalRef j_root = JNI_WebContentsImpl_CreateJavaAXSnapshot( + env, assistant_tree.get(), assistant_tree->nodes.front().get(), true); Java_WebContentsImpl_onAccessibilitySnapshot(env, j_root, callback); } @@ -266,6 +272,31 @@ WebContentsAndroid::GetTopLevelNativeWindow(JNIEnv* env, return window_android->GetJavaObject(); } +void WebContentsAndroid::SetTopLevelNativeWindow( + JNIEnv* env, + const JavaParamRef& obj, + const JavaParamRef& jwindow_android) { + ui::WindowAndroid* window = + ui::WindowAndroid::FromJavaWindowAndroid(jwindow_android); + auto* old_window = web_contents_->GetTopLevelNativeWindow(); + if (window == old_window) + return; + + auto* view = web_contents_->GetNativeView(); + if (old_window) + view->RemoveFromParent(); + if (window) + window->AddChild(view); +} + +void WebContentsAndroid::SetViewAndroidDelegate( + JNIEnv* env, + const JavaParamRef& obj, + const JavaParamRef& jview_delegate) { + ui::ViewAndroid* view_android = web_contents_->GetView()->GetNativeView(); + view_android->SetDelegate(jview_delegate); +} + ScopedJavaLocalRef WebContentsAndroid::GetMainFrame( JNIEnv* env, const JavaParamRef& obj) const { @@ -353,9 +384,9 @@ RenderWidgetHostViewAndroid* jint WebContentsAndroid::GetBackgroundColor(JNIEnv* env, const JavaParamRef& obj) { RenderWidgetHostViewAndroid* rwhva = GetRenderWidgetHostViewAndroid(); - if (!rwhva) + if (!rwhva || !rwhva->GetCachedBackgroundColor()) return SK_ColorWHITE; - return rwhva->GetCachedBackgroundColor(); + return *rwhva->GetCachedBackgroundColor(); } ScopedJavaLocalRef WebContentsAndroid::GetLastCommittedURL( @@ -387,8 +418,9 @@ void WebContentsAndroid::OnShow(JNIEnv* env, const JavaParamRef& obj) { void WebContentsAndroid::SetImportance( JNIEnv* env, const base::android::JavaParamRef& obj, - jint importance) { - web_contents_->SetImportance(static_cast(importance)); + jint main_frame_importance) { + web_contents_->SetMainFrameImportance( + static_cast(main_frame_importance)); } void WebContentsAndroid::SuspendAllMediaPlayers( @@ -404,19 +436,6 @@ void WebContentsAndroid::SetAudioMuted(JNIEnv* env, web_contents_->SetAudioMuted(mute); } -void WebContentsAndroid::ShowInterstitialPage(JNIEnv* env, - const JavaParamRef& obj, - const JavaParamRef& jurl, - jlong delegate_ptr) { - GURL url(base::android::ConvertJavaStringToUTF8(env, jurl)); - InterstitialPageDelegateAndroid* delegate = - reinterpret_cast(delegate_ptr); - InterstitialPage* interstitial = InterstitialPage::Create( - web_contents_, false, url, delegate); - delegate->set_interstitial_page(interstitial); - interstitial->Show(); -} - jboolean WebContentsAndroid::IsShowingInterstitialPage( JNIEnv* env, const JavaParamRef& obj) { @@ -802,4 +821,40 @@ void WebContentsAndroid::SetMediaSession( Java_WebContentsImpl_setMediaSession(env, obj_, j_media_session); } +void WebContentsAndroid::SendOrientationChangeEvent( + JNIEnv* env, + const JavaParamRef& obj, + jint orientation) { + base::RecordAction(base::UserMetricsAction("ScreenOrientationChange")); + WebContentsViewAndroid* view = + static_cast(web_contents_->GetView()); + view->set_device_orientation(orientation); + RenderWidgetHostViewAndroid* rwhva = GetRenderWidgetHostViewAndroid(); + if (rwhva) + rwhva->UpdateScreenInfo(web_contents_->GetView()->GetNativeView()); + + web_contents_->OnScreenOrientationChange(); +} + +void WebContentsAndroid::OnScaleFactorChanged( + JNIEnv* env, + const JavaParamRef& obj) { + RenderWidgetHostViewAndroid* rwhva = GetRenderWidgetHostViewAndroid(); + if (rwhva) { + // |SendScreenRects()| indirectly calls GetViewSize() that asks Java layer. + web_contents_->SendScreenRects(); + rwhva->SynchronizeVisualProperties(); + } +} + +int WebContentsAndroid::GetTopControlsShrinkBlinkHeightPixForTesting( + JNIEnv* env, + const JavaParamRef& obj) { + RenderWidgetHostViewAndroid* rwhva = GetRenderWidgetHostViewAndroid(); + float scale = web_contents_->GetNativeView()->GetDipScale(); + return (rwhva && rwhva->DoBrowserControlsShrinkBlinkSize()) + ? rwhva->GetTopControlsHeight() * scale + : 0; +} + } // 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 2896fa2a5ec..04455bd0666 100644 --- a/chromium/content/browser/web_contents/web_contents_android.h +++ b/chromium/content/browser/web_contents/web_contents_android.h @@ -41,6 +41,14 @@ class CONTENT_EXPORT WebContentsAndroid base::android::ScopedJavaLocalRef GetTopLevelNativeWindow( JNIEnv* env, const base::android::JavaParamRef& obj); + void SetTopLevelNativeWindow( + JNIEnv* env, + const base::android::JavaParamRef& obj, + const base::android::JavaParamRef& jwindow_android); + void SetViewAndroidDelegate( + JNIEnv* env, + const base::android::JavaParamRef& obj, + const base::android::JavaParamRef& jview_delegate); base::android::ScopedJavaLocalRef GetMainFrame( JNIEnv* env, const base::android::JavaParamRef& obj) const; @@ -92,10 +100,6 @@ class CONTENT_EXPORT WebContentsAndroid const base::android::JavaParamRef& jobj, jboolean mute); - void ShowInterstitialPage(JNIEnv* env, - const base::android::JavaParamRef& obj, - const base::android::JavaParamRef& jurl, - jlong delegate_ptr); jboolean IsShowingInterstitialPage( JNIEnv* env, const base::android::JavaParamRef& obj); @@ -225,6 +229,20 @@ class CONTENT_EXPORT WebContentsAndroid void SetMediaSession( const base::android::ScopedJavaLocalRef& j_media_session); + void SendOrientationChangeEvent( + JNIEnv* env, + const base::android::JavaParamRef& obj, + jint orientation); + + void OnScaleFactorChanged(JNIEnv* env, + const base::android::JavaParamRef& obj); + + // Returns the amount of the top controls height if controls are in the state + // of shrinking Blink's view size, otherwise 0. + int GetTopControlsShrinkBlinkHeightPixForTesting( + JNIEnv* env, + const base::android::JavaParamRef& obj); + private: RenderWidgetHostViewAndroid* GetRenderWidgetHostViewAndroid(); @@ -242,6 +260,7 @@ class CONTENT_EXPORT WebContentsAndroid const std::vector& sizes); WebContentsImpl* web_contents_; + NavigationControllerAndroid navigation_controller_; base::android::ScopedJavaGlobalRef obj_; diff --git a/chromium/content/browser/web_contents/web_contents_delegate_unittest.cc b/chromium/content/browser/web_contents/web_contents_delegate_unittest.cc index 1845a660f90..f6d6593e8ec 100644 --- a/chromium/content/browser/web_contents/web_contents_delegate_unittest.cc +++ b/chromium/content/browser/web_contents/web_contents_delegate_unittest.cc @@ -7,7 +7,6 @@ #include #include "base/compiler_specific.h" -#include "base/message_loop/message_loop.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/test/test_browser_context.h" #include "content/test/test_render_view_host.h" @@ -22,44 +21,48 @@ class WebContentsDelegateTest : public RenderViewHostImplTestHarness { }; TEST_F(WebContentsDelegateTest, UnregisterInDestructor) { - std::unique_ptr contents_a(static_cast( - WebContents::Create(WebContents::CreateParams(browser_context())))); - std::unique_ptr contents_b(static_cast( - WebContents::Create(WebContents::CreateParams(browser_context())))); - EXPECT_EQ(nullptr, contents_a->GetDelegate()); - EXPECT_EQ(nullptr, contents_b->GetDelegate()); + std::unique_ptr contents_a( + WebContents::Create(WebContents::CreateParams(browser_context()))); + WebContentsImpl* raw_contents_a = + static_cast(contents_a.get()); + std::unique_ptr contents_b( + WebContents::Create(WebContents::CreateParams(browser_context()))); + WebContentsImpl* raw_contents_b = + static_cast(contents_b.get()); + EXPECT_EQ(nullptr, raw_contents_a->GetDelegate()); + EXPECT_EQ(nullptr, raw_contents_b->GetDelegate()); std::unique_ptr delegate( new MockWebContentsDelegate()); // Setting a delegate should work correctly. - contents_a->SetDelegate(delegate.get()); - EXPECT_EQ(delegate.get(), contents_a->GetDelegate()); - EXPECT_TRUE(contents_b->GetDelegate() == nullptr); + raw_contents_a->SetDelegate(delegate.get()); + EXPECT_EQ(delegate.get(), raw_contents_a->GetDelegate()); + EXPECT_TRUE(raw_contents_b->GetDelegate() == nullptr); // A delegate can be a delegate to multiple WebContentsImpl. - contents_b->SetDelegate(delegate.get()); - EXPECT_EQ(delegate.get(), contents_a->GetDelegate()); - EXPECT_EQ(delegate.get(), contents_b->GetDelegate()); + raw_contents_b->SetDelegate(delegate.get()); + EXPECT_EQ(delegate.get(), raw_contents_a->GetDelegate()); + EXPECT_EQ(delegate.get(), raw_contents_b->GetDelegate()); // Setting the same delegate multiple times should work correctly. - contents_b->SetDelegate(delegate.get()); - EXPECT_EQ(delegate.get(), contents_a->GetDelegate()); - EXPECT_EQ(delegate.get(), contents_b->GetDelegate()); + raw_contents_b->SetDelegate(delegate.get()); + EXPECT_EQ(delegate.get(), raw_contents_a->GetDelegate()); + EXPECT_EQ(delegate.get(), raw_contents_b->GetDelegate()); // Setting delegate to NULL should work correctly. - contents_b->SetDelegate(nullptr); - EXPECT_EQ(delegate.get(), contents_a->GetDelegate()); - EXPECT_TRUE(contents_b->GetDelegate() == nullptr); + raw_contents_b->SetDelegate(nullptr); + EXPECT_EQ(delegate.get(), raw_contents_a->GetDelegate()); + EXPECT_TRUE(raw_contents_b->GetDelegate() == nullptr); // Destroying the delegate while it is still the delegate for a // WebContentsImpl should unregister it. - contents_b->SetDelegate(delegate.get()); - EXPECT_EQ(delegate.get(), contents_a->GetDelegate()); - EXPECT_EQ(delegate.get(), contents_b->GetDelegate()); + raw_contents_b->SetDelegate(delegate.get()); + EXPECT_EQ(delegate.get(), raw_contents_a->GetDelegate()); + EXPECT_EQ(delegate.get(), raw_contents_b->GetDelegate()); delegate.reset(nullptr); - EXPECT_TRUE(contents_a->GetDelegate() == nullptr); - EXPECT_TRUE(contents_b->GetDelegate() == nullptr); + EXPECT_TRUE(raw_contents_a->GetDelegate() == nullptr); + EXPECT_TRUE(raw_contents_b->GetDelegate() == nullptr); // Destroy the WebContentses and run the message loop to prevent leaks. contents_a.reset(); diff --git a/chromium/content/browser/web_contents/web_contents_impl.cc b/chromium/content/browser/web_contents/web_contents_impl.cc index 942a35d355a..6ae9bf2f9c6 100644 --- a/chromium/content/browser/web_contents/web_contents_impl.cc +++ b/chromium/content/browser/web_contents/web_contents_impl.cc @@ -60,6 +60,7 @@ #include "content/browser/loader/loader_io_thread_notifier.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/manifest/manifest_manager_host.h" +#include "content/browser/media/audio_stream_broker.h" #include "content/browser/media/audio_stream_monitor.h" #include "content/browser/media/capture/web_contents_audio_muter.h" #include "content/browser/media/media_web_contents_observer.h" @@ -264,14 +265,16 @@ class CloseDialogCallbackWrapper } // namespace -WebContents* WebContents::Create(const WebContents::CreateParams& params) { +std::unique_ptr WebContents::Create( + const WebContents::CreateParams& params) { return WebContentsImpl::CreateWithOpener(params, FindOpenerRFH(params)); } -WebContents* WebContents::CreateWithSessionStorage( +std::unique_ptr WebContents::CreateWithSessionStorage( const WebContents::CreateParams& params, const SessionStorageNamespaceMap& session_storage_namespace_map) { - WebContentsImpl* new_contents = new WebContentsImpl(params.browser_context); + std::unique_ptr new_contents( + new WebContentsImpl(params.browser_context)); RenderFrameHostImpl* opener_rfh = FindOpenerRFH(params); FrameTreeNode* opener = nullptr; if (opener_rfh) @@ -286,6 +289,12 @@ WebContents* WebContents::CreateWithSessionStorage( .SetSessionStorageNamespace(it->first, it->second.get()); } + if (params.guest_delegate) { + // This makes |new_contents| act as a guest. + // For more info, see comment above class BrowserPluginGuest. + BrowserPluginGuest::Create(new_contents.get(), params.guest_delegate); + } + new_contents->Init(params); return new_contents; } @@ -514,6 +523,8 @@ WebContentsImpl::WebContentsImpl(BrowserContext* browser_context) BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode()), audio_stream_monitor_(this), bluetooth_connected_device_count_(0), + media_device_group_id_salt_base_( + BrowserContext::CreateRandomMediaDeviceIDSalt()), #if !defined(OS_ANDROID) page_scale_factor_is_one_(true), #endif // !defined(OS_ANDROID) @@ -638,14 +649,15 @@ WebContentsImpl::~WebContentsImpl() { SetDelegate(nullptr); } -WebContentsImpl* WebContentsImpl::CreateWithOpener( +std::unique_ptr WebContentsImpl::CreateWithOpener( const WebContents::CreateParams& params, RenderFrameHostImpl* opener_rfh) { TRACE_EVENT0("browser", "WebContentsImpl::CreateWithOpener"); FrameTreeNode* opener = nullptr; if (opener_rfh) opener = opener_rfh->frame_tree_node(); - WebContentsImpl* new_contents = new WebContentsImpl(params.browser_context); + std::unique_ptr new_contents( + new WebContentsImpl(params.browser_context)); new_contents->SetOpenerForNewContents(opener, params.opener_suppressed); // If the opener is sandboxed, a new popup must inherit the opener's sandbox @@ -679,7 +691,7 @@ WebContentsImpl* WebContentsImpl::CreateWithOpener( if (params.guest_delegate) { // This makes |new_contents| act as a guest. // For more info, see comment above class BrowserPluginGuest. - BrowserPluginGuest::Create(new_contents, params.guest_delegate); + BrowserPluginGuest::Create(new_contents.get(), params.guest_delegate); // We are instantiating a WebContents for browser plugin. Set its subframe // bit to true. new_contents->is_subframe_ = true; @@ -829,12 +841,6 @@ bool WebContentsImpl::OnMessageReceived(RenderFrameHostImpl* render_frame_host, IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginHostMsg_Attach, OnBrowserPluginMessage(render_frame_host, message)) -#endif -#if defined(OS_ANDROID) - IPC_MESSAGE_HANDLER(FrameHostMsg_FindMatchRects_Reply, - OnFindMatchRectsReply) - IPC_MESSAGE_HANDLER(FrameHostMsg_GetNearestFindResult_Reply, - OnGetNearestFindResultReply) #endif IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -1244,6 +1250,10 @@ const PageImportanceSignals& WebContentsImpl::GetPageImportanceSignals() const { return page_importance_signals_; } +const std::string& WebContentsImpl::GetMediaDeviceGroupIDSaltBase() const { + return media_device_group_id_salt_base_; +} + const base::string16& WebContentsImpl::GetTitle() const { // Transient entries take precedence. They are used for interstitial pages // that are shown on top of existing pages. @@ -1391,6 +1401,9 @@ bool WebContentsImpl::IsBeingCaptured() const { } bool WebContentsImpl::IsAudioMuted() const { + if (base::FeatureList::IsEnabled(features::kAudioServiceAudioStreams)) { + return audio_stream_factory_ && audio_stream_factory_->IsMuted(); + } return audio_muter_.get() && audio_muter_->is_muting(); } @@ -1401,13 +1414,17 @@ void WebContentsImpl::SetAudioMuted(bool mute) { if (mute == IsAudioMuted()) return; - if (mute) { - if (!audio_muter_) - audio_muter_.reset(new WebContentsAudioMuter(this)); - audio_muter_->StartMuting(); + if (base::FeatureList::IsEnabled(features::kAudioServiceAudioStreams)) { + GetAudioStreamFactory()->SetMuted(mute); } else { - DCHECK(audio_muter_); - audio_muter_->StopMuting(); + if (mute) { + if (!audio_muter_) + audio_muter_.reset(new WebContentsAudioMuter(this)); + audio_muter_->StartMuting(); + } else { + DCHECK(audio_muter_); + audio_muter_->StopMuting(); + } } for (auto& observer : observers_) @@ -1425,6 +1442,19 @@ bool WebContentsImpl::IsConnectedToBluetoothDevice() const { return bluetooth_connected_device_count_ > 0; } +bool WebContentsImpl::HasPictureInPictureVideo() const { + return has_picture_in_picture_video_; +} + +void WebContentsImpl::SetHasPictureInPictureVideo( + bool has_picture_in_picture_video) { + // If status of |this| is already accurate, there is no need to update. + if (has_picture_in_picture_video == has_picture_in_picture_video_) + return; + has_picture_in_picture_video_ = has_picture_in_picture_video; + NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); +} + bool WebContentsImpl::IsCrashed() const { return (crashed_status_ == base::TERMINATION_STATUS_PROCESS_CRASHED || crashed_status_ == base::TERMINATION_STATUS_ABNORMAL_TERMINATION || @@ -1480,8 +1510,8 @@ void WebContentsImpl::OnAudioStateChanged(bool is_audible) { was_ever_audible_ = was_ever_audible_ || is_audible; - if (delegate_) - delegate_->OnAudioStateChanged(this, is_audible); + for (auto& observer : observers_) + observer.OnAudioStateChanged(is_audible); } base::TimeTicks WebContentsImpl::GetLastActiveTime() const { @@ -1509,6 +1539,19 @@ void WebContentsImpl::WasShown() { last_active_time_ = base::TimeTicks::Now(); SetVisibility(Visibility::VISIBLE); + + for (FrameTreeNode* node : frame_tree_.Nodes()) { + RenderFrameProxyHost* parent = node->render_manager()->GetProxyToParent(); + if (!parent) + continue; + + if (parent->cross_process_frame_connector()->IsVisible()) { + // MaybeLogCrash will check 1) if there was a crash or not and 2) if the + // crash might have been already logged earlier as kCrashedWhileVisible. + parent->cross_process_frame_connector()->MaybeLogCrash( + CrossProcessFrameConnector::CrashVisibility::kShownAfterCrashing); + } + } } void WebContentsImpl::WasHidden() { @@ -1548,31 +1591,14 @@ bool WebContentsImpl::HasRecentInteractiveInputEvent() const { } #if defined(OS_ANDROID) -std::set WebContentsImpl::GetAllRenderWidgetHosts() { - std::set set; +void WebContentsImpl::SetMainFrameImportance( + ChildProcessImportance importance) { + GetMainFrame()->GetRenderWidgetHost()->SetImportance(importance); if (ShowingInterstitialPage()) { - auto* host = - static_cast(interstitial_page_->GetMainFrame()) - ->GetRenderWidgetHost(); - DCHECK(host); - set.insert(host); - } - for (RenderFrameHost* rfh : GetAllFrames()) { - auto* host = static_cast(rfh)->GetRenderWidgetHost(); - DCHECK(host); - set.insert(host); + static_cast(interstitial_page_->GetMainFrame()) + ->GetRenderWidgetHost() + ->SetImportance(importance); } - return set; -} - -void WebContentsImpl::SetImportance(ChildProcessImportance importance) { - // Importance should be set on interstitial page as well, which is included - // the set returned by |GetAllRenderWidgetHosts()|. - for (auto* host : GetAllRenderWidgetHosts()) - host->SetImportance(importance); - - // TODO(boliu): If this is ever used on platforms other than Android, make - // sure to also update inner WebContents. } #endif @@ -1588,15 +1614,40 @@ Visibility WebContentsImpl::GetVisibility() const { return visibility_; } +// TODO(alexmos): rename to NeedToFireBeforeUnloadOrUnload(). bool WebContentsImpl::NeedToFireBeforeUnload() { // TODO(creis): Should we fire even for interstitial pages? - return WillNotifyDisconnection() && !ShowingInterstitialPage() && - !GetRenderViewHost()->SuddenTerminationAllowed(); + if (ShowingInterstitialPage()) + return false; + + if (!WillNotifyDisconnection()) + return false; + + // Don't fire if the main frame's RenderViewHost indicates that beforeunload + // and unload have already executed (e.g., after receiving a ClosePage ACK) + // or should be ignored. + if (GetRenderViewHost()->SuddenTerminationAllowed()) + return false; + + // TODO(alexmos): This checks for both beforeunload and unload handlers from + // the whole main frame process. Remove this and explicitly check whether + // the main frame has each of these handlers. This can be done for + // beforeunload via RenderFrameHostImpl::ShouldDispatchBeforeUnload(), and + // something similar is needed for unload. + if (!GetMainFrame()->GetProcess()->SuddenTerminationAllowed()) + return true; + + // Check whether any subframes need to run beforeunload handlers. + // + // TODO(alexmos): Also check whether subframes need to run unload handlers in + // addition to beforeunload. + return GetMainFrame()->ShouldDispatchBeforeUnload( + true /* check_subframes_only */); } void WebContentsImpl::DispatchBeforeUnload() { - bool for_cross_site_transition = false; - GetMainFrame()->DispatchBeforeUnload(for_cross_site_transition, false); + GetMainFrame()->DispatchBeforeUnload( + RenderFrameHostImpl::BeforeUnloadType::TAB_CLOSE, false); } void WebContentsImpl::AttachToOuterWebContentsFrame( @@ -1679,15 +1730,10 @@ void WebContentsImpl::DidChangeVisibleSecurityState() { void WebContentsImpl::NotifyPreferencesChanged() { std::set render_view_host_set; for (FrameTreeNode* node : frame_tree_.Nodes()) { - RenderWidgetHost* render_widget_host = - node->current_frame_host()->GetRenderWidgetHost(); - if (!render_widget_host) - continue; - RenderViewHost* render_view_host = RenderViewHost::From(render_widget_host); - if (!render_view_host) - continue; - render_view_host_set.insert(render_view_host); + render_view_host_set.insert( + node->current_frame_host()->GetRenderViewHost()); } + for (RenderViewHost* render_view_host : render_view_host_set) render_view_host->OnWebkitPreferencesChanged(); } @@ -1703,7 +1749,7 @@ void WebContentsImpl::FreezePage() { SendPageMessage(new PageMsg_FreezePage(MSG_ROUTING_NONE)); } -WebContents* WebContentsImpl::Clone() { +std::unique_ptr WebContentsImpl::Clone() { // We use our current SiteInstance since the cloned entry will use it anyway. // We pass our own opener so that the cloned page can access it if it was set // before. @@ -1713,10 +1759,11 @@ WebContents* WebContentsImpl::Clone() { RenderFrameHostImpl* opener_rfh = nullptr; if (opener) opener_rfh = opener->current_frame_host(); - WebContentsImpl* tc = CreateWithOpener(create_params, opener_rfh); + std::unique_ptr tc = + CreateWithOpener(create_params, opener_rfh); tc->GetController().CopyStateFrom(controller_, true); for (auto& observer : observers_) - observer.DidCloneToNewWebContents(this, tc); + observer.DidCloneToNewWebContents(this, tc.get()); return tc; } @@ -1772,6 +1819,10 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) { scoped_refptr site_instance = params.site_instance; if (!site_instance) site_instance = SiteInstance::Create(params.browser_context); + if (params.desired_renderer_state == CreateParams::kNoRendererProcess) { + static_cast(site_instance.get()) + ->PreventAssociationWithSpareProcess(); + } // A main RenderFrameHost always has a RenderWidgetHost, since it is always a // local root by definition. @@ -1850,7 +1901,8 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) { } // Create the renderer process in advance if requested. - if (params.initialize_renderer) { + if (params.desired_renderer_state == + CreateParams::kInitializeAndWarmupRendererProcess) { if (!GetRenderManager()->current_frame_host()->IsRenderFrameLive()) { GetRenderManager()->InitRenderView(GetRenderViewHost(), nullptr); } @@ -1870,8 +1922,12 @@ void WebContentsImpl::OnWebContentsDestroyed(WebContentsImpl* web_contents) { // Clear a pending contents that has been closed before being shown. for (auto iter = pending_contents_.begin(); iter != pending_contents_.end(); ++iter) { - if (iter->second != web_contents) + if (iter->second.get() != web_contents) continue; + + // Someone else has deleted the WebContents. That should never happen! + // TODO(erikchen): Fix semantics here. https://crbug.com/832879. + iter->second.release(); pending_contents_.erase(iter); return; } @@ -1961,7 +2017,7 @@ void WebContentsImpl::RenderWidgetDeleted( if (render_widget_host == mouse_lock_widget_) LostMouseLock(mouse_lock_widget_); - CancelKeyboardLock(keyboard_lock_widget_); + CancelKeyboardLock(render_widget_host); } void WebContentsImpl::RenderWidgetGotFocus( @@ -2120,7 +2176,9 @@ RenderWidgetHostImpl* WebContentsImpl::GetRenderWidgetHostWithPageFocus() { return focused_web_contents->GetMainFrame()->GetRenderWidgetHost(); } -void WebContentsImpl::EnterFullscreenMode(const GURL& origin) { +void WebContentsImpl::EnterFullscreenMode( + const GURL& origin, + const blink::WebFullscreenOptions& options) { // This method is being called to enter renderer-initiated fullscreen mode. // Make sure any existing fullscreen widget is shut down first. RenderWidgetHostView* const widget_view = GetFullscreenRenderWidgetHostView(); @@ -2130,7 +2188,7 @@ void WebContentsImpl::EnterFullscreenMode(const GURL& origin) { } if (delegate_) { - delegate_->EnterFullscreenModeForTab(this, origin); + delegate_->EnterFullscreenModeForTab(this, origin, options); if (keyboard_lock_widget_) delegate_->RequestKeyboardLock(this, esc_key_locked_); @@ -2173,7 +2231,7 @@ void WebContentsImpl::ExitFullscreenMode(bool will_cause_resize) { if (!will_cause_resize) { if (RenderWidgetHostView* rwhv = GetRenderWidgetHostView()) { if (RenderWidgetHost* render_widget_host = rwhv->GetRenderWidgetHost()) - render_widget_host->WasResized(); + render_widget_host->SynchronizeVisualProperties(); } } @@ -2409,31 +2467,32 @@ void WebContentsImpl::CreateNewWindow( create_params.renderer_initiated_creation = main_frame_route_id != MSG_ROUTING_NONE; - WebContentsImpl* new_contents = nullptr; + std::unique_ptr new_contents; if (!is_guest) { create_params.context = view_->GetNativeView(); create_params.initial_size = GetContainerBounds().size(); - new_contents = static_cast( - WebContents::Create(create_params)); + new_contents = WebContents::Create(create_params); } else { - new_contents = GetBrowserPluginGuest()->CreateNewGuestWindow(create_params); + new_contents = base::WrapUnique( + GetBrowserPluginGuest()->CreateNewGuestWindow(create_params)); } - new_contents->GetController().SetSessionStorageNamespace( - partition_id, - session_storage_namespace); + WebContentsImpl* raw_new_contents = + static_cast(new_contents.get()); + raw_new_contents->GetController().SetSessionStorageNamespace( + partition_id, session_storage_namespace); // If the new frame has a name, make sure any SiteInstances that can find // this named frame have proxies for it. Must be called after // SetSessionStorageNamespace, since this calls CreateRenderView, which uses // GetSessionStorageNamespace. if (!params.frame_name.empty()) - new_contents->GetRenderManager()->CreateProxiesForNewNamedFrame(); + raw_new_contents->GetRenderManager()->CreateProxiesForNewNamedFrame(); // 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) { - WebContentsView* new_view = new_contents->view_.get(); + WebContentsView* new_view = raw_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. @@ -2443,20 +2502,21 @@ void WebContentsImpl::CreateNewWindow( // Save the created window associated with the route so we can show it // later. DCHECK_NE(MSG_ROUTING_NONE, main_frame_widget_route_id); - pending_contents_[std::make_pair( - render_process_id, main_frame_widget_route_id)] = new_contents; - AddDestructionObserver(new_contents); + pending_contents_[std::make_pair(render_process_id, + main_frame_widget_route_id)] = + std::move(new_contents); + AddDestructionObserver(raw_new_contents); } if (delegate_) { delegate_->WebContentsCreated(this, render_process_id, opener->GetRoutingID(), params.frame_name, - params.target_url, new_contents); + params.target_url, raw_new_contents); } if (opener) { for (auto& observer : observers_) { - observer.DidOpenRequestedURL(new_contents, opener, params.target_url, + observer.DidOpenRequestedURL(raw_new_contents, opener, params.target_url, params.referrer, params.disposition, ui::PAGE_TRANSITION_LINK, false, // started_from_context_menu @@ -2473,18 +2533,19 @@ void WebContentsImpl::CreateNewWindow( // When the opener is suppressed, the original renderer cannot access the // new window. As a result, we need to show and navigate the window here. bool was_blocked = false; + + base::WeakPtr weak_new_contents = + raw_new_contents->weak_factory_.GetWeakPtr(); if (delegate_) { gfx::Rect initial_rect; - base::WeakPtr weak_new_contents = - new_contents->weak_factory_.GetWeakPtr(); - - delegate_->AddNewContents( - this, new_contents, params.disposition, initial_rect, - params.user_gesture, &was_blocked); + delegate_->AddNewContents(this, std::move(new_contents), + params.disposition, initial_rect, + params.user_gesture, &was_blocked); if (!weak_new_contents) return; // The delegate deleted |new_contents| during AddNewContents(). } + if (!was_blocked) { OpenURLParams open_params(params.target_url, params.referrer, WindowOpenDisposition::CURRENT_TAB, @@ -2494,11 +2555,12 @@ void WebContentsImpl::CreateNewWindow( if (delegate_ && !is_guest && !delegate_->ShouldResumeRequestsForCreatedWindow()) { + DCHECK(weak_new_contents); // We are in asynchronous add new contents path, delay opening url - new_contents->delayed_open_url_params_.reset( + weak_new_contents->delayed_open_url_params_.reset( new OpenURLParams(open_params)); } else { - new_contents->OpenURL(open_params); + weak_new_contents->OpenURL(open_params); } } } @@ -2555,24 +2617,26 @@ void WebContentsImpl::ShowCreatedWindow(int process_id, WindowOpenDisposition disposition, const gfx::Rect& initial_rect, bool user_gesture) { - WebContentsImpl* popup = + std::unique_ptr popup = GetCreatedWindow(process_id, main_frame_widget_route_id); if (popup) { + WebContentsImpl* raw_popup = static_cast(popup.get()); WebContentsDelegate* delegate = GetDelegate(); - popup->is_resume_pending_ = true; + raw_popup->is_resume_pending_ = true; if (!delegate || delegate->ShouldResumeRequestsForCreatedWindow()) - popup->ResumeLoadingCreatedWebContents(); + raw_popup->ResumeLoadingCreatedWebContents(); + base::WeakPtr weak_popup = + raw_popup->weak_factory_.GetWeakPtr(); if (delegate) { - base::WeakPtr weak_popup = - popup->weak_factory_.GetWeakPtr(); - delegate->AddNewContents(this, popup, disposition, initial_rect, - user_gesture, nullptr); + delegate->AddNewContents(this, std::move(popup), disposition, + initial_rect, user_gesture, nullptr); if (!weak_popup) return; // The delegate deleted |popup| during AddNewContents(). } - RenderWidgetHostImpl* rwh = popup->GetMainFrame()->GetRenderWidgetHost(); + RenderWidgetHostImpl* rwh = + weak_popup->GetMainFrame()->GetRenderWidgetHost(); DCHECK_EQ(main_frame_widget_route_id, rwh->GetRoutingID()); rwh->Send(new ViewMsg_Move_ACK(rwh->GetRoutingID())); } @@ -2614,7 +2678,8 @@ void WebContentsImpl::ShowCreatedWidget(int process_id, fullscreen_widget_routing_id_ = route_id; if (delegate_ && delegate_->EmbedsFullscreenWidget()) { widget_host_view->InitAsChild(GetRenderWidgetHostView()->GetNativeView()); - delegate_->EnterFullscreenModeForTab(this, GURL()); + delegate_->EnterFullscreenModeForTab(this, GURL(), + blink::WebFullscreenOptions()); } else { widget_host_view->InitAsFullscreen(view); } @@ -2633,7 +2698,7 @@ void WebContentsImpl::ShowCreatedWidget(int process_id, render_widget_host_impl->set_allow_privileged_mouse_lock(is_fullscreen); } -WebContentsImpl* WebContentsImpl::GetCreatedWindow( +std::unique_ptr WebContentsImpl::GetCreatedWindow( int process_id, int main_frame_widget_route_id) { auto key = std::make_pair(process_id, main_frame_widget_route_id); @@ -2644,17 +2709,18 @@ WebContentsImpl* WebContentsImpl::GetCreatedWindow( if (iter == pending_contents_.end()) return nullptr; - WebContentsImpl* new_contents = iter->second; + std::unique_ptr new_contents = std::move(iter->second); pending_contents_.erase(key); - RemoveDestructionObserver(new_contents); + WebContentsImpl* raw_new_contents = + static_cast(new_contents.get()); + RemoveDestructionObserver(raw_new_contents); // Don't initialize the guest WebContents immediately. - if (BrowserPluginGuest::IsGuest(new_contents)) + if (BrowserPluginGuest::IsGuest(raw_new_contents)) return new_contents; if (!new_contents->GetMainFrame()->GetProcess()->HasConnection() || !new_contents->GetMainFrame()->GetView()) { - // TODO(nick): http://crbug.com/674318 -- Who deletes |new_contents|? return nullptr; } @@ -2848,8 +2914,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { if (GetOuterWebContents()) return GetOuterWebContents()->GetTextInputManager(); - if (!text_input_manager_) - text_input_manager_.reset(new TextInputManager()); + if (!text_input_manager_) { + text_input_manager_.reset(new TextInputManager( + GetBrowserContext() && + !GetBrowserContext()->IsOffTheRecord()) /* should_do_learning */); + } return text_input_manager_.get(); } @@ -2939,8 +3008,7 @@ void WebContentsImpl::UpdatePreferredSize(const gfx::Size& pref_size) { void WebContentsImpl::ResizeDueToAutoResize( RenderWidgetHostImpl* render_widget_host, - const gfx::Size& new_size, - uint64_t sequence_number) { + const gfx::Size& new_size) { if (render_widget_host != GetRenderViewHost()->GetWidget()) return; @@ -2954,7 +3022,7 @@ void WebContentsImpl::ResizeDueToAutoResize( RenderWidgetHostImpl* host = node->current_frame_host()->GetRenderWidgetHost(); if (host != render_widget_host) - host->WasResized(); + host->SynchronizeVisualProperties(); } } @@ -3904,6 +3972,9 @@ void WebContentsImpl::DidNavigateMainFramePreCommit( if (IsFullscreenForCurrentTab()) ExitFullscreen(false); DCHECK(!IsFullscreenForCurrentTab()); + + // Clean up keyboard lock state when navigating. + CancelKeyboardLock(keyboard_lock_widget_); } void WebContentsImpl::DidNavigateMainFramePostCommit( @@ -4103,7 +4174,8 @@ void WebContentsImpl::ViewSource(RenderFrameHostImpl* frame) { auto navigation_entry = std::make_unique( site_instance_for_view_source, frame_entry->url(), referrer_for_view_source, title_for_view_source, ui::PAGE_TRANSITION_LINK, - /* is_renderer_initiated = */ false); + /* is_renderer_initiated = */ false, + /* blob_url_loader_factory = */ nullptr); navigation_entry->SetVirtualURL(GURL(content::kViewSourceScheme + std::string(":") + frame_entry->url().spec())); @@ -4120,8 +4192,8 @@ void WebContentsImpl::ViewSource(RenderFrameHostImpl* frame) { new_frame_entry->SetPageState(new_page_state); // Create a new WebContents, which is used to display the source code. - WebContentsImpl* view_source_contents = - static_cast(Create(CreateParams(GetBrowserContext()))); + std::unique_ptr view_source_contents = + Create(CreateParams(GetBrowserContext())); // Restore the previously created NavigationEntry. std::vector> navigation_entries; @@ -4133,7 +4205,7 @@ void WebContentsImpl::ViewSource(RenderFrameHostImpl* frame) { gfx::Rect initial_rect; constexpr bool kUserGesture = true; bool ignored_was_blocked; - delegate_->AddNewContents(this, view_source_contents, + delegate_->AddNewContents(this, std::move(view_source_contents), WindowOpenDisposition::NEW_FOREGROUND_TAB, initial_rect, kUserGesture, &ignored_was_blocked); // Note that the |delegate_| could have deleted |view_source_contents| during @@ -4147,9 +4219,10 @@ void WebContentsImpl::SubresourceResponseStarted(const GURL& url, } void WebContentsImpl::ResourceLoadComplete( + RenderFrameHost* render_frame_host, mojom::ResourceLoadInfoPtr resource_load_info) { for (auto& observer : observers_) { - observer.ResourceLoadComplete(*resource_load_info); + observer.ResourceLoadComplete(render_frame_host, *resource_load_info); } } @@ -4175,18 +4248,6 @@ void WebContentsImpl::PrintCrossProcessSubframe( subframe_host); } -void WebContentsImpl::UpdatePictureInPictureSurfaceId( - const viz::SurfaceId& surface_id, - const gfx::Size& natural_size) { - if (delegate_) - delegate_->UpdatePictureInPictureSurfaceId(surface_id, natural_size); -} - -void WebContentsImpl::ExitPictureInPicture() { - if (delegate_) - delegate_->ExitPictureInPicture(); -} - #if defined(OS_ANDROID) base::android::ScopedJavaLocalRef WebContentsImpl::GetJavaRenderFrameHostDelegate() { @@ -4338,22 +4399,6 @@ void WebContentsImpl::OnFindReply(RenderFrameHostImpl* source, } #if defined(OS_ANDROID) -void WebContentsImpl::OnFindMatchRectsReply( - RenderFrameHostImpl* source, - int version, - const std::vector& rects, - const gfx::RectF& active_rect) { - GetOrCreateFindRequestManager()->OnFindMatchRectsReply(source, version, rects, - active_rect); -} - -void WebContentsImpl::OnGetNearestFindResultReply(RenderFrameHostImpl* source, - int request_id, - float distance) { - GetOrCreateFindRequestManager()->OnGetNearestFindResultReply( - source, request_id, distance); -} - void WebContentsImpl::OnOpenDateTimeDialog( RenderViewHostImpl* source, const ViewHostMsg_DateTimeDialogValue_Params& value) { @@ -4662,16 +4707,13 @@ void WebContentsImpl::NotifyViewSwapped(RenderViewHost* old_host, void WebContentsImpl::NotifyFrameSwapped(RenderFrameHost* old_host, RenderFrameHost* new_host) { #if defined(OS_ANDROID) - // Try to copy importance from either |old_host| or parent of |new_host|. - // If both are null, then this is the very first frame host created from Init. - // There is no need to pass importance in this case because there is no chance - // for anything to call SetImportance yet. - RenderFrameHostImpl* importance_host = static_cast( - old_host ? old_host : new_host->GetParent()); - if (importance_host) { + // Copy importance from |old_host| if |new_host| is a main frame. + if (old_host && !new_host->GetParent()) { static_cast(new_host) ->GetRenderWidgetHost() - ->SetImportance(importance_host->GetRenderWidgetHost()->importance()); + ->SetImportance(static_cast(old_host) + ->GetRenderWidgetHost() + ->importance()); } #endif for (auto& observer : observers_) @@ -5788,7 +5830,7 @@ bool WebContentsImpl::CreateRenderViewForRenderManager( RenderWidgetHostView* rwh_view = render_view_host->GetWidget()->GetView(); if (rwh_view) { if (RenderWidgetHost* render_widget_host = rwh_view->GetRenderWidgetHost()) - render_widget_host->WasResized(); + render_widget_host->SynchronizeVisualProperties(); } #endif @@ -6113,6 +6155,18 @@ void WebContentsImpl::BrowserPluginGuestWillDetach() { outermost->SetAsFocusedWebContentsIfNecessary(); } +gfx::Size WebContentsImpl::EnterPictureInPicture( + const viz::SurfaceId& surface_id, + const gfx::Size& natural_size) { + return delegate_ ? delegate_->EnterPictureInPicture(surface_id, natural_size) + : gfx::Size(); +} + +void WebContentsImpl::ExitPictureInPicture() { + if (delegate_) + delegate_->ExitPictureInPicture(); +} + #if defined(OS_ANDROID) void WebContentsImpl::NotifyFindMatchRectsReply( int version, @@ -6158,6 +6212,19 @@ void WebContentsImpl::ClearDeviceEmulationSize() { view_size_before_emulation_ = gfx::Size(); } +ForwardingAudioStreamFactory* WebContentsImpl::GetAudioStreamFactory() { + if (!audio_stream_factory_) { + audio_stream_factory_.emplace( + this, + content::ServiceManagerConnection::GetForProcess() + ->GetConnector() + ->Clone(), + AudioStreamBrokerFactory::CreateImpl()); + } + + return &*audio_stream_factory_; +} + void WebContentsImpl::MediaStartedPlaying( const WebContentsObserver::MediaPlayerInfo& media_info, const WebContentsObserver::MediaPlayerId& id) { diff --git a/chromium/content/browser/web_contents/web_contents_impl.h b/chromium/content/browser/web_contents/web_contents_impl.h index aeda7bbac34..cd1c992c773 100644 --- a/chromium/content/browser/web_contents/web_contents_impl.h +++ b/chromium/content/browser/web_contents/web_contents_impl.h @@ -19,6 +19,7 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/observer_list.h" +#include "base/optional.h" #include "base/process/process.h" #include "base/time/time.h" #include "base/values.h" @@ -33,6 +34,7 @@ #include "content/browser/frame_host/render_frame_host_delegate.h" #include "content/browser/frame_host/render_frame_host_manager.h" #include "content/browser/media/audio_stream_monitor.h" +#include "content/browser/media/forwarding_audio_stream_factory.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_delegate.h" @@ -137,7 +139,7 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, ~WebContentsImpl() override; - static WebContentsImpl* CreateWithOpener( + static std::unique_ptr CreateWithOpener( const WebContents::CreateParams& params, RenderFrameHostImpl* opener_rfh); @@ -286,16 +288,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, return manifest_manager_host_.get(); } - // TODO(https://crbug.com/826293): This is a simple mitigation to validate - // that an action that requires a user gesture actually has one in the - // trustworthy browser process, rather than relying on the untrustworthy - // renderer. This should be eventually merged into and accounted for in the - // user activation work. - bool HasRecentInteractiveInputEvent() const; - #if defined(OS_ANDROID) - std::set GetAllRenderWidgetHosts(); - void SetImportance(ChildProcessImportance importance); + void SetMainFrameImportance(ChildProcessImportance importance); #endif // WebContents ------------------------------------------------------ @@ -355,6 +349,7 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, void SetAudioMuted(bool mute) override; bool IsCurrentlyAudible() override; bool IsConnectedToBluetoothDevice() const override; + bool HasPictureInPictureVideo() const override; bool IsCrashed() const override; void SetIsCrashed(base::TerminationStatus status, int error_code) override; base::TerminationStatus GetCrashedStatus() const override; @@ -379,7 +374,7 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, void Stop() override; void FreezePage() override; - WebContents* Clone() override; + std::unique_ptr Clone() override; void ReloadFocusedFrame(bool bypass_cache) override; void Undo() override; void Redo() override; @@ -480,6 +475,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, bool CompletedFirstVisuallyNonEmptyPaint() const override; #endif + bool HasRecentInteractiveInputEvent() const override; + // Implementation of PageNavigator. WebContents* OpenURL(const OpenURLParams& params) override; @@ -538,7 +535,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, #if defined(OS_ANDROID) void GetNFC(device::mojom::NFCRequest request) override; #endif - void EnterFullscreenMode(const GURL& origin) override; + void EnterFullscreenMode(const GURL& origin, + const blink::WebFullscreenOptions& options) override; void ExitFullscreenMode(bool will_cause_resize) override; bool ShouldRouteMessageEvent( RenderFrameHost* target_rfh, @@ -584,11 +582,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, void SubresourceResponseStarted(const GURL& url, net::CertStatus cert_status) override; void ResourceLoadComplete( + RenderFrameHost* render_frame_host, mojom::ResourceLoadInfoPtr resource_load_information) override; - void UpdatePictureInPictureSurfaceId(const viz::SurfaceId& surface_id, - const gfx::Size& natural_size) override; - void ExitPictureInPicture() override; - // RenderViewHostDelegate ---------------------------------------------------- RenderViewHostDelegateView* GetDelegateView() override; bool OnMessageReceived(RenderViewHostImpl* render_view_host, @@ -696,8 +691,7 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, const ScreenInfo& screen_info, bool width_changed) override; void ResizeDueToAutoResize(RenderWidgetHostImpl* render_widget_host, - const gfx::Size& new_size, - uint64_t sequence_number) override; + const gfx::Size& new_size) override; gfx::Size GetAutoResizeSize() override; void ResetAutoResizeSize() override; KeyboardEventProcessingResult PreHandleKeyboardEvent( @@ -871,6 +865,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, return &audio_stream_monitor_; } + ForwardingAudioStreamFactory* GetAudioStreamFactory(); + // Called by MediaWebContentsObserver when playback starts or stops. See the // WebContentsObserver function stubs for more details. void MediaStartedPlaying( @@ -936,6 +932,20 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, // that the guest will be detached. void BrowserPluginGuestWillDetach(); + // Notifies the Picture-in-Picture controller that there is a new player + // entering Picture-in-Picture. + // Returns the size of the Picture-in-Picture window. + gfx::Size EnterPictureInPicture(const viz::SurfaceId&, + const gfx::Size& natural_size); + + // Updates the Picture-in-Picture controller with a signal that + // Picture-in-Picture mode has ended. + void ExitPictureInPicture(); + + // Updates the tracking information for |this| to know if there is + // a video currently in Picture-in-Picture mode. + void SetHasPictureInPictureVideo(bool has_picture_in_picture_video); + #if defined(OS_ANDROID) // Called by FindRequestManager when all of the find match rects are in. void NotifyFindMatchRectsReply(int version, @@ -943,10 +953,16 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, const gfx::RectF& active_rect); #endif + // Returns a base salt used to generate group IDs for media-device + // enumerations. + const std::string& GetMediaDeviceGroupIDSaltBase() const; + private: friend class WebContentsObserver; friend class WebContents; // To implement factory methods. + friend class RenderFrameHostImplBeforeUnloadBrowserTest; + FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, NoJSMessageOnInterstitials); FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, UpdateTitle); FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, FindOpenerRVHWhenPending); @@ -1147,13 +1163,6 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, int active_match_ordinal, bool final_update); #if defined(OS_ANDROID) - void OnFindMatchRectsReply(RenderFrameHostImpl* source, - int version, - const std::vector& rects, - const gfx::RectF& active_rect); - void OnGetNearestFindResultReply(RenderFrameHostImpl* source, - int request_id, - float distance); void OnOpenDateTimeDialog( RenderViewHostImpl* source, const ViewHostMsg_DateTimeDialogValue_Params& value); @@ -1260,8 +1269,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, // Finds the new WebContentsImpl by |main_frame_widget_route_id|, initializes // it for renderer-initiated creation, and returns it. Note that this can only // be called once as this call also removes it from the internal map. - WebContentsImpl* GetCreatedWindow(int process_id, - int main_frame_widget_route_id); + std::unique_ptr GetCreatedWindow(int process_id, + int main_frame_widget_route_id); // Sends a Page message IPC. void SendPageMessage(IPC::Message* msg); @@ -1380,7 +1389,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, // Tracks created WebContentsImpl objects that have not been shown yet. They // are identified by the process ID and routing ID passed to CreateNewWindow. typedef std::pair ProcessRoutingIdPair; - std::map pending_contents_; + std::map> + pending_contents_; // This map holds widgets that were created on behalf of the renderer but // haven't been shown yet. @@ -1644,11 +1654,16 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, // Monitors power levels for audio streams associated with this WebContents. AudioStreamMonitor audio_stream_monitor_; + // Coordinates all the audio streams for this WebContents. Lazily initialized. + base::Optional audio_stream_factory_; + // Created on-demand to mute all audio output from this WebContents. std::unique_ptr audio_muter_; size_t bluetooth_connected_device_count_; + bool has_picture_in_picture_video_ = false; + // Notifies ResourceDispatcherHostImpl of various events related to loading. std::unique_ptr loader_io_thread_notifier_; @@ -1668,6 +1683,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, PageImportanceSignals page_importance_signals_; + std::string media_device_group_id_salt_base_; + #if !defined(OS_ANDROID) bool page_scale_factor_is_one_; #endif // !defined(OS_ANDROID) 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 5a902419130..7c85b1f8fe1 100644 --- a/chromium/content/browser/web_contents/web_contents_impl_browsertest.cc +++ b/chromium/content/browser/web_contents/web_contents_impl_browsertest.cc @@ -2,15 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include #include #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/optional.h" +#include "base/path_service.h" #include "base/run_loop.h" #include "base/strings/pattern.h" #include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "base/values.h" #include "build/build_config.h" @@ -23,6 +26,7 @@ #include "content/browser/web_contents/web_contents_view.h" #include "content/common/frame_messages.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/invalidate_type.h" #include "content/public/browser/javascript_dialog_manager.h" #include "content/public/browser/load_notification_details.h" #include "content/public/browser/navigation_controller.h" @@ -35,6 +39,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" +#include "content/public/common/content_client.h" #include "content/public/common/content_paths.h" #include "content/public/common/url_constants.h" #include "content/public/test/browser_test_utils.h" @@ -45,6 +50,7 @@ #include "content/public/test/url_loader_interceptor.h" #include "content/shell/browser/shell.h" #include "content/test/content_browser_test_utils_internal.h" +#include "content/test/test_content_browser_client.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/controllable_http_response.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -78,6 +84,28 @@ void ResizeWebContentsView(Shell* shell, const gfx::Size& size, #endif // defined(OS_MACOSX) } +// Class to test that OverrideWebkitPrefs has been called for all relevant +// RenderViewHosts. +class NotifyPreferencesChangedTestContentBrowserClient + : public TestContentBrowserClient { + public: + NotifyPreferencesChangedTestContentBrowserClient() = default; + + void OverrideWebkitPrefs(RenderViewHost* render_view_host, + WebPreferences* prefs) override { + override_webkit_prefs_rvh_set_.insert(render_view_host); + } + + const std::unordered_set& override_webkit_prefs_rvh_set() { + return override_webkit_prefs_rvh_set_; + } + + private: + std::unordered_set override_webkit_prefs_rvh_set_; + + DISALLOW_COPY_AND_ASSIGN(NotifyPreferencesChangedTestContentBrowserClient); +}; + class WebContentsImplBrowserTest : public ContentBrowserTest { public: WebContentsImplBrowserTest() {} @@ -87,8 +115,6 @@ class WebContentsImplBrowserTest : public ContentBrowserTest { } void SetUpOnMainThread() override { - host_resolver()->AddRuleWithLatency("slow.com", "127.0.0.1", - 1000 * 60 * 60 /* ms */); // Setup the server to allow serving separate sites, so we can perform // cross-process navigation. host_resolver()->AddRule("*", "127.0.0.1"); @@ -629,40 +655,71 @@ class ResourceLoadObserver : public WebContentsObserver { return resource_load_infos_; } + const std::vector& resource_is_associated_with_main_frame() const { + return resource_is_associated_with_main_frame_; + } + const std::vector& memory_cached_loaded_urls() const { return memory_cached_loaded_urls_; } // Use this method with the SCOPED_TRACE macro, so it shows the caller context // if it fails. - void CheckResourceLoaded(const GURL& url, - const GURL& referrer, - const std::string& load_method, - content::ResourceType resource_type, - const std::string& mime_type, - const std::string& ip_address, - bool was_cached, - bool first_network_request, - const base::TimeTicks& before_request, - const base::TimeTicks& after_request) { + void CheckResourceLoaded( + const GURL& url, + const GURL& referrer, + const std::string& load_method, + content::ResourceType resource_type, + const base::FilePath::StringPieceType& served_file_name, + const std::string& mime_type, + const std::string& ip_address, + bool was_cached, + bool first_network_request, + const base::TimeTicks& before_request, + const base::TimeTicks& after_request) { bool resource_load_info_found = false; for (const auto& resource_load_info : resource_load_infos_) { - if (resource_load_info->url == url) { - resource_load_info_found = true; - EXPECT_EQ(referrer, resource_load_info->referrer); - EXPECT_EQ(load_method, resource_load_info->method); - EXPECT_EQ(resource_type, resource_load_info->resource_type); - if (!first_network_request) - EXPECT_GT(resource_load_info->request_id, 0); - EXPECT_EQ(mime_type, resource_load_info->mime_type); - if (!ip_address.empty()) { - ASSERT_TRUE(resource_load_info->ip); - EXPECT_EQ(ip_address, resource_load_info->ip->ToString()); - } - EXPECT_EQ(was_cached, resource_load_info->was_cached); - // Simple sanity check of the request start time. - EXPECT_GT(resource_load_info->request_start, before_request); - EXPECT_GT(after_request, resource_load_info->request_start); + if (resource_load_info->url != url) + continue; + + resource_load_info_found = true; + int64_t file_size = -1; + if (!served_file_name.empty()) { + base::ScopedAllowBlockingForTesting allow_blocking; + base::FilePath test_dir; + ASSERT_TRUE(base::PathService::Get(content::DIR_TEST_DATA, &test_dir)); + base::FilePath served_file = test_dir.Append(served_file_name); + ASSERT_TRUE(GetFileSize(served_file, &file_size)); + } + EXPECT_EQ(referrer, resource_load_info->referrer); + EXPECT_EQ(load_method, resource_load_info->method); + EXPECT_EQ(resource_type, resource_load_info->resource_type); + if (!first_network_request) + EXPECT_GT(resource_load_info->request_id, 0); + EXPECT_EQ(mime_type, resource_load_info->mime_type); + ASSERT_TRUE(resource_load_info->network_info->ip_port_pair); + EXPECT_EQ(ip_address, + resource_load_info->network_info->ip_port_pair->host()); + EXPECT_EQ(was_cached, resource_load_info->was_cached); + // Simple sanity check of the load timing info. + auto CheckTime = [before_request, after_request](auto actual) { + EXPECT_LE(before_request, actual); + EXPECT_GT(after_request, actual); + }; + const net::LoadTimingInfo& timing = resource_load_info->load_timing_info; + CheckTime(timing.request_start); + CheckTime(timing.receive_headers_end); + CheckTime(timing.send_start); + CheckTime(timing.send_end); + if (!was_cached) { + CheckTime(timing.connect_timing.dns_start); + CheckTime(timing.connect_timing.dns_end); + CheckTime(timing.connect_timing.connect_start); + CheckTime(timing.connect_timing.connect_end); + } + if (file_size != -1) { + EXPECT_EQ(file_size, resource_load_info->raw_body_bytes); + EXPECT_LT(file_size, resource_load_info->total_received_bytes); } } EXPECT_TRUE(resource_load_info_found); @@ -671,13 +728,18 @@ class ResourceLoadObserver : public WebContentsObserver { void Reset() { resource_load_infos_.clear(); memory_cached_loaded_urls_.clear(); + resource_is_associated_with_main_frame_.clear(); } private: // WebContentsObserver implementation: void ResourceLoadComplete( + content::RenderFrameHost* render_frame_host, const mojom::ResourceLoadInfo& resource_load_info) override { + EXPECT_NE(nullptr, render_frame_host); resource_load_infos_.push_back(resource_load_info.Clone()); + resource_is_associated_with_main_frame_.push_back( + render_frame_host->GetParent() == nullptr); } void DidLoadResourceFromMemoryCache(const GURL& url, @@ -688,6 +750,7 @@ class ResourceLoadObserver : public WebContentsObserver { std::vector memory_cached_loaded_urls_; std::vector resource_load_infos_; + std::vector resource_is_associated_with_main_frame_; DISALLOW_COPY_AND_ASSIGN(ResourceLoadObserver); }; @@ -701,23 +764,19 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, ResourceLoadComplete) { NavigateToURL(shell(), page_url); base::TimeTicks after = base::TimeTicks::Now(); ASSERT_EQ(3U, observer.resource_load_infos().size()); - // TODO(crbug.com/826082): we should test the IP address/MIME type parameters - // for frames once they are reported correctly. SCOPE_TRACED(observer.CheckResourceLoaded( page_url, /*referrer=*/GURL(), "GET", content::RESOURCE_TYPE_MAIN_FRAME, - /*mime-type*/ "", - /*ip_address=*/"", + FILE_PATH_LITERAL("page_with_iframe.html"), "text/html", "127.0.0.1", /*was_cached=*/false, /*first_network_request=*/true, before, after)); SCOPE_TRACED(observer.CheckResourceLoaded( embedded_test_server()->GetURL("/image.jpg"), - /*referrer=*/page_url, "GET", content::RESOURCE_TYPE_IMAGE, "image/jpeg", - "127.0.0.1", + /*referrer=*/page_url, "GET", content::RESOURCE_TYPE_IMAGE, + FILE_PATH_LITERAL("image.jpg"), "image/jpeg", "127.0.0.1", /*was_cached=*/false, /*first_network_request=*/false, before, after)); SCOPE_TRACED(observer.CheckResourceLoaded( embedded_test_server()->GetURL("/title1.html"), /*referrer=*/page_url, "GET", content::RESOURCE_TYPE_SUB_FRAME, - /*mime_type=*/"", - /*ip_address=*/"", + FILE_PATH_LITERAL("title1.html"), "text/html", "127.0.0.1", /*was_cached=*/false, /*first_network_request=*/false, before, after)); } @@ -733,20 +792,20 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, NavigateToURL(shell(), page_url); base::TimeTicks after = base::TimeTicks::Now(); - // TODO(crbug.com/826082): we should test the IP address/MIME type parameters - // for frames once they are reported correctly. GURL resource_url = embedded_test_server()->GetURL("/cachetime"); ASSERT_EQ(2U, observer.resource_load_infos().size()); SCOPE_TRACED(observer.CheckResourceLoaded( page_url, /*referrer=*/GURL(), "GET", content::RESOURCE_TYPE_MAIN_FRAME, - /*mime-type*/ "", - /*ip_address=*/"", /*was_cached=*/false, /*first_network_request=*/true, - before, after)); + /*served_file_name=*/FILE_PATH_LITERAL(""), "text/html", "127.0.0.1", + /*was_cached=*/false, + /*first_network_request=*/true, before, after)); + SCOPE_TRACED(observer.CheckResourceLoaded( resource_url, /*referrer=*/page_url, "GET", content::RESOURCE_TYPE_SCRIPT, - "text/html", "127.0.0.1", + /*served_file_name=*/FILE_PATH_LITERAL(""), "text/html", "127.0.0.1", /*was_cached=*/false, /*first_network_request=*/false, before, after)); - EXPECT_TRUE(observer.resource_load_infos()[1]->network_accessed); + EXPECT_TRUE( + observer.resource_load_infos()[1]->network_info->network_accessed); EXPECT_TRUE(observer.memory_cached_loaded_urls().empty()); observer.Reset(); @@ -757,7 +816,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, ASSERT_EQ(1U, observer.resource_load_infos().size()); SCOPE_TRACED(observer.CheckResourceLoaded( page_url, /*referrer=*/GURL(), "GET", content::RESOURCE_TYPE_MAIN_FRAME, - /*mime-type*/ "", /*ip_address=*/"", + /*served_file_name=*/FILE_PATH_LITERAL(""), "text/html", "127.0.0.1", /*was_cached=*/false, /*first_network_request=*/false, before, after)); ASSERT_EQ(1U, observer.memory_cached_loaded_urls().size()); EXPECT_EQ(resource_url, observer.memory_cached_loaded_urls()[0]); @@ -774,14 +833,15 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, ASSERT_EQ(2U, observer.resource_load_infos().size()); SCOPE_TRACED(observer.CheckResourceLoaded( page_url, /*referrer=*/GURL(), "GET", content::RESOURCE_TYPE_MAIN_FRAME, - /*mime-type*/ "", /*ip_address=*/"", + /*served_file_name=*/FILE_PATH_LITERAL(""), "text/html", "127.0.0.1", /*was_cached=*/false, /*first_network_request=*/true, before, after)); SCOPE_TRACED(observer.CheckResourceLoaded( resource_url, /*referrer=*/page_url, "GET", content::RESOURCE_TYPE_SCRIPT, - "text/html", "127.0.0.1", + /*served_file_name=*/FILE_PATH_LITERAL(""), "text/html", "127.0.0.1", /*was_cached=*/true, /*first_network_request=*/false, before, after)); EXPECT_TRUE(observer.memory_cached_loaded_urls().empty()); - EXPECT_FALSE(observer.resource_load_infos()[1]->network_accessed); + EXPECT_FALSE( + observer.resource_load_infos()[1]->network_info->network_accessed); } IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, @@ -791,16 +851,17 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, NavigateToURL(shell(), GURL(embedded_test_server()->GetURL("/page_with_image.html"))); ASSERT_EQ(2U, observer.resource_load_infos().size()); - // TODO(crbug.com/826082): network_accessed should be true on the frame. - EXPECT_FALSE(observer.resource_load_infos()[0]->network_accessed); - EXPECT_TRUE(observer.resource_load_infos()[1]->network_accessed); + EXPECT_TRUE( + observer.resource_load_infos()[0]->network_info->network_accessed); + EXPECT_TRUE( + observer.resource_load_infos()[1]->network_info->network_accessed); observer.Reset(); NavigateToURL(shell(), GURL("chrome://gpu")); ASSERT_LE(1U, observer.resource_load_infos().size()); for (const mojom::ResourceLoadInfoPtr& resource_load_info : observer.resource_load_infos()) { - EXPECT_FALSE(resource_load_info->network_accessed); + EXPECT_FALSE(resource_load_info->network_info->network_accessed); } } @@ -818,9 +879,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, ASSERT_EQ(2U, observer.resource_load_infos().size()); const mojom::ResourceLoadInfoPtr& page_load_info = observer.resource_load_infos()[0]; - // TODO(crbug.com/826082): turn this back on when the ResourceDispatcher - // receives redirects for the main frame. - // EXPECT_EQ(page_destination_url, page_load_info->url); + EXPECT_EQ(page_destination_url, page_load_info->url); EXPECT_EQ(page_original_url, page_load_info->original_url); GURL image_destination_url(embedded_test_server()->GetURL("/blank.jpg")); @@ -866,6 +925,87 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, observer.resource_load_infos()[1]->net_error); } +IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, + ResourceLoadCompleteAlwaysAccessNetwork) { + ResourceLoadObserver observer(shell()); + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL cacheable_url(embedded_test_server()->GetURL("/set-header")); + NavigateToURL(shell(), cacheable_url); + ASSERT_EQ(1U, observer.resource_load_infos().size()); + EXPECT_FALSE( + observer.resource_load_infos()[0]->network_info->always_access_network); + observer.Reset(); + + std::array headers = { + "cache-control: no-cache", "cache-control: no-store", "pragma: no-cache"}; + for (const std::string& header : headers) { + GURL no_cache_url(embedded_test_server()->GetURL("/set-header?" + header)); + NavigateToURL(shell(), no_cache_url); + ASSERT_EQ(1U, observer.resource_load_infos().size()); + EXPECT_TRUE( + observer.resource_load_infos()[0]->network_info->always_access_network); + observer.Reset(); + } +} + +IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, + ResourceLoadCompleteWithRedirects) { + ResourceLoadObserver observer(shell()); + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL target_url(embedded_test_server()->GetURL("/title1.html")); + GURL intermediate_url( + embedded_test_server()->GetURL("/server-redirect?" + target_url.spec())); + GURL start_url(embedded_test_server()->GetURL("/server-redirect?" + + intermediate_url.spec())); + + NavigateToURL(shell(), start_url); + + ASSERT_EQ(1U, observer.resource_load_infos().size()); + EXPECT_EQ(target_url, observer.resource_load_infos()[0]->url); + + ASSERT_EQ(2U, observer.resource_load_infos()[0]->redirect_info_chain.size()); + EXPECT_EQ(intermediate_url, + observer.resource_load_infos()[0]->redirect_info_chain[0]->url); + EXPECT_TRUE(observer.resource_load_infos()[0] + ->redirect_info_chain[0] + ->network_info->network_accessed); + EXPECT_FALSE(observer.resource_load_infos()[0] + ->redirect_info_chain[0] + ->network_info->always_access_network); + EXPECT_EQ(target_url, + observer.resource_load_infos()[0]->redirect_info_chain[1]->url); + EXPECT_TRUE(observer.resource_load_infos()[0] + ->redirect_info_chain[1] + ->network_info->network_accessed); + EXPECT_FALSE(observer.resource_load_infos()[0] + ->redirect_info_chain[1] + ->network_info->always_access_network); +} + +IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, + ResourceLoadCompleteIsMainFrame) { + ResourceLoadObserver observer(shell()); + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL url(embedded_test_server()->GetURL("/page_with_image.html")); + NavigateToURL(shell(), url); + ASSERT_EQ(2U, observer.resource_load_infos().size()); + EXPECT_EQ(url, observer.resource_load_infos()[0]->url); + EXPECT_TRUE(observer.resource_is_associated_with_main_frame()[0]); + EXPECT_TRUE(observer.resource_is_associated_with_main_frame()[1]); + observer.Reset(); + + // Load that same page inside an iframe. + GURL data_url("data:text/html,"); + NavigateToURL(shell(), data_url); + ASSERT_EQ(2U, observer.resource_load_infos().size()); + EXPECT_EQ(url, observer.resource_load_infos()[0]->url); + EXPECT_FALSE(observer.resource_is_associated_with_main_frame()[0]); + EXPECT_FALSE(observer.resource_is_associated_with_main_frame()[1]); +} + struct LoadProgressDelegateAndObserver : public WebContentsDelegate, public WebContentsObserver { explicit LoadProgressDelegateAndObserver(Shell* shell) @@ -1062,7 +1202,11 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, ChangeDisplayMode) { delegate.set_mode(blink::kWebDisplayModeFullscreen); // Simulate widget is entering fullscreen (changing size is enough). - shell()->web_contents()->GetRenderViewHost()->GetWidget()->WasResized(); + shell() + ->web_contents() + ->GetRenderViewHost() + ->GetWidget() + ->SynchronizeVisualProperties(); ASSERT_TRUE(ExecuteScript(shell(), "document.title = " @@ -1393,8 +1537,10 @@ class TestWCDelegateForDialogsAndFullscreen : public JavaScriptDialogManager, return this; } - void EnterFullscreenModeForTab(WebContents* web_contents, - const GURL& origin) override { + void EnterFullscreenModeForTab( + WebContents* web_contents, + const GURL& origin, + const blink::WebFullscreenOptions& options) override { is_fullscreen_ = true; } @@ -1411,12 +1557,12 @@ class TestWCDelegateForDialogsAndFullscreen : public JavaScriptDialogManager, } void AddNewContents(WebContents* source, - WebContents* new_contents, + std::unique_ptr new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_rect, bool user_gesture, bool* was_blocked) override { - popup_.reset(new_contents); + popup_ = std::move(new_contents); if (waiting_for_ == kNewContents) run_loop_->Quit(); @@ -1569,10 +1715,10 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, WebContents* base_web_contents = shell()->web_contents(); ASSERT_TRUE(base_web_contents); - GURL url(embedded_test_server()->GetURL("c.com", "/title3.html")); WebContents::CreateParams create_params( base_web_contents->GetBrowserContext()); - create_params.initialize_renderer = true; + create_params.desired_renderer_state = + WebContents::CreateParams::kInitializeAndWarmupRendererProcess; create_params.initial_size = base_web_contents->GetContainerBounds().size(); std::unique_ptr web_contents(WebContents::Create(create_params)); @@ -1581,11 +1727,18 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, // There is no navigation (to about:blank or something like that). EXPECT_FALSE(web_contents->IsLoading()); + // The WebContents have an associated main frame and a renderer process that + // has either already launched (or is in the process of being launched). ASSERT_TRUE(web_contents->GetMainFrame()); EXPECT_TRUE(web_contents->GetMainFrame()->IsRenderFrameLive()); EXPECT_TRUE(web_contents->GetController().IsInitialBlankNavigation()); - int renderer_id = web_contents->GetMainFrame()->GetProcess()->GetID(); + RenderProcessHost* process = web_contents->GetMainFrame()->GetProcess(); + int renderer_id = process->GetID(); + ASSERT_TRUE(process); + EXPECT_TRUE(process->HasConnection()); + // Navigate the WebContents. + GURL url(embedded_test_server()->GetURL("c.com", "/title3.html")); TestNavigationObserver same_tab_observer(web_contents.get()); NavigationController::LoadURLParams params(url); params.transition_type = ui::PageTransitionFromInt( @@ -1595,6 +1748,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, EXPECT_TRUE(same_tab_observer.last_navigation_succeeded()); // Check that pre-warmed process is used. + EXPECT_EQ(process, web_contents->GetMainFrame()->GetProcess()); EXPECT_EQ(renderer_id, web_contents->GetMainFrame()->GetProcess()->GetID()); EXPECT_EQ(1, web_contents->GetController().GetEntryCount()); NavigationEntry* entry = @@ -1603,6 +1757,67 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, EXPECT_EQ(url, entry->GetURL()); } +// Regression test for https://crbug.com/840409. +IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, + CreateWebContentsWithoutRendererProcess) { + ASSERT_TRUE(embedded_test_server()->Start()); + WebContents* base_web_contents = shell()->web_contents(); + ASSERT_TRUE(base_web_contents); + + for (int i = 1; i <= 2; i++) { + SCOPED_TRACE(testing::Message() << "Iteration #" << i); + + WebContents::CreateParams create_params( + base_web_contents->GetBrowserContext()); + create_params.desired_renderer_state = + WebContents::CreateParams::kNoRendererProcess; + create_params.initial_size = base_web_contents->GetContainerBounds().size(); + std::unique_ptr web_contents( + WebContents::Create(create_params)); + ASSERT_TRUE(web_contents); + base::RunLoop().RunUntilIdle(); + + // There is no navigation (to about:blank or something like that) yet. + EXPECT_FALSE(web_contents->IsLoading()); + + // The WebContents have an associated main frame and a RenderProcessHost + // object, but no actual OS process has been launched yet. + ASSERT_TRUE(web_contents->GetMainFrame()); + EXPECT_FALSE(web_contents->GetMainFrame()->IsRenderFrameLive()); + EXPECT_TRUE(web_contents->GetController().IsInitialBlankNavigation()); + RenderProcessHost* process = web_contents->GetMainFrame()->GetProcess(); + int renderer_id = process->GetID(); + ASSERT_TRUE(process); + EXPECT_FALSE(process->HasConnection()); + EXPECT_EQ(base::kNullProcessHandle, process->GetProcess().Handle()); + + // Navigate the WebContents. + GURL url(embedded_test_server()->GetURL("c.com", "/title3.html")); + TestNavigationObserver same_tab_observer(web_contents.get()); + NavigationController::LoadURLParams params(url); + params.transition_type = ui::PageTransitionFromInt( + ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); + web_contents->GetController().LoadURLWithParams(params); + same_tab_observer.Wait(); + EXPECT_TRUE(same_tab_observer.last_navigation_succeeded()); + + // The process should be launched now. + EXPECT_TRUE(process->HasConnection()); + EXPECT_NE(base::kNullProcessHandle, process->GetProcess().Handle()); + + // Check that the RenderProcessHost and its ID didn't change. + EXPECT_EQ(process, web_contents->GetMainFrame()->GetProcess()); + EXPECT_EQ(renderer_id, web_contents->GetMainFrame()->GetProcess()->GetID()); + + // Verify that the navigation succeeded. + EXPECT_EQ(1, web_contents->GetController().GetEntryCount()); + NavigationEntry* entry = + web_contents->GetController().GetLastCommittedEntry(); + ASSERT_TRUE(entry); + EXPECT_EQ(url, entry->GetURL()); + } +} + IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, NavigatingToWebUIDoesNotUsePreWarmedProcess) { GURL web_ui_url(std::string(kChromeUIScheme) + "://" + @@ -1613,7 +1828,8 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, WebContents::CreateParams create_params( base_web_contents->GetBrowserContext()); - create_params.initialize_renderer = true; + create_params.desired_renderer_state = + WebContents::CreateParams::kInitializeAndWarmupRendererProcess; create_params.initial_size = base_web_contents->GetContainerBounds().size(); std::unique_ptr web_contents(WebContents::Create(create_params)); @@ -1859,7 +2075,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, EXPECT_TRUE(NavigateToURL(shell(), url)); // alert - wc->EnterFullscreenMode(url); + wc->EnterFullscreenMode(url, blink::WebFullscreenOptions()); EXPECT_TRUE(wc->IsFullscreenForCurrentTab()); std::string script = "alert('hi')"; test_delegate.WillWaitForDialog(); @@ -1868,7 +2084,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, EXPECT_FALSE(wc->IsFullscreenForCurrentTab()); // confirm - wc->EnterFullscreenMode(url); + wc->EnterFullscreenMode(url, blink::WebFullscreenOptions()); EXPECT_TRUE(wc->IsFullscreenForCurrentTab()); script = "confirm('hi')"; test_delegate.WillWaitForDialog(); @@ -1877,7 +2093,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, EXPECT_FALSE(wc->IsFullscreenForCurrentTab()); // prompt - wc->EnterFullscreenMode(url); + wc->EnterFullscreenMode(url, blink::WebFullscreenOptions()); EXPECT_TRUE(wc->IsFullscreenForCurrentTab()); script = "prompt('hi')"; test_delegate.WillWaitForDialog(); @@ -1886,7 +2102,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, EXPECT_FALSE(wc->IsFullscreenForCurrentTab()); // beforeunload - wc->EnterFullscreenMode(url); + wc->EnterFullscreenMode(url, blink::WebFullscreenOptions()); EXPECT_TRUE(wc->IsFullscreenForCurrentTab()); // Disable the hang monitor (otherwise there will be a race between the // beforeunload dialog and the beforeunload hang timer) and give the page a @@ -1915,7 +2131,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, EXPECT_TRUE(NavigateToURL(shell(), url)); // popup - wc->EnterFullscreenMode(url); + wc->EnterFullscreenMode(url, blink::WebFullscreenOptions()); EXPECT_TRUE(wc->IsFullscreenForCurrentTab()); std::string script = "window.open('', '', 'width=200,height=100')"; test_delegate.WillWaitForNewContents(); @@ -1945,7 +2161,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, test_delegate.Wait(); // Put the main contents into fullscreen ... - wc->EnterFullscreenMode(url); + wc->EnterFullscreenMode(url, blink::WebFullscreenOptions()); EXPECT_TRUE(wc->IsFullscreenForCurrentTab()); // ... and ensure that a call to window.focus() from it causes loss of @@ -1984,7 +2200,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, class UpdateTargetURLWaiter : public WebContentsDelegate { public: - UpdateTargetURLWaiter(WebContents* web_contents) { + explicit UpdateTargetURLWaiter(WebContents* web_contents) { web_contents->SetDelegate(this); } @@ -2034,126 +2250,171 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, UpdateTargetURL) { target_url_waiter.WaitForUpdatedTargetURL()); } -// TODO(mmenke): Beef up testing of LoadState a little. In particular, check -// LoadState itself, not just the host name, check upload progress, check the -// param, and make sure RDH pushes the data to the browser process. +namespace { + +class LoadStateWaiter : public WebContentsDelegate { + public: + explicit LoadStateWaiter(content::WebContents* contents) + : web_contents_(contents) { + contents->SetDelegate(this); + } + ~LoadStateWaiter() override = default; + + // Waits until the WebContents changes its LoadStateHost to |host|. + void Wait(net::LoadState load_state, const base::string16& host) { + waiting_host_ = host; + waiting_state_ = load_state; + if (!LoadStateMatches(web_contents_)) { + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + DCHECK(LoadStateMatches(web_contents_)); + } + } + + // WebContentsDelegate: + void NavigationStateChanged(WebContents* source, + InvalidateTypes changed_flags) override { + if (!quit_closure_) + return; + if (!(changed_flags & INVALIDATE_TYPE_LOAD)) + return; + if (LoadStateMatches(source)) + std::move(quit_closure_).Run(); + } + + private: + bool LoadStateMatches(content::WebContents* contents) { + DCHECK(contents == web_contents_); + return waiting_host_ == contents->GetLoadStateHost() && + waiting_state_ == contents->GetLoadState().state; + } + base::OnceClosure quit_closure_; + content::WebContents* web_contents_ = nullptr; + base::string16 waiting_host_; + net::LoadState waiting_state_; + + DISALLOW_COPY_AND_ASSIGN(LoadStateWaiter); +}; + +} // namespace + +// TODO(csharrison,mmenke): Beef up testing of LoadState a little. In +// particular, check upload progress and check the LoadState param. IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, UpdateLoadState) { + base::string16 a_host = url_formatter::IDNToUnicode("a.com"); + base::string16 b_host = url_formatter::IDNToUnicode("b.com"); + base::string16 paused_host = url_formatter::IDNToUnicode("paused.com"); + // Controlled responses for image requests made in the test. They will // alternate being the "most interesting" for the purposes of notifying the // WebContents. auto a_response = std::make_unique( embedded_test_server(), "/a_img"); - auto slow_response = - std::make_unique( - embedded_test_server(), "/slow_img"); - auto c_response = + auto b_response = std::make_unique( - embedded_test_server(), "/c_img"); - ASSERT_TRUE(embedded_test_server()->Start()); + embedded_test_server(), "/b_img"); - // This is a hack to ensure that the resource scheduler has at least one - // loading client for the duration of the test. Could alternatively delay some - // subresources on the main target page, but it would require care to ensure - // *all* other resources are completed before the test properly gets started. - Shell* popup = CreateBrowser(); - const GURL kPopupUrl(embedded_test_server()->GetURL("/title1.html")); - TestNavigationManager popup_delayer(popup->web_contents(), kPopupUrl); - popup->LoadURL(kPopupUrl); - EXPECT_TRUE(popup_delayer.WaitForResponse()); + LoadStateWaiter waiter(shell()->web_contents()); + ASSERT_TRUE(embedded_test_server()->Start()); EXPECT_TRUE(NavigateToURL( shell(), embedded_test_server()->GetURL( - "a.com", "/cross_site_iframe_factory.html?a(b(c))"))); + "a.com", "/cross_site_iframe_factory.html?a(b)"))); WebContentsImpl* web_contents = static_cast(shell()->web_contents()); FrameTreeNode* a_frame = web_contents->GetFrameTree()->root(); - FrameTreeNode* slow_frame = a_frame->child_at(0); - FrameTreeNode* c_frame = slow_frame->child_at(0); + FrameTreeNode* b_frame = a_frame->child_at(0); // Start loading the respective resources in each frame. auto load_resource = [](FrameTreeNode* frame, const std::string url) { - std::string partial_script = R"( + const char kLoadResourceScript[] = R"( var img = new Image(); img.src = '%s'; document.body.appendChild(img); )"; - std::string script = - base::StringPrintf(partial_script.c_str(), url.c_str()); + std::string script = base::StringPrintf(kLoadResourceScript, url.c_str()); EXPECT_TRUE(ExecuteScript(frame, script)); }; - // Blocks until the img element in |frame| finishes. - auto wait_for_img_finished = [](FrameTreeNode* frame) { - bool finished = false; - std::string script = R"( - var img = document.getElementsByTagName('img')[0]; - if (img.complete) - window.domAutomationController.send(true); - else - img.onload = img.onerror = window.domAutomationController.send(true); - )"; - EXPECT_TRUE(ExecuteScriptAndExtractBool(frame, script.c_str(), &finished)); - }; - - // Requests a load state notification from the RDHI and waits until the update - // is posted back on the UI thread. Due to PostTaskAndReply, relies on - // UpdateLoadInfo synchronously posting a task to the WebContents. - auto update_load_state_and_wait = []() { - base::RunLoop run_loop; - BrowserThread::PostTaskAndReply( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&ResourceDispatcherHostImpl::UpdateLoadInfo, - base::Unretained(ResourceDispatcherHostImpl::Get())), - run_loop.QuitClosure()); - run_loop.Run(); - }; - // There should be no outgoing requests, so the load state should be empty. - update_load_state_and_wait(); - EXPECT_TRUE(web_contents->GetLoadStateHost().empty()); - EXPECT_EQ(url_formatter::IDNToUnicode(kPopupUrl.host()), - popup->web_contents()->GetLoadStateHost()); + waiter.Wait(net::LOAD_STATE_IDLE, base::string16()); + + // The |frame_pauser| pauses the navigation after every step. It will only + // finish by calling WaitForNavigationFinished or ResumeNavigation. + GURL paused_url(embedded_test_server()->GetURL("paused.com", "/title1.html")); + TestNavigationManager frame_pauser(web_contents, paused_url); + const char kLoadFrameScript[] = R"( + var frame = document.createElement('iframe'); + frame.src = "%s"; + document.body.appendChild(frame); + )"; + EXPECT_TRUE(ExecuteScript( + web_contents, + base::StringPrintf(kLoadFrameScript, paused_url.spec().c_str()))); + + // Wait for the response to be ready, but never finish it. + EXPECT_TRUE(frame_pauser.WaitForResponse()); + EXPECT_FALSE(frame_pauser.was_successful()); + waiter.Wait(net::LOAD_STATE_WAITING_FOR_DELEGATE, paused_host); load_resource(a_frame, "/a_img"); a_response->WaitForRequest(); - update_load_state_and_wait(); - EXPECT_EQ(url_formatter::IDNToUnicode("a.com"), - web_contents->GetLoadStateHost()); - - // slow_img should never get past DNS resolution for the remainder of the - // test. Ensure that a_img is further along (and therefore more interesting). - load_resource(slow_frame, "http://slow.com/slow_img"); - update_load_state_and_wait(); - EXPECT_EQ(url_formatter::IDNToUnicode("a.com"), - web_contents->GetLoadStateHost()); - - // Finish a_img and start c_img, ensure it passes slow_img. + waiter.Wait(net::LOAD_STATE_WAITING_FOR_RESPONSE, a_host); + + // Start loading b_img and have it pass a_img by providing one byte of data. + load_resource(b_frame, "/b_img"); + b_response->WaitForRequest(); + + const char kPartialResponse[] = "HTTP/1.1 200 OK\r\n\r\nx"; + b_response->Send(kPartialResponse); + waiter.Wait(net::LOAD_STATE_READING_RESPONSE, b_host); + + // Finish b_img and expect that a_img is back to being most interesting. + b_response->Done(); + waiter.Wait(net::LOAD_STATE_WAITING_FOR_RESPONSE, a_host); + + // Advance and finish a_img. + a_response->Send(kPartialResponse); + waiter.Wait(net::LOAD_STATE_READING_RESPONSE, a_host); a_response->Done(); - load_resource(c_frame, "/c_img"); - wait_for_img_finished(a_frame); - c_response->WaitForRequest(); - update_load_state_and_wait(); - EXPECT_EQ(url_formatter::IDNToUnicode("c.com"), - web_contents->GetLoadStateHost()); - - // Finish c_img and ensure slow_img (the last outgoing request) is the most - // interesting. - c_response->Done(); - wait_for_img_finished(c_frame); - update_load_state_and_wait(); - EXPECT_EQ(url_formatter::IDNToUnicode("slow.com"), - web_contents->GetLoadStateHost()); + + // Now the only request in flight should be the delayed frame. + waiter.Wait(net::LOAD_STATE_WAITING_FOR_DELEGATE, paused_host); + frame_pauser.ResumeNavigation(); + waiter.Wait(net::LOAD_STATE_IDLE, base::string16()); } -// Disabled due to flakes on Linux. https://crbug.com/832191 -#if defined(OS_LINUX) -#define MAYBE_PausePageScheduledTasks DISABLED_PausePageScheduledTasks -#else -#define MAYBE_PausePageScheduledTasks PausePageScheduledTasks -#endif -IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, - MAYBE_PausePageScheduledTasks) { +IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, NotifyPreferencesChanged) { + ASSERT_TRUE(embedded_test_server()->Start()); + WebContentsImpl* web_contents = + static_cast(shell()->web_contents()); + RenderFrameHost* main_frame = web_contents->GetMainFrame(); + + // Navigate to a site with two iframes in different origins. + GURL url = embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b,c)"); + EXPECT_TRUE(NavigateToURL(shell(), url)); + + auto* main_frame_rvh = main_frame->GetRenderViewHost(); + auto* b_subframe_rvh = ChildFrameAt(main_frame, 0)->GetRenderViewHost(); + auto* c_subframe_rvh = ChildFrameAt(main_frame, 1)->GetRenderViewHost(); + + NotifyPreferencesChangedTestContentBrowserClient new_client; + ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client); + + web_contents->NotifyPreferencesChanged(); + + // We should have updated the preferences for all three RenderViewHosts. + EXPECT_EQ(std::unordered_set( + {main_frame_rvh, b_subframe_rvh, c_subframe_rvh}), + new_client.override_webkit_prefs_rvh_set()); + + SetBrowserClientForTesting(old_client); +} + +IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, PausePageScheduledTasks) { EXPECT_TRUE(embedded_test_server()->Start()); GURL test_url = embedded_test_server()->GetURL("/pause_schedule_task.html"); @@ -2161,23 +2422,31 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); int text_length; - EXPECT_TRUE(content::ExecuteScriptAndExtractInt( - shell(), - "domAutomationController.send(document.getElementById('textfield')." - "value.length)", - &text_length)); - EXPECT_GT(text_length, 0); + while (true) { + EXPECT_TRUE(content::ExecuteScriptAndExtractInt( + shell(), + "domAutomationController.send(document.getElementById('textfield')." + "value.length)", + &text_length)); + + // Wait until |text_length| exceed 0. + if (text_length > 0) + break; + } // Suspend blink schedule tasks. shell()->web_contents()->PausePageScheduledTasks(true); - EXPECT_TRUE(content::ExecuteScriptAndExtractInt( - shell(), - "domAutomationController.send(document.getElementById('textfield')." - "value.length)", - &text_length)); - EXPECT_GT(text_length, 0); + // Make the javascript work. + for (int i = 0; i < 10; i++) { + EXPECT_TRUE(content::ExecuteScriptAndExtractInt( + shell(), + "domAutomationController.send(document.getElementById('textfield')." + "value.length)", + &text_length)); + } + // Check if |next_text_length| is equal to |text_length|. int next_text_length; EXPECT_TRUE(content::ExecuteScriptAndExtractInt( shell(), @@ -2189,16 +2458,21 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, // Resume the paused blink schedule tasks. shell()->web_contents()->PausePageScheduledTasks(false); - // We call a document.getElementById five times in order to give the - // javascript time to run with DOM again. - for (int i = 0; i < 5; i++) { + // Wait for an amount of time in order to give the javascript time to + // work again. If the javascript doesn't work again, the test will fail due to + // the time out. + while (true) { EXPECT_TRUE(content::ExecuteScriptAndExtractInt( shell(), "domAutomationController.send(document.getElementById('textfield')." "value.length)", &next_text_length)); + if (next_text_length > text_length) + break; } + // Check if |next_text_length| exceeds |text_length| because the blink + // schedule tasks have resumed. EXPECT_TRUE(content::ExecuteScriptAndExtractInt( shell(), "domAutomationController.send(document.getElementById('textfield')." 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 b88f6ee6a1c..9b07e24d464 100644 --- a/chromium/content/browser/web_contents/web_contents_impl_unittest.cc +++ b/chromium/content/browser/web_contents/web_contents_impl_unittest.cc @@ -56,6 +56,7 @@ #include "content/test/test_web_contents.h" #include "net/test/cert_test_util.h" #include "net/test/test_data_directory.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/frame/sandbox_flags.h" #include "third_party/skia/include/core/SkColor.h" @@ -318,8 +319,10 @@ class FakeFullscreenDelegate : public WebContentsDelegate { FakeFullscreenDelegate() : fullscreened_contents_(nullptr) {} ~FakeFullscreenDelegate() override {} - void EnterFullscreenModeForTab(WebContents* web_contents, - const GURL& origin) override { + void EnterFullscreenModeForTab( + WebContents* web_contents, + const GURL& origin, + const blink::WebFullscreenOptions& options) override { fullscreened_contents_ = web_contents; } @@ -836,9 +839,10 @@ TEST_F(WebContentsImplTest, NavigateFromRestoredSitelessUrl) { const GURL native_url("non-site-url://stuffandthings"); std::vector> entries; std::unique_ptr new_entry = - NavigationControllerImpl::CreateNavigationEntry( + NavigationController::CreateNavigationEntry( native_url, Referrer(), ui::PAGE_TRANSITION_LINK, false, - std::string(), browser_context()); + std::string(), browser_context(), + nullptr /* blob_url_loader_factory */); entries.push_back(std::move(new_entry)); controller().Restore(0, RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries); ASSERT_EQ(0u, entries.size()); @@ -883,9 +887,10 @@ TEST_F(WebContentsImplTest, NavigateFromRestoredRegularUrl) { const GURL regular_url("http://www.yahoo.com"); std::vector> entries; std::unique_ptr new_entry = - NavigationControllerImpl::CreateNavigationEntry( + NavigationController::CreateNavigationEntry( regular_url, Referrer(), ui::PAGE_TRANSITION_LINK, false, - std::string(), browser_context()); + std::string(), browser_context(), + nullptr /* blob_url_loader_factory */); entries.push_back(std::move(new_entry)); controller().Restore(0, RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries); ASSERT_EQ(0u, entries.size()); @@ -1487,8 +1492,8 @@ TEST_F(WebContentsImplTest, NavigationExitsFullscreen) { // Toggle fullscreen mode on (as if initiated via IPC from renderer). EXPECT_FALSE(contents()->IsFullscreenForCurrentTab()); EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents())); - orig_rfh->OnMessageReceived( - FrameHostMsg_ToggleFullscreen(orig_rfh->GetRoutingID(), true)); + orig_rfh->OnMessageReceived(FrameHostMsg_EnterFullscreen( + orig_rfh->GetRoutingID(), blink::WebFullscreenOptions())); EXPECT_TRUE(contents()->IsFullscreenForCurrentTab()); EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents())); @@ -1543,8 +1548,8 @@ TEST_F(WebContentsImplTest, HistoryNavigationExitsFullscreen) { for (int i = 0; i < 2; ++i) { // Toggle fullscreen mode on (as if initiated via IPC from renderer). - orig_rfh->OnMessageReceived( - FrameHostMsg_ToggleFullscreen(orig_rfh->GetRoutingID(), true)); + orig_rfh->OnMessageReceived(FrameHostMsg_EnterFullscreen( + orig_rfh->GetRoutingID(), blink::WebFullscreenOptions())); EXPECT_TRUE(contents()->IsFullscreenForCurrentTab()); EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents())); @@ -1587,8 +1592,8 @@ TEST_F(WebContentsImplTest, CrashExitsFullscreen) { // Toggle fullscreen mode on (as if initiated via IPC from renderer). EXPECT_FALSE(contents()->IsFullscreenForCurrentTab()); EXPECT_FALSE(fake_delegate.IsFullscreenForTabOrPending(contents())); - main_test_rfh()->OnMessageReceived(FrameHostMsg_ToggleFullscreen( - main_test_rfh()->GetRoutingID(), true)); + main_test_rfh()->OnMessageReceived(FrameHostMsg_EnterFullscreen( + main_test_rfh()->GetRoutingID(), blink::WebFullscreenOptions())); EXPECT_TRUE(contents()->IsFullscreenForCurrentTab()); EXPECT_TRUE(fake_delegate.IsFullscreenForTabOrPending(contents())); @@ -2580,7 +2585,7 @@ TEST_F(WebContentsImplTest, CopyStateFromAndPruneSourceInterstitial) { // Create another NavigationController. GURL url3("http://foo2"); std::unique_ptr other_contents( - static_cast(CreateTestWebContents())); + static_cast(CreateTestWebContents().release())); NavigationControllerImpl& other_controller = other_contents->GetController(); other_contents->NavigateAndCommit(url3); other_contents->ExpectSetHistoryOffsetAndLength(1, 2); @@ -2605,7 +2610,7 @@ TEST_F(WebContentsImplTest, CopyStateFromAndPruneTargetInterstitial) { // Create another NavigationController. std::unique_ptr other_contents( - static_cast(CreateTestWebContents())); + static_cast(CreateTestWebContents().release())); NavigationControllerImpl& other_controller = other_contents->GetController(); // Navigate it to url2. @@ -2655,7 +2660,7 @@ TEST_F(WebContentsImplTest, FilterURLs) { // Create and navigate another WebContents. std::unique_ptr other_contents( - static_cast(CreateTestWebContents())); + static_cast(CreateTestWebContents().release())); TestWebContentsObserver other_observer(other_contents.get()); other_contents->NavigateAndCommit(url_normalized); @@ -2667,29 +2672,38 @@ TEST_F(WebContentsImplTest, FilterURLs) { // Test that if a pending contents is deleted before it is shown, we don't // crash. TEST_F(WebContentsImplTest, PendingContentsDestroyed) { - std::unique_ptr other_contents( - static_cast(CreateTestWebContents())); - contents()->AddPendingContents(other_contents.get()); + std::unique_ptr other_contents( + static_cast(CreateTestWebContents().release())); + content::TestWebContents* raw_other_contents = + static_cast(other_contents.get()); + contents()->AddPendingContents(std::move(other_contents)); RenderWidgetHost* widget = - other_contents->GetMainFrame()->GetRenderWidgetHost(); + raw_other_contents->GetMainFrame()->GetRenderWidgetHost(); int process_id = widget->GetProcess()->GetID(); int widget_id = widget->GetRoutingID(); - other_contents.reset(); + + // TODO(erikchen): Fix ownership semantics of WebContents. Nothing should be + // able to delete it beside from the owner. https://crbug.com/832879. + delete raw_other_contents; EXPECT_EQ(nullptr, contents()->GetCreatedWindow(process_id, widget_id)); } TEST_F(WebContentsImplTest, PendingContentsShown) { - std::unique_ptr other_contents( - static_cast(CreateTestWebContents())); - contents()->AddPendingContents(other_contents.get()); + std::unique_ptr other_contents( + static_cast(CreateTestWebContents().release())); + content::WebContents* raw_other_contents = other_contents.get(); + content::TestWebContents* test_web_contents = + static_cast(other_contents.get()); + contents()->AddPendingContents(std::move(other_contents)); + RenderWidgetHost* widget = - other_contents->GetMainFrame()->GetRenderWidgetHost(); + test_web_contents->GetMainFrame()->GetRenderWidgetHost(); int process_id = widget->GetProcess()->GetID(); int widget_id = widget->GetRoutingID(); // The first call to GetCreatedWindow pops it off the pending list. - EXPECT_EQ(other_contents.get(), - contents()->GetCreatedWindow(process_id, widget_id)); + EXPECT_EQ(raw_other_contents, + contents()->GetCreatedWindow(process_id, widget_id).get()); // A second call should return nullptr, verifying that it's been forgotten. EXPECT_EQ(nullptr, contents()->GetCreatedWindow(process_id, widget_id)); } @@ -3463,6 +3477,30 @@ TEST_F(WebContentsImplTest, MediaWakeLock) { EXPECT_TRUE(has_video_wake_lock()); EXPECT_FALSE(has_audio_wake_lock()); + viz::SurfaceId surface_id = + viz::SurfaceId(viz::FrameSinkId(1, 1), + viz::LocalSurfaceId( + 11, base::UnguessableToken::Deserialize(0x111111, 0))); + + // Send the video in Picture-in-Picture mode, power blocker should still be + // on. + rfh->OnMessageReceived( + MediaPlayerDelegateHostMsg_OnPictureInPictureModeStarted( + 0, kPlayerAudioVideoId, surface_id, gfx::Size(), 0)); + EXPECT_TRUE(has_video_wake_lock()); + EXPECT_FALSE(has_audio_wake_lock()); + + // Hiding the window should keep the power blocker. + contents()->WasHidden(); + EXPECT_TRUE(has_video_wake_lock()); + EXPECT_FALSE(has_audio_wake_lock()); + + // Leaving Picture-in-Picture should reset the power blocker. + rfh->OnMessageReceived(MediaPlayerDelegateHostMsg_OnPictureInPictureModeEnded( + 0, kPlayerAudioVideoId, 1 /* request_id */)); + EXPECT_FALSE(has_video_wake_lock()); + EXPECT_FALSE(has_audio_wake_lock()); + // Crash the renderer. main_test_rfh()->GetProcess()->SimulateCrash(); @@ -3506,9 +3544,23 @@ TEST_F(WebContentsImplTest, ThemeColorChangeDependingOnFirstVisiblePaint) { EXPECT_EQ(SK_ColorGREEN, observer.last_theme_color()); } -TEST_F(WebContentsImplTest, PictureInPictureMediaPlayerIdWasChanged) { +class PictureInPictureDelegate : public WebContentsDelegate { + public: + PictureInPictureDelegate() = default; + + MOCK_METHOD2(EnterPictureInPicture, + gfx::Size(const viz::SurfaceId&, const gfx::Size&)); + + private: + DISALLOW_COPY_AND_ASSIGN(PictureInPictureDelegate); +}; + +TEST_F(WebContentsImplTest, EnterPictureInPicture) { const int kPlayerVideoOnlyId = 30; /* arbitrary and used for tests */ + PictureInPictureDelegate delegate; + contents()->SetDelegate(&delegate); + MediaWebContentsObserver* observer = contents()->media_web_contents_observer(); TestRenderFrameHost* rfh = main_test_rfh(); @@ -3518,18 +3570,27 @@ TEST_F(WebContentsImplTest, PictureInPictureMediaPlayerIdWasChanged) { // set. EXPECT_FALSE(observer->GetPictureInPictureVideoMediaPlayerId().has_value()); + viz::SurfaceId surface_id = + viz::SurfaceId(viz::FrameSinkId(1, 1), + viz::LocalSurfaceId( + 11, base::UnguessableToken::Deserialize(0x111111, 0))); + + EXPECT_CALL(delegate, EnterPictureInPicture(surface_id, gfx::Size(42, 42))); + rfh->OnMessageReceived( - MediaPlayerDelegateHostMsg_OnPictureInPictureSourceChanged( - rfh->GetRoutingID(), kPlayerVideoOnlyId)); + MediaPlayerDelegateHostMsg_OnPictureInPictureModeStarted( + rfh->GetRoutingID(), kPlayerVideoOnlyId, surface_id /* surface_id */, + gfx::Size(42, 42) /* natural_size */, 1 /* request_id */)); EXPECT_TRUE(observer->GetPictureInPictureVideoMediaPlayerId().has_value()); EXPECT_EQ(kPlayerVideoOnlyId, observer->GetPictureInPictureVideoMediaPlayerId()->second); - // Picture-in-Picture media player id should be reset when the media is - // destroyed. + // Picture-in-Picture media player id should not be reset when the media is + // destroyed (e.g. video stops playing). This allows the Picture-in-Picture + // window to continue to control the media. rfh->OnMessageReceived(MediaPlayerDelegateHostMsg_OnMediaDestroyed( rfh->GetRoutingID(), kPlayerVideoOnlyId)); - EXPECT_FALSE(observer->GetPictureInPictureVideoMediaPlayerId().has_value()); + EXPECT_TRUE(observer->GetPictureInPictureVideoMediaPlayerId().has_value()); } TEST_F(WebContentsImplTest, ParseDownloadHeaders) { 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 4f8a55220b8..28c90658587 100644 --- a/chromium/content/browser/web_contents/web_contents_view_android.cc +++ b/chromium/content/browser/web_contents/web_contents_view_android.cc @@ -11,6 +11,7 @@ #include "cc/layers/layer.h" #include "content/browser/accessibility/browser_accessibility_manager_android.h" #include "content/browser/android/content_feature_list.h" +#include "content/browser/android/content_ui_event_handler.h" #include "content/browser/android/gesture_listener_manager.h" #include "content/browser/android/select_popup.h" #include "content/browser/android/selection/selection_popup_controller.h" @@ -29,6 +30,8 @@ #include "ui/base/clipboard/clipboard.h" #include "ui/display/screen.h" #include "ui/events/android/drag_event_android.h" +#include "ui/events/android/gesture_event_android.h" +#include "ui/events/android/key_event_android.h" #include "ui/events/android/motion_event_android.h" #include "ui/gfx/android/java_bitmap.h" #include "ui/gfx/image/image_skia.h" @@ -103,14 +106,21 @@ WebContentsViewAndroid::WebContentsViewAndroid( WebContentsViewDelegate* delegate) : web_contents_(web_contents), delegate_(delegate), - view_(this, ui::ViewAndroid::LayoutType::NORMAL), + view_(ui::ViewAndroid::LayoutType::NORMAL), synchronous_compositor_client_(nullptr) { view_.SetLayer(cc::Layer::Create()); + view_.set_event_handler(this); } WebContentsViewAndroid::~WebContentsViewAndroid() { if (view_.GetLayer()) view_.GetLayer()->RemoveFromParent(); + view_.set_event_handler(nullptr); +} + +void WebContentsViewAndroid::SetContentUiEventHandler( + std::unique_ptr handler) { + content_ui_event_handler_ = std::move(handler); } void WebContentsViewAndroid::SetSelectPopup( @@ -507,11 +517,43 @@ bool WebContentsViewAndroid::OnMouseEvent(const ui::MotionEventAndroid& event) { return manager && manager->OnHoverEvent(event); } +bool WebContentsViewAndroid::OnGenericMotionEvent( + const ui::MotionEventAndroid& event) { + if (content_ui_event_handler_) + return content_ui_event_handler_->OnGenericMotionEvent(event); + return false; +} + +bool WebContentsViewAndroid::OnKeyUp(const ui::KeyEventAndroid& event) { + if (content_ui_event_handler_) + return content_ui_event_handler_->OnKeyUp(event); + return false; +} + +bool WebContentsViewAndroid::DispatchKeyEvent( + const ui::KeyEventAndroid& event) { + if (content_ui_event_handler_) + return content_ui_event_handler_->DispatchKeyEvent(event); + return false; +} + +bool WebContentsViewAndroid::ScrollBy(float delta_x, float delta_y) { + if (content_ui_event_handler_) + content_ui_event_handler_->ScrollBy(delta_x, delta_y); + return false; +} + +bool WebContentsViewAndroid::ScrollTo(float x, float y) { + if (content_ui_event_handler_) + content_ui_event_handler_->ScrollTo(x, y); + return false; +} + void WebContentsViewAndroid::OnSizeChanged() { auto* rwhv = ::content::GetRenderWidgetHostViewAndroid(web_contents_); if (rwhv) { web_contents_->SendScreenRects(); - rwhv->WasResized(); + rwhv->SynchronizeVisualProperties(); } } 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 9c21662393b..3b6ab05eeac 100644 --- a/chromium/content/browser/web_contents/web_contents_view_android.h +++ b/chromium/content/browser/web_contents/web_contents_view_android.h @@ -15,10 +15,12 @@ #include "content/public/common/drop_data.h" #include "ui/android/overscroll_refresh.h" #include "ui/android/view_android.h" -#include "ui/android/view_client.h" +#include "ui/android/view_android_observer.h" +#include "ui/events/android/event_handler_android.h" #include "ui/gfx/geometry/rect_f.h" namespace content { +class ContentUiEventHandler; class RenderWidgetHostViewAndroid; class SelectPopup; class SelectionPopupController; @@ -28,12 +30,14 @@ class WebContentsImpl; // Android-specific implementation of the WebContentsView. class WebContentsViewAndroid : public WebContentsView, public RenderViewHostDelegateView, - public ui::ViewClient { + public ui::EventHandlerAndroid { public: WebContentsViewAndroid(WebContentsImpl* web_contents, WebContentsViewDelegate* delegate); ~WebContentsViewAndroid() override; + void SetContentUiEventHandler(std::unique_ptr handler); + // Sets the object that show/hide popup view for tag. std::unique_ptr select_popup_; 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 3aea2d29dc4..984fa41d064 100644 --- a/chromium/content/browser/web_contents/web_contents_view_aura.cc +++ b/chromium/content/browser/web_contents/web_contents_view_aura.cc @@ -11,7 +11,7 @@ #include "base/command_line.h" #include "base/files/file_util.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_current.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "components/viz/common/features.h" @@ -932,8 +932,7 @@ void WebContentsViewAura::StartDragging( int result_op = 0; { gfx::NativeView content_native_view = GetContentNativeView(); - base::MessageLoop::ScopedNestableTaskAllower allow( - base::MessageLoop::current()); + base::MessageLoopCurrent::ScopedNestableTaskAllower allow; result_op = aura::client::GetDragDropClient(root_window) ->StartDragAndDrop(data, root_window, 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 35a9c560d09..dbc35b6c764 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 @@ -122,56 +122,6 @@ class ScreenshotTracker : public NavigationEntryScreenshotManager { DISALLOW_COPY_AND_ASSIGN(ScreenshotTracker); }; -class InputEventMessageFilterWaitsForAcks : public BrowserMessageFilter { - public: - InputEventMessageFilterWaitsForAcks() - : BrowserMessageFilter(InputMsgStart), - type_(blink::WebInputEvent::kUndefined), - state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {} - - void WaitForAck(blink::WebInputEvent::Type type) { - base::RunLoop run_loop; - base::AutoReset reset_quit(&quit_, run_loop.QuitClosure()); - base::AutoReset reset_type(&type_, type); - run_loop.Run(); - } - - InputEventAckState last_ack_state() const { return state_; } - - protected: - ~InputEventMessageFilterWaitsForAcks() override {} - - private: - void ReceivedEventAck(blink::WebInputEvent::Type type, - InputEventAckState state) { - if (type_ == type) { - state_ = state; - quit_.Run(); - } - } - - // BrowserMessageFilter: - bool OnMessageReceived(const IPC::Message& message) override { - if (message.type() == InputHostMsg_HandleInputEvent_ACK::ID) { - InputHostMsg_HandleInputEvent_ACK::Param params; - InputHostMsg_HandleInputEvent_ACK::Read(&message, ¶ms); - blink::WebInputEvent::Type type = std::get<0>(params).type; - InputEventAckState ack = std::get<0>(params).state; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::BindOnce(&InputEventMessageFilterWaitsForAcks::ReceivedEventAck, - this, type, ack)); - } - return false; - } - - base::Closure quit_; - blink::WebInputEvent::Type type_; - InputEventAckState state_; - - DISALLOW_COPY_AND_ASSIGN(InputEventMessageFilterWaitsForAcks); -}; - class WebContentsViewAuraTest : public ContentBrowserTest { public: WebContentsViewAuraTest() : screenshot_manager_(nullptr) {} @@ -338,12 +288,8 @@ class WebContentsViewAuraTest : public ContentBrowserTest { GetRenderViewHost()->GetWidget()->GetView()); } - InputEventMessageFilterWaitsForAcks* filter() { - return filter_.get(); - } - void WaitAFrame() { - while (!GetRenderWidgetHost()->ScheduleComposite()) + while (!GetRenderWidgetHost()->RequestRepaintForTesting()) GiveItSomeTime(); frame_observer_->WaitForAnyFrameSubmission(); } @@ -356,11 +302,6 @@ class WebContentsViewAuraTest : public ContentBrowserTest { screenshot_manager_->SetScreenshotInterval(interval_ms); } - void AddInputEventMessageFilter() { - filter_ = new InputEventMessageFilterWaitsForAcks(); - GetRenderWidgetHost()->GetProcess()->AddFilter(filter_.get()); - } - // ContentBrowserTest: void PostRunTestOnMainThread() override { // Delete this before the WebContents is destroyed. @@ -370,7 +311,6 @@ class WebContentsViewAuraTest : public ContentBrowserTest { private: ScreenshotTracker* screenshot_manager_; - scoped_refptr filter_; std::unique_ptr frame_observer_; DISALLOW_COPY_AND_ASSIGN(WebContentsViewAuraTest); @@ -965,8 +905,6 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, MAYBE_OverscrollNavigationTouchThrottling) { ASSERT_NO_FATAL_FAILURE(StartTestWithPage("/overscroll_navigation.html")); - AddInputEventMessageFilter(); - WebContentsImpl* web_contents = static_cast(shell()->web_contents()); aura::Window* content = web_contents->GetContentNativeView(); @@ -984,24 +922,35 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, ExecuteSyncJSFunction(web_contents->GetMainFrame(), "reset_touchmove_count()"); } + InputEventAckWaiter touch_start_waiter( + GetRenderWidgetHost(), + base::BindRepeating([](content::InputEventAckSource, + content::InputEventAckState state, + const blink::WebInputEvent& event) { + return event.GetType() == blink::WebGestureEvent::kTouchStart && + state == content::INPUT_EVENT_ACK_STATE_NOT_CONSUMED; + })); // Send touch press. SyntheticWebTouchEvent touch; touch.PressPoint(bounds.x() + 2, bounds.y() + 10); GetRenderWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); - filter()->WaitForAck(blink::WebInputEvent::kTouchStart); + touch_start_waiter.Wait(); WaitAFrame(); - // Assert on the ack, because we'll end up waiting for acks that will never - // come if this is not true. - ASSERT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state()); - // Send first touch move, and then a scroll begin. touch.MovePoint(0, bounds.x() + 20 + 1 * dx, bounds.y() + 100); + InputEventAckWaiter touch_move_waiter( + GetRenderWidgetHost(), + base::BindRepeating([](content::InputEventAckSource, + content::InputEventAckState state, + const blink::WebInputEvent& event) { + return event.GetType() == blink::WebGestureEvent::kTouchMove && + state == content::INPUT_EVENT_ACK_STATE_NOT_CONSUMED; + })); GetRenderWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); - filter()->WaitForAck(blink::WebInputEvent::kTouchMove); - ASSERT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state()); + touch_move_waiter.Wait(); blink::WebGestureEvent scroll_begin = SyntheticWebGestureEventBuilder::BuildScrollBegin( @@ -1034,10 +983,9 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, ui::LatencyInfo()); WaitAFrame(); - blink::WebGestureEvent scroll_end( - blink::WebInputEvent::kGestureScrollEnd, - blink::WebInputEvent::kNoModifiers, - ui::EventTimeStampToSeconds(ui::EventTimeForNow())); + blink::WebGestureEvent scroll_end(blink::WebInputEvent::kGestureScrollEnd, + blink::WebInputEvent::kNoModifiers, + ui::EventTimeForNow()); GetRenderWidgetHost()->ForwardGestureEventWithLatencyInfo( scroll_end, ui::LatencyInfo()); WaitAFrame(); 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 968c5157ab4..4640550b848 100644 --- a/chromium/content/browser/web_contents/web_contents_view_mac.h +++ b/chromium/content/browser/web_contents/web_contents_view_mac.h @@ -35,6 +35,10 @@ namespace gfx { class Vector2d; } +namespace ui { +class Layer; +} + CONTENT_EXPORT @interface WebContentsViewCocoa : BaseView { @private @@ -128,6 +132,8 @@ class WebContentsViewMac : public WebContentsView, // CloseTabAfterEventTracking() implementation. void CloseTab(); + void SetParentUiLayer(ui::Layer* parent_ui_layer); + WebContentsImpl* web_contents() { return web_contents_; } WebContentsViewDelegate* delegate() { return delegate_.get(); } @@ -156,6 +162,8 @@ class WebContentsViewMac : public WebContentsView, // Whether to allow other views. bool allow_other_views_; + ui::Layer* parent_ui_layer_ = nullptr; + std::unique_ptr 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 6a9ee15776b..00b2d2a729d 100644 --- a/chromium/content/browser/web_contents/web_contents_view_mac.mm +++ b/chromium/content/browser/web_contents/web_contents_view_mac.mm @@ -11,7 +11,7 @@ #import "base/mac/mac_util.h" #import "base/mac/scoped_sending_event.h" #include "base/mac/sdk_forward_declarations.h" -#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_current.h" #import "base/message_loop/message_pump_mac.h" #include "content/browser/frame_host/popup_menu_helper_mac.h" #include "content/browser/renderer_host/display_util.h" @@ -28,6 +28,7 @@ #include "skia/ext/skia_utils_mac.h" #import "third_party/mozilla/NSPasteboard+Utils.h" #include "ui/base/clipboard/custom_data_helper.h" +#include "ui/base/cocoa/cocoa_base_utils.h" #include "ui/base/dragdrop/cocoa_dnd_util.h" #include "ui/display/screen.h" #include "ui/gfx/image/image_skia_util_mac.h" @@ -164,8 +165,7 @@ void WebContentsViewMac::StartDragging( // The drag invokes a nested event loop, arrange to continue // processing events. - base::MessageLoop::ScopedNestableTaskAllower allow( - base::MessageLoop::current()); + base::MessageLoopCurrent::ScopedNestableTaskAllower allow; NSDragOperation mask = static_cast(allowed_operations); NSPoint offset = NSPointFromCGPoint( gfx::PointAtOffsetFromOrigin(image_offset).ToCGPoint()); @@ -318,9 +318,11 @@ void WebContentsViewMac::OnMenuClosed() { } gfx::Rect WebContentsViewMac::GetViewBounds() const { - // This method is not currently used on mac. - NOTIMPLEMENTED(); - return gfx::Rect(); + NSRect window_bounds = + [cocoa_view_ convertRect:[cocoa_view_ bounds] toView:nil]; + window_bounds.origin = ui::ConvertPointFromWindowToScreen( + [cocoa_view_ window], window_bounds.origin); + return gfx::ScreenRectFromNSRect(window_bounds); } void WebContentsViewMac::SetAllowOtherViews(bool allow) { @@ -371,6 +373,8 @@ RenderWidgetHostViewBase* WebContentsViewMac::CreateViewForWidget( view->SetDelegate(rw_delegate.get()); } view->SetAllowPauseForResizeOrRepaint(!allow_other_views_); + if (parent_ui_layer_) + view->SetParentUiLayer(parent_ui_layer_); // Fancy layout comes later; for now just make it our size and resize it // with us. In case there are other siblings of the content area, we want @@ -444,6 +448,14 @@ void WebContentsViewMac::CloseTab() { web_contents_->Close(web_contents_->GetRenderViewHost()); } +void WebContentsViewMac::SetParentUiLayer(ui::Layer* parent_ui_layer) { + parent_ui_layer_ = parent_ui_layer; + RenderWidgetHostViewBase* view = static_cast( + web_contents_->GetRenderWidgetHostView()); + if (view) + view->SetParentUiLayer(parent_ui_layer); +} + } // namespace content @implementation WebContentsViewCocoa @@ -658,6 +670,11 @@ void WebContentsViewMac::CloseTab() { FocusThroughTabTraversal(direction == NSSelectingPrevious); } +- (void)cr_setParentUiLayer:(ui::Layer*)parentUiLayer { + if (webContentsView_) + webContentsView_->SetParentUiLayer(parentUiLayer); +} + - (void)updateWebContentsVisibility { WebContentsImpl* webContents = [self webContents]; if (!webContents || webContents->IsBeingDestroyed()) 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 d8dd5f43252..4ef9fcdda1c 100644 --- a/chromium/content/browser/web_contents/web_drag_source_mac.mm +++ b/chromium/content/browser/web_contents/web_drag_source_mac.mm @@ -145,12 +145,8 @@ void PromiseWriterHelper(const DropData& drop_data, // 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(url::kJavaScriptScheme)) { - net::UnescapeRule::Type unescapeRules = - net::UnescapeRule::SPACES | net::UnescapeRule::PATH_SEPARATORS | - net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS | - net::UnescapeRule::SPOOFING_AND_CONTROL_CHARS; std::string unescapedUrlString = - net::UnescapeURLComponent(dropData_->url.spec(), unescapeRules); + net::UnescapeBinaryURLComponent(dropData_->url.spec()); std::string escapedUrlString = net::EscapeUrlEncodedData(unescapedUrlString, false); url = [NSURL URLWithString:SysUTF8ToNSString(escapedUrlString)]; diff --git a/chromium/content/browser/web_package/signed_exchange_cert_fetcher.cc b/chromium/content/browser/web_package/signed_exchange_cert_fetcher.cc index 30b0a30eb6b..083d6c6fa95 100644 --- a/chromium/content/browser/web_package/signed_exchange_cert_fetcher.cc +++ b/chromium/content/browser/web_package/signed_exchange_cert_fetcher.cc @@ -11,6 +11,7 @@ #include "base/trace_event/trace_event.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/web_package/signed_exchange_consts.h" +#include "content/browser/web_package/signed_exchange_devtools_proxy.h" #include "content/browser/web_package/signed_exchange_utils.h" #include "content/common/throttling_url_loader.h" #include "content/public/common/resource_type.h" @@ -69,15 +70,16 @@ SignedExchangeCertFetcher::CreateAndStart( const GURL& cert_url, url::Origin request_initiator, bool force_fetch, + SignedExchangeVersion version, CertificateCallback callback, - const signed_exchange_utils::LogCallback& error_message_callback) { + SignedExchangeDevToolsProxy* devtools_proxy) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"), "SignedExchangeCertFetcher::CreateAndStart"); std::unique_ptr cert_fetcher( new SignedExchangeCertFetcher( std::move(shared_url_loader_factory), std::move(throttles), cert_url, - std::move(request_initiator), force_fetch, std::move(callback), - std::move(error_message_callback))); + std::move(request_initiator), force_fetch, version, + std::move(callback), devtools_proxy)); cert_fetcher->Start(); return cert_fetcher; } @@ -88,13 +90,15 @@ SignedExchangeCertFetcher::SignedExchangeCertFetcher( const GURL& cert_url, url::Origin request_initiator, bool force_fetch, + SignedExchangeVersion version, CertificateCallback callback, - const signed_exchange_utils::LogCallback& error_message_callback) + SignedExchangeDevToolsProxy* devtools_proxy) : shared_url_loader_factory_(std::move(shared_url_loader_factory)), throttles_(std::move(throttles)), resource_request_(std::make_unique()), + version_(version), callback_(std::move(callback)), - error_message_callback_(std::move(error_message_callback)) { + devtools_proxy_(devtools_proxy) { // TODO(https://crbug.com/803774): Revisit more ResourceRequest flags. resource_request_->url = cert_url; resource_request_->request_initiator = std::move(request_initiator); @@ -109,11 +113,20 @@ SignedExchangeCertFetcher::SignedExchangeCertFetcher( net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_CACHE; } resource_request_->render_frame_id = MSG_ROUTING_NONE; + if (devtools_proxy_) { + cert_request_id_ = base::UnguessableToken::Create(); + resource_request_->enable_load_timing = true; + } } SignedExchangeCertFetcher::~SignedExchangeCertFetcher() = default; void SignedExchangeCertFetcher::Start() { + if (devtools_proxy_) { + DCHECK(cert_request_id_); + devtools_proxy_->CertificateRequestSent(*cert_request_id_, + *resource_request_); + } url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( std::move(shared_url_loader_factory_), std::move(throttles_), 0 /* routing_id */, @@ -130,6 +143,7 @@ void SignedExchangeCertFetcher::Abort() { body_.reset(); handle_watcher_ = nullptr; body_string_.clear(); + devtools_proxy_ = nullptr; std::move(callback_).Run(nullptr); } @@ -143,10 +157,10 @@ void SignedExchangeCertFetcher::OnHandleReady(MojoResult result) { if (rv == MOJO_RESULT_OK) { if (body_string_.size() + num_bytes > g_max_cert_size_for_signed_exchange) { body_->EndReadData(num_bytes); - Abort(); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeCertFetcher::OnHandleReady", error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_, "SignedExchangeCertFetcher::OnHandleReady", "The response body size of certificate message exceeds the limit."); + Abort(); return; } body_string_.append(static_cast(buffer), num_bytes); @@ -169,13 +183,15 @@ void SignedExchangeCertFetcher::OnDataComplete() { handle_watcher_ = nullptr; std::unique_ptr cert_chain = - SignedExchangeCertificateChain::Parse(body_string_); + SignedExchangeCertificateChain::Parse( + version_, base::as_bytes(base::make_span(body_string_)), + devtools_proxy_); body_string_.clear(); if (!cert_chain) { - std::move(callback_).Run(nullptr); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeCertFetcher::OnDataComplete", error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_, "SignedExchangeCertFetcher::OnDataComplete", "Failed to get certificate chain from message."); + std::move(callback_).Run(nullptr); return; } std::move(callback_).Run(std::move(cert_chain)); @@ -187,26 +203,29 @@ void SignedExchangeCertFetcher::OnDataComplete() { void SignedExchangeCertFetcher::OnReceiveResponse( const network::ResourceResponseHead& head, network::mojom::DownloadedTempFilePtr downloaded_file) { + if (devtools_proxy_) { + DCHECK(cert_request_id_); + devtools_proxy_->CertificateResponseReceived(*cert_request_id_, + resource_request_->url, head); + } TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), "SignedExchangeCertFetcher::OnReceiveResponse"); if (head.headers->response_code() != net::HTTP_OK) { - Abort(); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeCertFetcher::OnReceiveResponse", error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_, "SignedExchangeCertFetcher::OnReceiveResponse", base::StringPrintf("Invalid reponse code: %d", head.headers->response_code())); + Abort(); return; } if (head.content_length > 0) { if (base::checked_cast(head.content_length) > g_max_cert_size_for_signed_exchange) { - Abort(); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeCertFetcher::OnReceiveResponse", - error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_, "SignedExchangeCertFetcher::OnReceiveResponse", base::StringPrintf("Invalid content length: %" PRIu64, head.content_length)); - + Abort(); return; } body_string_.reserve(head.content_length); @@ -267,6 +286,10 @@ void SignedExchangeCertFetcher::OnComplete( const network::URLLoaderCompletionStatus& status) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"), "SignedExchangeCertFetcher::OnComplete"); + if (devtools_proxy_) { + DCHECK(cert_request_id_); + devtools_proxy_->CertificateRequestCompleted(*cert_request_id_, status); + } if (!handle_watcher_) Abort(); } diff --git a/chromium/content/browser/web_package/signed_exchange_cert_fetcher.h b/chromium/content/browser/web_package/signed_exchange_cert_fetcher.h index 55f77debb9d..23de0610fb8 100644 --- a/chromium/content/browser/web_package/signed_exchange_cert_fetcher.h +++ b/chromium/content/browser/web_package/signed_exchange_cert_fetcher.h @@ -12,8 +12,8 @@ #include "base/callback_helpers.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" +#include "base/unguessable_token.h" #include "content/browser/web_package/signed_exchange_certificate_chain.h" -#include "content/browser/web_package/signed_exchange_utils.h" #include "content/common/content_export.h" #include "services/network/public/mojom/url_loader.mojom.h" #include "url/origin.h" @@ -28,6 +28,7 @@ class SimpleWatcher; namespace content { +class SignedExchangeDevToolsProxy; class ThrottlingURLLoader; class URLLoaderThrottle; @@ -51,8 +52,9 @@ class CONTENT_EXPORT SignedExchangeCertFetcher const GURL& cert_url, url::Origin request_initiator, bool force_fetch, + SignedExchangeVersion version, CertificateCallback callback, - const signed_exchange_utils::LogCallback& error_message_callback); + SignedExchangeDevToolsProxy* devtools_proxy); ~SignedExchangeCertFetcher() override; @@ -72,8 +74,9 @@ class CONTENT_EXPORT SignedExchangeCertFetcher const GURL& cert_url, url::Origin request_initiator, bool force_fetch, + SignedExchangeVersion version, CertificateCallback callback, - const signed_exchange_utils::LogCallback& error_message_callback); + SignedExchangeDevToolsProxy* devtools_proxy); void Start(); void Abort(); void OnHandleReady(MojoResult result); @@ -98,14 +101,18 @@ class CONTENT_EXPORT SignedExchangeCertFetcher scoped_refptr shared_url_loader_factory_; std::vector> throttles_; std::unique_ptr resource_request_; + const SignedExchangeVersion version_; CertificateCallback callback_; - signed_exchange_utils::LogCallback error_message_callback_; std::unique_ptr url_loader_; mojo::ScopedDataPipeConsumerHandle body_; std::unique_ptr handle_watcher_; std::string body_string_; + // This is owned by SignedExchangeHandler which is the owner of |this|. + SignedExchangeDevToolsProxy* devtools_proxy_; + base::Optional cert_request_id_; + DISALLOW_COPY_AND_ASSIGN(SignedExchangeCertFetcher); }; diff --git a/chromium/content/browser/web_package/signed_exchange_cert_fetcher_factory.cc b/chromium/content/browser/web_package/signed_exchange_cert_fetcher_factory.cc index 40d63e33e51..20234806225 100644 --- a/chromium/content/browser/web_package/signed_exchange_cert_fetcher_factory.cc +++ b/chromium/content/browser/web_package/signed_exchange_cert_fetcher_factory.cc @@ -25,9 +25,9 @@ class SignedExchangeCertFetcherFactoryImpl std::unique_ptr CreateFetcherAndStart( const GURL& cert_url, bool force_fetch, + SignedExchangeVersion version, SignedExchangeCertFetcher::CertificateCallback callback, - const signed_exchange_utils::LogCallback& error_message_callback) - override; + SignedExchangeDevToolsProxy* devtools_proxy) override; private: url::Origin request_initiator_; @@ -39,16 +39,17 @@ std::unique_ptr SignedExchangeCertFetcherFactoryImpl::CreateFetcherAndStart( const GURL& cert_url, bool force_fetch, + SignedExchangeVersion version, SignedExchangeCertFetcher::CertificateCallback callback, - const signed_exchange_utils::LogCallback& error_message_callback) { + SignedExchangeDevToolsProxy* devtools_proxy) { DCHECK(url_loader_factory_); DCHECK(url_loader_throttles_getter_); std::vector> throttles = std::move(url_loader_throttles_getter_).Run(); return SignedExchangeCertFetcher::CreateAndStart( std::move(url_loader_factory_), std::move(throttles), cert_url, - std::move(request_initiator_), force_fetch, std::move(callback), - std::move(error_message_callback)); + std::move(request_initiator_), force_fetch, version, std::move(callback), + devtools_proxy); } // static diff --git a/chromium/content/browser/web_package/signed_exchange_cert_fetcher_factory.h b/chromium/content/browser/web_package/signed_exchange_cert_fetcher_factory.h index 4567bf09a74..dc01c1c3bd2 100644 --- a/chromium/content/browser/web_package/signed_exchange_cert_fetcher_factory.h +++ b/chromium/content/browser/web_package/signed_exchange_cert_fetcher_factory.h @@ -10,7 +10,6 @@ #include "base/callback_forward.h" #include "content/browser/web_package/signed_exchange_cert_fetcher.h" -#include "content/browser/web_package/signed_exchange_utils.h" #include "content/common/content_export.h" #include "url/origin.h" @@ -20,6 +19,7 @@ class SharedURLLoaderFactory; namespace content { +class SignedExchangeDevToolsProxy; class SignedExchangeCertFetcher; class URLLoaderThrottle; @@ -32,8 +32,9 @@ class CONTENT_EXPORT SignedExchangeCertFetcherFactory { virtual std::unique_ptr CreateFetcherAndStart( const GURL& cert_url, bool force_fetch, + SignedExchangeVersion version, SignedExchangeCertFetcher::CertificateCallback callback, - const signed_exchange_utils::LogCallback& error_message_callback) = 0; + SignedExchangeDevToolsProxy* devtools_proxy) = 0; using URLLoaderThrottlesGetter = base::RepeatingCallback< std::vector>()>; diff --git a/chromium/content/browser/web_package/signed_exchange_cert_fetcher_unittest.cc b/chromium/content/browser/web_package/signed_exchange_cert_fetcher_unittest.cc index ea7c1eba82e..19e3ed513c7 100644 --- a/chromium/content/browser/web_package/signed_exchange_cert_fetcher_unittest.cc +++ b/chromium/content/browser/web_package/signed_exchange_cert_fetcher_unittest.cc @@ -9,16 +9,15 @@ #include "base/strings/string_piece.h" #include "base/test/scoped_task_environment.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" -#include "content/browser/web_package/signed_exchange_utils.h" #include "content/public/common/resource_type.h" #include "content/public/common/url_loader_throttle.h" -#include "content/public/common/weak_wrapper_shared_url_loader_factory.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/system/data_pipe_utils.h" #include "net/base/load_flags.h" #include "net/cert/x509_util.h" #include "net/test/cert_test_util.h" #include "net/test/test_data_directory.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -76,7 +75,8 @@ class MockURLLoader final : public network::mojom::URLLoader { : binding_(this, std::move(url_loader_request)) {} ~MockURLLoader() override = default; - MOCK_METHOD0(FollowRedirect, void()); + MOCK_METHOD1(FollowRedirect, + void(const base::Optional&)); MOCK_METHOD0(ProceedWithResponse, void()); MOCK_METHOD2(SetPriority, void(net::RequestPriority priority, @@ -208,10 +208,11 @@ class SignedExchangeCertFetcherTest : public testing::Test { base::Unretained(&cert_result_)); return SignedExchangeCertFetcher::CreateAndStart( - base::MakeRefCounted( + base::MakeRefCounted( &mock_loader_factory_), std::move(throttles_), url_, request_initiator_, force_fetch, - std::move(callback), signed_exchange_utils::LogCallback()); + SignedExchangeVersion::kB0, std::move(callback), + nullptr /* devtools_proxy */); } void CallOnReceiveResponse() { @@ -317,7 +318,7 @@ TEST_F(SignedExchangeCertFetcherTest, ForceFetchAndFail) { mock_loader_factory_.url_request()->load_flags); mock_loader_factory_.client_ptr()->OnComplete( - network::URLLoaderCompletionStatus(net::ERR_FAILED)); + network::URLLoaderCompletionStatus(net::ERR_INVALID_SIGNED_EXCHANGE)); RunUntilIdle(); EXPECT_TRUE(callback_called_); @@ -534,7 +535,7 @@ TEST_F(SignedExchangeCertFetcherTest, Throttle_AbortsOnRequest) { CreateFetcherAndStart(false /* force_fetch */); RunUntilIdle(); - throttle->delegate()->CancelWithError(net::ERR_FAILED); + throttle->delegate()->CancelWithError(net::ERR_INVALID_SIGNED_EXCHANGE); RunUntilIdle(); @@ -561,7 +562,7 @@ TEST_F(SignedExchangeCertFetcherTest, Throttle_AbortsOnRedirect) { EXPECT_TRUE(throttle->will_redirect_request_called()); - throttle->delegate()->CancelWithError(net::ERR_FAILED); + throttle->delegate()->CancelWithError(net::ERR_INVALID_SIGNED_EXCHANGE); RunUntilIdle(); EXPECT_TRUE(callback_called_); @@ -593,7 +594,7 @@ TEST_F(SignedExchangeCertFetcherTest, Throttle_AbortsOnResponse) { EXPECT_FALSE(callback_called_); - throttle->delegate()->CancelWithError(net::ERR_FAILED); + throttle->delegate()->CancelWithError(net::ERR_INVALID_SIGNED_EXCHANGE); RunUntilIdle(); EXPECT_TRUE(callback_called_); diff --git a/chromium/content/browser/web_package/signed_exchange_certificate_chain.cc b/chromium/content/browser/web_package/signed_exchange_certificate_chain.cc index ea587d6979d..d94125eae13 100644 --- a/chromium/content/browser/web_package/signed_exchange_certificate_chain.cc +++ b/chromium/content/browser/web_package/signed_exchange_certificate_chain.cc @@ -5,9 +5,13 @@ #include "content/browser/web_package/signed_exchange_certificate_chain.h" #include "base/command_line.h" +#include "base/format_macros.h" #include "base/memory/ptr_util.h" +#include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" +#include "components/cbor/cbor_reader.h" #include "content/browser/web_package/signed_exchange_consts.h" +#include "content/browser/web_package/signed_exchange_utils.h" #include "content/public/common/content_switches.h" #include "net/cert/x509_certificate.h" @@ -15,54 +19,225 @@ namespace content { namespace { -bool ConsumeByte(base::StringPiece* data, uint8_t* out) { +bool ConsumeByte(base::span* data, uint8_t* out) { if (data->empty()) return false; *out = (*data)[0]; - data->remove_prefix(1); + *data = data->subspan(1); return true; } -bool Consume2Bytes(base::StringPiece* data, uint16_t* out) { +bool Consume2Bytes(base::span* data, uint16_t* out) { if (data->size() < 2) return false; - *out = (static_cast((*data)[0]) << 8) | - static_cast((*data)[1]); - data->remove_prefix(2); + *out = ((*data)[0] << 8) | (*data)[1]; + *data = data->subspan(2); return true; } -bool Consume3Bytes(base::StringPiece* data, uint32_t* out) { +bool Consume3Bytes(base::span* data, uint32_t* out) { if (data->size() < 3) return false; - *out = (static_cast((*data)[0]) << 16) | - (static_cast((*data)[1]) << 8) | - static_cast((*data)[2]); - data->remove_prefix(3); + *out = ((*data)[0] << 16) | ((*data)[1] << 8) | (*data)[2]; + *data = data->subspan(3); return true; } -} // namespace - -// static -std::unique_ptr -SignedExchangeCertificateChain::Parse(base::StringPiece cert_response_body) { +std::unique_ptr ParseB0( + base::span message, + SignedExchangeDevToolsProxy* devtools_proxy) { + TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), + "SignedExchangeCertificateChain::ParseB0"); base::Optional> der_certs = - GetCertChainFromMessage(cert_response_body); - if (!der_certs) + SignedExchangeCertificateChain::GetCertChainFromMessage(message); + if (!der_certs) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB0", + "Failed to parse the response as a TLS 1.3 Certificate message."); return nullptr; + } scoped_refptr cert = net::X509Certificate::CreateFromDERCertChain(*der_certs); - if (!cert) + if (!cert) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB0", + "X509Certificate::CreateFromDERCertChain failed."); + return nullptr; + } + + TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("loading"), + "SignedExchangeCertificateChain::ParseB1"); + // The V0 certificate format doesn't support OCSP nor SCT. + return base::WrapUnique( + new SignedExchangeCertificateChain(cert, "" /* ocsp */, "" /* sct */)); +} + +// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#cert-chain-format +std::unique_ptr ParseB1( + base::span message, + SignedExchangeDevToolsProxy* devtools_proxy) { + TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), + "SignedExchangeCertificateChain::ParseB1"); + + cbor::CBORReader::DecoderError error; + base::Optional value = + cbor::CBORReader::Read(message, &error); + if (!value.has_value()) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + base::StringPrintf("Failed to decode CBORValue. CBOR error: %s", + cbor::CBORReader::ErrorCodeToString(error))); + return nullptr; + } + if (!value->is_array()) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + base::StringPrintf( + "Expected top-level CBORValue to be an array. Actual type: %d", + static_cast(value->type()))); + return nullptr; + } + + const cbor::CBORValue::ArrayValue& top_level_array = value->GetArray(); + // Expect at least 2 elements (magic string and main certificate). + if (top_level_array.size() < 2) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + base::StringPrintf( + "Expected top-level array to have at least 2 elements." + "Actual element count: %" PRIuS, + top_level_array.size())); + return nullptr; + } + if (!top_level_array[0].is_string() || + top_level_array[0].GetString() != kCertChainCborMagic) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + "First element of cert chain CBOR does not match the magic string."); + return nullptr; + } + + std::vector der_certs; + der_certs.reserve(top_level_array.size() - 1); + std::string ocsp; + std::string sct; + + for (size_t i = 1; i < top_level_array.size(); i++) { + if (!top_level_array[i].is_map()) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + base::StringPrintf( + "Expected certificate map, got non-map type at index %zu." + " Actual type: %d", + i, static_cast(top_level_array[i].type()))); + return nullptr; + } + const cbor::CBORValue::MapValue& cert_map = top_level_array[i].GetMap(); + + // Step 1. Each cert value MUST be a DER-encoded X.509v3 certificate + // ([RFC5280]). Other key/value pairs in the same array item define + // properties of this certificate. [spec text] + auto cert_iter = cert_map.find(cbor::CBORValue(kCertKey)); + if (cert_iter == cert_map.end() || !cert_iter->second.is_bytestring()) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + base::StringPrintf( + "cert is not found or not a bytestring, at index %zu.", i)); + return nullptr; + } + der_certs.push_back(cert_iter->second.GetBytestringAsString()); + + auto ocsp_iter = cert_map.find(cbor::CBORValue(kOcspKey)); + if (i == 1) { + // Step 2. The first certificate’s ocsp value if any MUST be a complete, + // DER-encoded OCSP response for that certificate (using the ASN.1 type + // OCSPResponse defined in [RFC2560]). ... [spec text] + if (ocsp_iter == cert_map.end() || !ocsp_iter->second.is_bytestring()) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + "ocsp is not a bytestring, or not found in the first cert map."); + return nullptr; + } + ocsp = ocsp_iter->second.GetBytestringAsString().as_string(); + if (ocsp.empty()) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + "ocsp must not be empty."); + return nullptr; + } + } else if (ocsp_iter != cert_map.end()) { + // Step 2. ... Subsequent certificates MUST NOT have an ocsp value. [spec + // text] + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + base::StringPrintf( + "ocsp value found in a subsequent cert map, at index %zu.", i)); + return nullptr; + } + + // Step 3. Each certificate’s sct value MUST be a + // SignedCertificateTimestampList for that certificate as defined by Section + // 3.3 of [RFC6962]. [spec text] + // + // We use SCTs only of the main certificate. + // TODO(crbug.com/815025): Update the spec text once + // https://github.com/WICG/webpackage/issues/175 is resolved. + if (i == 1) { + auto sct_iter = cert_map.find(cbor::CBORValue(kSctKey)); + if (sct_iter != cert_map.end()) { + if (!sct_iter->second.is_bytestring()) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + "sct is not a bytestring."); + return nullptr; + } + sct = sct_iter->second.GetBytestringAsString().as_string(); + if (sct.empty()) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + "sct must not be empty."); + return nullptr; + } + } + } + } + scoped_refptr cert = + net::X509Certificate::CreateFromDERCertChain(der_certs); + if (!cert) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeCertificateChain::ParseB1", + "X509Certificate::CreateFromDERCertChain failed."); return nullptr; - return base::WrapUnique(new SignedExchangeCertificateChain(cert)); + } + + TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("loading"), + "SignedExchangeCertificateChain::ParseB1"); + return base::WrapUnique(new SignedExchangeCertificateChain(cert, ocsp, sct)); +} + +} // namespace + +// static +std::unique_ptr +SignedExchangeCertificateChain::Parse( + SignedExchangeVersion version, + base::span cert_response_body, + SignedExchangeDevToolsProxy* devtools_proxy) { + switch (version) { + case SignedExchangeVersion::kB0: + return ParseB0(cert_response_body, devtools_proxy); + case SignedExchangeVersion::kB1: + return ParseB1(cert_response_body, devtools_proxy); + } + NOTREACHED(); + return nullptr; } // static base::Optional> SignedExchangeCertificateChain::GetCertChainFromMessage( - base::StringPiece message) { + base::span message) { uint8_t cert_request_context_size = 0; if (!ConsumeByte(&message, &cert_request_context_size)) { DVLOG(1) << "Can't read certificate request request context size."; @@ -79,9 +254,9 @@ SignedExchangeCertificateChain::GetCertChainFromMessage( return base::nullopt; } - if (cert_list_size != message.length()) { + if (cert_list_size != message.size()) { DVLOG(1) << "Certificate list size error: cert_list_size=" << cert_list_size - << " remaining=" << message.length(); + << " remaining=" << message.size(); return base::nullopt; } @@ -92,32 +267,35 @@ SignedExchangeCertificateChain::GetCertChainFromMessage( DVLOG(1) << "Can't read certificate data size."; return base::nullopt; } - if (message.length() < cert_data_size) { + if (message.size() < cert_data_size) { DVLOG(1) << "Certificate data size error: cert_data_size=" - << cert_data_size << " remaining=" << message.length(); + << cert_data_size << " remaining=" << message.size(); return base::nullopt; } - certs.emplace_back(message.substr(0, cert_data_size)); - message.remove_prefix(cert_data_size); + certs.emplace_back(base::StringPiece( + reinterpret_cast(message.data()), cert_data_size)); + message = message.subspan(cert_data_size); uint16_t extensions_size = 0; if (!Consume2Bytes(&message, &extensions_size)) { DVLOG(1) << "Can't read extensions size."; return base::nullopt; } - if (message.length() < extensions_size) { + if (message.size() < extensions_size) { DVLOG(1) << "Extensions size error: extensions_size=" << extensions_size - << " remaining=" << message.length(); + << " remaining=" << message.size(); return base::nullopt; } - message.remove_prefix(extensions_size); + message = message.subspan(extensions_size); } return certs; } SignedExchangeCertificateChain::SignedExchangeCertificateChain( - scoped_refptr cert) - : cert_(cert) { + scoped_refptr cert, + const std::string& ocsp, + const std::string& sct) + : cert_(cert), ocsp_(ocsp), sct_(sct) { DCHECK(cert); } diff --git a/chromium/content/browser/web_package/signed_exchange_certificate_chain.h b/chromium/content/browser/web_package/signed_exchange_certificate_chain.h index d98d57e5e73..7b6ece476c3 100644 --- a/chromium/content/browser/web_package/signed_exchange_certificate_chain.h +++ b/chromium/content/browser/web_package/signed_exchange_certificate_chain.h @@ -11,6 +11,7 @@ #include "base/memory/scoped_refptr.h" #include "base/optional.h" #include "base/strings/string_piece_forward.h" +#include "content/browser/web_package/signed_exchange_consts.h" #include "content/common/content_export.h" namespace net { @@ -19,28 +20,41 @@ class X509Certificate; namespace content { +class SignedExchangeDevToolsProxy; + // SignedExchangeCertificateChain contains all information in signed exchange // certificate chain. // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#cert-chain-format class CONTENT_EXPORT SignedExchangeCertificateChain { public: static std::unique_ptr Parse( - base::StringPiece cert_response_body); + SignedExchangeVersion version, + base::span cert_response_body, + SignedExchangeDevToolsProxy* devtools_proxy); + + // Regular consumers should use the static Parse() rather than directly + // calling this. + SignedExchangeCertificateChain(scoped_refptr cert, + const std::string& ocsp, + const std::string& sct); // Parses a TLS 1.3 Certificate message containing X.509v3 certificates and // returns a vector of cert_data. Returns nullopt when failed to parse. static base::Optional> GetCertChainFromMessage( - base::StringPiece message); + base::span message); ~SignedExchangeCertificateChain(); const scoped_refptr& cert() const { return cert_; } + const std::string& ocsp() const { return ocsp_; } + const std::string& sct() const { return sct_; } private: - explicit SignedExchangeCertificateChain( - scoped_refptr cert); - scoped_refptr cert_; + + // Version b1 specific fields: + std::string ocsp_; + std::string sct_; }; } // namespace content diff --git a/chromium/content/browser/web_package/signed_exchange_certificate_chain_fuzzer.cc b/chromium/content/browser/web_package/signed_exchange_certificate_chain_fuzzer.cc index e2ccc4ba21e..56d34a6ad96 100644 --- a/chromium/content/browser/web_package/signed_exchange_certificate_chain_fuzzer.cc +++ b/chromium/content/browser/web_package/signed_exchange_certificate_chain_fuzzer.cc @@ -9,8 +9,10 @@ namespace content { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - base::StringPiece input(reinterpret_cast(data), size); - SignedExchangeCertificateChain::GetCertChainFromMessage(input); + SignedExchangeCertificateChain::Parse(SignedExchangeVersion::kB0, + base::make_span(data, size), nullptr); + SignedExchangeCertificateChain::Parse(SignedExchangeVersion::kB1, + base::make_span(data, size), nullptr); return 0; } diff --git a/chromium/content/browser/web_package/signed_exchange_certificate_chain_unittest.cc b/chromium/content/browser/web_package/signed_exchange_certificate_chain_unittest.cc index 1dd2681422e..932f1cbf474 100644 --- a/chromium/content/browser/web_package/signed_exchange_certificate_chain_unittest.cc +++ b/chromium/content/browser/web_package/signed_exchange_certificate_chain_unittest.cc @@ -4,8 +4,16 @@ #include "content/browser/web_package/signed_exchange_certificate_chain.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/optional.h" +#include "base/path_service.h" #include "base/strings/string_piece.h" +#include "components/cbor/cbor_values.h" +#include "components/cbor/cbor_writer.h" +#include "content/public/common/content_paths.h" +#include "net/cert/x509_util.h" +#include "net/test/cert_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -17,12 +25,16 @@ base::Optional> GetCertChain( const uint8_t* input, size_t input_size) { return SignedExchangeCertificateChain::GetCertChainFromMessage( - base::StringPiece(reinterpret_cast(input), input_size)); + base::make_span(input, input_size)); +} + +cbor::CBORValue CBORByteString(base::StringPiece str) { + return cbor::CBORValue(str, cbor::CBORValue::Type::BYTE_STRING); } } // namespace -TEST(SignedExchangeCertificateParseTest, OneCert) { +TEST(SignedExchangeCertificateParseB0Test, OneCert) { const uint8_t input[] = { // clang-format off 0x00, // request context size @@ -46,7 +58,7 @@ TEST(SignedExchangeCertificateParseTest, OneCert) { testing::ElementsAreArray(kExpected, arraysize(kExpected))); } -TEST(SignedExchangeCertificateParseTest, OneCertWithExtension) { +TEST(SignedExchangeCertificateParseB0Test, OneCertWithExtension) { const uint8_t input[] = { // clang-format off 0x00, // request context size @@ -71,7 +83,7 @@ TEST(SignedExchangeCertificateParseTest, OneCertWithExtension) { testing::ElementsAreArray(kExpected, arraysize(kExpected))); } -TEST(SignedExchangeCertificateParseTest, TwoCerts) { +TEST(SignedExchangeCertificateParseB0Test, TwoCerts) { const uint8_t input[] = { // clang-format off 0x00, // request context size @@ -163,11 +175,11 @@ TEST(SignedExchangeCertificateParseTest, TwoCerts) { testing::ElementsAreArray(kExpected2, arraysize(kExpected2))); } -TEST(SignedExchangeCertificateParseTest, Empty) { +TEST(SignedExchangeCertificateParseB0Test, Empty) { EXPECT_FALSE(GetCertChain(nullptr, 0)); } -TEST(SignedExchangeCertificateParseTest, InvalidRequestContextSize) { +TEST(SignedExchangeCertificateParseB0Test, InvalidRequestContextSize) { const uint8_t input[] = { // clang-format off 0x01, // request context size: must be zero @@ -177,7 +189,7 @@ TEST(SignedExchangeCertificateParseTest, InvalidRequestContextSize) { EXPECT_FALSE(GetCertChain(input, arraysize(input))); } -TEST(SignedExchangeCertificateParseTest, CanNotReadCertListSize1) { +TEST(SignedExchangeCertificateParseB0Test, CanNotReadCertListSize1) { const uint8_t input[] = { // clang-format off 0x00, // request context size @@ -187,7 +199,7 @@ TEST(SignedExchangeCertificateParseTest, CanNotReadCertListSize1) { EXPECT_FALSE(GetCertChain(input, arraysize(input))); } -TEST(SignedExchangeCertificateParseTest, CanNotReadCertListSize2) { +TEST(SignedExchangeCertificateParseB0Test, CanNotReadCertListSize2) { const uint8_t input[] = { // clang-format off 0x00, // request context size @@ -197,7 +209,7 @@ TEST(SignedExchangeCertificateParseTest, CanNotReadCertListSize2) { EXPECT_FALSE(GetCertChain(input, arraysize(input))); } -TEST(SignedExchangeCertificateParseTest, CertListSizeError) { +TEST(SignedExchangeCertificateParseB0Test, CertListSizeError) { const uint8_t input[] = { // clang-format off 0x00, // request context size @@ -211,7 +223,7 @@ TEST(SignedExchangeCertificateParseTest, CertListSizeError) { EXPECT_FALSE(GetCertChain(input, arraysize(input))); } -TEST(SignedExchangeCertificateParseTest, CanNotReadCertDataSize) { +TEST(SignedExchangeCertificateParseB0Test, CanNotReadCertDataSize) { const uint8_t input[] = { // clang-format off 0x00, // request context size @@ -223,7 +235,7 @@ TEST(SignedExchangeCertificateParseTest, CanNotReadCertDataSize) { EXPECT_FALSE(GetCertChain(input, arraysize(input))); } -TEST(SignedExchangeCertificateParseTest, CertDataSizeError) { +TEST(SignedExchangeCertificateParseB0Test, CertDataSizeError) { const uint8_t input[] = { // clang-format off 0x00, // request context size @@ -236,7 +248,7 @@ TEST(SignedExchangeCertificateParseTest, CertDataSizeError) { EXPECT_FALSE(GetCertChain(input, arraysize(input))); } -TEST(SignedExchangeCertificateParseTest, CanNotReadExtensionsSize) { +TEST(SignedExchangeCertificateParseB0Test, CanNotReadExtensionsSize) { const uint8_t input[] = { // clang-format off 0x00, // request context size @@ -250,7 +262,7 @@ TEST(SignedExchangeCertificateParseTest, CanNotReadExtensionsSize) { EXPECT_FALSE(GetCertChain(input, arraysize(input))); } -TEST(SignedExchangeCertificateParseTest, ExtensionsSizeError) { +TEST(SignedExchangeCertificateParseB0Test, ExtensionsSizeError) { const uint8_t input[] = { // clang-format off 0x00, // request context size @@ -264,4 +276,183 @@ TEST(SignedExchangeCertificateParseTest, ExtensionsSizeError) { EXPECT_FALSE(GetCertChain(input, arraysize(input))); } +TEST(SignedExchangeCertificateParseB1Test, Empty) { + auto parsed = SignedExchangeCertificateChain::Parse( + SignedExchangeVersion::kB1, base::span(), nullptr); + EXPECT_FALSE(parsed); +} + +TEST(SignedExchangeCertificateParseB1Test, EmptyChain) { + cbor::CBORValue::ArrayValue cbor_array; + cbor_array.push_back(cbor::CBORValue(u8"\U0001F4DC\u26D3")); + + auto serialized = + cbor::CBORWriter::Write(cbor::CBORValue(std::move(cbor_array))); + ASSERT_TRUE(serialized.has_value()); + + auto parsed = SignedExchangeCertificateChain::Parse( + SignedExchangeVersion::kB1, base::make_span(*serialized), nullptr); + EXPECT_FALSE(parsed); +} + +TEST(SignedExchangeCertificateParseB1Test, MissingCert) { + cbor::CBORValue::MapValue cbor_map; + cbor_map[cbor::CBORValue("sct")] = CBORByteString("SCT"); + cbor_map[cbor::CBORValue("ocsp")] = CBORByteString("OCSP"); + + cbor::CBORValue::ArrayValue cbor_array; + cbor_array.push_back(cbor::CBORValue(u8"\U0001F4DC\u26D3")); + cbor_array.push_back(cbor::CBORValue(std::move(cbor_map))); + + auto serialized = + cbor::CBORWriter::Write(cbor::CBORValue(std::move(cbor_array))); + ASSERT_TRUE(serialized.has_value()); + + auto parsed = SignedExchangeCertificateChain::Parse( + SignedExchangeVersion::kB1, base::make_span(*serialized), nullptr); + EXPECT_FALSE(parsed); +} + +TEST(SignedExchangeCertificateParseB1Test, OneCert) { + net::CertificateList certs; + ASSERT_TRUE( + net::LoadCertificateFiles({"subjectAltName_sanity_check.pem"}, &certs)); + ASSERT_EQ(1U, certs.size()); + base::StringPiece cert_der = + net::x509_util::CryptoBufferAsStringPiece(certs[0]->cert_buffer()); + + cbor::CBORValue::MapValue cbor_map; + cbor_map[cbor::CBORValue("sct")] = CBORByteString("SCT"); + cbor_map[cbor::CBORValue("cert")] = CBORByteString(cert_der); + cbor_map[cbor::CBORValue("ocsp")] = CBORByteString("OCSP"); + + cbor::CBORValue::ArrayValue cbor_array; + cbor_array.push_back(cbor::CBORValue(u8"\U0001F4DC\u26D3")); + cbor_array.push_back(cbor::CBORValue(std::move(cbor_map))); + + auto serialized = + cbor::CBORWriter::Write(cbor::CBORValue(std::move(cbor_array))); + ASSERT_TRUE(serialized.has_value()); + + auto parsed = SignedExchangeCertificateChain::Parse( + SignedExchangeVersion::kB1, base::make_span(*serialized), nullptr); + ASSERT_TRUE(parsed); + EXPECT_EQ(cert_der, net::x509_util::CryptoBufferAsStringPiece( + parsed->cert()->cert_buffer())); + ASSERT_EQ(0U, parsed->cert()->intermediate_buffers().size()); + EXPECT_EQ(parsed->ocsp(), base::make_optional("OCSP")); + EXPECT_EQ(parsed->sct(), base::make_optional("SCT")); +} + +TEST(SignedExchangeCertificateParseB1Test, MissingOCSPInFirstCert) { + net::CertificateList certs; + ASSERT_TRUE( + net::LoadCertificateFiles({"subjectAltName_sanity_check.pem"}, &certs)); + ASSERT_EQ(1U, certs.size()); + base::StringPiece cert_der = + net::x509_util::CryptoBufferAsStringPiece(certs[0]->cert_buffer()); + + cbor::CBORValue::MapValue cbor_map; + cbor_map[cbor::CBORValue("sct")] = CBORByteString("SCT"); + cbor_map[cbor::CBORValue("cert")] = CBORByteString(cert_der); + + cbor::CBORValue::ArrayValue cbor_array; + cbor_array.push_back(cbor::CBORValue(u8"\U0001F4DC\u26D3")); + cbor_array.push_back(cbor::CBORValue(std::move(cbor_map))); + + auto serialized = + cbor::CBORWriter::Write(cbor::CBORValue(std::move(cbor_array))); + ASSERT_TRUE(serialized.has_value()); + + auto parsed = SignedExchangeCertificateChain::Parse( + SignedExchangeVersion::kB1, base::make_span(*serialized), nullptr); + EXPECT_FALSE(parsed); +} + +TEST(SignedExchangeCertificateParseB1Test, TwoCerts) { + net::CertificateList certs; + ASSERT_TRUE(net::LoadCertificateFiles( + {"subjectAltName_sanity_check.pem", "root_ca_cert.pem"}, &certs)); + ASSERT_EQ(2U, certs.size()); + base::StringPiece cert1_der = + net::x509_util::CryptoBufferAsStringPiece(certs[0]->cert_buffer()); + base::StringPiece cert2_der = + net::x509_util::CryptoBufferAsStringPiece(certs[1]->cert_buffer()); + + cbor::CBORValue::MapValue cbor_map1; + cbor_map1[cbor::CBORValue("sct")] = CBORByteString("SCT"); + cbor_map1[cbor::CBORValue("cert")] = CBORByteString(cert1_der); + cbor_map1[cbor::CBORValue("ocsp")] = CBORByteString("OCSP"); + + cbor::CBORValue::MapValue cbor_map2; + cbor_map2[cbor::CBORValue("cert")] = CBORByteString(cert2_der); + + cbor::CBORValue::ArrayValue cbor_array; + cbor_array.push_back(cbor::CBORValue(u8"\U0001F4DC\u26D3")); + cbor_array.push_back(cbor::CBORValue(std::move(cbor_map1))); + cbor_array.push_back(cbor::CBORValue(std::move(cbor_map2))); + + auto serialized = + cbor::CBORWriter::Write(cbor::CBORValue(std::move(cbor_array))); + ASSERT_TRUE(serialized.has_value()); + + auto parsed = SignedExchangeCertificateChain::Parse( + SignedExchangeVersion::kB1, base::make_span(*serialized), nullptr); + ASSERT_TRUE(parsed); + EXPECT_EQ(cert1_der, net::x509_util::CryptoBufferAsStringPiece( + parsed->cert()->cert_buffer())); + ASSERT_EQ(1U, parsed->cert()->intermediate_buffers().size()); + EXPECT_EQ(cert2_der, net::x509_util::CryptoBufferAsStringPiece( + parsed->cert()->intermediate_buffers()[0].get())); + EXPECT_EQ(parsed->ocsp(), base::make_optional("OCSP")); + EXPECT_EQ(parsed->sct(), base::make_optional("SCT")); +} + +TEST(SignedExchangeCertificateParseB1Test, HavingOCSPInSecnodCert) { + net::CertificateList certs; + ASSERT_TRUE(net::LoadCertificateFiles( + {"subjectAltName_sanity_check.pem", "root_ca_cert.pem"}, &certs)); + ASSERT_EQ(2U, certs.size()); + base::StringPiece cert1_der = + net::x509_util::CryptoBufferAsStringPiece(certs[0]->cert_buffer()); + base::StringPiece cert2_der = + net::x509_util::CryptoBufferAsStringPiece(certs[1]->cert_buffer()); + + cbor::CBORValue::MapValue cbor_map1; + cbor_map1[cbor::CBORValue("sct")] = CBORByteString("SCT"); + cbor_map1[cbor::CBORValue("cert")] = CBORByteString(cert1_der); + cbor_map1[cbor::CBORValue("ocsp")] = CBORByteString("OCSP1"); + + cbor::CBORValue::MapValue cbor_map2; + cbor_map2[cbor::CBORValue("cert")] = CBORByteString(cert2_der); + cbor_map2[cbor::CBORValue("ocsp")] = CBORByteString("OCSP2"); + + cbor::CBORValue::ArrayValue cbor_array; + cbor_array.push_back(cbor::CBORValue(u8"\U0001F4DC\u26D3")); + cbor_array.push_back(cbor::CBORValue(std::move(cbor_map1))); + cbor_array.push_back(cbor::CBORValue(std::move(cbor_map2))); + + auto serialized = + cbor::CBORWriter::Write(cbor::CBORValue(std::move(cbor_array))); + ASSERT_TRUE(serialized.has_value()); + + auto parsed = SignedExchangeCertificateChain::Parse( + SignedExchangeVersion::kB1, base::make_span(*serialized), nullptr); + EXPECT_FALSE(parsed); +} + +TEST(SignedExchangeCertificateParseB1Test, ParseGoldenFile) { + base::FilePath path; + base::PathService::Get(content::DIR_TEST_DATA, &path); + path = path.AppendASCII("htxg").AppendASCII( + "wildcard_example.org.public.pem.cbor"); + std::string contents; + ASSERT_TRUE(base::ReadFileToString(path, &contents)); + + auto parsed = SignedExchangeCertificateChain::Parse( + SignedExchangeVersion::kB1, base::as_bytes(base::make_span(contents)), + nullptr); + ASSERT_TRUE(parsed); +} + } // namespace content diff --git a/chromium/content/browser/web_package/signed_exchange_consts.h b/chromium/content/browser/web_package/signed_exchange_consts.h index a289b8d015d..9dedf00f449 100644 --- a/chromium/content/browser/web_package/signed_exchange_consts.h +++ b/chromium/content/browser/web_package/signed_exchange_consts.h @@ -10,18 +10,27 @@ namespace content { constexpr char kAcceptHeaderSignedExchangeSuffix[] = ",application/signed-exchange;v=b0"; +enum class SignedExchangeVersion { kB0, kB1 }; + // Field names defined in the application/signed-exchange content type: // https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html#application-signed-exchange constexpr char kCertSha256Key[] = "certSha256"; +constexpr char kCertUrl[] = "certUrl"; constexpr char kDateKey[] = "date"; constexpr char kExpiresKey[] = "expires"; constexpr char kHeadersKey[] = "headers"; +constexpr char kIntegrity[] = "integrity"; constexpr char kMethodKey[] = ":method"; constexpr char kSignature[] = "signature"; +constexpr char kSig[] = "sig"; constexpr char kStatusKey[] = ":status"; constexpr char kUrlKey[] = ":url"; constexpr char kValidityUrlKey[] = "validityUrl"; +constexpr char kCertChainCborMagic[] = u8"\U0001F4DC\u26D3"; // "📜⛓" +constexpr char kCertKey[] = "cert"; +constexpr char kOcspKey[] = "ocsp"; +constexpr char kSctKey[] = "sct"; } // namespace content diff --git a/chromium/content/browser/web_package/signed_exchange_devtools_proxy.cc b/chromium/content/browser/web_package/signed_exchange_devtools_proxy.cc new file mode 100644 index 00000000000..3a04504e15d --- /dev/null +++ b/chromium/content/browser/web_package/signed_exchange_devtools_proxy.cc @@ -0,0 +1,185 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/web_package/signed_exchange_devtools_proxy.h" + +#include "base/trace_event/trace_event.h" +#include "content/browser/devtools/render_frame_devtools_agent_host.h" +#include "content/browser/frame_host/frame_tree_node.h" +#include "content/browser/web_package/signed_exchange_header.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/web_contents.h" + +namespace content { + +namespace { + +void AddErrorMessageToConsoleOnUI( + base::RepeatingCallback frame_tree_node_id_getter, + std::string error_message) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + WebContents* web_contents = + WebContents::FromFrameTreeNodeId(frame_tree_node_id_getter.Run()); + if (!web_contents) + return; + web_contents->GetMainFrame()->AddMessageToConsole( + content::CONSOLE_MESSAGE_LEVEL_ERROR, error_message); +} + +void CertificateRequestSentOnUI( + base::RepeatingCallback frame_tree_node_id_getter, + const base::UnguessableToken& request_id, + const base::UnguessableToken& loader_id, + const network::ResourceRequest& request, + const GURL& signed_exchange_url) { + FrameTreeNode* frame_tree_node = + FrameTreeNode::GloballyFindByID(frame_tree_node_id_getter.Run()); + if (!frame_tree_node) + return; + RenderFrameDevToolsAgentHost::OnSignedExchangeCertificateRequestSent( + frame_tree_node, request_id, loader_id, request, signed_exchange_url); +} + +void CertificateResponseReceivedOnUI( + base::RepeatingCallback frame_tree_node_id_getter, + const base::UnguessableToken& request_id, + const base::UnguessableToken& loader_id, + const GURL& url, + scoped_refptr response) { + FrameTreeNode* frame_tree_node = + FrameTreeNode::GloballyFindByID(frame_tree_node_id_getter.Run()); + if (!frame_tree_node) + return; + RenderFrameDevToolsAgentHost::OnSignedExchangeCertificateResponseReceived( + frame_tree_node, request_id, loader_id, url, response->head); +} + +void CertificateRequestCompletedOnUI( + base::RepeatingCallback frame_tree_node_id_getter, + const base::UnguessableToken& request_id, + const network::URLLoaderCompletionStatus& status) { + FrameTreeNode* frame_tree_node = + FrameTreeNode::GloballyFindByID(frame_tree_node_id_getter.Run()); + if (!frame_tree_node) + return; + RenderFrameDevToolsAgentHost::OnSignedExchangeCertificateRequestCompleted( + frame_tree_node, request_id, status); +} + +void OnSignedExchangeReceivedOnUI( + base::RepeatingCallback frame_tree_node_id_getter, + const GURL& outer_request_url, + scoped_refptr outer_response, + base::Optional devtools_navigation_token, + base::Optional header, + base::Optional ssl_info, + std::vector error_messages) { + FrameTreeNode* frame_tree_node = + FrameTreeNode::GloballyFindByID(frame_tree_node_id_getter.Run()); + if (!frame_tree_node) + return; + RenderFrameDevToolsAgentHost::OnSignedExchangeReceived( + frame_tree_node, devtools_navigation_token, outer_request_url, + outer_response->head, header, ssl_info, error_messages); +} + +} // namespace + +SignedExchangeDevToolsProxy::SignedExchangeDevToolsProxy( + const GURL& outer_request_url, + const network::ResourceResponseHead& outer_response, + base::RepeatingCallback frame_tree_node_id_getter, + base::Optional devtools_navigation_token, + bool report_raw_headers) + : outer_request_url_(outer_request_url), + outer_response_(outer_response), + frame_tree_node_id_getter_(frame_tree_node_id_getter), + devtools_navigation_token_(devtools_navigation_token), + devtools_enabled_(report_raw_headers) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); +} + +SignedExchangeDevToolsProxy::~SignedExchangeDevToolsProxy() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); +} + +void SignedExchangeDevToolsProxy::ReportErrorMessage( + const std::string& message) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + error_messages_.push_back(message); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&AddErrorMessageToConsoleOnUI, frame_tree_node_id_getter_, + std::move(message))); +} + +void SignedExchangeDevToolsProxy::CertificateRequestSent( + const base::UnguessableToken& request_id, + const network::ResourceRequest& request) { + if (!devtools_enabled_) + return; + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce( + &CertificateRequestSentOnUI, frame_tree_node_id_getter_, request_id, + devtools_navigation_token_ ? *devtools_navigation_token_ : request_id, + request, outer_request_url_)); +} + +void SignedExchangeDevToolsProxy::CertificateResponseReceived( + const base::UnguessableToken& request_id, + const GURL& url, + const network::ResourceResponseHead& head) { + if (!devtools_enabled_) + return; + + // Make a deep copy of ResourceResponseHead before passing it cross-thread. + auto resource_response = base::MakeRefCounted(); + resource_response->head = head; + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce( + &CertificateResponseReceivedOnUI, frame_tree_node_id_getter_, + request_id, + devtools_navigation_token_ ? *devtools_navigation_token_ : request_id, + url, resource_response->DeepCopy())); +} + +void SignedExchangeDevToolsProxy::CertificateRequestCompleted( + const base::UnguessableToken& request_id, + const network::URLLoaderCompletionStatus& status) { + if (!devtools_enabled_) + return; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&CertificateRequestCompletedOnUI, + frame_tree_node_id_getter_, request_id, status)); +} + +void SignedExchangeDevToolsProxy::OnSignedExchangeReceived( + const base::Optional& header, + const net::SSLInfo* ssl_info) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!devtools_enabled_) + return; + base::Optional ssl_info_opt; + if (ssl_info) + ssl_info_opt = *ssl_info; + + // Make a deep copy of ResourceResponseHead before passing it cross-thread. + auto resource_response = base::MakeRefCounted(); + resource_response->head = outer_response_; + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&OnSignedExchangeReceivedOnUI, frame_tree_node_id_getter_, + outer_request_url_, resource_response->DeepCopy(), + devtools_navigation_token_, header, + std::move(ssl_info_opt), std::move(error_messages_))); +} + +} // namespace content diff --git a/chromium/content/browser/web_package/signed_exchange_devtools_proxy.h b/chromium/content/browser/web_package/signed_exchange_devtools_proxy.h new file mode 100644 index 00000000000..e73b1119ec5 --- /dev/null +++ b/chromium/content/browser/web_package/signed_exchange_devtools_proxy.h @@ -0,0 +1,86 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_DEVTOOLS_PROXY_H_ +#define CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_DEVTOOLS_PROXY_H_ + +#include +#include + +#include "base/callback.h" +#include "base/macros.h" +#include "base/optional.h" +#include "base/unguessable_token.h" +#include "content/common/content_export.h" +#include "services/network/public/cpp/resource_response.h" + +class GURL; + +namespace base { +class UnguessableToken; +} // namespace base + +namespace net { +class SSLInfo; +} // namespace net + +namespace network { +struct ResourceRequest; +struct ResourceResponseHead; +struct URLLoaderCompletionStatus; +} // namespace network + +namespace content { +class SignedExchangeHeader; + +// SignedExchangeDevToolsProxy lives on the IO thread and sends messages to +// DevTools via the UI thread to show signed exchange related information. +class CONTENT_EXPORT SignedExchangeDevToolsProxy { + public: + // |frame_tree_node_id_getter| callback will be called on the UI thread to get + // the frame tree node ID. Note: We are using callback beause when Network + // Service is not enabled the ID is not available while handling prefetch + // requests on the IO thread. + // When the signed exchange request is a navigation request, + // |devtools_navigation_token| can be used to find the matching request in + // DevTools. But when the signed exchange request is a prefetch request, the + // browser process doesn't know the request id used in DevTools. So DevTools + // looks up the inflight requests using |outer_request_url| to find the + // matching request. + SignedExchangeDevToolsProxy( + const GURL& outer_request_url, + const network::ResourceResponseHead& outer_response, + base::RepeatingCallback frame_tree_node_id_getter, + base::Optional devtools_navigation_token, + bool report_raw_headers); + ~SignedExchangeDevToolsProxy(); + + void ReportErrorMessage(const std::string& message); + void CertificateRequestSent(const base::UnguessableToken& request_id, + const network::ResourceRequest& request); + void CertificateResponseReceived(const base::UnguessableToken& request_id, + const GURL& url, + const network::ResourceResponseHead& head); + void CertificateRequestCompleted( + const base::UnguessableToken& request_id, + const network::URLLoaderCompletionStatus& status); + + void OnSignedExchangeReceived( + const base::Optional& header, + const net::SSLInfo* ssl_info); + + private: + const GURL outer_request_url_; + const network::ResourceResponseHead outer_response_; + const base::RepeatingCallback frame_tree_node_id_getter_; + const base::Optional devtools_navigation_token_; + const bool devtools_enabled_; + std::vector error_messages_; + + DISALLOW_COPY_AND_ASSIGN(SignedExchangeDevToolsProxy); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_DEVTOOLS_PROXY_H_ diff --git a/chromium/content/browser/web_package/signed_exchange_handler.cc b/chromium/content/browser/web_package/signed_exchange_handler.cc index d4e878f17b2..52e127a28e9 100644 --- a/chromium/content/browser/web_package/signed_exchange_handler.cc +++ b/chromium/content/browser/web_package/signed_exchange_handler.cc @@ -4,24 +4,23 @@ #include "content/browser/web_package/signed_exchange_handler.h" -#include "base/feature_list.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "content/browser/loader/merkle_integrity_source_stream.h" #include "content/browser/web_package/signed_exchange_cert_fetcher_factory.h" #include "content/browser/web_package/signed_exchange_certificate_chain.h" -#include "content/browser/web_package/signed_exchange_consts.h" +#include "content/browser/web_package/signed_exchange_devtools_proxy.h" #include "content/browser/web_package/signed_exchange_header.h" #include "content/browser/web_package/signed_exchange_signature_verifier.h" #include "content/browser/web_package/signed_exchange_utils.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" -#include "content/public/common/content_features.h" #include "content/public/common/url_loader_throttle.h" #include "mojo/public/cpp/system/string_data_pipe_producer.h" #include "net/base/io_buffer.h" +#include "net/base/net_errors.h" #include "net/cert/cert_status_flags.h" #include "net/cert/cert_verifier.h" #include "net/cert/x509_certificate.h" @@ -56,26 +55,6 @@ base::Time GetVerificationTime() { return base::Time::Now(); } -void AddErrorMessageToConsole(int frame_tree_node_id, - const std::string& message) { - // |frame_tree_node_id| is -1 for unittests. - if (frame_tree_node_id == -1) - return; - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::BindOnce(&AddErrorMessageToConsole, frame_tree_node_id, message)); - return; - } - DCHECK_CURRENTLY_ON(BrowserThread::UI); - WebContents* web_contents = - WebContents::FromFrameTreeNodeId(frame_tree_node_id); - if (!web_contents) - return; - web_contents->GetMainFrame()->AddMessageToConsole( - content::CONSOLE_MESSAGE_LEVEL_ERROR, message); -} - } // namespace // static @@ -96,7 +75,7 @@ SignedExchangeHandler::SignedExchangeHandler( ExchangeHeadersCallback headers_callback, std::unique_ptr cert_fetcher_factory, scoped_refptr request_context_getter, - int frame_tree_node_id) + std::unique_ptr devtools_proxy) : headers_callback_(std::move(headers_callback)), source_(std::move(body)), cert_fetcher_factory_(std::move(cert_fetcher_factory)), @@ -104,45 +83,42 @@ SignedExchangeHandler::SignedExchangeHandler( net_log_(net::NetLogWithSource::Make( request_context_getter_->GetURLRequestContext()->net_log(), net::NetLogSourceType::CERT_VERIFIER_JOB)), - error_message_callback_( - base::BindRepeating(&AddErrorMessageToConsole, frame_tree_node_id)), + devtools_proxy_(std::move(devtools_proxy)), weak_factory_(this) { - DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange)); + DCHECK(signed_exchange_utils::IsSignedExchangeHandlingEnabled()); TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), "SignedExchangeHandler::SignedExchangeHandler"); - base::Optional content_type_version_param; - if (!SignedExchangeHeaderParser::GetVersionParamFromContentType( - content_type, &content_type_version_param) || - !content_type_version_param || *content_type_version_param != "b0") { + if (!SignedExchangeHeaderParser::GetVersionParamFromContentType(content_type, + &version_) || + (version_ != SignedExchangeVersion::kB0 && + version_ != SignedExchangeVersion::kB1)) { base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&SignedExchangeHandler::RunErrorCallback, - weak_factory_.GetWeakPtr(), net::ERR_FAILED)); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHandler::SignedExchangeHandler", error_message_callback_, + weak_factory_.GetWeakPtr(), + net::ERR_INVALID_SIGNED_EXCHANGE)); + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::SignedExchangeHandler", base::StringPrintf("Unsupported version of the content type. Currentry " "content type must be " - "\"application/signed-exchange;v=b0\". But the " + "\"application/signed-exchange;v={b0,b1}\". But the " "response content type was \"%s\"", content_type.c_str())); return; } // Triggering the read (asynchronously) for the encoded header length. - SetupBuffers(SignedExchangeHeader::kEncodedHeaderLengthInBytes); + SetupBuffers(SignedExchangeHeader::kEncodedLengthInBytes); base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&SignedExchangeHandler::DoHeaderLoop, weak_factory_.GetWeakPtr())); TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("loading"), - "SignedExchangeCertFetcher::SignedExchangeHandler"); + "SignedExchangeHandler::SignedExchangeHandler"); } SignedExchangeHandler::~SignedExchangeHandler() = default; -SignedExchangeHandler::SignedExchangeHandler() - : error_message_callback_(base::BindRepeating(&AddErrorMessageToConsole, - -1 /* frame_tree_node_id */)), - weak_factory_(this) {} +SignedExchangeHandler::SignedExchangeHandler() : weak_factory_(this) {} void SignedExchangeHandler::SetupBuffers(size_t size) { header_buf_ = base::MakeRefCounted(size); @@ -165,18 +141,18 @@ void SignedExchangeHandler::DidReadHeader(bool completed_syncly, int result) { TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), "SignedExchangeHandler::DidReadHeader"); if (result < 0) { - RunErrorCallback(static_cast(result)); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHandler::DidReadHeader", error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::DidReadHeader", base::StringPrintf("Error reading body stream. result: %d", result)); + RunErrorCallback(static_cast(result)); return; } if (result == 0) { - RunErrorCallback(net::ERR_FAILED); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHandler::DidReadHeader", error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::DidReadHeader", "Stream ended while reading signed exchange header."); + RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE); return; } @@ -185,11 +161,11 @@ void SignedExchangeHandler::DidReadHeader(bool completed_syncly, int result) { switch (state_) { case State::kReadingHeadersLength: if (!ParseHeadersLength()) - RunErrorCallback(net::ERR_FAILED); + RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE); break; case State::kReadingHeaders: if (!ParseHeadersAndFetchCertificate()) - RunErrorCallback(net::ERR_FAILED); + RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE); break; default: NOTREACHED(); @@ -213,20 +189,20 @@ void SignedExchangeHandler::DidReadHeader(bool completed_syncly, int result) { DoHeaderLoop(); } TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("loading"), - "SignedExchangeCertFetcher::DidReadHeader"); + "SignedExchangeHandler::DidReadHeader"); } bool SignedExchangeHandler::ParseHeadersLength() { TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), - "SignedExchangeHandler::ParseHeadersLength"); + "SignedExchangeHandler::ParseEncodedLength"); DCHECK_EQ(state_, State::kReadingHeadersLength); - headers_length_ = SignedExchangeHeader::ParseHeadersLength( + headers_length_ = SignedExchangeHeader::ParseEncodedLength( base::make_span(reinterpret_cast(header_buf_->data()), - SignedExchangeHeader::kEncodedHeaderLengthInBytes)); + SignedExchangeHeader::kEncodedLengthInBytes)); if (headers_length_ == 0 || headers_length_ > kMaxHeadersCBORLength) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHandler::ParseHeadersLength", error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::ParseEncodedLength", base::StringPrintf("Invalid CBOR header length: %zu", headers_length_)); return false; } @@ -235,7 +211,7 @@ bool SignedExchangeHandler::ParseHeadersLength() { SetupBuffers(headers_length_); state_ = State::kReadingHeaders; TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("loading"), - "SignedExchangeCertFetcher::ParseHeadersLength"); + "SignedExchangeHandler::ParseEncodedLength"); return true; } @@ -247,13 +223,14 @@ bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() { header_ = SignedExchangeHeader::Parse( base::make_span(reinterpret_cast(header_buf_->data()), headers_length_), - error_message_callback_); + devtools_proxy_.get()); header_read_buf_ = nullptr; header_buf_ = nullptr; if (!header_) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::ParseHeadersAndFetchCertificate", - error_message_callback_, "Failed to parse SignedExchange header."); + "Failed to parse SignedExchange header."); return false; } @@ -261,14 +238,15 @@ bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() { // TODO(https://crbug.com/819467): When we will support ed25519Key, |cert_url| // may be empty. DCHECK(cert_url.is_valid()); + DCHECK(version_.has_value()); DCHECK(cert_fetcher_factory_); cert_fetcher_ = std::move(cert_fetcher_factory_) ->CreateFetcherAndStart( - cert_url, false, + cert_url, false, *version_, base::BindOnce(&SignedExchangeHandler::OnCertReceived, base::Unretained(this)), - error_message_callback_); + devtools_proxy_.get()); state_ = State::kFetchingCertificate; TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("loading"), @@ -278,6 +256,8 @@ bool SignedExchangeHandler::ParseHeadersAndFetchCertificate() { void SignedExchangeHandler::RunErrorCallback(net::Error error) { DCHECK_NE(state_, State::kHeadersCallbackCalled); + if (devtools_proxy_) + devtools_proxy_->OnSignedExchangeReceived(header_, nullptr); std::move(headers_callback_) .Run(error, GURL(), std::string(), network::ResourceResponseHead(), nullptr); @@ -290,30 +270,30 @@ void SignedExchangeHandler::OnCertReceived( "SignedExchangeHandler::OnCertReceived"); DCHECK_EQ(state_, State::kFetchingCertificate); if (!cert_chain) { - RunErrorCallback(net::ERR_FAILED); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHandler::OnCertReceived", error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::OnCertReceived", "Failed to fetch the certificate."); + RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE); return; } if (SignedExchangeSignatureVerifier::Verify(*header_, cert_chain->cert(), GetVerificationTime(), - error_message_callback_) != + devtools_proxy_.get()) != SignedExchangeSignatureVerifier::Result::kSuccess) { - RunErrorCallback(net::ERR_FAILED); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHandler::OnCertReceived", error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::OnCertReceived", "Failed to verify the signed exchange header."); + RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE); return; } net::URLRequestContext* request_context = request_context_getter_->GetURLRequestContext(); if (!request_context) { - RunErrorCallback(net::ERR_CONTEXT_SHUT_DOWN); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHandler::OnCertReceived", error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::OnCertReceived", "No request context available."); + RunErrorCallback(net::ERR_CONTEXT_SHUT_DOWN); return; } @@ -325,13 +305,10 @@ void SignedExchangeHandler::OnCertReceived( net::CertVerifier* cert_verifier = g_cert_verifier_for_testing ? g_cert_verifier_for_testing : request_context->cert_verifier(); - // TODO(https://crbug.com/815024): Get the OCSP response from the - // “status_request” extension of the main-certificate, and check the lifetime - // (nextUpdate - thisUpdate) is less than 7 days. int result = cert_verifier->Verify( net::CertVerifier::RequestParams( unverified_cert_chain_->cert(), header_->request_url().host(), - config.GetCertVerifyFlags(), std::string() /* ocsp_response */, + config.GetCertVerifyFlags(), unverified_cert_chain_->ocsp(), net::CertificateList()), net::SSLConfigService::GetCRLSet().get(), &cert_verify_result_, base::BindRepeating(&SignedExchangeHandler::OnCertVerifyComplete, @@ -346,15 +323,48 @@ void SignedExchangeHandler::OnCertReceived( "SignedExchangeHandler::OnCertReceived"); } +bool SignedExchangeHandler::CheckOCSPStatus( + const net::OCSPVerifyResult& ocsp_result) { + // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#cross-origin-trust + // Step 6.3 Validate that main-certificate has an ocsp property (Section 3.3) + // with a valid OCSP response whose lifetime (nextUpdate - thisUpdate) is less + // than 7 days ([RFC6960]). [spec text] + // + // OCSP verification is done in CertVerifier::Verify(), so we just check the + // result here. + + // The b0 implementation checkpoint has no OCSP check. + if (version_ == SignedExchangeVersion::kB0) + return true; + + if (ocsp_result.response_status != net::OCSPVerifyResult::PROVIDED || + ocsp_result.revocation_status != net::OCSPRevocationStatus::GOOD) + return false; + + return true; +} + void SignedExchangeHandler::OnCertVerifyComplete(int result) { TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), "SignedExchangeHandler::OnCertVerifyComplete"); if (result != net::OK) { - RunErrorCallback(static_cast(result)); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHandler::OnCertVerifyComplete", error_message_callback_, - base::StringPrintf("Certificate verification error: %d", result)); + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::OnCertVerifyComplete", + base::StringPrintf("Certificate verification error: %s", + net::ErrorToShortString(result).c_str())); + RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE); + return; + } + + if (!CheckOCSPStatus(cert_verify_result_.ocsp_result)) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::OnCertVerifyComplete", + base::StringPrintf( + "OCSP check failed. response status: %d, revocation status: %d", + cert_verify_result_.ocsp_result.response_status, + cert_verify_result_.ocsp_result.revocation_status)); + RunErrorCallback(static_cast(net::ERR_FAILED)); return; } @@ -375,10 +385,10 @@ void SignedExchangeHandler::OnCertVerifyComplete(int result) { std::string mi_header_value; if (!response_head.headers->EnumerateHeader(nullptr, kMiHeader, &mi_header_value)) { - RunErrorCallback(net::ERR_FAILED); - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHandler::OnCertVerifyComplete", error_message_callback_, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy_.get(), "SignedExchangeHandler::OnCertVerifyComplete", "Signed exchange has no MI: header"); + RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE); return; } auto mi_stream = std::make_unique( @@ -395,6 +405,10 @@ void SignedExchangeHandler::OnCertVerifyComplete(int result) { ssl_info.is_fatal_cert_error = net::IsCertStatusError(ssl_info.cert_status) && !net::IsCertStatusMinorError(ssl_info.cert_status); + + if (devtools_proxy_) + devtools_proxy_->OnSignedExchangeReceived(header_, &ssl_info); + response_head.ssl_info = std::move(ssl_info); // TODO(https://crbug.com/815025): Verify the Certificate Transparency status. std::move(headers_callback_) diff --git a/chromium/content/browser/web_package/signed_exchange_handler.h b/chromium/content/browser/web_package/signed_exchange_handler.h index 84f9fe4bd6f..d1df8d39d5b 100644 --- a/chromium/content/browser/web_package/signed_exchange_handler.h +++ b/chromium/content/browser/web_package/signed_exchange_handler.h @@ -10,8 +10,8 @@ #include "base/callback.h" #include "base/optional.h" #include "base/time/time.h" +#include "content/browser/web_package/signed_exchange_consts.h" #include "content/browser/web_package/signed_exchange_header.h" -#include "content/browser/web_package/signed_exchange_utils.h" #include "content/common/content_export.h" #include "mojo/public/cpp/system/data_pipe.h" #include "net/base/completion_callback.h" @@ -28,6 +28,7 @@ class CertVerifyResult; class DrainableIOBuffer; class SourceStream; class URLRequestContextGetter; +struct OCSPVerifyResult; } // namespace net namespace network { @@ -39,6 +40,7 @@ namespace content { class SignedExchangeCertFetcher; class SignedExchangeCertFetcherFactory; class SignedExchangeCertificateChain; +class SignedExchangeDevToolsProxy; // IMPORTANT: Currenly SignedExchangeHandler partially implements the verifying // logic. @@ -70,7 +72,7 @@ class CONTENT_EXPORT SignedExchangeHandler { ExchangeHeadersCallback headers_callback, std::unique_ptr cert_fetcher_factory, scoped_refptr request_context_getter, - int frame_tree_node_id); + std::unique_ptr devtools_proxy); ~SignedExchangeHandler(); protected: @@ -94,8 +96,10 @@ class CONTENT_EXPORT SignedExchangeHandler { void OnCertReceived( std::unique_ptr cert_chain); void OnCertVerifyComplete(int result); + bool CheckOCSPStatus(const net::OCSPVerifyResult& ocsp_result); ExchangeHeadersCallback headers_callback_; + base::Optional version_; std::unique_ptr source_; State state_ = State::kReadingHeadersLength; @@ -123,7 +127,7 @@ class CONTENT_EXPORT SignedExchangeHandler { // with Network Service. net::NetLogWithSource net_log_; - signed_exchange_utils::LogCallback error_message_callback_; + std::unique_ptr devtools_proxy_; base::WeakPtrFactory weak_factory_; diff --git a/chromium/content/browser/web_package/signed_exchange_handler_unittest.cc b/chromium/content/browser/web_package/signed_exchange_handler_unittest.cc index fbcb5b29a0b..46b4a38736b 100644 --- a/chromium/content/browser/web_package/signed_exchange_handler_unittest.cc +++ b/chromium/content/browser/web_package/signed_exchange_handler_unittest.cc @@ -12,7 +12,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/scoped_task_environment.h" #include "content/browser/web_package/signed_exchange_cert_fetcher_factory.h" -#include "content/browser/web_package/signed_exchange_utils.h" +#include "content/browser/web_package/signed_exchange_devtools_proxy.h" #include "content/public/common/content_features.h" #include "content/public/common/content_paths.h" #include "net/base/io_buffer.h" @@ -22,8 +22,15 @@ #include "net/test/cert_test_util.h" #include "net/test/test_data_directory.h" #include "net/url_request/url_request_test_util.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using testing::_; +using testing::Property; +using testing::Return; +using testing::SetArgPointee; +using testing::Truly; + namespace content { namespace { @@ -33,7 +40,7 @@ const int kOutputBufferSize = 4096; std::string GetTestFileContents(base::StringPiece name) { base::FilePath path; - PathService::Get(content::DIR_TEST_DATA, &path); + base::PathService::Get(content::DIR_TEST_DATA, &path); path = path.AppendASCII("htxg").AppendASCII(name); std::string contents; @@ -60,12 +67,13 @@ class MockSignedExchangeCertFetcherFactory std::unique_ptr CreateFetcherAndStart( const GURL& cert_url, bool force_fetch, + SignedExchangeVersion version, SignedExchangeCertFetcher::CertificateCallback callback, - const signed_exchange_utils::LogCallback& error_message_callback) - override { + SignedExchangeDevToolsProxy* devtools_proxy) override { EXPECT_EQ(cert_url, expected_cert_url_); - auto cert_chain = SignedExchangeCertificateChain::Parse(cert_str_); + auto cert_chain = SignedExchangeCertificateChain::Parse( + version, base::as_bytes(base::make_span(cert_str_)), devtools_proxy); EXPECT_TRUE(cert_chain); base::SequencedTaskRunnerHandle::Get()->PostTask( @@ -78,18 +86,31 @@ class MockSignedExchangeCertFetcherFactory std::string cert_str_; }; +class GMockCertVerifier : public net::CertVerifier { + public: + MOCK_METHOD6(Verify, + int(const net::CertVerifier::RequestParams& params, + net::CRLSet* crl_set, + net::CertVerifyResult* verify_result, + const net::CompletionCallback& callback, + std::unique_ptr* out_req, + const net::NetLogWithSource& net_log)); +}; + } // namespace class SignedExchangeHandlerTest : public ::testing::TestWithParam { public: SignedExchangeHandlerTest() - : mock_cert_verifier_(std::make_unique()), - request_initiator_( + : request_initiator_( url::Origin::Create(GURL("https://htxg.example.com/test.htxg"))) {} + virtual std::string ContentType() { + return "application/signed-exchange;v=b0"; + } + void SetUp() override { - SignedExchangeHandler::SetCertVerifierForTesting(mock_cert_verifier_.get()); SignedExchangeHandler::SetVerificationTimeForTesting( base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(kSignatureHeaderDate)); @@ -104,11 +125,11 @@ class SignedExchangeHandlerTest request_context_getter_ = new net::TestURLRequestContextGetter( scoped_task_environment_.GetMainThreadTaskRunner()); handler_ = std::make_unique( - "application/signed-exchange;v=b0", std::move(source), + ContentType(), std::move(source), base::BindOnce(&SignedExchangeHandlerTest::OnHeaderFound, base::Unretained(this)), std::move(cert_fetcher_factory), request_context_getter_, - -1 /* frame_tree_node_id */); + nullptr /* devtools_proxy */); } void TearDown() override { @@ -117,6 +138,11 @@ class SignedExchangeHandlerTest base::Optional()); } + void SetCertVerifier(std::unique_ptr cert_verifier) { + cert_verifier_ = std::move(cert_verifier); + SignedExchangeHandler::SetCertVerifierForTesting(cert_verifier_.get()); + } + // Reads from |stream| until an error occurs or the EOF is reached. // When an error occurs, returns the net error code. When an EOF is reached, // returns the number of bytes read. If |output| is non-null, appends data @@ -166,7 +192,7 @@ class SignedExchangeHandlerTest protected: MockSignedExchangeCertFetcherFactory* mock_cert_fetcher_factory_; - std::unique_ptr mock_cert_verifier_; + std::unique_ptr cert_verifier_; net::MockSourceStream* source_; std::unique_ptr handler_; @@ -193,13 +219,19 @@ class SignedExchangeHandlerTest std::unique_ptr payload_stream_; }; +class SignedExchangeHandlerB1Test : public SignedExchangeHandlerTest { + std::string ContentType() override { + return "application/signed-exchange;v=b1"; + } +}; + TEST_P(SignedExchangeHandlerTest, Empty) { source_->AddReadResult(nullptr, 0, net::OK, GetParam()); WaitForHeader(); ASSERT_TRUE(read_header()); - EXPECT_EQ(net::ERR_FAILED, error()); + EXPECT_EQ(net::ERR_INVALID_SIGNED_EXCHANGE, error()); } TEST_P(SignedExchangeHandlerTest, Simple) { @@ -214,8 +246,10 @@ TEST_P(SignedExchangeHandlerTest, Simple) { net::CertVerifyResult dummy_result; dummy_result.verified_cert = original_cert; dummy_result.cert_status = net::OK; - mock_cert_verifier_->AddResultForCertAndHost(original_cert, "*.example.org", - dummy_result, net::OK); + auto mock_cert_verifier = std::make_unique(); + mock_cert_verifier->AddResultForCertAndHost(original_cert, "*.example.org", + dummy_result, net::OK); + SetCertVerifier(std::move(mock_cert_verifier)); std::string contents = GetTestFileContents("test.example.org_test.htxg"); source_->AddReadResult(contents.data(), contents.size(), net::OK, GetParam()); @@ -248,7 +282,10 @@ TEST_P(SignedExchangeHandlerTest, MimeType) { GURL("https://cert.example.org/cert.msg"), GetTestFileContents("wildcard_example.org.public.pem.msg")); - mock_cert_verifier_->set_default_result(net::OK); + auto mock_cert_verifier = std::make_unique(); + mock_cert_verifier->set_default_result(net::OK); + SetCertVerifier(std::move(mock_cert_verifier)); + std::string contents = GetTestFileContents("test.example.org_hello.txt.htxg"); source_->AddReadResult(contents.data(), contents.size(), net::OK, GetParam()); source_->AddReadResult(nullptr, 0, net::OK, GetParam()); @@ -277,7 +314,7 @@ TEST_P(SignedExchangeHandlerTest, ParseError) { WaitForHeader(); ASSERT_TRUE(read_header()); - EXPECT_EQ(net::ERR_FAILED, error()); + EXPECT_EQ(net::ERR_INVALID_SIGNED_EXCHANGE, error()); } TEST_P(SignedExchangeHandlerTest, TruncatedInHeader) { @@ -289,7 +326,7 @@ TEST_P(SignedExchangeHandlerTest, TruncatedInHeader) { WaitForHeader(); ASSERT_TRUE(read_header()); - EXPECT_EQ(net::ERR_FAILED, error()); + EXPECT_EQ(net::ERR_INVALID_SIGNED_EXCHANGE, error()); } TEST_P(SignedExchangeHandlerTest, CertSha256Mismatch) { @@ -302,7 +339,9 @@ TEST_P(SignedExchangeHandlerTest, CertSha256Mismatch) { // Set the default result of MockCertVerifier to OK, to check that the // verification of SignedExchange must fail even if the certificate is valid. - mock_cert_verifier_->set_default_result(net::OK); + auto mock_cert_verifier = std::make_unique(); + mock_cert_verifier->set_default_result(net::OK); + SetCertVerifier(std::move(mock_cert_verifier)); std::string contents = GetTestFileContents("test.example.org_test.htxg"); source_->AddReadResult(contents.data(), contents.size(), net::OK, GetParam()); @@ -311,7 +350,7 @@ TEST_P(SignedExchangeHandlerTest, CertSha256Mismatch) { WaitForHeader(); ASSERT_TRUE(read_header()); - EXPECT_EQ(net::ERR_FAILED, error()); + EXPECT_EQ(net::ERR_INVALID_SIGNED_EXCHANGE, error()); // Drain the MockSourceStream, otherwise its destructer causes DCHECK failure. ReadStream(source_, nullptr); } @@ -328,8 +367,10 @@ TEST_P(SignedExchangeHandlerTest, VerifyCertFailure) { net::CertVerifyResult dummy_result; dummy_result.verified_cert = original_cert; dummy_result.cert_status = net::OK; - mock_cert_verifier_->AddResultForCertAndHost(original_cert, "*.example.org", - dummy_result, net::OK); + auto mock_cert_verifier = std::make_unique(); + mock_cert_verifier->AddResultForCertAndHost(original_cert, "*.example.org", + dummy_result, net::OK); + SetCertVerifier(std::move(mock_cert_verifier)); // The certificate is for "*.example.com". But the request URL of the htxg // file is "https://test.example.com/test/". So the certification verification @@ -342,14 +383,243 @@ TEST_P(SignedExchangeHandlerTest, VerifyCertFailure) { WaitForHeader(); ASSERT_TRUE(read_header()); - EXPECT_EQ(net::ERR_CERT_INVALID, error()); + EXPECT_EQ(net::ERR_INVALID_SIGNED_EXCHANGE, error()); + // Drain the MockSourceStream, otherwise its destructer causes DCHECK failure. + ReadStream(source_, nullptr); +} + +TEST_P(SignedExchangeHandlerB1Test, Simple) { + mock_cert_fetcher_factory_->ExpectFetch( + GURL("https://cert.example.org/cert.msg"), + GetTestFileContents("wildcard_example.org.public.pem.cbor")); + + // Make the MockCertVerifier treat the certificate "wildcard.pem" as valid for + // "*.example.org". + scoped_refptr original_cert = + LoadCertificate("wildcard.pem"); + net::CertVerifyResult dummy_result; + dummy_result.verified_cert = original_cert; + dummy_result.cert_status = net::OK; + dummy_result.ocsp_result.response_status = net::OCSPVerifyResult::PROVIDED; + dummy_result.ocsp_result.revocation_status = net::OCSPRevocationStatus::GOOD; + auto mock_cert_verifier = std::make_unique(); + mock_cert_verifier->AddResultForCertAndHost(original_cert, "*.example.org", + dummy_result, net::OK); + SetCertVerifier(std::move(mock_cert_verifier)); + + std::string contents = GetTestFileContents("test.example.org_test.htxg"); + source_->AddReadResult(contents.data(), contents.size(), net::OK, GetParam()); + source_->AddReadResult(nullptr, 0, net::OK, GetParam()); + + WaitForHeader(); + + ASSERT_TRUE(read_header()); + EXPECT_EQ(net::OK, error()); + EXPECT_EQ(200, resource_response().headers->response_code()); + EXPECT_EQ("text/html", resource_response().mime_type); + EXPECT_EQ("utf-8", resource_response().charset); + EXPECT_FALSE(resource_response().load_timing.request_start_time.is_null()); + EXPECT_FALSE(resource_response().load_timing.request_start.is_null()); + EXPECT_FALSE(resource_response().load_timing.send_start.is_null()); + EXPECT_FALSE(resource_response().load_timing.send_end.is_null()); + EXPECT_FALSE(resource_response().load_timing.receive_headers_end.is_null()); + + std::string payload; + int rv = ReadPayloadStream(&payload); + + std::string expected_payload = GetTestFileContents("test.html"); + + EXPECT_EQ(payload, expected_payload); + EXPECT_EQ(rv, static_cast(expected_payload.size())); +} + +TEST_P(SignedExchangeHandlerB1Test, OCSPNotChecked) { + mock_cert_fetcher_factory_->ExpectFetch( + GURL("https://cert.example.org/cert.msg"), + GetTestFileContents("wildcard_example.org.public.pem.cbor")); + + // Make the MockCertVerifier treat the certificate "wildcard.pem" as valid for + // "*.example.org". + scoped_refptr original_cert = + LoadCertificate("wildcard.pem"); + net::CertVerifyResult dummy_result; + dummy_result.verified_cert = original_cert; + dummy_result.cert_status = net::OK; + dummy_result.ocsp_result.response_status = net::OCSPVerifyResult::NOT_CHECKED; + auto mock_cert_verifier = std::make_unique(); + mock_cert_verifier->AddResultForCertAndHost(original_cert, "*.example.org", + dummy_result, net::OK); + SetCertVerifier(std::move(mock_cert_verifier)); + + std::string contents = GetTestFileContents("test.example.org_test.htxg"); + source_->AddReadResult(contents.data(), contents.size(), net::OK, GetParam()); + source_->AddReadResult(nullptr, 0, net::OK, GetParam()); + + WaitForHeader(); + + ASSERT_TRUE(read_header()); + EXPECT_EQ(net::ERR_FAILED, error()); + // Drain the MockSourceStream, otherwise its destructer causes DCHECK failure. + ReadStream(source_, nullptr); +} + +TEST_P(SignedExchangeHandlerB1Test, OCSPNotProvided) { + mock_cert_fetcher_factory_->ExpectFetch( + GURL("https://cert.example.org/cert.msg"), + GetTestFileContents("wildcard_example.org.public.pem.cbor")); + + // Make the MockCertVerifier treat the certificate "wildcard.pem" as valid for + // "*.example.org". + scoped_refptr original_cert = + LoadCertificate("wildcard.pem"); + net::CertVerifyResult dummy_result; + dummy_result.verified_cert = original_cert; + dummy_result.cert_status = net::OK; + dummy_result.ocsp_result.response_status = net::OCSPVerifyResult::MISSING; + auto mock_cert_verifier = std::make_unique(); + mock_cert_verifier->AddResultForCertAndHost(original_cert, "*.example.org", + dummy_result, net::OK); + SetCertVerifier(std::move(mock_cert_verifier)); + + std::string contents = GetTestFileContents("test.example.org_test.htxg"); + source_->AddReadResult(contents.data(), contents.size(), net::OK, GetParam()); + source_->AddReadResult(nullptr, 0, net::OK, GetParam()); + + WaitForHeader(); + + ASSERT_TRUE(read_header()); + EXPECT_EQ(net::ERR_FAILED, error()); + // Drain the MockSourceStream, otherwise its destructer causes DCHECK failure. + ReadStream(source_, nullptr); +} + +TEST_P(SignedExchangeHandlerB1Test, OCSPInvalid) { + mock_cert_fetcher_factory_->ExpectFetch( + GURL("https://cert.example.org/cert.msg"), + GetTestFileContents("wildcard_example.org.public.pem.cbor")); + + // Make the MockCertVerifier treat the certificate "wildcard.pem" as valid for + // "*.example.org". + scoped_refptr original_cert = + LoadCertificate("wildcard.pem"); + net::CertVerifyResult dummy_result; + dummy_result.verified_cert = original_cert; + dummy_result.cert_status = net::OK; + dummy_result.ocsp_result.response_status = + net::OCSPVerifyResult::INVALID_DATE; + auto mock_cert_verifier = std::make_unique(); + mock_cert_verifier->AddResultForCertAndHost(original_cert, "*.example.org", + dummy_result, net::OK); + SetCertVerifier(std::move(mock_cert_verifier)); + + std::string contents = GetTestFileContents("test.example.org_test.htxg"); + source_->AddReadResult(contents.data(), contents.size(), net::OK, GetParam()); + source_->AddReadResult(nullptr, 0, net::OK, GetParam()); + + WaitForHeader(); + + ASSERT_TRUE(read_header()); + EXPECT_EQ(net::ERR_FAILED, error()); + // Drain the MockSourceStream, otherwise its destructer causes DCHECK failure. + ReadStream(source_, nullptr); +} + +TEST_P(SignedExchangeHandlerB1Test, OCSPRevoked) { + mock_cert_fetcher_factory_->ExpectFetch( + GURL("https://cert.example.org/cert.msg"), + GetTestFileContents("wildcard_example.org.public.pem.cbor")); + + // Make the MockCertVerifier treat the certificate "wildcard.pem" as valid for + // "*.example.org". + scoped_refptr original_cert = + LoadCertificate("wildcard.pem"); + net::CertVerifyResult dummy_result; + dummy_result.verified_cert = original_cert; + dummy_result.cert_status = net::OK; + dummy_result.ocsp_result.response_status = net::OCSPVerifyResult::PROVIDED; + dummy_result.ocsp_result.revocation_status = + net::OCSPRevocationStatus::REVOKED; + auto mock_cert_verifier = std::make_unique(); + mock_cert_verifier->AddResultForCertAndHost(original_cert, "*.example.org", + dummy_result, net::OK); + SetCertVerifier(std::move(mock_cert_verifier)); + + std::string contents = GetTestFileContents("test.example.org_test.htxg"); + source_->AddReadResult(contents.data(), contents.size(), net::OK, GetParam()); + source_->AddReadResult(nullptr, 0, net::OK, GetParam()); + + WaitForHeader(); + + ASSERT_TRUE(read_header()); + EXPECT_EQ(net::ERR_FAILED, error()); // Drain the MockSourceStream, otherwise its destructer causes DCHECK failure. ReadStream(source_, nullptr); } +// Test that fetching a signed exchange properly extracts and +// attempts to verify both the certificate and the OCSP response. +TEST_P(SignedExchangeHandlerB1Test, CertVerifierParams) { + mock_cert_fetcher_factory_->ExpectFetch( + GURL("https://cert.example.org/cert.msg"), + GetTestFileContents("wildcard_example.org.public.pem.cbor")); + + scoped_refptr original_cert = + LoadCertificate("wildcard.pem"); + net::CertVerifyResult fake_result; + fake_result.verified_cert = original_cert; + fake_result.cert_status = net::OK; + fake_result.ocsp_result.response_status = net::OCSPVerifyResult::PROVIDED; + fake_result.ocsp_result.revocation_status = net::OCSPRevocationStatus::GOOD; + + // "wildcard_example.org.public.pem.cbor" has this dummy data instead of a + // real OCSP response. + constexpr base::StringPiece kExpectedOCSPDer = "OCSP"; + + std::unique_ptr gmock_cert_verifier = + std::make_unique(); + EXPECT_CALL( + *gmock_cert_verifier, + Verify( + AllOf(Property(&net::CertVerifier::RequestParams::ocsp_response, + kExpectedOCSPDer), + Property( + &net::CertVerifier::RequestParams::certificate, + Truly([&original_cert]( + const scoped_refptr& cert) { + return original_cert->EqualsIncludingChain(cert.get()); + })), + Property(&net::CertVerifier::RequestParams::hostname, + "test.example.org")), + _ /* crl_set */, _ /* verify_result */, _ /* callback */, + _ /* out_req */, _ /* net_log */ + )) + .WillOnce(DoAll(SetArgPointee<2>(fake_result), Return(net::OK))); + SetCertVerifier(std::move(gmock_cert_verifier)); + + std::string contents = GetTestFileContents("test.example.org_test.htxg"); + source_->AddReadResult(contents.data(), contents.size(), net::OK, GetParam()); + source_->AddReadResult(nullptr, 0, net::OK, GetParam()); + + WaitForHeader(); + + ASSERT_TRUE(read_header()); + EXPECT_EQ(net::OK, error()); + std::string payload; + int rv = ReadPayloadStream(&payload); + std::string expected_payload = GetTestFileContents("test.html"); + + EXPECT_EQ(expected_payload, payload); + EXPECT_EQ(static_cast(expected_payload.size()), rv); +} + INSTANTIATE_TEST_CASE_P(SignedExchangeHandlerTests, SignedExchangeHandlerTest, ::testing::Values(net::MockSourceStream::SYNC, net::MockSourceStream::ASYNC)); +INSTANTIATE_TEST_CASE_P(SignedExchangeHandlerB1Tests, + SignedExchangeHandlerB1Test, + ::testing::Values(net::MockSourceStream::SYNC, + net::MockSourceStream::ASYNC)); + } // namespace content diff --git a/chromium/content/browser/web_package/signed_exchange_header.cc b/chromium/content/browser/web_package/signed_exchange_header.cc index a97e746c4eb..e0bfe678b2a 100644 --- a/chromium/content/browser/web_package/signed_exchange_header.cc +++ b/chromium/content/browser/web_package/signed_exchange_header.cc @@ -67,14 +67,13 @@ bool IsMethodCacheable(base::StringPiece method) { return method == "GET" || method == "HEAD" || method == "POST"; } -bool ParseRequestMap( - const cbor::CBORValue& value, - SignedExchangeHeader* out, - const signed_exchange_utils::LogCallback& error_message_callback) { +bool ParseRequestMap(const cbor::CBORValue& value, + SignedExchangeHeader* out, + SignedExchangeDevToolsProxy* devtools_proxy) { TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), "ParseRequestMap"); if (!value.is_map()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", base::StringPrintf( "Expected request map, got non-map type. Actual type: %d", static_cast(value.type()))); @@ -86,21 +85,20 @@ bool ParseRequestMap( auto url_iter = request_map.find( cbor::CBORValue(kUrlKey, cbor::CBORValue::Type::BYTE_STRING)); if (url_iter == request_map.end() || !url_iter->second.is_bytestring()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", ":url is not found or not a bytestring."); return false; } out->set_request_url(GURL(url_iter->second.GetBytestringAsString())); if (!out->request_url().is_valid()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, ":url is not a valid URL."); + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", ":url is not a valid URL."); return false; } if (out->request_url().has_ref()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, - ":url can't have a fragment."); + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", ":url can't have a fragment."); return false; } @@ -108,8 +106,8 @@ bool ParseRequestMap( cbor::CBORValue(kMethodKey, cbor::CBORValue::Type::BYTE_STRING)); if (method_iter == request_map.end() || !method_iter->second.is_bytestring()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", ":method is not found or not a bytestring."); return false; } @@ -119,8 +117,8 @@ bool ParseRequestMap( // [spec text] if (!net::HttpUtil::IsMethodSafe(method_str.as_string()) || !IsMethodCacheable(method_str)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", base::StringPrintf( "Request method is not safe or not cacheable. method: %s", method_str.as_string().c_str())); @@ -130,8 +128,8 @@ bool ParseRequestMap( for (const auto& it : request_map) { if (!it.first.is_bytestring() || !it.second.is_bytestring()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", "Non-bytestring value in the request map."); return false; } @@ -142,8 +140,8 @@ bool ParseRequestMap( // TODO(kouhei): Add spec ref here once // https://github.com/WICG/webpackage/issues/161 is resolved. if (name_str != base::ToLowerASCII(name_str)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", base::StringPrintf( "Request header name should be lower-cased. header name: %s", name_str.as_string().c_str())); @@ -153,8 +151,8 @@ bool ParseRequestMap( // 4. If exchange’s headers contain a stateful header field, as defined in // Section 4.1, return “invalid”. [spec text] if (IsStatefulRequestHeader(name_str)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", base::StringPrintf( "Exchange contains stateful request header. header name: %s", name_str.as_string().c_str())); @@ -166,14 +164,13 @@ bool ParseRequestMap( return true; } -bool ParseResponseMap( - const cbor::CBORValue& value, - SignedExchangeHeader* out, - const signed_exchange_utils::LogCallback& error_message_callback) { +bool ParseResponseMap(const cbor::CBORValue& value, + SignedExchangeHeader* out, + SignedExchangeDevToolsProxy* devtools_proxy) { TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), "ParseResponseMap"); if (!value.is_map()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseResponseMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseResponseMap", base::StringPrintf( "Expected request map, got non-map type. Actual type: %d", static_cast(value.type()))); @@ -185,8 +182,8 @@ bool ParseResponseMap( cbor::CBORValue(kStatusKey, cbor::CBORValue::Type::BYTE_STRING)); if (status_iter == response_map.end() || !status_iter->second.is_bytestring()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", ":status is not found or not a bytestring."); return false; } @@ -194,8 +191,8 @@ bool ParseResponseMap( status_iter->second.GetBytestringAsString(); int response_code; if (!base::StringToInt(response_code_str, &response_code)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", "Failed to parse status code to integer."); return false; } @@ -203,8 +200,8 @@ bool ParseResponseMap( for (const auto& it : response_map) { if (!it.first.is_bytestring() || !it.second.is_bytestring()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", "Non-bytestring value in the response map."); return false; } @@ -212,8 +209,8 @@ bool ParseResponseMap( if (name_str == kStatusKey) continue; if (!net::HttpUtil::IsValidHeaderName(name_str)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseResponseMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseResponseMap", base::StringPrintf("Invalid header name. header_name: %s", name_str.as_string().c_str())); return false; @@ -222,8 +219,8 @@ bool ParseResponseMap( // TODO(kouhei): Add spec ref here once // https://github.com/WICG/webpackage/issues/161 is resolved. if (name_str != base::ToLowerASCII(name_str)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseResponseMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseResponseMap", base::StringPrintf( "Response header name should be lower-cased. header_name: %s", name_str.as_string().c_str())); @@ -233,8 +230,8 @@ bool ParseResponseMap( // 4. If exchange’s headers contain a stateful header field, as defined in // Section 4.1, return “invalid”. [spec text] if (IsStatefulResponseHeader(name_str)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseResponseMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseResponseMap", base::StringPrintf( "Exchange contains stateful response header. header_name: %s", name_str.as_string().c_str())); @@ -243,13 +240,13 @@ bool ParseResponseMap( base::StringPiece value_str = it.second.GetBytestringAsString(); if (!net::HttpUtil::IsValidHeaderValue(value_str)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseRequestMap", error_message_callback, "Invalid header value."); + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseRequestMap", "Invalid header value."); return false; } if (!out->AddResponseHeader(name_str, value_str)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "ParseResponseMap", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "ParseResponseMap", base::StringPrintf("Duplicate header value. header_name: %s", name_str.as_string().c_str())); return false; @@ -262,12 +259,12 @@ bool ParseResponseMap( } // namespace -constexpr size_t SignedExchangeHeader::kEncodedHeaderLengthInBytes; +constexpr size_t SignedExchangeHeader::kEncodedLengthInBytes; // static -size_t SignedExchangeHeader::ParseHeadersLength( +size_t SignedExchangeHeader::ParseEncodedLength( base::span input) { - DCHECK_EQ(input.size(), SignedExchangeHeader::kEncodedHeaderLengthInBytes); + DCHECK_EQ(input.size(), SignedExchangeHeader::kEncodedLengthInBytes); return static_cast(input[0]) << 16 | static_cast(input[1]) << 8 | static_cast(input[2]); } @@ -275,21 +272,21 @@ size_t SignedExchangeHeader::ParseHeadersLength( // static base::Optional SignedExchangeHeader::Parse( base::span input, - const signed_exchange_utils::LogCallback& error_message_callback) { + SignedExchangeDevToolsProxy* devtools_proxy) { TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), "SignedExchangeHeader::Parse"); cbor::CBORReader::DecoderError error; base::Optional value = cbor::CBORReader::Read(input, &error); if (!value.has_value()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeader::Parse", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeader::Parse", base::StringPrintf("Failed to decode CBORValue. CBOR error: %s", cbor::CBORReader::ErrorCodeToString(error))); return base::nullopt; } if (!value->is_array()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeader::Parse", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeader::Parse", base::StringPrintf( "Expected top-level CBORValue to be an array. Actual type : %d", static_cast(value->type()))); @@ -299,8 +296,8 @@ base::Optional SignedExchangeHeader::Parse( const cbor::CBORValue::ArrayValue& top_level_array = value->GetArray(); constexpr size_t kTopLevelArraySize = 2; if (top_level_array.size() != kTopLevelArraySize) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeader::Parse", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeader::Parse", base::StringPrintf("Expected top-level array to have 2 elements. " "Actual element count: %" PRIuS, top_level_array.size())); @@ -309,33 +306,33 @@ base::Optional SignedExchangeHeader::Parse( SignedExchangeHeader ret; - if (!ParseRequestMap(top_level_array[0], &ret, error_message_callback)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeader::Parse", error_message_callback, + if (!ParseRequestMap(top_level_array[0], &ret, devtools_proxy)) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeader::Parse", "Failed to parse request map."); return base::nullopt; } - if (!ParseResponseMap(top_level_array[1], &ret, error_message_callback)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeader::Parse", error_message_callback, + if (!ParseResponseMap(top_level_array[1], &ret, devtools_proxy)) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeader::Parse", "Failed to parse response map."); return base::nullopt; } auto signature_iter = ret.response_headers_.find(kSignature); if (signature_iter == ret.response_headers_.end()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeader::Parse", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeader::Parse", "No signature header found."); return base::nullopt; } base::Optional> signatures = SignedExchangeHeaderParser::ParseSignature( - signature_iter->second, error_message_callback); + signature_iter->second, devtools_proxy); if (!signatures || signatures->empty()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeader::Parse", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeader::Parse", "Failed to parse signature."); return base::nullopt; } diff --git a/chromium/content/browser/web_package/signed_exchange_header.h b/chromium/content/browser/web_package/signed_exchange_header.h index 7c31a43806f..e544f17994e 100644 --- a/chromium/content/browser/web_package/signed_exchange_header.h +++ b/chromium/content/browser/web_package/signed_exchange_header.h @@ -14,7 +14,6 @@ #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "content/browser/web_package/signed_exchange_header_parser.h" -#include "content/browser/web_package/signed_exchange_utils.h" #include "content/common/content_export.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" @@ -22,17 +21,18 @@ namespace content { +class SignedExchangeDevToolsProxy; + // SignedExchangeHeader contains all information captured in signed exchange // envelope but the payload. // https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html class CONTENT_EXPORT SignedExchangeHeader { public: - static constexpr size_t kEncodedHeaderLengthInBytes = 3; - // Parse big-endian encoded length of the following CBOR-encoded - // signed exchange header. + static constexpr size_t kEncodedLengthInBytes = 3; + // Parse encoded length of the variable-length field in the signed exchange. // Note: |input| must be pointing to a valid memory address that has at least - // |kEncodedHeaderLengthInBytes|. - static size_t ParseHeadersLength(base::span input); + // |kEncodedLengthInBytes|. + static size_t ParseEncodedLength(base::span input); using HeaderMap = std::map; @@ -43,7 +43,7 @@ class CONTENT_EXPORT SignedExchangeHeader { // https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html#cross-origin-trust static base::Optional Parse( base::span input, - const signed_exchange_utils::LogCallback& error_message_callback); + SignedExchangeDevToolsProxy* devtools_proxy); SignedExchangeHeader(); SignedExchangeHeader(const SignedExchangeHeader&); SignedExchangeHeader(SignedExchangeHeader&&); diff --git a/chromium/content/browser/web_package/signed_exchange_header_parser.cc b/chromium/content/browser/web_package/signed_exchange_header_parser.cc index 310d29f4450..97cc0a4c7e3 100644 --- a/chromium/content/browser/web_package/signed_exchange_header_parser.cc +++ b/chromium/content/browser/web_package/signed_exchange_header_parser.cc @@ -195,7 +195,7 @@ class StructuredHeaderParser { base::Optional> SignedExchangeHeaderParser::ParseSignature( base::StringPiece signature_str, - const signed_exchange_utils::LogCallback& error_message_callback) { + SignedExchangeDevToolsProxy* devtools_proxy) { TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), "SignedExchangeHeaderParser::ParseSignature"); @@ -203,8 +203,8 @@ SignedExchangeHeaderParser::ParseSignature( std::vector values; parser.ParseParameterisedLabelList(&values); if (!parser.ParsedSuccessfully()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeaderParser::ParseSignature", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeaderParser::ParseSignature", "Failed to parse signature header."); return base::nullopt; } @@ -215,35 +215,35 @@ SignedExchangeHeaderParser::ParseSignature( signatures.push_back(Signature()); Signature& sig = signatures.back(); sig.label = value.label; - sig.sig = value.params["sig"]; + sig.sig = value.params[kSig]; if (sig.sig.empty()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeaderParser::ParseSignature", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeaderParser::ParseSignature", "'sig' parameter is not set,"); return base::nullopt; } - sig.integrity = value.params["integrity"]; + sig.integrity = value.params[kIntegrity]; if (sig.integrity.empty()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeaderParser::ParseSignature", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeaderParser::ParseSignature", "'integrity' parameter is not set."); return base::nullopt; } - sig.cert_url = GURL(value.params["certUrl"]); + sig.cert_url = GURL(value.params[kCertUrl]); if (!sig.cert_url.is_valid() || sig.cert_url.has_ref()) { // TODO(https://crbug.com/819467) : When we will support "ed25519Key", the // params may not have "certUrl". - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeaderParser::ParseSignature", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeaderParser::ParseSignature", "'certUrl' parameter is not a valid URL."); return base::nullopt; } - const std::string cert_sha256_string = value.params["certSha256"]; + const std::string cert_sha256_string = value.params[kCertSha256Key]; if (cert_sha256_string.size() != crypto::kSHA256Length) { // TODO(https://crbug.com/819467) : When we will support "ed25519Key", the // params may not have "certSha256". - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeaderParser::ParseSignature", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeaderParser::ParseSignature", "'certSha256' parameter is not a SHA-256 digest."); return base::nullopt; } @@ -252,28 +252,28 @@ SignedExchangeHeaderParser::ParseSignature( sig.cert_sha256 = std::move(cert_sha256); // TODO(https://crbug.com/819467): Support ed25519key. // sig.ed25519_key = value.params["ed25519Key"]; - sig.validity_url = GURL(value.params["validityUrl"]); + sig.validity_url = GURL(value.params[kValidityUrlKey]); if (!sig.validity_url.is_valid()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeaderParser::ParseSignature", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeaderParser::ParseSignature", "'validityUrl' parameter is not a valid URL."); return base::nullopt; } if (sig.validity_url.has_ref()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeaderParser::ParseSignature", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeaderParser::ParseSignature", "'validityUrl' parameter can't have a fragment."); return base::nullopt; } - if (!base::StringToUint64(value.params["date"], &sig.date)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeaderParser::ParseSignature", error_message_callback, + if (!base::StringToUint64(value.params[kDateKey], &sig.date)) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeaderParser::ParseSignature", "'date' parameter is not a number."); return base::nullopt; } - if (!base::StringToUint64(value.params["expires"], &sig.expires)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeHeaderParser::ParseSignature", error_message_callback, + if (!base::StringToUint64(value.params[kExpiresKey], &sig.expires)) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeHeaderParser::ParseSignature", "'expires' parameter is not a number."); return base::nullopt; } @@ -286,7 +286,7 @@ SignedExchangeHeaderParser::ParseSignature( // static bool SignedExchangeHeaderParser::GetVersionParamFromContentType( base::StringPiece content_type, - base::Optional* version_param) { + base::Optional* version_param) { DCHECK(version_param); StructuredHeaderParser parser(content_type); ParameterisedLabel parameterised_label; @@ -297,7 +297,12 @@ bool SignedExchangeHeaderParser::GetVersionParamFromContentType( if (it == parameterised_label.params.end()) { *version_param = base::nullopt; } else { - *version_param = it->second; + if (it->second == "b0") + *version_param = SignedExchangeVersion::kB0; + else if (it->second == "b1") + *version_param = SignedExchangeVersion::kB1; + else + return false; } return true; } diff --git a/chromium/content/browser/web_package/signed_exchange_header_parser.h b/chromium/content/browser/web_package/signed_exchange_header_parser.h index 68b23813eb1..01697eb5362 100644 --- a/chromium/content/browser/web_package/signed_exchange_header_parser.h +++ b/chromium/content/browser/web_package/signed_exchange_header_parser.h @@ -13,13 +13,15 @@ #include "base/macros.h" #include "base/optional.h" #include "base/strings/string_piece.h" -#include "content/browser/web_package/signed_exchange_utils.h" +#include "content/browser/web_package/signed_exchange_consts.h" #include "content/common/content_export.h" #include "net/base/hash_value.h" #include "url/gurl.h" namespace content { +class SignedExchangeDevToolsProxy; + // Provide parsers for signed-exchange headers. // https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html class CONTENT_EXPORT SignedExchangeHeaderParser { @@ -45,14 +47,14 @@ class CONTENT_EXPORT SignedExchangeHeaderParser { // https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html#signature-header static base::Optional> ParseSignature( base::StringPiece signature_str, - const signed_exchange_utils::LogCallback& error_message_callback); + SignedExchangeDevToolsProxy* devtools_proxy); // Parses |content_type| to get the value of "v=" parameter of the signed - // exchange. Example: "b0" for "application/signed-exchange;v=b0". Returns - // false if failed to parse. + // exchange, and converts to SignedExchangeVersion. Returns false if failed to + // parse. static bool GetVersionParamFromContentType( base::StringPiece content_type, - base::Optional* version_param); + base::Optional* version_param); }; } // namespace content diff --git a/chromium/content/browser/web_package/signed_exchange_header_parser_unittest.cc b/chromium/content/browser/web_package/signed_exchange_header_parser_unittest.cc index a433f84d124..32f912e9490 100644 --- a/chromium/content/browser/web_package/signed_exchange_header_parser_unittest.cc +++ b/chromium/content/browser/web_package/signed_exchange_header_parser_unittest.cc @@ -5,7 +5,6 @@ #include "content/browser/web_package/signed_exchange_header_parser.h" #include "base/callback.h" -#include "content/browser/web_package/signed_exchange_utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { @@ -58,7 +57,7 @@ TEST_F(SignedExchangeHeaderParserTest, ParseSignature) { 0x00, 0x63, 0xf7, 0xd0, 0xe5, 0x62, 0x9e, 0x1f, 0x11, 0x7c}}; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); ASSERT_TRUE(signatures.has_value()); ASSERT_EQ(signatures->size(), 2u); @@ -98,7 +97,7 @@ TEST_F(SignedExchangeHeaderParserTest, IncompleteSignature) { " certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;" " date=1511128380; expires=1511733180"; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); EXPECT_FALSE(signatures.has_value()); } @@ -114,7 +113,7 @@ TEST_F(SignedExchangeHeaderParserTest, DuplicatedParam) { " certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;" " date=1511128380; expires=1511733180"; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); EXPECT_FALSE(signatures.has_value()); } @@ -129,7 +128,7 @@ TEST_F(SignedExchangeHeaderParserTest, InvalidCertURL) { " certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;" " date=1511128380; expires=1511733180"; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); EXPECT_FALSE(signatures.has_value()); } @@ -144,7 +143,7 @@ TEST_F(SignedExchangeHeaderParserTest, CertURLWithFragment) { " certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;" " date=1511128380; expires=1511733180"; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); EXPECT_FALSE(signatures.has_value()); } @@ -159,7 +158,7 @@ TEST_F(SignedExchangeHeaderParserTest, RelativeCertURL) { " certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;" " date=1511128380; expires=1511733180"; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); EXPECT_FALSE(signatures.has_value()); } @@ -174,7 +173,7 @@ TEST_F(SignedExchangeHeaderParserTest, InvalidValidityUrl) { " certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;" " date=1511128380; expires=1511733180"; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); EXPECT_FALSE(signatures.has_value()); } @@ -189,7 +188,7 @@ TEST_F(SignedExchangeHeaderParserTest, ValidityUrlWithFragment) { " certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;" " date=1511128380; expires=1511733180"; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); EXPECT_FALSE(signatures.has_value()); } @@ -204,7 +203,7 @@ TEST_F(SignedExchangeHeaderParserTest, RelativeValidityUrl) { " certSha256=*W7uB969dFW3Mb5ZefPS9Tq5ZbH5iSmOILpjv2qEArmI;" " date=1511128380; expires=1511733180"; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); EXPECT_FALSE(signatures.has_value()); } @@ -219,20 +218,20 @@ TEST_F(SignedExchangeHeaderParserTest, InvalidCertSHA256) { " certSha256=*W7uB969dFW3Mb5ZefPS9;" " date=1511128380; expires=1511733180"; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); EXPECT_FALSE(signatures.has_value()); } TEST_F(SignedExchangeHeaderParserTest, OpenQuoteAtEnd) { const char hdr_string[] = "sig1; sig=\""; auto signatures = SignedExchangeHeaderParser::ParseSignature( - hdr_string, signed_exchange_utils::LogCallback()); + hdr_string, nullptr /* devtools_proxy */); EXPECT_FALSE(signatures.has_value()); } TEST_F(SignedExchangeHeaderParserTest, VersionParam_None) { const char content_type[] = "application/signed-exchange"; - base::Optional version; + base::Optional version; EXPECT_TRUE(SignedExchangeHeaderParser::GetVersionParamFromContentType( content_type, &version)); EXPECT_FALSE(version); @@ -240,43 +239,43 @@ TEST_F(SignedExchangeHeaderParserTest, VersionParam_None) { TEST_F(SignedExchangeHeaderParserTest, VersionParam_NoneWithSemicolon) { const char content_type[] = "application/signed-exchange;"; - base::Optional version; + base::Optional version; EXPECT_FALSE(SignedExchangeHeaderParser::GetVersionParamFromContentType( content_type, &version)); } TEST_F(SignedExchangeHeaderParserTest, VersionParam_EmptyString) { const char content_type[] = "application/signed-exchange;v="; - base::Optional version; + base::Optional version; EXPECT_FALSE(SignedExchangeHeaderParser::GetVersionParamFromContentType( content_type, &version)); } TEST_F(SignedExchangeHeaderParserTest, VersionParam_Simple) { const char content_type[] = "application/signed-exchange;v=b0"; - base::Optional version; + base::Optional version; EXPECT_TRUE(SignedExchangeHeaderParser::GetVersionParamFromContentType( content_type, &version)); ASSERT_TRUE(version); - EXPECT_EQ(*version, "b0"); + EXPECT_EQ(*version, SignedExchangeVersion::kB0); } TEST_F(SignedExchangeHeaderParserTest, VersionParam_SimpleWithSpace) { - const char content_type[] = "application/signed-exchange; v=b0"; - base::Optional version; + const char content_type[] = "application/signed-exchange; v=b1"; + base::Optional version; EXPECT_TRUE(SignedExchangeHeaderParser::GetVersionParamFromContentType( content_type, &version)); ASSERT_TRUE(version); - EXPECT_EQ(*version, "b0"); + EXPECT_EQ(*version, SignedExchangeVersion::kB1); } TEST_F(SignedExchangeHeaderParserTest, VersionParam_SimpleWithDoublequotes) { const char content_type[] = "application/signed-exchange;v=\"b0\""; - base::Optional version; + base::Optional version; EXPECT_TRUE(SignedExchangeHeaderParser::GetVersionParamFromContentType( content_type, &version)); ASSERT_TRUE(version); - EXPECT_EQ(*version, "b0"); + EXPECT_EQ(*version, SignedExchangeVersion::kB0); } } // namespace content diff --git a/chromium/content/browser/web_package/signed_exchange_header_unittest.cc b/chromium/content/browser/web_package/signed_exchange_header_unittest.cc index a12c0c7f2a4..9c532156460 100644 --- a/chromium/content/browser/web_package/signed_exchange_header_unittest.cc +++ b/chromium/content/browser/web_package/signed_exchange_header_unittest.cc @@ -11,7 +11,6 @@ #include "components/cbor/cbor_values.h" #include "components/cbor/cbor_writer.h" #include "content/browser/web_package/signed_exchange_consts.h" -#include "content/browser/web_package/signed_exchange_utils.h" #include "content/public/common/content_paths.h" #include "testing/gtest/include/gtest/gtest.h" @@ -50,14 +49,14 @@ base::Optional GenerateHeaderAndParse( auto serialized = cbor::CBORWriter::Write(cbor::CBORValue(std::move(array))); return SignedExchangeHeader::Parse( base::make_span(serialized->data(), serialized->size()), - signed_exchange_utils::LogCallback()); + nullptr /* devtools_proxy */); } } // namespace -TEST(SignedExchangeHeaderTest, ParseHeaderLength) { +TEST(SignedExchangeHeaderTest, ParseEncodedLength) { constexpr struct { - uint8_t bytes[SignedExchangeHeader::kEncodedHeaderLengthInBytes]; + uint8_t bytes[SignedExchangeHeader::kEncodedLengthInBytes]; size_t expected; } kTestCases[] = { {{0x00, 0x00, 0x01}, 1u}, {{0x01, 0xe2, 0x40}, 123456u}, @@ -66,14 +65,14 @@ TEST(SignedExchangeHeaderTest, ParseHeaderLength) { int test_element_index = 0; for (const auto& test_case : kTestCases) { SCOPED_TRACE(testing::Message() << "testing case " << test_element_index++); - EXPECT_EQ(SignedExchangeHeader::ParseHeadersLength(test_case.bytes), + EXPECT_EQ(SignedExchangeHeader::ParseEncodedLength(test_case.bytes), test_case.expected); } } TEST(SignedExchangeHeaderTest, ParseGoldenFile) { base::FilePath test_htxg_path; - PathService::Get(content::DIR_TEST_DATA, &test_htxg_path); + base::PathService::Get(content::DIR_TEST_DATA, &test_htxg_path); test_htxg_path = test_htxg_path.AppendASCII("htxg").AppendASCII( "test.example.org_test.htxg"); @@ -81,18 +80,17 @@ TEST(SignedExchangeHeaderTest, ParseGoldenFile) { ASSERT_TRUE(base::ReadFileToString(test_htxg_path, &contents)); auto* contents_bytes = reinterpret_cast(contents.data()); - ASSERT_GT(contents.size(), SignedExchangeHeader::kEncodedHeaderLengthInBytes); - size_t header_size = SignedExchangeHeader::ParseHeadersLength(base::make_span( - contents_bytes, SignedExchangeHeader::kEncodedHeaderLengthInBytes)); + ASSERT_GT(contents.size(), SignedExchangeHeader::kEncodedLengthInBytes); + size_t header_size = SignedExchangeHeader::ParseEncodedLength(base::make_span( + contents_bytes, SignedExchangeHeader::kEncodedLengthInBytes)); ASSERT_GT(contents.size(), - SignedExchangeHeader::kEncodedHeaderLengthInBytes + header_size); + SignedExchangeHeader::kEncodedLengthInBytes + header_size); const auto cbor_bytes = base::make_span( - contents_bytes + SignedExchangeHeader::kEncodedHeaderLengthInBytes, + contents_bytes + SignedExchangeHeader::kEncodedLengthInBytes, header_size); const base::Optional header = - SignedExchangeHeader::Parse(cbor_bytes, - signed_exchange_utils::LogCallback()); + SignedExchangeHeader::Parse(cbor_bytes, nullptr /* devtools_proxy */); ASSERT_TRUE(header.has_value()); EXPECT_EQ(header->request_url(), GURL("https://test.example.org/test/")); EXPECT_EQ(header->request_method(), "GET"); diff --git a/chromium/content/browser/web_package/signed_exchange_signature_verifier.cc b/chromium/content/browser/web_package/signed_exchange_signature_verifier.cc index bbc94fbeaf9..cf1322d96aa 100644 --- a/chromium/content/browser/web_package/signed_exchange_signature_verifier.cc +++ b/chromium/content/browser/web_package/signed_exchange_signature_verifier.cc @@ -20,6 +20,10 @@ #include "crypto/signature_verifier.h" #include "net/cert/asn1_util.h" #include "net/cert/x509_util.h" +#include "third_party/boringssl/src/include/openssl/bytestring.h" +#include "third_party/boringssl/src/include/openssl/ec.h" +#include "third_party/boringssl/src/include/openssl/ec_key.h" +#include "third_party/boringssl/src/include/openssl/evp.h" namespace content { @@ -138,48 +142,65 @@ base::Optional GenerateSignedMessageCBOR( return cbor::CBORValue(map); } -bool VerifySignature( - base::span sig, - base::span msg, - scoped_refptr cert, - const signed_exchange_utils::LogCallback& error_message_callback) { +bool VerifySignature(base::span sig, + base::span msg, + scoped_refptr cert, + SignedExchangeDevToolsProxy* devtools_proxy) { TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), "VerifySignature"); base::StringPiece spki; if (!net::asn1::ExtractSPKIFromDERCert( net::x509_util::CryptoBufferAsStringPiece(cert->cert_buffer()), &spki)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "VerifySignature", error_message_callback, "Failed to extract SPKI."); + TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("loading"), "VerifySignature", + "error", "Failed to extract SPKI."); return false; } - size_t size_bits; - net::X509Certificate::PublicKeyType type; - net::X509Certificate::GetPublicKeyInfo(cert->cert_buffer(), &size_bits, - &type); - if (type != net::X509Certificate::kPublicKeyTypeRSA) { - // TODO(crbug.com/803774): Add support for ecdsa_secp256r1_sha256 and - // ecdsa_secp384r1_sha384. - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "VerifySignature", error_message_callback, - base::StringPrintf("Unsupported public key type: %d", type)); + crypto::SignatureVerifier::SignatureAlgorithm algorithm; + CBS cbs; + CBS_init(&cbs, reinterpret_cast(spki.data()), spki.size()); + bssl::UniquePtr pkey(EVP_parse_public_key(&cbs)); + if (!pkey || CBS_len(&cbs) != 0) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "VerifySignature", "Failed to parse public key."); return false; } + switch (EVP_PKEY_id(pkey.get())) { + case EVP_PKEY_RSA: + algorithm = crypto::SignatureVerifier::RSA_PSS_SHA256; + break; + case EVP_PKEY_EC: { + const EC_GROUP* group = + EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey.get())); + switch (EC_GROUP_get_curve_name(group)) { + case NID_X9_62_prime256v1: + algorithm = crypto::SignatureVerifier::ECDSA_SHA256; + break; + default: + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "VerifySignature", "Unsupported EC group."); + return false; + } + break; + } + default: + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "VerifySignature", "Unsupported public key type."); + return false; + } - // TODO(crbug.com/803774): This is missing the digitalSignature key usage bit - // check. crypto::SignatureVerifier verifier; - if (!verifier.VerifyInit( - crypto::SignatureVerifier::RSA_PSS_SHA256, sig.data(), sig.size(), - reinterpret_cast(spki.data()), spki.size())) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "VerifySignature", error_message_callback, "VerifyInit failed."); + if (!net::x509_util::SignatureVerifierInitWithCertificate( + &verifier, algorithm, sig, cert->cert_buffer())) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "VerifySignature", + "SignatureVerifierInitWithCertificate failed."); return false; } - verifier.VerifyUpdate(msg.data(), msg.size()); + verifier.VerifyUpdate(msg); if (!verifier.VerifyFinal()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "VerifySignature", error_message_callback, "VerifyFinal failed."); + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "VerifySignature", "VerifyFinal failed."); return false; } TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("loading"), "VerifySignature"); @@ -264,13 +285,13 @@ SignedExchangeSignatureVerifier::Result SignedExchangeSignatureVerifier::Verify( const SignedExchangeHeader& header, scoped_refptr certificate, const base::Time& verification_time, - const signed_exchange_utils::LogCallback& error_message_callback) { + SignedExchangeDevToolsProxy* devtools_proxy) { TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"), "SignedExchangeSignatureVerifier::Verify"); if (!VerifyTimestamps(header, verification_time)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeSignatureVerifier::Verify", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeSignatureVerifier::Verify", base::StringPrintf( "Invalid timestamp. creation_time: %" PRIu64 ", expires_time: %" PRIu64 ", verification_time: %" PRIu64, @@ -280,15 +301,15 @@ SignedExchangeSignatureVerifier::Result SignedExchangeSignatureVerifier::Verify( } if (!certificate) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeSignatureVerifier::Verify", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeSignatureVerifier::Verify", "No certificate set."); return Result::kErrNoCertificate; } if (!header.signature().cert_sha256.has_value()) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeSignatureVerifier::Verify", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeSignatureVerifier::Verify", "No certSha256 set."); return Result::kErrNoCertificateSHA256; } @@ -297,16 +318,16 @@ SignedExchangeSignatureVerifier::Result SignedExchangeSignatureVerifier::Verify( if (*header.signature().cert_sha256 != net::X509Certificate::CalculateFingerprint256( certificate->cert_buffer())) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeSignatureVerifier::Verify", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeSignatureVerifier::Verify", "certSha256 mismatch."); return Result::kErrCertificateSHA256Mismatch; } auto message = GenerateSignedMessage(header); if (!message) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeSignatureVerifier::Verify", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeSignatureVerifier::Verify", "Failed to reconstruct signed message."); return Result::kErrInvalidSignatureFormat; } @@ -315,16 +336,16 @@ SignedExchangeSignatureVerifier::Result SignedExchangeSignatureVerifier::Verify( if (!VerifySignature( base::make_span(reinterpret_cast(sig.data()), sig.size()), - *message, certificate, error_message_callback)) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeSignatureVerifier::Verify", error_message_callback, + *message, certificate, devtools_proxy)) { + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeSignatureVerifier::Verify", "Failed to verify signature \"sig\"."); return Result::kErrSignatureVerificationFailed; } if (!base::EqualsCaseInsensitiveASCII(header.signature().integrity, "mi")) { - signed_exchange_utils::RunErrorMessageCallbackAndEndTraceEvent( - "SignedExchangeSignatureVerifier::Verify", error_message_callback, + signed_exchange_utils::ReportErrorAndEndTraceEvent( + devtools_proxy, "SignedExchangeSignatureVerifier::Verify", "The current implemention only supports \"mi\" integrity scheme."); return Result::kErrInvalidSignatureIntegrity; } diff --git a/chromium/content/browser/web_package/signed_exchange_signature_verifier.h b/chromium/content/browser/web_package/signed_exchange_signature_verifier.h index 42260d2d767..fe8da78f44a 100644 --- a/chromium/content/browser/web_package/signed_exchange_signature_verifier.h +++ b/chromium/content/browser/web_package/signed_exchange_signature_verifier.h @@ -12,7 +12,6 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/optional.h" -#include "content/browser/web_package/signed_exchange_utils.h" #include "content/common/content_export.h" #include "net/cert/x509_certificate.h" @@ -23,6 +22,7 @@ class Time; namespace content { class SignedExchangeHeader; +class SignedExchangeDevToolsProxy; // SignedExchangeSignatureVerifier verifies the signature of the given // signed exchange. This is done by reconstructing the signed message @@ -47,11 +47,10 @@ class CONTENT_EXPORT SignedExchangeSignatureVerifier final { kErrInvalidTimestamp }; - static Result Verify( - const SignedExchangeHeader& header, - scoped_refptr certificate, - const base::Time& verification_time, - const signed_exchange_utils::LogCallback& error_message_callback); + static Result Verify(const SignedExchangeHeader& header, + scoped_refptr certificate, + const base::Time& verification_time, + SignedExchangeDevToolsProxy* devtools_proxy); static base::Optional> EncodeCanonicalExchangeHeaders( const SignedExchangeHeader& header); diff --git a/chromium/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc b/chromium/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc index a2d952aba1a..b902e1e9f9b 100644 --- a/chromium/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc +++ b/chromium/content/browser/web_package/signed_exchange_signature_verifier_unittest.cc @@ -7,7 +7,6 @@ #include "base/callback.h" #include "content/browser/web_package/signed_exchange_header.h" #include "content/browser/web_package/signed_exchange_header_parser.h" -#include "content/browser/web_package/signed_exchange_utils.h" #include "net/cert/x509_certificate.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -64,7 +63,7 @@ const uint64_t kSignatureHeaderDate = 1517892341; const uint64_t kSignatureHeaderExpires = 1517895941; // clang-format off -constexpr char kSignatureHeader[] = +constexpr char kSignatureHeaderRSA[] = "sig; " "sig=*RhjjWuXi87riQUu90taBHFJgTo8XBhiCe9qTJMP7/XVPu2diRGipo06HoGsyXkidHiiW" "743JgoNmO7CjfeVXLXQgKDxtGidATtPsVadAT4JpBDZJWSUg5qAbWcASXjyO38Uhq9gJkeu4w" @@ -78,6 +77,32 @@ constexpr char kSignatureHeader[] = "date=1517892341; expires=1517895941"; // clang-format on +// See content/testdata/htxg/README on how to generate this data. +// clang-format off +constexpr char kSignatureHeaderECDSAP256[] = + "label; " + "sig=*MEYCIQDQYQAHAlpznkP/btvuNnvGY5ycO+NOOTFXsoBjF22UhAIhAMyzyMikudaQeIPYB" + "fh2JdqZVBwgsQ6a3Cn8lFA0XYGW; " + "validityUrl=\"https://example.com/resource.validity.msg\"; " + "integrity=\"mi\"; " + "certUrl=\"https://example.com/cert.msg\"; " + "certSha256=*CfDj40tr5B7oo6IaWwQF2L1uDgsHH0fA2YOCB7E0tAQ; " + "date=1517892341; expires=1517895941"; +// clang-format on + +// See content/testdata/htxg/README on how to generate this data. +// clang-format off +constexpr char kSignatureHeaderECDSAP384[] = + "label; " + "sig=*MGQCMDtOqWBsWjx1+WZta9tBpuuMJMLMwp8/eHu+PwNw95qCMMjD1xJiLIm0HUtFzdzSC" + "wIwVxSUD9IB2t4JIHz6IJPddqR1ex38kkSvOYSmFEwqVPRM1sqAcEtvwdpSU+cLJYbS; " + "validityUrl=\"https://example.com/resource.validity.msg\"; " + "integrity=\"mi\"; " + "certUrl=\"https://example.com/cert.msg\"; " + "certSha256=*8X8y8nj8vDJHSSa0cxn+TCu+8zGpIJfbdzAnd5cW+jA; " + "date=1517892341; expires=1517895941"; +// clang-format on + // |expires| (1518497142) is more than 7 days (604800 seconds) after |date| // (1517892341). // clang-format off @@ -95,7 +120,7 @@ constexpr char kSignatureHeaderInvalidExpires[] = "date=1517892341; expires=1518497142"; // clang-format on -constexpr char kCertPEM[] = R"( +constexpr char kCertPEMRSA[] = R"( -----BEGIN CERTIFICATE----- MIID9zCCAt+gAwIBAgIUde2ndSB4271TAGDk0Ft+WuCCGnMwDQYJKoZIhvcNAQEL BQAwUDELMAkGA1UEBhMCSlAxEjAQBgNVBAgTCU1pbmF0by1rdTEOMAwGA1UEBxMF @@ -121,18 +146,137 @@ U0/55zFz2OzNoAHaoBzMRxn4ZSc3+lxl0K1+cCP8ivhwkxdz/vhz5RfOjpSinxqt wYxI2+BLS6X5NpI= -----END CERTIFICATE-----)"; -TEST(SignedExchangeSignatureVerifier, Verify) { - base::Time verification_time = - base::Time::UnixEpoch() + - base::TimeDelta::FromSeconds(kSignatureHeaderDate); +constexpr char kCertPEMECDSAP256[] = R"( +-----BEGIN CERTIFICATE----- +MIICQzCCASsCAQEwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxEzARBgNV +BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoM +B1Rlc3QgQ0ExFTATBgNVBAMMDFRlc3QgUm9vdCBDQTAeFw0xODAzMjMwNDU3MzRa +Fw0xOTAzMTgwNDU3MzRaMDcxGTAXBgNVBAMMEHRlc3QuZXhhbXBsZS5vcmcxDTAL +BgNVBAoMBFRlc3QxCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD +QgAECQYn3HDPPhtMv2hzyjI7E3FU89EjnzTtvLd9OP55GLAsaE/FCTWbx6rKOxF7 +O4jP0N3PsIzr+nT1lIix+HpxujANBgkqhkiG9w0BAQsFAAOCAQEAhKdVMvKm7gBz +af6nfCkLGRo56KJasi6lJh2byF17vdqq+mSXR+jHZtsRsRZJyl+C+jaSzrT0TnMA +kLg+U4ZnKZD5sTo7TWnRlTA4G4tOrWaq1tn89FWqe+hbvn6dEyTZ1XFPaO6hzeNH +ZM5H+bIpngvGmP1lf7K6PtC3Tx/S938zBdQrfKz/4ZB0S5cmIyIUBnlj3PDWtLsB +KS4wvSnjPj1EyVKxTQH1PdB2NqC4eT8bgFcryNWrkMOWdOUNhGWB55nVwI8yNPQO +4OrKJLsDZir3v7dzcU9U1erBp4+udGFIfW86g24FX1gn3SavtO6lZt59AFLptyQ6 +LWh1CMv1aQ== +-----END CERTIFICATE-----)"; + +constexpr char kCertPEMECDSAP384[] = R"( +-----BEGIN CERTIFICATE----- +MIICYDCCAUgCAQEwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxEzARBgNV +BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoM +B1Rlc3QgQ0ExFTATBgNVBAMMDFRlc3QgUm9vdCBDQTAeFw0xODA0MDkwMTUyMzVa +Fw0xOTA0MDQwMTUyMzVaMDcxGTAXBgNVBAMMEHRlc3QuZXhhbXBsZS5vcmcxDTAL +BgNVBAoMBFRlc3QxCzAJBgNVBAYTAlVTMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE +YK0FPc6B2UkDO3GHS95PLss9e82f8RdQDIZE9UPUSOJ1UISOT19j/SJq3gyoY+pK +J818LhVe+ywgdH+tKosO6v1l2o/EffIRDjCfN/aSUuQjkkSwgyL62/9687+486z6 +MA0GCSqGSIb3DQEBCwUAA4IBAQB61Q+/68hsD5OapG+2CDsJI+oR91H+Jv+tRMby +of47O0hJGISuAB9xcFhIcMKwBReODpBmzwSO713NNU/oaG/XysHH1TNZZodTtWD9 +Z1g5AJamfwvFS+ObqzOtyFUdFS4NBAE4lXi5XnHa2hU2Bkm+abVYLqyAGw1kh2ES +DGC2vA1lb2Uy9bgLCYYkZoESjb/JYRQjCmqlwYKOozU7ZbIe3zJPjRWYP1Tuany5 ++rYllWk/DJlMVjs/fbf0jj32vrevCgul43iWMgprOw1ncuK8l5nND/o5aN2mwMDw +Xhe5DP7VATeQq3yGV3ps+rCTHDP6qSHDEWP7DqHQdSsxtI0E +-----END CERTIFICATE-----)"; + +class SignedExchangeSignatureVerifierTest : public ::testing::Test { + protected: + SignedExchangeSignatureVerifierTest() {} + + const base::Time VerificationTime() { + return base::Time::UnixEpoch() + + base::TimeDelta::FromSeconds(kSignatureHeaderDate); + } + + void TestVerifierGivenValidInput( + const SignedExchangeHeader& header, + scoped_refptr certificate) { + EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kSuccess, + SignedExchangeSignatureVerifier::Verify( + header, certificate, VerificationTime(), + nullptr /* devtools_proxy */)); + + EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kErrInvalidTimestamp, + SignedExchangeSignatureVerifier::Verify( + header, certificate, + base::Time::UnixEpoch() + + base::TimeDelta::FromSeconds(kSignatureHeaderDate - 1), + nullptr /* devtools_proxy */ + )); + + EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kSuccess, + SignedExchangeSignatureVerifier::Verify( + header, certificate, + base::Time::UnixEpoch() + + base::TimeDelta::FromSeconds(kSignatureHeaderExpires), + nullptr /* devtools_proxy */ + )); + + EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kErrInvalidTimestamp, + SignedExchangeSignatureVerifier::Verify( + header, certificate, + base::Time::UnixEpoch() + + base::TimeDelta::FromSeconds(kSignatureHeaderExpires + 1), + nullptr /* devtools_proxy */ + )); + + SignedExchangeHeader invalid_expires_header(header); + auto invalid_expires_signature = SignedExchangeHeaderParser::ParseSignature( + kSignatureHeaderInvalidExpires, nullptr /* devtools_proxy */); + ASSERT_TRUE(invalid_expires_signature.has_value()); + ASSERT_EQ(1u, invalid_expires_signature->size()); + invalid_expires_header.SetSignatureForTesting( + (*invalid_expires_signature)[0]); + EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kErrInvalidTimestamp, + SignedExchangeSignatureVerifier::Verify( + invalid_expires_header, certificate, VerificationTime(), + nullptr /* devtools_proxy */ + )); + + SignedExchangeHeader corrupted_header(header); + corrupted_header.set_request_url(GURL("https://example.com/bad.html")); + EXPECT_EQ(SignedExchangeSignatureVerifier::Result:: + kErrSignatureVerificationFailed, + SignedExchangeSignatureVerifier::Verify( + corrupted_header, certificate, VerificationTime(), + nullptr /* devtools_proxy */ + )); + + SignedExchangeHeader badsig_header(header); + SignedExchangeHeaderParser::Signature badsig = header.signature(); + badsig.sig[0]++; + badsig_header.SetSignatureForTesting(badsig); + EXPECT_EQ(SignedExchangeSignatureVerifier::Result:: + kErrSignatureVerificationFailed, + SignedExchangeSignatureVerifier::Verify( + badsig_header, certificate, VerificationTime(), + nullptr /* devtools_proxy */ + )); + + SignedExchangeHeader badsigsha256_header(header); + SignedExchangeHeaderParser::Signature badsigsha256 = header.signature(); + badsigsha256.cert_sha256->data[0]++; + badsigsha256_header.SetSignatureForTesting(badsigsha256); + EXPECT_EQ( + SignedExchangeSignatureVerifier::Result::kErrCertificateSHA256Mismatch, + SignedExchangeSignatureVerifier::Verify(badsigsha256_header, + certificate, VerificationTime(), + nullptr /* devtools_proxy */ + )); + } +}; + +TEST_F(SignedExchangeSignatureVerifierTest, VerifyRSA) { auto signature = SignedExchangeHeaderParser::ParseSignature( - kSignatureHeader, signed_exchange_utils::LogCallback()); + kSignatureHeaderRSA, nullptr /* devtools_proxy */); ASSERT_TRUE(signature.has_value()); ASSERT_EQ(1u, signature->size()); net::CertificateList certlist = net::X509Certificate::CreateCertificateListFromBytes( - kCertPEM, arraysize(kCertPEM), net::X509Certificate::FORMAT_AUTO); + kCertPEMRSA, base::size(kCertPEMRSA), + net::X509Certificate::FORMAT_AUTO); ASSERT_EQ(1u, certlist.size()); SignedExchangeHeader header; @@ -145,73 +289,63 @@ TEST(SignedExchangeSignatureVerifier, Verify) { "mi", "mi-sha256=4ld4G-h-sQSoLBD39ndIO15O_82NXSzq9UMFEYI02JQ"); header.SetSignatureForTesting((*signature)[0]); - auto certificate = certlist[0]; - - EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kSuccess, - SignedExchangeSignatureVerifier::Verify( - header, certificate, verification_time, - signed_exchange_utils::LogCallback())); - - EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kErrInvalidTimestamp, - SignedExchangeSignatureVerifier::Verify( - header, certificate, - base::Time::UnixEpoch() + - base::TimeDelta::FromSeconds(kSignatureHeaderDate - 1), - signed_exchange_utils::LogCallback())); - - EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kSuccess, - SignedExchangeSignatureVerifier::Verify( - header, certificate, - base::Time::UnixEpoch() + - base::TimeDelta::FromSeconds(kSignatureHeaderExpires), - signed_exchange_utils::LogCallback())); - - EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kErrInvalidTimestamp, - SignedExchangeSignatureVerifier::Verify( - header, certificate, - base::Time::UnixEpoch() + - base::TimeDelta::FromSeconds(kSignatureHeaderExpires + 1), - signed_exchange_utils::LogCallback())); - - SignedExchangeHeader invalid_expires_header(header); - auto invalid_expires_signature = SignedExchangeHeaderParser::ParseSignature( - kSignatureHeaderInvalidExpires, signed_exchange_utils::LogCallback()); - ASSERT_TRUE(invalid_expires_signature.has_value()); - ASSERT_EQ(1u, invalid_expires_signature->size()); - invalid_expires_header.SetSignatureForTesting( - (*invalid_expires_signature)[0]); - EXPECT_EQ(SignedExchangeSignatureVerifier::Result::kErrInvalidTimestamp, - SignedExchangeSignatureVerifier::Verify( - invalid_expires_header, certificate, verification_time, - signed_exchange_utils::LogCallback())); - - SignedExchangeHeader corrupted_header(header); - corrupted_header.set_request_url(GURL("https://example.com/bad.html")); - EXPECT_EQ( - SignedExchangeSignatureVerifier::Result::kErrSignatureVerificationFailed, - SignedExchangeSignatureVerifier::Verify( - corrupted_header, certificate, verification_time, - signed_exchange_utils::LogCallback())); - - SignedExchangeHeader badsig_header(header); - SignedExchangeHeaderParser::Signature badsig = header.signature(); - badsig.sig[0]++; - badsig_header.SetSignatureForTesting(badsig); + TestVerifierGivenValidInput(header, certlist[0]); +} + +TEST_F(SignedExchangeSignatureVerifierTest, VerifyECDSAP256) { + auto signature = SignedExchangeHeaderParser::ParseSignature( + kSignatureHeaderECDSAP256, nullptr /* devtools_proxy */); + ASSERT_TRUE(signature.has_value()); + ASSERT_EQ(1u, signature->size()); + + net::CertificateList certlist = + net::X509Certificate::CreateCertificateListFromBytes( + kCertPEMECDSAP256, base::size(kCertPEMECDSAP256), + net::X509Certificate::FORMAT_AUTO); + ASSERT_EQ(1u, certlist.size()); + + SignedExchangeHeader header; + header.set_request_method("GET"); + header.set_request_url(GURL("https://test.example.org/test/")); + header.set_response_code(net::HTTP_OK); + header.AddResponseHeader("content-type", "text/html; charset=utf-8"); + header.AddResponseHeader("content-encoding", "mi-sha256"); + header.AddResponseHeader( + "mi", "mi-sha256=wmp4dRMYgxP3tSMCwV_I0CWOCiHZpAihKZk19bsN9RI"); + + header.SetSignatureForTesting((*signature)[0]); + + TestVerifierGivenValidInput(header, certlist[0]); +} + +TEST_F(SignedExchangeSignatureVerifierTest, VerifyECDSAP384) { + auto signature = SignedExchangeHeaderParser::ParseSignature( + kSignatureHeaderECDSAP384, nullptr /* devtools_proxy */); + ASSERT_TRUE(signature.has_value()); + ASSERT_EQ(1u, signature->size()); + + net::CertificateList certlist = + net::X509Certificate::CreateCertificateListFromBytes( + kCertPEMECDSAP384, base::size(kCertPEMECDSAP384), + net::X509Certificate::FORMAT_AUTO); + ASSERT_EQ(1u, certlist.size()); + + SignedExchangeHeader header; + header.set_request_method("GET"); + header.set_request_url(GURL("https://test.example.org/test/")); + header.set_response_code(net::HTTP_OK); + header.AddResponseHeader("content-type", "text/html; charset=utf-8"); + header.AddResponseHeader("content-encoding", "mi-sha256"); + header.AddResponseHeader( + "mi", "mi-sha256=wmp4dRMYgxP3tSMCwV_I0CWOCiHZpAihKZk19bsN9RIG"); + + header.SetSignatureForTesting((*signature)[0]); + EXPECT_EQ( SignedExchangeSignatureVerifier::Result::kErrSignatureVerificationFailed, - SignedExchangeSignatureVerifier::Verify( - badsig_header, certificate, verification_time, - signed_exchange_utils::LogCallback())); - - SignedExchangeHeader badsigsha256_header(header); - SignedExchangeHeaderParser::Signature badsigsha256 = header.signature(); - badsigsha256.cert_sha256->data[0]++; - badsigsha256_header.SetSignatureForTesting(badsigsha256); - EXPECT_EQ( - SignedExchangeSignatureVerifier::Result::kErrCertificateSHA256Mismatch, - SignedExchangeSignatureVerifier::Verify( - badsigsha256_header, certificate, verification_time, - signed_exchange_utils::LogCallback())); + SignedExchangeSignatureVerifier::Verify(header, certlist[0], + VerificationTime(), + nullptr /* devtools_proxy */)); } } // namespace diff --git a/chromium/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.cc b/chromium/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.cc index ce2bce7b937..08476ba6348 100644 --- a/chromium/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.cc +++ b/chromium/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.cc @@ -7,6 +7,7 @@ #include "base/feature_list.h" #include "content/browser/loader/resource_requester_info.h" #include "content/browser/loader/url_loader_factory_impl.h" +#include "content/browser/web_package/signed_exchange_utils.h" #include "content/public/common/content_features.h" #include "net/url_request/url_request_context_getter.h" #include "services/network/public/cpp/features.h" @@ -20,7 +21,7 @@ SignedExchangeURLLoaderFactoryForNonNetworkService:: : resource_context_(resource_context), url_request_context_getter_(url_request_context_getter) { DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); - DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange)); + DCHECK(signed_exchange_utils::IsSignedExchangeHandlingEnabled()); } SignedExchangeURLLoaderFactoryForNonNetworkService:: @@ -50,6 +51,11 @@ void SignedExchangeURLLoaderFactoryForNonNetworkService::CreateLoaderAndStart( std::move(client), traffic_annotation); } +void SignedExchangeURLLoaderFactoryForNonNetworkService::Clone( + network::mojom::URLLoaderFactoryRequest request) { + NOTREACHED(); +} + std::unique_ptr SignedExchangeURLLoaderFactoryForNonNetworkService::Clone() { NOTREACHED(); diff --git a/chromium/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.h b/chromium/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.h index e101f3d060e..f29ecdb4fe2 100644 --- a/chromium/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.h +++ b/chromium/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.h @@ -34,6 +34,7 @@ class SignedExchangeURLLoaderFactoryForNonNetworkService network::mojom::URLLoaderClientPtr client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) override; + void Clone(network::mojom::URLLoaderFactoryRequest request) override; std::unique_ptr Clone() override; private: diff --git a/chromium/content/browser/web_package/signed_exchange_utils.cc b/chromium/content/browser/web_package/signed_exchange_utils.cc index 5fa03263441..4086fd67a6c 100644 --- a/chromium/content/browser/web_package/signed_exchange_utils.cc +++ b/chromium/content/browser/web_package/signed_exchange_utils.cc @@ -4,20 +4,51 @@ #include "content/browser/web_package/signed_exchange_utils.h" -#include "base/callback.h" +#include "base/feature_list.h" +#include "base/time/time.h" #include "base/trace_event/trace_event.h" +#include "content/browser/web_package/signed_exchange_devtools_proxy.h" +#include "content/browser/web_package/web_package_request_handler.h" +#include "content/public/common/content_features.h" +#include "services/network/public/cpp/resource_response.h" +#include "third_party/blink/public/common/origin_trials/trial_token_validator.h" namespace content { namespace signed_exchange_utils { -void RunErrorMessageCallbackAndEndTraceEvent(const char* name, - const LogCallback& callback, - const std::string& error_message) { - if (callback) - callback.Run(error_message); +void ReportErrorAndEndTraceEvent(SignedExchangeDevToolsProxy* devtools_proxy, + const char* trace_event_name, + const std::string& error_message) { + if (devtools_proxy) + devtools_proxy->ReportErrorMessage(error_message); + TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("loading"), trace_event_name, + "error", error_message); +} + +bool IsSignedExchangeHandlingEnabled() { + return base::FeatureList::IsEnabled(features::kSignedHTTPExchange) || + base::FeatureList::IsEnabled(features::kSignedHTTPExchangeOriginTrial); +} - TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("loading"), name, "error", - error_message); +bool ShouldHandleAsSignedHTTPExchange( + const GURL& request_url, + const network::ResourceResponseHead& head) { + // Currently we don't support the signed exchange which is returned from a + // service worker. + // TODO(crbug/803774): Decide whether we should support it or not. + if (head.was_fetched_via_service_worker) + return false; + if (!WebPackageRequestHandler::IsSupportedMimeType(head.mime_type)) + return false; + if (base::FeatureList::IsEnabled(features::kSignedHTTPExchange)) + return true; + if (!base::FeatureList::IsEnabled(features::kSignedHTTPExchangeOriginTrial)) + return false; + std::unique_ptr validator = + std::make_unique(); + return validator->RequestEnablesFeature(request_url, head.headers.get(), + features::kSignedHTTPExchange.name, + base::Time::Now()); } } // namespace signed_exchange_utils diff --git a/chromium/content/browser/web_package/signed_exchange_utils.h b/chromium/content/browser/web_package/signed_exchange_utils.h index d48d8347ad1..b5c4220daba 100644 --- a/chromium/content/browser/web_package/signed_exchange_utils.h +++ b/chromium/content/browser/web_package/signed_exchange_utils.h @@ -7,19 +7,36 @@ #include -#include "base/callback_forward.h" +class GURL; + +namespace network { +struct ResourceResponseHead; +} // namespace network namespace content { -namespace signed_exchange_utils { -using LogCallback = base::RepeatingCallback; +class SignedExchangeDevToolsProxy; + +namespace signed_exchange_utils { -// Runs |callback| with |error_message| if |callback| is not null. And calls -// TRACE_EVENT_END() with "disabled-by-default-loading" category, |name| and -// |error_meassage|. -void RunErrorMessageCallbackAndEndTraceEvent(const char* name, - const LogCallback& callback, - const std::string& error_message); +// Utility method to call SignedExchangeDevToolsProxy::ReportErrorMessage() and +// TRACE_EVENT_END() to report the error to both DevTools and about:tracing. If +// |devtools_proxy| is nullptr, it just calls TRACE_EVENT_END(). +void ReportErrorAndEndTraceEvent(SignedExchangeDevToolsProxy* devtools_proxy, + const char* trace_event_name, + const std::string& error_message); + +// Returns true when SignedHTTPExchange feature or SignedHTTPExchangeOriginTrial +// feature is enabled. +bool IsSignedExchangeHandlingEnabled(); + +// Returns true when the response should be handled as a signed exchange by +// checking the mime type and the feature flags. When SignedHTTPExchange feature +// is not enabled and SignedHTTPExchangeOriginTrial feature is enabled, this +// method also checks the Origin Trial header. +bool ShouldHandleAsSignedHTTPExchange( + const GURL& request_url, + const network::ResourceResponseHead& head); } // namespace signed_exchange_utils } // namespace content diff --git a/chromium/content/browser/web_package/web_package_loader.cc b/chromium/content/browser/web_package/web_package_loader.cc index 6e563d35a87..ecd1e160eb0 100644 --- a/chromium/content/browser/web_package/web_package_loader.cc +++ b/chromium/content/browser/web_package/web_package_loader.cc @@ -6,12 +6,15 @@ #include +#include "base/callback.h" #include "base/feature_list.h" #include "base/strings/stringprintf.h" #include "content/browser/loader/data_pipe_to_source_stream.h" #include "content/browser/loader/source_stream_to_data_pipe.h" #include "content/browser/web_package/signed_exchange_cert_fetcher_factory.h" +#include "content/browser/web_package/signed_exchange_devtools_proxy.h" #include "content/browser/web_package/signed_exchange_handler.h" +#include "content/browser/web_package/signed_exchange_utils.h" #include "content/public/common/content_features.h" #include "net/cert/cert_status_flags.h" #include "net/http/http_util.h" @@ -75,32 +78,33 @@ class WebPackageLoader::ResponseTimingInfo { }; WebPackageLoader::WebPackageLoader( - const network::ResourceResponseHead& original_response, + const network::ResourceResponseHead& outer_response, network::mojom::URLLoaderClientPtr forwarding_client, network::mojom::URLLoaderClientEndpointsPtr endpoints, url::Origin request_initiator, uint32_t url_loader_options, - int frame_tree_node_id, + std::unique_ptr devtools_proxy, scoped_refptr url_loader_factory, URLLoaderThrottlesGetter url_loader_throttles_getter, scoped_refptr request_context_getter) - : original_response_timing_info_( - std::make_unique(original_response)), + : outer_response_timing_info_( + std::make_unique(outer_response)), + outer_response_(outer_response), forwarding_client_(std::move(forwarding_client)), url_loader_client_binding_(this), request_initiator_(request_initiator), url_loader_options_(url_loader_options), - frame_tree_node_id_(frame_tree_node_id), + devtools_proxy_(std::move(devtools_proxy)), url_loader_factory_(std::move(url_loader_factory)), url_loader_throttles_getter_(std::move(url_loader_throttles_getter)), request_context_getter_(std::move(request_context_getter)), weak_factory_(this) { - DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange)); + DCHECK(signed_exchange_utils::IsSignedExchangeHandlingEnabled()); // Can't use HttpResponseHeaders::GetMimeType() because SignedExchangeHandler // checks "v=" parameter. - original_response.headers->EnumerateHeader(nullptr, "content-type", - &content_type_); + outer_response.headers->EnumerateHeader(nullptr, "content-type", + &content_type_); url_loader_.Bind(std::move(endpoints->url_loader)); @@ -185,13 +189,14 @@ void WebPackageLoader::OnStartLoadingResponseBody( base::BindOnce(&WebPackageLoader::OnHTTPExchangeFound, weak_factory_.GetWeakPtr()), std::move(cert_fetcher_factory), std::move(request_context_getter_), - frame_tree_node_id_); + std::move(devtools_proxy_)); } void WebPackageLoader::OnComplete( const network::URLLoaderCompletionStatus& status) {} -void WebPackageLoader::FollowRedirect() { +void WebPackageLoader::FollowRedirect( + const base::Optional& modified_request_headers) { NOTREACHED(); } @@ -239,10 +244,10 @@ void WebPackageLoader::OnHTTPExchangeFound( } // TODO(https://crbug.com/803774): Handle no-GET request_method as a error. - DCHECK(original_response_timing_info_); + DCHECK(outer_response_timing_info_); forwarding_client_->OnReceiveRedirect( CreateRedirectInfo(request_url), - std::move(original_response_timing_info_)->CreateRedirectResponseHead()); + std::move(outer_response_timing_info_)->CreateRedirectResponseHead()); forwarding_client_.reset(); const base::Optional& ssl_info = resource_response.ssl_info; diff --git a/chromium/content/browser/web_package/web_package_loader.h b/chromium/content/browser/web_package/web_package_loader.h index 115bc2b8513..0bafea07c9a 100644 --- a/chromium/content/browser/web_package/web_package_loader.h +++ b/chromium/content/browser/web_package/web_package_loader.h @@ -5,7 +5,9 @@ #ifndef CONTENT_BROWSER_WEB_PACKAGE_WEB_PACKAGE_LOADER_H_ #define CONTENT_BROWSER_WEB_PACKAGE_WEB_PACKAGE_LOADER_H_ +#include "base/callback.h" #include "base/optional.h" +#include "base/unguessable_token.h" #include "content/common/content_export.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/system/simple_watcher.h" @@ -26,6 +28,7 @@ class SharedURLLoaderFactory; namespace content { +class SignedExchangeDevToolsProxy; class SignedExchangeHandler; class SignedExchangeHandlerFactory; class URLLoaderThrottle; @@ -43,12 +46,12 @@ class WebPackageLoader final : public network::mojom::URLLoaderClient, std::vector>()>; WebPackageLoader( - const network::ResourceResponseHead& original_response, + const network::ResourceResponseHead& outer_response, network::mojom::URLLoaderClientPtr forwarding_client, network::mojom::URLLoaderClientEndpointsPtr endpoints, url::Origin request_initiator, uint32_t url_loader_options, - int frame_tree_node_id, + std::unique_ptr devtools_proxy, scoped_refptr url_loader_factory, URLLoaderThrottlesGetter url_loader_throttles_getter, scoped_refptr request_context_getter); @@ -73,7 +76,8 @@ class WebPackageLoader final : public network::mojom::URLLoaderClient, void OnComplete(const network::URLLoaderCompletionStatus& status) override; // network::mojom::URLLoader implementation - void FollowRedirect() override; + void FollowRedirect(const base::Optional& + modified_request_headers) override; void ProceedWithResponse() override; void SetPriority(net::RequestPriority priority, int intra_priority_value) override; @@ -101,7 +105,10 @@ class WebPackageLoader final : public network::mojom::URLLoaderClient, void FinishReadingBody(int result); // This timing info is used to create a dummy redirect response. - std::unique_ptr original_response_timing_info_; + std::unique_ptr outer_response_timing_info_; + + // The outer response of signed HTTP exchange which was received from network. + const network::ResourceResponseHead outer_response_; // This client is alive until OnHTTPExchangeFound() is called. network::mojom::URLLoaderClientPtr forwarding_client_; @@ -126,7 +133,7 @@ class WebPackageLoader final : public network::mojom::URLLoaderClient, url::Origin request_initiator_; const uint32_t url_loader_options_; - const int frame_tree_node_id_; + std::unique_ptr devtools_proxy_; scoped_refptr url_loader_factory_; URLLoaderThrottlesGetter url_loader_throttles_getter_; scoped_refptr request_context_getter_; diff --git a/chromium/content/browser/web_package/web_package_prefetch_handler.cc b/chromium/content/browser/web_package/web_package_prefetch_handler.cc index 40f151b71f0..b8ced15cd0c 100644 --- a/chromium/content/browser/web_package/web_package_prefetch_handler.cc +++ b/chromium/content/browser/web_package/web_package_prefetch_handler.cc @@ -4,37 +4,29 @@ #include "content/browser/web_package/web_package_prefetch_handler.h" +#include "base/callback.h" +#include "base/feature_list.h" +#include "content/browser/web_package/signed_exchange_devtools_proxy.h" #include "content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.h" #include "content/browser/web_package/web_package_loader.h" -#include "content/browser/web_package/web_package_request_handler.h" #include "content/public/common/content_features.h" -#include "content/public/common/weak_wrapper_shared_url_loader_factory.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "net/url_request/url_request_context_getter.h" #include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" namespace content { -bool WebPackagePrefetchHandler::IsResponseForWebPackage( - const network::ResourceResponseHead& response) { - std::string mime_type; - if (base::FeatureList::IsEnabled(features::kSignedHTTPExchange) && - !response.was_fetched_via_service_worker && response.headers && - response.headers->GetMimeType(&mime_type) && - WebPackageRequestHandler::IsSupportedMimeType(mime_type)) { - return true; - } - return false; -} - WebPackagePrefetchHandler::WebPackagePrefetchHandler( - int frame_tree_node_id, + base::RepeatingCallback frame_tree_node_id_getter, + bool report_raw_headers, const network::ResourceResponseHead& response, network::mojom::URLLoaderPtr network_loader, network::mojom::URLLoaderClientRequest network_client_request, scoped_refptr network_loader_factory, url::Origin request_initiator, + const GURL& outer_request_url, URLLoaderThrottlesGetter loader_throttles_getter, ResourceContext* resource_context, scoped_refptr request_context_getter, @@ -57,8 +49,11 @@ WebPackagePrefetchHandler::WebPackagePrefetchHandler( web_package_loader_ = std::make_unique( response, std::move(client), std::move(endpoints), std::move(request_initiator), network::mojom::kURLLoadOptionNone, - frame_tree_node_id, std::move(url_loader_factory), - loader_throttles_getter, request_context_getter); + std::make_unique( + outer_request_url, response, std::move(frame_tree_node_id_getter), + base::nullopt /* devtools_navigation_token */, report_raw_headers), + std::move(url_loader_factory), loader_throttles_getter, + request_context_getter); } WebPackagePrefetchHandler::~WebPackagePrefetchHandler() = default; diff --git a/chromium/content/browser/web_package/web_package_prefetch_handler.h b/chromium/content/browser/web_package/web_package_prefetch_handler.h index cd69ad6c9b8..f6fc27cbdca 100644 --- a/chromium/content/browser/web_package/web_package_prefetch_handler.h +++ b/chromium/content/browser/web_package/web_package_prefetch_handler.h @@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_WEB_PACKAGE_WEB_PACKAGE_PREFETCH_HANDLER_H_ #define CONTENT_BROWSER_WEB_PACKAGE_WEB_PACKAGE_PREFETCH_HANDLER_H_ +#include "base/callback_forward.h" #include "base/macros.h" #include "mojo/public/cpp/bindings/binding.h" #include "services/network/public/mojom/url_loader.mojom.h" @@ -29,20 +30,19 @@ class WebPackagePrefetchHandler final : public network::mojom::URLLoaderClient { using URLLoaderThrottlesGetter = base::RepeatingCallback< std::vector>()>; - static bool IsResponseForWebPackage( - const network::ResourceResponseHead& response); - // This takes |network_loader| and |network_client| to set up the // WebPackageLoader (so that the loader can load data from the network). // |forwarding_client| is a pointer to the downstream client (typically who // creates this handler). WebPackagePrefetchHandler( - int frame_tree_node_id, + base::RepeatingCallback frame_tree_node_id_getter, + bool report_raw_headers, const network::ResourceResponseHead& response, network::mojom::URLLoaderPtr network_loader, network::mojom::URLLoaderClientRequest network_client_request, scoped_refptr network_loader_factory, url::Origin request_initiator, + const GURL& outer_request_url, URLLoaderThrottlesGetter loader_throttles_getter, ResourceContext* resource_context, scoped_refptr request_context_getter, diff --git a/chromium/content/browser/web_package/web_package_request_handler.cc b/chromium/content/browser/web_package/web_package_request_handler.cc index 8eadeabc9f2..f67d3068b54 100644 --- a/chromium/content/browser/web_package/web_package_request_handler.cc +++ b/chromium/content/browser/web_package/web_package_request_handler.cc @@ -8,6 +8,8 @@ #include "base/bind.h" #include "base/feature_list.h" +#include "content/browser/web_package/signed_exchange_devtools_proxy.h" +#include "content/browser/web_package/signed_exchange_utils.h" #include "content/browser/web_package/web_package_loader.h" #include "content/common/throttling_url_loader.h" #include "content/public/common/content_features.h" @@ -17,31 +19,37 @@ #include "services/network/public/cpp/resource_response.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/mojom/url_loader.mojom.h" +#include "third_party/blink/public/common/origin_trials/trial_token_validator.h" namespace content { // static bool WebPackageRequestHandler::IsSupportedMimeType( const std::string& mime_type) { - DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange)); return mime_type == "application/signed-exchange"; } WebPackageRequestHandler::WebPackageRequestHandler( url::Origin request_initiator, + const GURL& url, uint32_t url_loader_options, int frame_tree_node_id, + const base::UnguessableToken& devtools_navigation_token, + bool report_raw_headers, scoped_refptr url_loader_factory, URLLoaderThrottlesGetter url_loader_throttles_getter, scoped_refptr request_context_getter) : request_initiator_(std::move(request_initiator)), + url_(url), url_loader_options_(url_loader_options), frame_tree_node_id_(frame_tree_node_id), + devtools_navigation_token_(devtools_navigation_token), + report_raw_headers_(report_raw_headers), url_loader_factory_(url_loader_factory), url_loader_throttles_getter_(std::move(url_loader_throttles_getter)), request_context_getter_(std::move(request_context_getter)), weak_factory_(this) { - DCHECK(base::FeatureList::IsEnabled(features::kSignedHTTPExchange)); + DCHECK(signed_exchange_utils::IsSignedExchangeHandlingEnabled()); } WebPackageRequestHandler::~WebPackageRequestHandler() = default; @@ -68,10 +76,8 @@ bool WebPackageRequestHandler::MaybeCreateLoaderForResponse( network::mojom::URLLoaderPtr* loader, network::mojom::URLLoaderClientRequest* client_request, ThrottlingURLLoader* url_loader) { - std::string mime_type; - if (response.was_fetched_via_service_worker || !response.headers || - !response.headers->GetMimeType(&mime_type) || - !IsSupportedMimeType(mime_type)) { + if (!signed_exchange_utils::ShouldHandleAsSignedHTTPExchange( + request_initiator_.GetURL(), response)) { return false; } @@ -83,7 +89,11 @@ bool WebPackageRequestHandler::MaybeCreateLoaderForResponse( // to support SafeBrowsing checking of the content of the WebPackage. web_package_loader_ = std::make_unique( response, std::move(client), url_loader->Unbind(), - std::move(request_initiator_), url_loader_options_, frame_tree_node_id_, + std::move(request_initiator_), url_loader_options_, + std::make_unique( + std::move(url_), response, + base::BindRepeating([](int id) { return id; }, frame_tree_node_id_), + std::move(devtools_navigation_token_), report_raw_headers_), std::move(url_loader_factory_), std::move(url_loader_throttles_getter_), std::move(request_context_getter_)); return true; diff --git a/chromium/content/browser/web_package/web_package_request_handler.h b/chromium/content/browser/web_package/web_package_request_handler.h index d988475af35..40f75c63441 100644 --- a/chromium/content/browser/web_package/web_package_request_handler.h +++ b/chromium/content/browser/web_package/web_package_request_handler.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_WEB_PACKAGE_WEB_PACKAGE_REQUEST_HANDLER_H_ #include "base/memory/weak_ptr.h" +#include "base/unguessable_token.h" #include "content/browser/loader/navigation_loader_interceptor.h" #include "content/public/common/resource_type.h" #include "url/origin.h" @@ -32,8 +33,11 @@ class WebPackageRequestHandler final : public NavigationLoaderInterceptor { WebPackageRequestHandler( url::Origin request_initiator, + const GURL& url, uint32_t url_loader_options, int frame_tree_node_id, + const base::UnguessableToken& devtools_navigation_token, + bool report_raw_headers, scoped_refptr url_loader_factory, URLLoaderThrottlesGetter url_loader_throttles_getter, scoped_refptr request_context_getter); @@ -59,8 +63,11 @@ class WebPackageRequestHandler final : public NavigationLoaderInterceptor { std::unique_ptr web_package_loader_; url::Origin request_initiator_; + GURL url_; const uint32_t url_loader_options_; const int frame_tree_node_id_; + base::Optional devtools_navigation_token_; + const bool report_raw_headers_; scoped_refptr url_loader_factory_; URLLoaderThrottlesGetter url_loader_throttles_getter_; scoped_refptr request_context_getter_; diff --git a/chromium/content/browser/web_package/web_package_request_handler_browsertest.cc b/chromium/content/browser/web_package/web_package_request_handler_browsertest.cc index 038e49d457f..993fb9234c6 100644 --- a/chromium/content/browser/web_package/web_package_request_handler_browsertest.cc +++ b/chromium/content/browser/web_package/web_package_request_handler_browsertest.cc @@ -133,7 +133,7 @@ class WebPackageRequestHandlerBrowserTest static std::string ReadFile(const std::string& data_path) { base::ScopedAllowBlockingForTesting allow_io; base::FilePath root_path; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &root_path)); + CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &root_path)); std::string contents; CHECK(base::ReadFileToString(root_path.AppendASCII(data_path), &contents)); return contents; @@ -143,7 +143,7 @@ class WebPackageRequestHandlerBrowserTest std::string header_file_relative_path = data_path + kMockHeaderFileSuffix; base::ScopedAllowBlockingForTesting allow_io; base::FilePath root_path; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &root_path)); + CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &root_path)); if (!base::PathExists(root_path.AppendASCII(header_file_relative_path))) return "HTTP/1.0 200 OK\n"; return ReadFile(header_file_relative_path); @@ -153,7 +153,7 @@ class WebPackageRequestHandlerBrowserTest const std::string& data_path) { DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); base::FilePath root_path; - CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &root_path)); + CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &root_path)); net::URLRequestFilter::GetInstance()->AddUrlInterceptor( url, net::URLRequestMockHTTPJob::CreateInterceptorForSingleFile( root_path.AppendASCII(data_path))); diff --git a/chromium/content/browser/webauth/authenticator_impl.cc b/chromium/content/browser/webauth/authenticator_impl.cc index 08a8a7be414..2cc796d08e2 100644 --- a/chromium/content/browser/webauth/authenticator_impl.cc +++ b/chromium/content/browser/webauth/authenticator_impl.cc @@ -13,6 +13,7 @@ #include "base/logging.h" #include "base/rand_util.h" #include "base/timer/timer.h" +#include "build/build_config.h" #include "content/browser/bad_message.h" #include "content/browser/webauth/authenticator_type_converters.h" #include "content/public/browser/content_browser_client.h" @@ -240,6 +241,13 @@ device::CtapGetAssertionRequest CreateCtapGetAssertionRequest( request_parameter.SetUserVerification( mojo::ConvertTo( options->user_verification)); + + if (!options->cable_authentication_data.empty()) { + request_parameter.SetCableExtension( + mojo::ConvertTo< + std::vector>( + options->cable_authentication_data)); + } return request_parameter; } @@ -328,19 +336,9 @@ std::string Base64UrlEncode(const base::span input) { } // namespace AuthenticatorImpl::AuthenticatorImpl(RenderFrameHost* render_frame_host) - : WebContentsObserver(WebContents::FromRenderFrameHost(render_frame_host)), - render_frame_host_(render_frame_host), - timer_(std::make_unique()), - binding_(this), - weak_factory_(this) { - DCHECK(render_frame_host_); - DCHECK(timer_); - - protocols_.insert(device::FidoTransportProtocol::kUsbHumanInterfaceDevice); - if (base::FeatureList::IsEnabled(features::kWebAuthBle)) { - protocols_.insert(device::FidoTransportProtocol::kBluetoothLowEnergy); - } -} + : AuthenticatorImpl(render_frame_host, + nullptr /* connector */, + std::make_unique()) {} AuthenticatorImpl::AuthenticatorImpl(RenderFrameHost* render_frame_host, service_manager::Connector* connector, @@ -353,6 +351,21 @@ AuthenticatorImpl::AuthenticatorImpl(RenderFrameHost* render_frame_host, weak_factory_(this) { DCHECK(render_frame_host_); DCHECK(timer_); + + protocols_.insert(device::FidoTransportProtocol::kUsbHumanInterfaceDevice); + if (base::FeatureList::IsEnabled(features::kWebAuthBle)) { + protocols_.insert(device::FidoTransportProtocol::kBluetoothLowEnergy); + } + + if (base::FeatureList::IsEnabled(features::kWebAuthCable)) { + protocols_.insert( + device::FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy); + } +#if defined(OS_MACOSX) + if (base::FeatureList::IsEnabled(features::kWebAuthTouchId)) { + protocols_.insert(device::FidoTransportProtocol::kInternal); + } +#endif } AuthenticatorImpl::~AuthenticatorImpl() {} @@ -378,14 +391,13 @@ std::string AuthenticatorImpl::SerializeCollectedClientDataToJson( client_data.SetKey(kChallengeKey, base::Value(Base64UrlEncode(challenge))); client_data.SetKey(kOriginKey, base::Value(origin.Serialize())); - base::DictionaryValue token_binding_dict; - static constexpr char kTokenBindingStatusKey[] = "status"; - static constexpr char kTokenBindingIdKey[] = "id"; - static constexpr char kTokenBindingSupportedStatus[] = "supported"; - static constexpr char kTokenBindingNotSupportedStatus[] = "not-supported"; - static constexpr char kTokenBindingPresentStatus[] = "present"; - if (token_binding) { + base::DictionaryValue token_binding_dict; + static constexpr char kTokenBindingStatusKey[] = "status"; + static constexpr char kTokenBindingIdKey[] = "id"; + static constexpr char kTokenBindingSupportedStatus[] = "supported"; + static constexpr char kTokenBindingPresentStatus[] = "present"; + if (token_binding->empty()) { token_binding_dict.SetKey(kTokenBindingStatusKey, base::Value(kTokenBindingSupportedStatus)); @@ -395,12 +407,9 @@ std::string AuthenticatorImpl::SerializeCollectedClientDataToJson( token_binding_dict.SetKey(kTokenBindingIdKey, base::Value(Base64UrlEncode(*token_binding))); } - } else { - token_binding_dict.SetKey(kTokenBindingStatusKey, - base::Value(kTokenBindingNotSupportedStatus)); - } - client_data.SetKey(kTokenBindingKey, std::move(token_binding_dict)); + client_data.SetKey(kTokenBindingKey, std::move(token_binding_dict)); + } if (base::RandDouble() < 0.2) { // An extra key is sometimes added to ensure that RPs do not make @@ -510,6 +519,11 @@ void AuthenticatorImpl::MakeCredential( attestation_preference_ = options->attestation; + // Communication using Cable protocol is only supported for GetAssertion + // request on CTAP2 devices. + protocols_.erase( + device::FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy); + if (base::FeatureList::IsEnabled(features::kWebAuthCtap2)) { auto authenticator_selection_criteria = options->authenticator_selection @@ -603,18 +617,19 @@ void AuthenticatorImpl::GetAssertion( FilterCredentialList(std::move(options->allow_credentials)); // There are two different descriptions of what should happen when - // "allowCredentials" is empty. + // "allowCredentials" is empty for U2F. // a) WebAuthN 6.2.3 step 6[1] implies "NotAllowedError". // b) CTAP step 7.2 step 2[2] says the device should error out with // "CTAP2_ERR_OPTION_NOT_SUPPORTED". This also resolves to "NotAllowedError". // The behavior in both cases is consistent with the current implementation. - // TODO(crbug.com/831712): When CTAP2 authenticators are supported, this check - // should be enforced by handlers in fido/device on a per-device basis. + // When CTAP2 is enabled, however, this check is done by handlers in + // fido/device on a per-device basis. // [1] https://w3c.github.io/webauthn/#authenticatorgetassertion // [2] // https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html - if (handles.empty()) { + if (!base::FeatureList::IsEnabled(features::kWebAuthCtap2) && + handles.empty()) { InvokeCallbackAndCleanup( std::move(callback), webauth::mojom::AuthenticatorStatus::EMPTY_ALLOW_CREDENTIALS, nullptr); @@ -646,6 +661,10 @@ void AuthenticatorImpl::GetAssertion( base::BindOnce(&AuthenticatorImpl::OnSignResponse, weak_factory_.GetWeakPtr())); } else { + // Communication using Cable protocol is only supported for CTAP2 devices. + protocols_.erase( + device::FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy); + u2f_request_ = device::U2fSign::TrySign( connector_, protocols_, handles, ConstructClientDataHash(client_data_json_), application_parameter, diff --git a/chromium/content/browser/webauth/authenticator_impl_unittest.cc b/chromium/content/browser/webauth/authenticator_impl_unittest.cc index f309233ee08..26ad5fd258c 100644 --- a/chromium/content/browser/webauth/authenticator_impl_unittest.cc +++ b/chromium/content/browser/webauth/authenticator_impl_unittest.cc @@ -69,8 +69,8 @@ typedef struct { constexpr char kTestOrigin1[] = "https://a.google.com"; constexpr char kTestRelyingPartyId[] = "google.com"; -// Test data. CBOR test data can be built using the given diagnostic strings -// and the utility at "http://cbor.me/". +// Test data. CBOR test data can be built using the given +// diagnostic strings and the utility at "http://CBOR.me/". constexpr int32_t kCoseEs256 = -7; constexpr uint8_t kTestChallengeBytes[] = { @@ -80,13 +80,11 @@ constexpr uint8_t kTestChallengeBytes[] = { constexpr char kTestRegisterClientDataJsonString[] = R"({"challenge":"aHE0loIi7BcgLkJQX47SsWriLxa7BbiMJdueYCZF8UE","origin":)" - R"("https://a.google.com","tokenBinding":{"status":"not-supported"},)" - R"("type":"webauthn.create"})"; + R"("https://a.google.com", "type":"webauthn.create"})"; constexpr char kTestSignClientDataJsonString[] = R"({"challenge":"aHE0loIi7BcgLkJQX47SsWriLxa7BbiMJdueYCZF8UE","origin":)" - R"("https://a.google.com","tokenBinding":{"status":"not-supported"},)" - R"("type":"webauthn.get"})"; + R"("https://a.google.com", "type":"webauthn.get"})"; constexpr OriginClaimedAuthorityPair kValidRelyingPartyTestCases[] = { {"http://localhost", "localhost"}, @@ -337,11 +335,16 @@ class AuthenticatorImplTest : public content::RenderViewHostTestHarness { options->relying_party_id = origin_url.host(); options->appid = appid; - TestGetAssertionCallback cb; - authenticator->GetAssertion(std::move(options), cb.callback()); - cb.WaitForCallback(); + TestGetAssertionCallback callback_receiver; + authenticator->GetAssertion(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); - return cb.status(); + return callback_receiver.status(); + } + + bool SupportsTransportProtocol(::device::FidoTransportProtocol protocol) { + return base::ContainsKey(authenticator_impl_->protocols_, protocol); } private: @@ -361,10 +364,11 @@ TEST_F(AuthenticatorImplTest, MakeCredentialOriginAndRpIds) { PublicKeyCredentialCreationOptionsPtr options = GetTestPublicKeyCredentialCreationOptions(); options->relying_party->id = test_case.claimed_authority; - TestMakeCredentialCallback cb; - authenticator->MakeCredential(std::move(options), cb.callback()); - cb.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::INVALID_DOMAIN, cb.status()); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::INVALID_DOMAIN, callback_receiver.status()); } // These instances pass the origin and relying party checks and return at @@ -380,10 +384,12 @@ TEST_F(AuthenticatorImplTest, MakeCredentialOriginAndRpIds) { options->relying_party->id = test_case.claimed_authority; options->public_key_parameters = GetTestPublicKeyCredentialParameters(123); - TestMakeCredentialCallback cb; - authenticator->MakeCredential(std::move(options), cb.callback()); - cb.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::ALGORITHM_UNSUPPORTED, cb.status()); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::ALGORITHM_UNSUPPORTED, + callback_receiver.status()); } } @@ -397,10 +403,12 @@ TEST_F(AuthenticatorImplTest, MakeCredentialNoSupportedAlgorithm) { GetTestPublicKeyCredentialCreationOptions(); options->public_key_parameters = GetTestPublicKeyCredentialParameters(123); - TestMakeCredentialCallback cb; - authenticator->MakeCredential(std::move(options), cb.callback()); - cb.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::ALGORITHM_UNSUPPORTED, cb.status()); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::ALGORITHM_UNSUPPORTED, + callback_receiver.status()); } // Test that service returns USER_VERIFICATION_UNSUPPORTED if user verification @@ -413,10 +421,11 @@ TEST_F(AuthenticatorImplTest, GetAssertionUserVerification) { GetTestPublicKeyCredentialRequestOptions(); options->user_verification = webauth::mojom::UserVerificationRequirement::REQUIRED; - TestGetAssertionCallback cb; - authenticator->GetAssertion(std::move(options), cb.callback()); - cb.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::USER_VERIFICATION_UNSUPPORTED, cb.status()); + TestGetAssertionCallback callback_receiver; + authenticator->GetAssertion(std::move(options), callback_receiver.callback()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::USER_VERIFICATION_UNSUPPORTED, + callback_receiver.status()); } // Test that service returns AUTHENTICATOR_CRITERIA_UNSUPPORTED if user @@ -430,11 +439,12 @@ TEST_F(AuthenticatorImplTest, MakeCredentialUserVerification) { options->authenticator_selection->user_verification = webauth::mojom::UserVerificationRequirement::REQUIRED; - TestMakeCredentialCallback cb; - authenticator->MakeCredential(std::move(options), cb.callback()); - cb.WaitForCallback(); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); EXPECT_EQ(AuthenticatorStatus::AUTHENTICATOR_CRITERIA_UNSUPPORTED, - cb.status()); + callback_receiver.status()); } // Test that service returns AUTHENTICATOR_CRITERIA_UNSUPPORTED if resident key @@ -447,11 +457,12 @@ TEST_F(AuthenticatorImplTest, MakeCredentialResidentKey) { GetTestPublicKeyCredentialCreationOptions(); options->authenticator_selection->require_resident_key = true; - TestMakeCredentialCallback cb; - authenticator->MakeCredential(std::move(options), cb.callback()); - cb.WaitForCallback(); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); EXPECT_EQ(AuthenticatorStatus::AUTHENTICATOR_CRITERIA_UNSUPPORTED, - cb.status()); + callback_receiver.status()); } // Test that service returns AUTHENTICATOR_CRITERIA_UNSUPPORTED if a platform @@ -465,11 +476,12 @@ TEST_F(AuthenticatorImplTest, MakeCredentialPlatformAuthenticator) { options->authenticator_selection->authenticator_attachment = webauth::mojom::AuthenticatorAttachment::PLATFORM; - TestMakeCredentialCallback cb; - authenticator->MakeCredential(std::move(options), cb.callback()); - cb.WaitForCallback(); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); EXPECT_EQ(AuthenticatorStatus::AUTHENTICATOR_CRITERIA_UNSUPPORTED, - cb.status()); + callback_receiver.status()); } // Parses its arguments as JSON and expects that all the keys in the first are @@ -515,8 +527,7 @@ TEST_F(AuthenticatorImplTest, TestTokenBindingClientData) { const std::vector< std::pair>, const char*>> kTestCases = { - std::make_pair(base::nullopt, - R"({"tokenBinding":{"status":"not-supported"}})"), + std::make_pair(base::nullopt, ""), std::make_pair(std::vector{}, R"({"tokenBinding":{"status":"supported"}})"), std::make_pair( @@ -528,9 +539,15 @@ TEST_F(AuthenticatorImplTest, TestTokenBindingClientData) { const auto& token_binding = test.first; const std::string expected_json_subset = test.second; SCOPED_TRACE(expected_json_subset); + const std::string client_data = + GetTokenBindingTestClientDataJSON(token_binding); - CheckJSONIsSubsetOfJSON(expected_json_subset, - GetTokenBindingTestClientDataJSON(token_binding)); + if (!expected_json_subset.empty()) { + CheckJSONIsSubsetOfJSON(expected_json_subset, client_data); + } else { + EXPECT_TRUE(client_data.find("tokenBinding") == std::string::npos) + << client_data; + } } } @@ -538,7 +555,7 @@ TEST_F(AuthenticatorImplTest, TestMakeCredentialTimeout) { SimulateNavigation(GURL(kTestOrigin1)); PublicKeyCredentialCreationOptionsPtr options = GetTestPublicKeyCredentialCreationOptions(); - TestMakeCredentialCallback cb; + TestMakeCredentialCallback callback_receiver; // Set up service_manager::Connector for tests. auto fake_hid_manager = std::make_unique(); @@ -560,13 +577,14 @@ TEST_F(AuthenticatorImplTest, TestMakeCredentialTimeout) { AuthenticatorPtr authenticator = ConnectToAuthenticator(connector.get(), std::move(timer)); - authenticator->MakeCredential(std::move(options), cb.callback()); + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); // Trigger timer. base::RunLoop().RunUntilIdle(); task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); - cb.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, cb.status()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status()); } // Verify behavior for various combinations of origins and RP IDs. @@ -584,10 +602,11 @@ TEST_F(AuthenticatorImplTest, GetAssertionOriginAndRpIds) { GetTestPublicKeyCredentialRequestOptions(); options->relying_party_id = test_case.claimed_authority; - TestGetAssertionCallback cb; - authenticator->GetAssertion(std::move(options), cb.callback()); - cb.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::INVALID_DOMAIN, cb.status()); + TestGetAssertionCallback callback_receiver; + authenticator->GetAssertion(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::INVALID_DOMAIN, callback_receiver.status()); } } @@ -640,7 +659,7 @@ TEST_F(AuthenticatorImplTest, TestGetAssertionTimeout) { SimulateNavigation(GURL(kTestOrigin1)); PublicKeyCredentialRequestOptionsPtr options = GetTestPublicKeyCredentialRequestOptions(); - TestGetAssertionCallback cb; + TestGetAssertionCallback callback_receiver; // Set up service_manager::Connector for tests. auto fake_hid_manager = std::make_unique(); @@ -662,13 +681,13 @@ TEST_F(AuthenticatorImplTest, TestGetAssertionTimeout) { AuthenticatorPtr authenticator = ConnectToAuthenticator(connector.get(), std::move(timer)); - authenticator->GetAssertion(std::move(options), cb.callback()); + authenticator->GetAssertion(std::move(options), callback_receiver.callback()); // Trigger timer. base::RunLoop().RunUntilIdle(); task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); - cb.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, cb.status()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status()); } TEST_F(AuthenticatorImplTest, OversizedCredentialId) { @@ -698,18 +717,148 @@ TEST_F(AuthenticatorImplTest, OversizedCredentialId) { options->allow_credentials.emplace_back(std::move(credential)); - TestGetAssertionCallback cb; - authenticator->GetAssertion(std::move(options), cb.callback()); - cb.WaitForCallback(); + TestGetAssertionCallback callback_receiver; + authenticator->GetAssertion(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); if (should_be_valid) { - EXPECT_EQ(AuthenticatorStatus::SUCCESS, cb.status()); + EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status()); } else { - EXPECT_EQ(AuthenticatorStatus::CREDENTIAL_NOT_RECOGNIZED, cb.status()); + EXPECT_EQ(AuthenticatorStatus::CREDENTIAL_NOT_RECOGNIZED, + callback_receiver.status()); } } } +TEST_F(AuthenticatorImplTest, TestCableDiscoveryEnabledWithSwitch) { + TestServiceManagerContext service_manager_context; + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + std::vector{features::kWebAuthCtap2, + features::kWebAuthCable}, + std::vector{}); + + SimulateNavigation(GURL(kTestOrigin1)); + PublicKeyCredentialRequestOptionsPtr options = + GetTestPublicKeyCredentialRequestOptions(); + TestGetAssertionCallback callback_receiver; + + // Set up service_manager::Connector for tests. + auto fake_hid_manager = std::make_unique(); + service_manager::mojom::ConnectorRequest request; + auto connector = service_manager::Connector::Create(&request); + service_manager::Connector::TestApi test_api(connector.get()); + test_api.OverrideBinderForTesting( + service_manager::Identity(device::mojom::kServiceName), + device::mojom::HidManager::Name_, + base::Bind(&device::FakeHidManager::AddBinding, + base::Unretained(fake_hid_manager.get()))); + + // Set up a timer for testing. + auto task_runner = base::MakeRefCounted( + base::Time::Now(), base::TimeTicks::Now()); + auto timer = + std::make_unique(task_runner->GetMockTickClock()); + timer->SetTaskRunner(task_runner); + AuthenticatorPtr authenticator = + ConnectToAuthenticator(connector.get(), std::move(timer)); + authenticator->GetAssertion(std::move(options), callback_receiver.callback()); + + // Trigger timer. + base::RunLoop().RunUntilIdle(); + task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); + callback_receiver.WaitForCallback(); + + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status()); + EXPECT_TRUE(SupportsTransportProtocol( + device::FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy)); +} + +TEST_F(AuthenticatorImplTest, TestCableDiscoveryDisabledForMakeCredential) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + std::vector{features::kWebAuthCtap2, + features::kWebAuthCable}, + std::vector{}); + + SimulateNavigation(GURL(kTestOrigin1)); + PublicKeyCredentialCreationOptionsPtr options = + GetTestPublicKeyCredentialCreationOptions(); + TestMakeCredentialCallback callback_receiver; + + // Set up service_manager::Connector for tests. + auto fake_hid_manager = std::make_unique(); + service_manager::mojom::ConnectorRequest request; + auto connector = service_manager::Connector::Create(&request); + service_manager::Connector::TestApi test_api(connector.get()); + test_api.OverrideBinderForTesting( + service_manager::Identity(device::mojom::kServiceName), + device::mojom::HidManager::Name_, + base::Bind(&device::FakeHidManager::AddBinding, + base::Unretained(fake_hid_manager.get()))); + + // Set up a timer for testing. + auto task_runner = base::MakeRefCounted( + base::Time::Now(), base::TimeTicks::Now()); + auto timer = + std::make_unique(task_runner->GetMockTickClock()); + timer->SetTaskRunner(task_runner); + AuthenticatorPtr authenticator = + ConnectToAuthenticator(connector.get(), std::move(timer)); + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + + // Trigger timer. + base::RunLoop().RunUntilIdle(); + task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); + callback_receiver.WaitForCallback(); + + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status()); + EXPECT_FALSE(SupportsTransportProtocol( + device::FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy)); +} + +TEST_F(AuthenticatorImplTest, TestCableDiscoveryDisabledWithoutSwitch) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kWebAuthCtap2); + + SimulateNavigation(GURL(kTestOrigin1)); + PublicKeyCredentialRequestOptionsPtr options = + GetTestPublicKeyCredentialRequestOptions(); + TestGetAssertionCallback callback_receiver; + + // Set up service_manager::Connector for tests. + auto fake_hid_manager = std::make_unique(); + service_manager::mojom::ConnectorRequest request; + auto connector = service_manager::Connector::Create(&request); + service_manager::Connector::TestApi test_api(connector.get()); + test_api.OverrideBinderForTesting( + service_manager::Identity(device::mojom::kServiceName), + device::mojom::HidManager::Name_, + base::Bind(&device::FakeHidManager::AddBinding, + base::Unretained(fake_hid_manager.get()))); + + // Set up a timer for testing. + auto task_runner = base::MakeRefCounted( + base::Time::Now(), base::TimeTicks::Now()); + auto timer = + std::make_unique(task_runner->GetMockTickClock()); + timer->SetTaskRunner(task_runner); + AuthenticatorPtr authenticator = + ConnectToAuthenticator(connector.get(), std::move(timer)); + authenticator->GetAssertion(std::move(options), callback_receiver.callback()); + + // Trigger timer. + base::RunLoop().RunUntilIdle(); + task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); + callback_receiver.WaitForCallback(); + + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status()); + EXPECT_FALSE(SupportsTransportProtocol( + device::FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy)); +} + TEST_F(AuthenticatorImplTest, TestU2fDeviceDoesNotSupportMakeCredential) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature(features::kWebAuthCtap2); @@ -717,7 +866,7 @@ TEST_F(AuthenticatorImplTest, TestU2fDeviceDoesNotSupportMakeCredential) { SimulateNavigation(GURL(kTestOrigin1)); PublicKeyCredentialCreationOptionsPtr options = GetTestPublicKeyCredentialCreationOptions(); - TestMakeCredentialCallback cb; + TestMakeCredentialCallback callback_receiver; // Set up service_manager::Connector for tests. auto fake_hid_manager = std::make_unique(); @@ -734,13 +883,14 @@ TEST_F(AuthenticatorImplTest, TestU2fDeviceDoesNotSupportMakeCredential) { ConnectToAuthenticator(connector.get(), std::move(timer)); device::test::ScopedVirtualFidoDevice virtual_device; - authenticator->MakeCredential(std::move(options), cb.callback()); + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); // Trigger timer. base::RunLoop().RunUntilIdle(); task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); - cb.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, cb.status()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status()); } TEST_F(AuthenticatorImplTest, TestU2fDeviceDoesNotSupportGetAssertion) { @@ -750,7 +900,7 @@ TEST_F(AuthenticatorImplTest, TestU2fDeviceDoesNotSupportGetAssertion) { SimulateNavigation(GURL(kTestOrigin1)); PublicKeyCredentialRequestOptionsPtr options = GetTestPublicKeyCredentialRequestOptions(); - TestGetAssertionCallback cb; + TestGetAssertionCallback callback_receiver; // Set up service_manager::Connector for tests. auto fake_hid_manager = std::make_unique(); @@ -767,13 +917,49 @@ TEST_F(AuthenticatorImplTest, TestU2fDeviceDoesNotSupportGetAssertion) { ConnectToAuthenticator(connector.get(), std::move(timer)); device::test::ScopedVirtualFidoDevice virtual_device; - authenticator->GetAssertion(std::move(options), cb.callback()); + authenticator->GetAssertion(std::move(options), callback_receiver.callback()); // Trigger timer. base::RunLoop().RunUntilIdle(); task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); - cb.WaitForCallback(); - EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, cb.status()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status()); +} + +TEST_F(AuthenticatorImplTest, Ctap2AcceptsEmptyAllowCredentials) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kWebAuthCtap2); + + SimulateNavigation(GURL(kTestOrigin1)); + PublicKeyCredentialRequestOptionsPtr options = + GetTestPublicKeyCredentialRequestOptions(); + options->allow_credentials.clear(); + TestGetAssertionCallback callback_receiver; + + // Set up service_manager::Connector for tests. + auto fake_hid_manager = std::make_unique(); + service_manager::mojom::ConnectorRequest request; + auto connector = service_manager::Connector::Create(&request); + + // Set up a timer for testing. + auto task_runner = base::MakeRefCounted( + base::Time::Now(), base::TimeTicks::Now()); + auto timer = + std::make_unique(task_runner->GetMockTickClock()); + timer->SetTaskRunner(task_runner); + AuthenticatorPtr authenticator = + ConnectToAuthenticator(connector.get(), std::move(timer)); + + device::test::ScopedVirtualFidoDevice virtual_device; + authenticator->GetAssertion(std::move(options), callback_receiver.callback()); + + // Trigger timer. + base::RunLoop().RunUntilIdle(); + task_runner->FastForwardBy(base::TimeDelta::FromMinutes(1)); + callback_receiver.WaitForCallback(); + // Doesn't error out with EMPTY_ALLOW_CREDENTIALS but continues to a + // NOT_ALLOWED_ERROR. + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status()); } TEST_F(AuthenticatorImplTest, GetAssertionWithEmptyAllowCredentials) { @@ -786,11 +972,122 @@ TEST_F(AuthenticatorImplTest, GetAssertionWithEmptyAllowCredentials) { GetTestPublicKeyCredentialRequestOptions(); options->allow_credentials.clear(); - TestGetAssertionCallback cb; - authenticator->GetAssertion(std::move(options), cb.callback()); - cb.WaitForCallback(); + TestGetAssertionCallback callback_receiver; + authenticator->GetAssertion(std::move(options), callback_receiver.callback()); + callback_receiver.WaitForCallback(); + + EXPECT_EQ(AuthenticatorStatus::EMPTY_ALLOW_CREDENTIALS, + callback_receiver.status()); +} + +TEST_F(AuthenticatorImplTest, MakeCredentialAlreadyRegistered) { + device::test::ScopedVirtualFidoDevice scoped_virtual_device; + TestServiceManagerContext service_manager_context; - EXPECT_EQ(AuthenticatorStatus::EMPTY_ALLOW_CREDENTIALS, cb.status()); + SimulateNavigation(GURL(kTestOrigin1)); + AuthenticatorPtr authenticator = ConnectToAuthenticator(); + PublicKeyCredentialCreationOptionsPtr options = + GetTestPublicKeyCredentialCreationOptions(); + + // Exclude the one already registered credential. + options->exclude_credentials = GetTestAllowCredentials(); + ASSERT_TRUE(scoped_virtual_device.mutable_state()->InjectRegistration( + options->exclude_credentials[0]->id, kTestRelyingPartyId)); + + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + + EXPECT_EQ(AuthenticatorStatus::CREDENTIAL_EXCLUDED, + callback_receiver.status()); +} + +TEST_F(AuthenticatorImplTest, MakeCredentialPendingRequest) { + device::test::ScopedVirtualFidoDevice scoped_virtual_device; + TestServiceManagerContext service_manager_context; + + SimulateNavigation(GURL(kTestOrigin1)); + AuthenticatorPtr authenticator = ConnectToAuthenticator(); + + // Make first request. + PublicKeyCredentialCreationOptionsPtr options = + GetTestPublicKeyCredentialCreationOptions(); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + + // Make second request. + // TODO(crbug.com/785955): Rework to ensure there are potential race + // conditions once we have VirtualAuthenticatorEnvironment. + PublicKeyCredentialCreationOptionsPtr options2 = + GetTestPublicKeyCredentialCreationOptions(); + TestMakeCredentialCallback callback_receiver2; + authenticator->MakeCredential(std::move(options2), + callback_receiver2.callback()); + callback_receiver2.WaitForCallback(); + + EXPECT_EQ(AuthenticatorStatus::PENDING_REQUEST, callback_receiver2.status()); +} + +TEST_F(AuthenticatorImplTest, GetAssertionPendingRequest) { + device::test::ScopedVirtualFidoDevice scoped_virtual_device; + TestServiceManagerContext service_manager_context; + + SimulateNavigation(GURL(kTestOrigin1)); + AuthenticatorPtr authenticator = ConnectToAuthenticator(); + + // Make first request. + PublicKeyCredentialRequestOptionsPtr options = + GetTestPublicKeyCredentialRequestOptions(); + TestGetAssertionCallback callback_receiver; + authenticator->GetAssertion(std::move(options), callback_receiver.callback()); + + // Make second request. + // TODO(crbug.com/785955): Rework to ensure there are potential race + // conditions once we have VirtualAuthenticatorEnvironment. + PublicKeyCredentialRequestOptionsPtr options2 = + GetTestPublicKeyCredentialRequestOptions(); + TestGetAssertionCallback callback_receiver2; + authenticator->GetAssertion(std::move(options2), + callback_receiver2.callback()); + callback_receiver2.WaitForCallback(); + + EXPECT_EQ(AuthenticatorStatus::PENDING_REQUEST, callback_receiver2.status()); +} + +TEST_F(AuthenticatorImplTest, InvalidResponse) { + device::test::ScopedVirtualFidoDevice scoped_virtual_device; + TestServiceManagerContext service_manager_context; + + scoped_virtual_device.mutable_state()->simulate_invalid_response = true; + + SimulateNavigation(GURL(kTestOrigin1)); + AuthenticatorPtr authenticator = ConnectToAuthenticator(); + + { + PublicKeyCredentialRequestOptionsPtr options = + GetTestPublicKeyCredentialRequestOptions(); + TestGetAssertionCallback callback_receiver; + authenticator->GetAssertion(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, + callback_receiver.status()); + } + + { + PublicKeyCredentialCreationOptionsPtr options = + GetTestPublicKeyCredentialCreationOptions(); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, + callback_receiver.status()); + } } enum class IndividualAttestation { @@ -894,10 +1191,11 @@ class AuthenticatorContentBrowserClientTest : public AuthenticatorImplTest { options->relying_party->id = "example.com"; options->adjusted_timeout = base::TimeDelta::FromSeconds(1); options->attestation = test.attestation_requested; - TestMakeCredentialCallback cb; - authenticator->MakeCredential(std::move(options), cb.callback()); - cb.WaitForCallback(); - ASSERT_EQ(test.expected_status, cb.status()); + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + ASSERT_EQ(test.expected_status, callback_receiver.status()); if (test.expected_status != AuthenticatorStatus::SUCCESS) { ASSERT_STREQ("", test.expected_attestation_format); @@ -905,7 +1203,7 @@ class AuthenticatorContentBrowserClientTest : public AuthenticatorImplTest { } base::Optional attestation_value = - CBORReader::Read(cb.value()->attestation_object); + CBORReader::Read(callback_receiver.value()->attestation_object); ASSERT_TRUE(attestation_value); ASSERT_TRUE(attestation_value->is_map()); const auto& attestation = attestation_value->GetMap(); @@ -954,8 +1252,8 @@ class AuthenticatorContentBrowserClientTest : public AuthenticatorImplTest { << "', but expected to find '" << expected << "'"; } - // Asserts that the webauthn attestation CBOR map in |attestation| contains - // a single X.509 certificate containing |substring|. + // Asserts that the webauthn attestation CBOR map in + // |attestation| contains a single X.509 certificate containing |substring|. static void ExpectCertificateContainingSubstring( const CBORValue::MapValue& attestation, const std::string& substring) { diff --git a/chromium/content/browser/webauth/authenticator_type_converters.cc b/chromium/content/browser/webauth/authenticator_type_converters.cc index c8d238b97ad..47f14b163a7 100644 --- a/chromium/content/browser/webauth/authenticator_type_converters.cc +++ b/chromium/content/browser/webauth/authenticator_type_converters.cc @@ -7,6 +7,7 @@ #include #include "device/fido/fido_constants.h" +#include "device/fido/fido_parsing_utils.h" namespace mojo { @@ -19,6 +20,7 @@ using ::webauth::mojom::PublicKeyCredentialDescriptorPtr; using ::webauth::mojom::AuthenticatorSelectionCriteriaPtr; using ::webauth::mojom::AuthenticatorAttachment; using ::webauth::mojom::UserVerificationRequirement; +using ::webauth::mojom::CableAuthenticationPtr; // static ::device::FidoTransportProtocol @@ -77,9 +79,7 @@ TypeConverter, for (const auto& credential : input) { credential_descriptors.emplace_back(::device::PublicKeyCredentialDescriptor( - device::to_string( - ConvertTo<::device::CredentialType>(credential->type)), - credential->id)); + ConvertTo<::device::CredentialType>(credential->type), credential->id)); } return credential_descriptors; } @@ -161,4 +161,35 @@ TypeConverter<::device::PublicKeyCredentialUserEntity, return user_entity; } +// static +std::vector<::device::FidoCableDiscovery::CableDiscoveryData> +TypeConverter, + std::vector>:: + Convert(const std::vector& input) { + std::vector<::device::FidoCableDiscovery::CableDiscoveryData> discovery_data; + discovery_data.reserve(input.size()); + + for (const auto& data : input) { + ::device::FidoCableDiscovery::EidArray client_eid; + DCHECK_EQ(client_eid.size(), data->client_eid.size()); + std::copy(data->client_eid.begin(), data->client_eid.end(), + client_eid.begin()); + + ::device::FidoCableDiscovery::EidArray authenticator_eid; + DCHECK_EQ(authenticator_eid.size(), data->authenticator_eid.size()); + std::copy(data->authenticator_eid.begin(), data->authenticator_eid.end(), + authenticator_eid.begin()); + + ::device::FidoCableDiscovery::SessionKeyArray session_key; + DCHECK_EQ(session_key.size(), data->session_pre_key.size()); + std::copy(data->session_pre_key.begin(), data->session_pre_key.end(), + session_key.begin()); + + discovery_data.push_back(::device::FidoCableDiscovery::CableDiscoveryData{ + data->version, client_eid, authenticator_eid, session_key}); + } + + return discovery_data; +} + } // namespace mojo diff --git a/chromium/content/browser/webauth/authenticator_type_converters.h b/chromium/content/browser/webauth/authenticator_type_converters.h index 0f5c2a1a13c..97431318615 100644 --- a/chromium/content/browser/webauth/authenticator_type_converters.h +++ b/chromium/content/browser/webauth/authenticator_type_converters.h @@ -8,6 +8,7 @@ #include #include "device/fido/authenticator_selection_criteria.h" +#include "device/fido/fido_cable_discovery.h" #include "device/fido/fido_transport_protocol.h" #include "device/fido/public_key_credential_descriptor.h" #include "device/fido/public_key_credential_params.h" @@ -89,6 +90,14 @@ struct TypeConverter<::device::PublicKeyCredentialUserEntity, const ::webauth::mojom::PublicKeyCredentialUserEntityPtr& input); }; +template <> +struct TypeConverter< + std::vector<::device::FidoCableDiscovery::CableDiscoveryData>, + std::vector<::webauth::mojom::CableAuthenticationPtr>> { + static std::vector<::device::FidoCableDiscovery::CableDiscoveryData> Convert( + const std::vector<::webauth::mojom::CableAuthenticationPtr>& input); +}; + } // namespace mojo #endif // CONTENT_BROWSER_WEBAUTH_AUTHENTICATOR_TYPE_CONVERTERS_H_ diff --git a/chromium/content/browser/webauth/virtual_authenticator.cc b/chromium/content/browser/webauth/virtual_authenticator.cc index f830a24927a..71cdfac4400 100644 --- a/chromium/content/browser/webauth/virtual_authenticator.cc +++ b/chromium/content/browser/webauth/virtual_authenticator.cc @@ -9,6 +9,7 @@ #include "base/guid.h" #include "crypto/ec_private_key.h" +#include "device/fido/virtual_u2f_device.h" namespace content { @@ -26,7 +27,7 @@ void VirtualAuthenticator::AddBinding( } std::unique_ptr<::device::FidoDevice> VirtualAuthenticator::ConstructDevice() { - return std::make_unique<::device::VirtualFidoDevice>(state_); + return std::make_unique<::device::VirtualU2fDevice>(state_); } void VirtualAuthenticator::GetUniqueId(GetUniqueIdCallback callback) { diff --git a/chromium/content/browser/webauth/webauth_browsertest.cc b/chromium/content/browser/webauth/webauth_browsertest.cc index b86f292e90d..59b558ffc01 100644 --- a/chromium/content/browser/webauth/webauth_browsertest.cc +++ b/chromium/content/browser/webauth/webauth_browsertest.cc @@ -311,7 +311,8 @@ class WebAuthLocalClientBrowserTest : public WebAuthBrowserTestBase { std::move(rp), std::move(user), kTestChallenge, std::move(parameters), base::TimeDelta::FromSeconds(30), std::vector(), - nullptr, webauth::mojom::AttestationConveyancePreference::NONE); + nullptr, webauth::mojom::AttestationConveyancePreference::NONE, + nullptr); return mojo_options; } @@ -333,8 +334,8 @@ class WebAuthLocalClientBrowserTest : public WebAuthBrowserTestBase { auto mojo_options = webauth::mojom::PublicKeyCredentialRequestOptions::New( kTestChallenge, base::TimeDelta::FromSeconds(30), "acme.com", std::move(credentials), - webauth::mojom::UserVerificationRequirement::PREFERRED, base::nullopt); - + webauth::mojom::UserVerificationRequirement::PREFERRED, base::nullopt, + std::vector()); return mojo_options; } diff --git a/chromium/content/browser/webrtc/webrtc_audio_browsertest.cc b/chromium/content/browser/webrtc/webrtc_audio_browsertest.cc index 5b66e50b2a3..1a57c79ba8c 100644 --- a/chromium/content/browser/webrtc/webrtc_audio_browsertest.cc +++ b/chromium/content/browser/webrtc/webrtc_audio_browsertest.cc @@ -4,10 +4,12 @@ #include "base/command_line.h" #include "base/files/file_util.h" +#include "base/test/scoped_feature_list.h" #include "base/threading/platform_thread.h" #include "build/build_config.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/webrtc/webrtc_content_browsertest_base.h" +#include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/common/webrtc_ip_handling_policy.h" #include "content/public/test/browser_test_utils.h" @@ -15,22 +17,28 @@ #include "content/public/test/test_utils.h" #include "media/base/media_switches.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gtest/include/gtest/gtest-param-test.h" namespace content { -#if defined(OS_ANDROID) && defined(ADDRESS_SANITIZER) -// Renderer crashes under Android ASAN: https://crbug.com/408496. -#define MAYBE_WebRtcAudioBrowserTest DISABLED_WebRtcAudioBrowserTest -#else -#define MAYBE_WebRtcAudioBrowserTest WebRtcAudioBrowserTest -#endif - // This class tests the scenario when permission to access mic or camera is // granted. -class MAYBE_WebRtcAudioBrowserTest : public WebRtcContentBrowserTestBase { +class WebRtcAudioBrowserTest : public WebRtcContentBrowserTestBase, + public testing::WithParamInterface { public: - MAYBE_WebRtcAudioBrowserTest() {} - ~MAYBE_WebRtcAudioBrowserTest() override {} + WebRtcAudioBrowserTest() { + std::vector audio_service_oop_features = { + features::kAudioServiceAudioStreams, + features::kAudioServiceOutOfProcess}; + if (GetParam()) { + // Force audio service out of process to enabled. + audio_service_features_.InitWithFeatures(audio_service_oop_features, {}); + } else { + // Force audio service out of process to disabled. + audio_service_features_.InitWithFeatures({}, audio_service_oop_features); + } + } + ~WebRtcAudioBrowserTest() override {} void SetUpCommandLine(base::CommandLine* command_line) override { WebRtcContentBrowserTestBase::SetUpCommandLine(command_line); @@ -59,60 +67,76 @@ class MAYBE_WebRtcAudioBrowserTest : public WebRtcContentBrowserTestBase { MakeTypicalCall(javascript, "/media/peerconnection-call-audio.html"); } + + private: + base::test::ScopedFeatureList audio_service_features_; }; -IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, CanMakeVideoCallAndThenRenegotiateToAudio) { MakeAudioDetectingPeerConnectionCall( "callAndRenegotiateToAudio({audio: true, video:true}, {audio: true});"); } -IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, EstablishAudioVideoCallAndEnsureAudioIsPlaying) { MakeAudioDetectingPeerConnectionCall( "callAndEnsureAudioIsPlaying({audio:true, video:true});"); } -IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, EstablishAudioOnlyCallAndEnsureAudioIsPlaying) { MakeAudioDetectingPeerConnectionCall( "callAndEnsureAudioIsPlaying({audio:true});"); } -IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, EstablishIsac16KCallAndEnsureAudioIsPlaying) { MakeAudioDetectingPeerConnectionCall( "callWithIsac16KAndEnsureAudioIsPlaying({audio:true});"); } -IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, EstablishAudioVideoCallAndVerifyRemoteMutingWorks) { MakeAudioDetectingPeerConnectionCall( "callAndEnsureRemoteAudioTrackMutingWorks();"); } -IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, EstablishAudioVideoCallAndVerifyLocalMutingWorks) { MakeAudioDetectingPeerConnectionCall( "callAndEnsureLocalAudioTrackMutingWorks();"); } -IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, EnsureLocalVideoMuteDoesntMuteAudio) { MakeAudioDetectingPeerConnectionCall( "callAndEnsureLocalVideoMutingDoesntMuteAudio();"); } -IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, EnsureRemoteVideoMuteDoesntMuteAudio) { MakeAudioDetectingPeerConnectionCall( "callAndEnsureRemoteVideoMutingDoesntMuteAudio();"); } -IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAudioBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, EstablishAudioVideoCallAndVerifyUnmutingWorks) { MakeAudioDetectingPeerConnectionCall( "callAndEnsureAudioTrackUnmutingWorks();"); } +// We run these tests with the audio service both in and out of the the browser +// process to have waterfall coverage while the feature rolls out. It should be +// removed after launch. +#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN) +// Supported platforms. +INSTANTIATE_TEST_CASE_P(, WebRtcAudioBrowserTest, ::testing::Bool()); +#elif defined(OS_ANDROID) && defined(ADDRESS_SANITIZER) +// Renderer crashes under Android ASAN: https://crbug.com/408496. +#else +// Platforms where the out of process audio service isn't supported +INSTANTIATE_TEST_CASE_P(, WebRtcAudioBrowserTest, ::testing::Values(false)); +#endif + } // namespace content diff --git a/chromium/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc b/chromium/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc index 09e55b0a0f0..965c78d992f 100644 --- a/chromium/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc +++ b/chromium/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc @@ -38,7 +38,7 @@ const base::FilePath::CharType kBaseFilename[] = bool GetRenderProcessHostId(base::ProcessId* id) { content::RenderProcessHost::iterator it( content::RenderProcessHost::AllHostsIterator()); - *id = base::GetProcId(it.GetCurrentValue()->GetHandle()); + *id = it.GetCurrentValue()->GetProcess().Pid(); EXPECT_NE(base::kNullProcessId, *id); if (*id == base::kNullProcessId) return false; @@ -343,7 +343,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcAudioDebugRecordingsBrowserTest, base::FilePath file_path; for (; !it.IsAtEnd(); it.Advance()) { base::ProcessId render_process_id = - base::GetProcId(it.GetCurrentValue()->GetHandle()); + it.GetCurrentValue()->GetProcess().Pid(); EXPECT_NE(base::kNullProcessId, render_process_id); file_path = GetExpectedAecDumpFileName(base_file_path, render_process_id); diff --git a/chromium/content/browser/webrtc/webrtc_browsertest.cc b/chromium/content/browser/webrtc/webrtc_browsertest.cc index 820322191dc..cbbb5ee815c 100644 --- a/chromium/content/browser/webrtc/webrtc_browsertest.cc +++ b/chromium/content/browser/webrtc/webrtc_browsertest.cc @@ -15,6 +15,7 @@ #include "content/public/test/test_utils.h" #include "media/audio/audio_manager.h" #include "media/base/media_switches.h" +#include "media/media_buildflags.h" #include "net/test/embedded_test_server/embedded_test_server.h" namespace content { @@ -103,16 +104,7 @@ IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcBrowserTest, MakeTypicalPeerConnectionCall(javascript); } - -#if defined(OS_WIN) && !defined(NVALGRIND) -// Times out on Dr. Memory bots: https://crbug.com/545740 -#define MAYBE_CanSetupCallAndSendDtmf DISABLED_CanSetupCallAndSendDtmf -#else -#define MAYBE_CanSetupCallAndSendDtmf CanSetupCallAndSendDtmf -#endif - -IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcBrowserTest, - MAYBE_CanSetupCallAndSendDtmf) { +IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcBrowserTest, CanSetupCallAndSendDtmf) { MakeTypicalPeerConnectionCall("callAndSendDtmf(\'123,ABC\');"); } @@ -241,7 +233,7 @@ IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcBrowserTest, "testGetSettingsWhenRemoteDimensionsUnknown();"); } -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) && BUILDFLAG(USE_PROPRIETARY_CODECS) // This test is to make sure HW H264 work normally on supported devices, since // there is no SW H264 fallback available on Android. IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcBrowserTest, diff --git a/chromium/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc b/chromium/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc index d4d59b8c93a..7b67681f34c 100644 --- a/chromium/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc +++ b/chromium/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc @@ -109,8 +109,8 @@ IN_PROC_BROWSER_TEST_F(WebRtcCaptureFromElementBrowserTest, } IN_PROC_BROWSER_TEST_F(WebRtcCaptureFromElementBrowserTest, - VerifyCanvasCaptureOffscreenCanvasCommitFrames) { - MakeTypicalCall("testCanvasCapture(drawOffscreenCanvasCommit);", + VerifyCanvasCaptureOffscreenCanvasFrames) { + MakeTypicalCall("testCanvasCapture(drawOffscreenCanvas);", kCanvasCaptureTestHtmlFile); } diff --git a/chromium/content/browser/webrtc/webrtc_getusermedia_browsertest.cc b/chromium/content/browser/webrtc/webrtc_getusermedia_browsertest.cc index 254ff38788f..339d6f044fb 100644 --- a/chromium/content/browser/webrtc/webrtc_getusermedia_browsertest.cc +++ b/chromium/content/browser/webrtc/webrtc_getusermedia_browsertest.cc @@ -7,6 +7,7 @@ #include "base/command_line.h" #include "base/feature_list.h" #include "base/json/json_reader.h" +#include "base/test/scoped_feature_list.h" #include "base/threading/thread_restrictions.h" #include "base/values.h" #include "build/build_config.h" @@ -15,6 +16,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/webrtc/webrtc_content_browsertest_base.h" #include "content/browser/webrtc/webrtc_internals.h" +#include "content/public/common/content_features.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" @@ -22,7 +24,9 @@ #include "content/shell/browser/shell.h" #include "media/audio/audio_manager.h" #include "media/audio/fake_audio_input_stream.h" +#include "media/base/media_switches.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gtest/include/gtest/gtest-param-test.h" #if defined(OS_WIN) #include "base/win/windows_version.h" @@ -94,11 +98,22 @@ bool VerifyDisableLocalEcho(bool expect_value, namespace content { -class WebRtcGetUserMediaBrowserTest : public WebRtcContentBrowserTestBase { +class WebRtcGetUserMediaBrowserTest : public WebRtcContentBrowserTestBase, + public testing::WithParamInterface { public: WebRtcGetUserMediaBrowserTest() { // Automatically grant device permission. AppendUseFakeUIForMediaStreamFlag(); + std::vector audio_service_oop_features = { + features::kAudioServiceAudioStreams, + features::kAudioServiceOutOfProcess}; + if (GetParam()) { + // Force audio service out of process to enabled. + audio_service_features_.InitWithFeatures(audio_service_oop_features, {}); + } else { + // Force audio service out of process to disabled. + audio_service_features_.InitWithFeatures({}, audio_service_oop_features); + } } ~WebRtcGetUserMediaBrowserTest() override {} @@ -158,6 +173,9 @@ class WebRtcGetUserMediaBrowserTest : public WebRtcContentBrowserTestBase { ASSERT_FALSE(audio_ids->empty()); ASSERT_FALSE(video_ids->empty()); } + + private: + base::test::ScopedFeatureList audio_service_features_; }; // These tests will all make a getUserMedia call with different constraints and @@ -170,7 +188,7 @@ class WebRtcGetUserMediaBrowserTest : public WebRtcContentBrowserTestBase { #else #define MAYBE_GetVideoStreamAndStop GetVideoStreamAndStop #endif -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, MAYBE_GetVideoStreamAndStop) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -189,7 +207,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, #define MAYBE_RenderSameTrackMediastreamAndStop \ RenderSameTrackMediastreamAndStop #endif -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, MAYBE_RenderSameTrackMediastreamAndStop) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -201,7 +219,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, kRenderSameTrackMediastreamAndStop)); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, RenderClonedMediastreamAndStop) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -214,7 +232,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, kRenderClonedMediastreamAndStop)); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, kRenderClonedTrackMediastreamAndStop) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -226,7 +244,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, kRenderClonedTrackMediastreamAndStop)); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, kRenderDuplicatedMediastreamAndStop) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -238,7 +256,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, kRenderDuplicatedMediastreamAndStop)); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetAudioAndVideoStreamAndStop) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -249,7 +267,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, "%s({video: true, audio: true});", kGetUserMediaAndStop)); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetAudioAndVideoStreamAndClone) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -260,7 +278,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, } // TODO(crbug.com/803516) : Flaky on all platforms. -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, DISABLED_RenderVideoTrackInMultipleTagsAndPause) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -278,7 +296,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, #define MAYBE_GetUserMediaWithMandatorySourceID \ GetUserMediaWithMandatorySourceID #endif -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, MAYBE_GetUserMediaWithMandatorySourceID) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -304,7 +322,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, } #undef MAYBE_GetUserMediaWithMandatorySourceID -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetUserMediaWithInvalidMandatorySourceID) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -336,7 +354,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, kGetUserMediaAndExpectFailure, "", video_ids[0]))); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetUserMediaWithInvalidOptionalSourceID) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -369,8 +387,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, video_ids[0]))); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, - TwoGetUserMediaAndStop) { +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, TwoGetUserMediaAndStop) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -380,7 +397,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, "twoGetUserMediaAndStop({video: true, audio: true});"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, TwoGetUserMediaWithEqualConstraints) { std::string constraints1 = "{video: true, audio: true}"; const std::string& constraints2 = constraints1; @@ -390,7 +407,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, expected_result); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, TwoGetUserMediaWithSecondVideoCropped) { std::string constraints1 = "{video: true}"; std::string constraints2 = @@ -408,7 +425,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, #define MAYBE_TwoGetUserMediaWithFirstHdSecondVga \ TwoGetUserMediaWithFirstHdSecondVga #endif -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, MAYBE_TwoGetUserMediaWithFirstHdSecondVga) { std::string constraints1 = "{video: {width : {exact: 1280}, height: {exact: 720}}}"; @@ -421,7 +438,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, // Timing out on Windows 7 bot: http://crbug.com/443294 // Flaky: http://crbug.com/660656; possible the test is too perf sensitive. -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, DISABLED_TwoGetUserMediaWithFirst1080pSecondVga) { std::string constraints1 = "{video: {mandatory: {maxWidth:1920 , minWidth:1920 , maxHeight: 1080, " @@ -433,7 +450,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, expected_result); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetUserMediaWithTooHighVideoConstraintsValues) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -452,16 +469,16 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, EXPECT_EQ("OverconstrainedError", ExecuteJavascriptAndReturnResult(call)); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetUserMediaFailToAccessAudioDevice) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); NavigateToURL(shell(), url); - // Set the maximum allowed input and output streams to 0 - // so that the call to create a new audio input stream will fail. - media::AudioManager::Get()->SetMaxStreamCountForTesting(0, 0); + // Make sure we'll fail creating the audio stream. + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kFailAudioStreamCreation); const std::string call = base::StringPrintf( "%s({video: false, audio: true});", kGetUserMediaAndExpectFailure); @@ -471,7 +488,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, // This test makes two getUserMedia requests, one with impossible constraints // that should trigger an error, and one with valid constraints. The test // verifies getUserMedia can succeed after being given impossible constraints. -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, TwoGetUserMediaAndCheckCallbackAfterFailure) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -507,7 +524,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, #endif // This test calls getUserMedia and checks for aspect ratio behavior. -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, TestGetUserMediaAspectRatio4To3) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -522,7 +539,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, } // This test calls getUserMedia and checks for aspect ratio behavior. -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, TestGetUserMediaAspectRatio16To9) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -537,7 +554,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, } // This test calls getUserMedia and checks for aspect ratio behavior. -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, TestGetUserMediaAspectRatio1To1) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -554,7 +571,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, // This test calls getUserMedia in an iframe and immediately close the iframe // in the scope of the success callback. // Flaky: crbug.com/727601. -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, DISABLED_AudioInIFrameAndCloseInSuccessCb) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -567,7 +584,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, } // Flaky: crbug.com/807638 -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, DISABLED_VideoInIFrameAndCloseInSuccessCb) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -581,7 +598,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, // This test calls getUserMedia in an iframe and immediately close the iframe // in the scope of the failure callback. -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, VideoWithBadConstraintsInIFrameAndCloseInFailureCb) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -601,7 +618,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk(call); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, InvalidSourceIdInIFrameAndCloseInFailureCb) { ASSERT_TRUE(embedded_test_server()->Start()); @@ -615,7 +632,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk(call); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, DisableLocalEchoParameter) { base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalWebPlatformFeatures); @@ -628,13 +645,13 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, BrowserMainLoop::GetInstance()->media_stream_manager(); manager->SetGenerateStreamCallbackForTesting( - base::Bind(&VerifyDisableLocalEcho, false)); + base::BindOnce(&VerifyDisableLocalEcho, false)); std::string call = GenerateGetUserMediaWithDisableLocalEcho( "getUserMediaAndExpectSuccess", "false"); ExecuteJavascriptAndWaitForOk(call); manager->SetGenerateStreamCallbackForTesting( - base::Bind(&VerifyDisableLocalEcho, true)); + base::BindOnce(&VerifyDisableLocalEcho, true)); call = GenerateGetUserMediaWithDisableLocalEcho( "getUserMediaAndExpectSuccess", "true"); ExecuteJavascriptAndWaitForOk(call); @@ -643,14 +660,14 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, MediaStreamManager::GenerateStreamTestCallback()); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, GetAudioSettingsDefault) { +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetAudioSettingsDefault) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); NavigateToURL(shell(), url); ExecuteJavascriptAndWaitForOk("getAudioSettingsDefault()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetAudioSettingsNoEchoCancellation) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -658,7 +675,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk("getAudioSettingsNoEchoCancellation()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetAudioSettingsDeviceId) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -666,72 +683,39 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk("getAudioSettingsDeviceId()"); } - -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, - GetAudioStreamAndCheckMutingInitiallyUnmuted) { +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, SrcObjectAddVideoTrack) { ASSERT_TRUE(embedded_test_server()->Start()); - GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); NavigateToURL(shell(), url); - - // Expect stream to initially not be muted - media::FakeAudioInputStream::SetGlobalMutedState(false); - ExecuteJavascriptAndWaitForOk( - base::StringPrintf("%s(false);", kGetUserMediaForAudioMutingTest)); - - // Mute - media::FakeAudioInputStream::SetGlobalMutedState(true); - EXPECT_EQ("onmute: muted=true, readyState=live", - ExecuteJavascriptAndReturnResult( - "failTestAfterTimeout('Got no mute event', 1500);")); - // Unmute - media::FakeAudioInputStream::SetGlobalMutedState(false); - EXPECT_EQ("onunmute: muted=false, readyState=live", - ExecuteJavascriptAndReturnResult( - "failTestAfterTimeout('Got no unmute event', 1500);")); -} - -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, - GetAudioStreamAndCheckMutingInitiallyMuted) { - ASSERT_TRUE(embedded_test_server()->Start()); - - GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); - NavigateToURL(shell(), url); - - // Expect stream to initially be muted - media::FakeAudioInputStream::SetGlobalMutedState(true); - ExecuteJavascriptAndWaitForOk( - base::StringPrintf("%s(true);", kGetUserMediaForAudioMutingTest)); - - // Unmute - media::FakeAudioInputStream::SetGlobalMutedState(false); - EXPECT_EQ("onunmute: muted=false, readyState=live", - ExecuteJavascriptAndReturnResult( - "failTestAfterTimeout('Got no unmute event', 1500);")); - - // Mute - media::FakeAudioInputStream::SetGlobalMutedState(true); - EXPECT_EQ("onmute: muted=true, readyState=live", - ExecuteJavascriptAndReturnResult( - "failTestAfterTimeout('Got no mute event', 1500);")); + ExecuteJavascriptAndWaitForOk("srcObjectAddVideoTrack()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, SrcObjectAddVideoTrack) { +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, + SrcObjectReplaceInactiveTracks) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); NavigateToURL(shell(), url); - ExecuteJavascriptAndWaitForOk("srcObjectAddVideoTrack()"); + ExecuteJavascriptAndWaitForOk("srcObjectReplaceInactiveTracks()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, - SrcObjectRemoveVideoTrack) { +// Flaky on all platforms. https://crbug.com/835332 +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, + DISABLED_SrcObjectRemoveVideoTrack) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); NavigateToURL(shell(), url); ExecuteJavascriptAndWaitForOk("srcObjectRemoveVideoTrack()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +// Flaky on memory and leak sanitizers. https://crbug.com/843844 +#if defined(MEMORY_SANITIZER) || defined(LEAK_SANITIZER) +#define MAYBE_SrcObjectRemoveFirstOfTwoVideoTracks \ + DISABLED_SrcObjectRemoveFirstOfTwoVideoTracks +#else +#define MAYBE_SrcObjectRemoveFirstOfTwoVideoTracks \ + SrcObjectRemoveFirstOfTwoVideoTracks +#endif +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, SrcObjectRemoveFirstOfTwoVideoTracks) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -743,7 +727,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, // when a straightforward mechanism to detect the presence/absence of audio in a // media element with an assigned MediaStream becomes available. -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, SrcObjectReassignSameObject) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -751,14 +735,14 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk("srcObjectReassignSameObject()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ApplyConstraintsVideo) { +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, ApplyConstraintsVideo) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); NavigateToURL(shell(), url); ExecuteJavascriptAndWaitForOk("applyConstraintsVideo()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, ApplyConstraintsVideoTwoStreams) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -766,7 +750,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk("applyConstraintsVideoTwoStreams()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, ApplyConstraintsVideoOverconstrained) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -774,7 +758,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk("applyConstraintsVideoOverconstrained()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, ApplyConstraintsNonDevice) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -782,7 +766,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk("applyConstraintsNonDevice()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, ConcurrentGetUserMediaStop) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -790,7 +774,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk("concurrentGetUserMediaStop()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetUserMediaAfterStopElementCapture) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -798,7 +782,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk("getUserMediaAfterStopCanvasCapture()"); } -IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetUserMediaEchoCancellationOnAndOff) { ASSERT_TRUE(embedded_test_server()->Start()); GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); @@ -806,4 +790,77 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, ExecuteJavascriptAndWaitForOk("getUserMediaEchoCancellationOnAndOff()"); } +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, + GetAudioStreamAndCheckMutingInitiallyUnmuted) { + // Muting tests do not work with the out-of-process audio service. + // https://crbug.com/843490. + if (GetParam()) + return; + + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); + NavigateToURL(shell(), url); + + // Expect stream to initially not be muted + media::FakeAudioInputStream::SetGlobalMutedState(false); + ExecuteJavascriptAndWaitForOk( + base::StringPrintf("%s(false);", kGetUserMediaForAudioMutingTest)); + + // Mute + media::FakeAudioInputStream::SetGlobalMutedState(true); + EXPECT_EQ("onmute: muted=true, readyState=live", + ExecuteJavascriptAndReturnResult( + "failTestAfterTimeout('Got no mute event', 1500);")); + // Unmute + media::FakeAudioInputStream::SetGlobalMutedState(false); + EXPECT_EQ("onunmute: muted=false, readyState=live", + ExecuteJavascriptAndReturnResult( + "failTestAfterTimeout('Got no unmute event', 1500);")); +} + +IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, + GetAudioStreamAndCheckMutingInitiallyMuted) { + // Muting tests do not work with the out-of-process audio service. + // https://crbug.com/843490. + if (GetParam()) + return; + + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL url(embedded_test_server()->GetURL("/media/getusermedia.html")); + NavigateToURL(shell(), url); + + // Expect stream to initially be muted + media::FakeAudioInputStream::SetGlobalMutedState(true); + ExecuteJavascriptAndWaitForOk( + base::StringPrintf("%s(true);", kGetUserMediaForAudioMutingTest)); + + // Unmute + media::FakeAudioInputStream::SetGlobalMutedState(false); + EXPECT_EQ("onunmute: muted=false, readyState=live", + ExecuteJavascriptAndReturnResult( + "failTestAfterTimeout('Got no unmute event', 1500);")); + + // Mute + media::FakeAudioInputStream::SetGlobalMutedState(true); + EXPECT_EQ("onmute: muted=true, readyState=live", + ExecuteJavascriptAndReturnResult( + "failTestAfterTimeout('Got no mute event', 1500);")); +} + +// We run these tests with the audio service both in and out of the the browser +// process to have waterfall coverage while the feature rolls out. It should be +// removed after launch. +#if (defined(OS_LINUX) && !defined(CHROME_OS)) || defined(OS_MACOSX) || \ + defined(OS_WIN) +// Supported platforms. +INSTANTIATE_TEST_CASE_P(, WebRtcGetUserMediaBrowserTest, ::testing::Bool()); +#else +// Platforms where the out of process audio service is not supported +INSTANTIATE_TEST_CASE_P(, + WebRtcGetUserMediaBrowserTest, + ::testing::Values(false)); +#endif + } // namespace content diff --git a/chromium/content/browser/webrtc/webrtc_internals.cc b/chromium/content/browser/webrtc/webrtc_internals.cc index eb7e04af19b..b8e4b8b1cc5 100644 --- a/chromium/content/browser/webrtc/webrtc_internals.cc +++ b/chromium/content/browser/webrtc/webrtc_internals.cc @@ -109,10 +109,6 @@ WebRTCInternals::WebRTCInternals(int aggregate_updates_ms, DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!g_webrtc_internals); -// TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the -// build if WebRTC is disabled? -// https://crbug.com/817446 -#if BUILDFLAG(ENABLE_WEBRTC) audio_debug_recordings_file_path_ = GetContentClient()->browser()->GetDefaultDownloadDirectory(); event_log_recordings_file_path_ = audio_debug_recordings_file_path_; @@ -143,7 +139,6 @@ WebRTCInternals::WebRTCInternals(int aggregate_updates_ms, event_log_recordings_ = true; event_log_recordings_file_path_.clear(); } -#endif // BUILDFLAG(ENABLE_WEBRTC) g_webrtc_internals = this; } @@ -333,7 +328,6 @@ void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) { void WebRTCInternals::EnableAudioDebugRecordings( content::WebContents* web_contents) { DCHECK_CURRENTLY_ON(BrowserThread::UI); -#if BUILDFLAG(ENABLE_WEBRTC) #if defined(OS_ANDROID) EnableAudioDebugRecordingsOnAllRenderProcessHosts(); #else @@ -346,12 +340,10 @@ void WebRTCInternals::EnableAudioDebugRecordings( base::FilePath::StringType(), web_contents->GetTopLevelNativeWindow(), nullptr); #endif -#endif } void WebRTCInternals::DisableAudioDebugRecordings() { DCHECK_CURRENTLY_ON(BrowserThread::UI); -#if BUILDFLAG(ENABLE_WEBRTC) if (!audio_debug_recording_session_) return; audio_debug_recording_session_.reset(); @@ -365,7 +357,6 @@ void WebRTCInternals::DisableAudioDebugRecordings() { !i.IsAtEnd(); i.Advance()) { i.GetCurrentValue()->DisableAudioDebugRecordings(); } -#endif } bool WebRTCInternals::IsAudioDebugRecordingsEnabled() const { @@ -382,7 +373,6 @@ void WebRTCInternals::EnableLocalEventLogRecordings( content::WebContents* web_contents) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(CanToggleEventLogRecordings()); -#if BUILDFLAG(ENABLE_WEBRTC) #if defined(OS_ANDROID) WebRtcEventLogger* const logger = WebRtcEventLogger::Get(); if (logger) { @@ -398,11 +388,9 @@ void WebRTCInternals::EnableLocalEventLogRecordings( event_log_recordings_file_path_, nullptr, 0, FILE_PATH_LITERAL(""), web_contents->GetTopLevelNativeWindow(), nullptr); #endif -#endif } void WebRTCInternals::DisableLocalEventLogRecordings() { -#if BUILDFLAG(ENABLE_WEBRTC) event_log_recordings_ = false; // Tear down the dialog since the user has unchecked the event log checkbox. select_file_dialog_ = nullptr; @@ -411,7 +399,6 @@ void WebRTCInternals::DisableLocalEventLogRecordings() { if (logger) { logger->DisableLocalLogging(); } -#endif } bool WebRTCInternals::IsEventLogRecordingsEnabled() const { @@ -440,9 +427,9 @@ void WebRTCInternals::SendUpdate(const char* command, } } -void WebRTCInternals::RenderProcessExited(RenderProcessHost* host, - base::TerminationStatus status, - int exit_code) { +void WebRTCInternals::RenderProcessExited( + RenderProcessHost* host, + const ChildProcessTerminationInfo& info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); OnRendererExit(host->GetID()); render_process_id_set_.erase(host->GetID()); @@ -452,7 +439,6 @@ void WebRTCInternals::RenderProcessExited(RenderProcessHost* host, void WebRTCInternals::FileSelected(const base::FilePath& path, int /* unused_index */, void* /*unused_params */) { -#if BUILDFLAG(ENABLE_WEBRTC) DCHECK_CURRENTLY_ON(BrowserThread::UI); switch (selection_type_) { case SelectionType::kRtcEventLogs: { @@ -471,11 +457,9 @@ void WebRTCInternals::FileSelected(const base::FilePath& path, } default: { NOTREACHED(); } } -#endif } void WebRTCInternals::FileSelectionCanceled(void* params) { -#if BUILDFLAG(ENABLE_WEBRTC) DCHECK_CURRENTLY_ON(BrowserThread::UI); switch (selection_type_) { case SelectionType::kRtcEventLogs: @@ -488,7 +472,6 @@ void WebRTCInternals::FileSelectionCanceled(void* params) { NOTREACHED(); } select_file_dialog_ = nullptr; -#endif } void WebRTCInternals::OnRendererExit(int render_process_id) { @@ -544,7 +527,6 @@ void WebRTCInternals::OnRendererExit(int render_process_id) { } } -#if BUILDFLAG(ENABLE_WEBRTC) void WebRTCInternals::EnableAudioDebugRecordingsOnAllRenderProcessHosts() { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!audio_debug_recording_session_); @@ -561,7 +543,6 @@ void WebRTCInternals::EnableAudioDebugRecordingsOnAllRenderProcessHosts() { audio_debug_recordings_file_path_); } } -#endif void WebRTCInternals::MaybeClosePeerConnection(base::DictionaryValue* record) { bool is_open; diff --git a/chromium/content/browser/webrtc/webrtc_internals.h b/chromium/content/browser/webrtc/webrtc_internals.h index 04c8e2a2342..b8b0c6097c1 100644 --- a/chromium/content/browser/webrtc/webrtc_internals.h +++ b/chromium/content/browser/webrtc/webrtc_internals.h @@ -151,8 +151,7 @@ class CONTENT_EXPORT WebRTCInternals : public RenderProcessHostObserver, // RenderProcessHostObserver implementation. void RenderProcessExited(RenderProcessHost* host, - base::TerminationStatus status, - int exit_code) override; + const ChildProcessTerminationInfo& info) override; // ui::SelectFileDialog::Listener implementation. void FileSelected(const base::FilePath& path, @@ -163,11 +162,9 @@ class CONTENT_EXPORT WebRTCInternals : public RenderProcessHostObserver, // Called when a renderer exits (including crashes). void OnRendererExit(int render_process_id); -#if BUILDFLAG(ENABLE_WEBRTC) // Enables diagnostic audio recordings on all render process hosts using // |audio_debug_recordings_file_path_|. void EnableAudioDebugRecordingsOnAllRenderProcessHosts(); -#endif // Updates the number of open PeerConnections. Called when a PeerConnection // is stopped or removed. diff --git a/chromium/content/browser/webrtc/webrtc_internals_unittest.cc b/chromium/content/browser/webrtc/webrtc_internals_unittest.cc index fc9d9402e38..8e4dd0a0687 100644 --- a/chromium/content/browser/webrtc/webrtc_internals_unittest.cc +++ b/chromium/content/browser/webrtc/webrtc_internals_unittest.cc @@ -7,7 +7,6 @@ #include #include -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/values.h" #include "content/browser/webrtc/webrtc_internals_ui_observer.h" diff --git a/chromium/content/browser/websockets/README.md b/chromium/content/browser/websockets/README.md index 80f5e442af1..67593763a49 100644 --- a/chromium/content/browser/websockets/README.md +++ b/chromium/content/browser/websockets/README.md @@ -1,5 +1,5 @@ # WebSocket service -This directory contains the network::mojom::WebSocket implementation. It hosts +This directory contains the network.mojom.WebSocket implementation. It hosts the WebSocket API implementations in Blink over Mojo, and exports the WebSocket protocol implementation in net/websockets/ as a service. diff --git a/chromium/content/browser/websockets/websocket_manager.cc b/chromium/content/browser/websockets/websocket_manager.cc index fad1a1d3cb4..c1ae8621402 100644 --- a/chromium/content/browser/websockets/websocket_manager.cc +++ b/chromium/content/browser/websockets/websocket_manager.cc @@ -9,6 +9,7 @@ #include #include "base/callback.h" +#include "base/feature_list.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" @@ -23,6 +24,8 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host_observer.h" #include "content/public/browser/storage_partition.h" +#include "services/network/network_context.h" +#include "services/network/public/cpp/features.h" namespace content { @@ -63,11 +66,8 @@ class WebSocketManager::Delegate final : public network::WebSocket::Delegate { void ReportBadMessage(BadMessageReason reason, network::WebSocket* impl) override { bad_message::BadMessageReason reason_to_pass = - bad_message::WSI_INVALID_HEADER_VALUE; + bad_message::WSI_UNEXPECTED_ADD_CHANNEL_REQUEST; switch (reason) { - case BadMessageReason::kInvalidHeaderValue: - reason_to_pass = bad_message::WSI_INVALID_HEADER_VALUE; - break; case BadMessageReason::kUnexpectedAddChannelRequest: reason_to_pass = bad_message::WSI_UNEXPECTED_ADD_CHANNEL_REQUEST; break; @@ -157,31 +157,25 @@ class WebSocketManager::Handle : public base::SupportsUserData::Data, }; // static -void WebSocketManager::CreateWebSocketForFrame( +void WebSocketManager::CreateWebSocket( int process_id, int frame_id, - network::mojom::WebSocketRequest request) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // ForFrame() implies a frame: DCHECK this pre-condition. - RenderFrameHost* frame = RenderFrameHost::FromID(process_id, frame_id); - DCHECK(frame); - - CreateWebSocketWithOrigin(process_id, frame->GetLastCommittedOrigin(), - std::move(request), frame_id); -} - -// static -void WebSocketManager::CreateWebSocketWithOrigin( - int process_id, url::Origin origin, - network::mojom::WebSocketRequest request, - int frame_id) { + network::mojom::WebSocketRequest request) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); RenderProcessHost* host = RenderProcessHost::FromID(process_id); DCHECK(host); + if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { + StoragePartition* storage_partition = host->GetStoragePartition(); + network::mojom::NetworkContext* network_context = + storage_partition->GetNetworkContext(); + network_context->CreateWebSocket(std::move(request), process_id, frame_id, + origin); + return; + } + // Maintain a WebSocketManager per RenderProcessHost. While the instance of // WebSocketManager is allocated on the UI thread, it must only be used and // deleted from the IO thread. @@ -255,7 +249,7 @@ void WebSocketManager::DoCreateWebSocket( // Keep all network::WebSockets alive until either the client drops its // connection (see OnLostConnectionToClient) or we need to shutdown. - impls_.insert(CreateWebSocket( + impls_.insert(DoCreateWebSocketInternal( std::make_unique(this), std::move(request), throttler_.IssuePendingConnectionTracker(), process_id_, frame_id, std::move(origin), throttler_.CalculateDelay())); @@ -275,7 +269,7 @@ void WebSocketManager::ThrottlingPeriodTimerCallback() { throttling_period_timer_.Stop(); } -std::unique_ptr WebSocketManager::CreateWebSocket( +std::unique_ptr WebSocketManager::DoCreateWebSocketInternal( std::unique_ptr delegate, network::mojom::WebSocketRequest request, network::WebSocketThrottler::PendingConnection pending_connection_tracker, diff --git a/chromium/content/browser/websockets/websocket_manager.h b/chromium/content/browser/websockets/websocket_manager.h index 089368539cd..006b5bd6ae9 100644 --- a/chromium/content/browser/websockets/websocket_manager.h +++ b/chromium/content/browser/websockets/websocket_manager.h @@ -29,18 +29,15 @@ class StoragePartition; class CONTENT_EXPORT WebSocketManager : public net::URLRequestContextGetterObserver { public: - // Called on the UI thread: create a websocket for a frame. - static void CreateWebSocketForFrame(int process_id, - int frame_id, - network::mojom::WebSocketRequest request); - - // Called on the UI thread: create a websocket for a worker. Web workers of - // any type (dedicated, shared, service worker) do not have a frame. - static void CreateWebSocketWithOrigin( - int process_id, - url::Origin origin, - network::mojom::WebSocketRequest request, - int frame_id = MSG_ROUTING_NONE); + // Called on the UI thread: create a websocket. + // - For frames, |frame_id| should be their own id. + // - For dedicated workers, |frame_id| should be its parent frame's id. + // - For shared workers and service workers, |frame_id| should be + // MSG_ROUTING_NONE because they do not have a frame. + static void CreateWebSocket(int process_id, + int frame_id, + url::Origin origin, + network::mojom::WebSocketRequest request); // net::URLRequestContextGetterObserver implementation. void OnContextShuttingDown() override; @@ -62,7 +59,7 @@ class CONTENT_EXPORT WebSocketManager void ThrottlingPeriodTimerCallback(); // This is virtual to support testing. - virtual std::unique_ptr CreateWebSocket( + virtual std::unique_ptr DoCreateWebSocketInternal( std::unique_ptr delegate, network::mojom::WebSocketRequest request, network::WebSocketThrottler::PendingConnection pending_connection_tracker, diff --git a/chromium/content/browser/websockets/websocket_manager_unittest.cc b/chromium/content/browser/websockets/websocket_manager_unittest.cc index 75c97090474..1cee833398c 100644 --- a/chromium/content/browser/websockets/websocket_manager_unittest.cc +++ b/chromium/content/browser/websockets/websocket_manager_unittest.cc @@ -76,7 +76,7 @@ class TestWebSocketManager : public WebSocketManager { } private: - std::unique_ptr CreateWebSocket( + std::unique_ptr DoCreateWebSocketInternal( std::unique_ptr delegate, network::mojom::WebSocketRequest request, network::WebSocketThrottler::PendingConnection pending_connection_tracker, 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 0f8982a83ef..d741b28c8be 100644 --- a/chromium/content/browser/webui/content_web_ui_controller_factory.cc +++ b/chromium/content/browser/webui/content_web_ui_controller_factory.cc @@ -8,21 +8,20 @@ #include "content/browser/accessibility/accessibility_ui.h" #include "content/browser/appcache/appcache_internals_ui.h" #include "content/browser/gpu/gpu_internals_ui.h" +#include "content/browser/histograms_internals_ui.h" #include "content/browser/indexed_db/indexed_db_internals_ui.h" #include "content/browser/media/media_internals_ui.h" #include "content/browser/net/network_errors_listing_ui.h" +#include "content/browser/process_internals/process_internals_ui.h" #include "content/browser/service_worker/service_worker_internals_ui.h" #include "content/browser/tracing/tracing_ui.h" +#include "content/browser/webrtc/webrtc_internals_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" #include "media/media_buildflags.h" -#if BUILDFLAG(ENABLE_WEBRTC) -#include "content/browser/webrtc/webrtc_internals_ui.h" -#endif - namespace content { WebUI::TypeID ContentWebUIControllerFactory::GetWebUIType( @@ -35,12 +34,14 @@ WebUI::TypeID ContentWebUIControllerFactory::GetWebUIType( url.host_piece() == kChromeUITracingHost || #endif url.host_piece() == kChromeUIGpuHost || + url.host_piece() == kChromeUIHistogramHost || url.host_piece() == kChromeUIIndexedDBInternalsHost || url.host_piece() == kChromeUIMediaInternalsHost || url.host_piece() == kChromeUIServiceWorkerInternalsHost || url.host_piece() == kChromeUIAccessibilityHost || url.host_piece() == kChromeUIAppCacheInternalsHost || - url.host_piece() == kChromeUINetworkErrorsListingHost) { + url.host_piece() == kChromeUINetworkErrorsListingHost || + url.host_piece() == kChromeUIProcessInternalsHost) { return const_cast(this); } return WebUI::kNoWebUI; @@ -65,6 +66,8 @@ WebUIController* ContentWebUIControllerFactory::CreateWebUIControllerForURL( return new AppCacheInternalsUI(web_ui); if (url.host_piece() == kChromeUIGpuHost) return new GpuInternalsUI(web_ui); + if (url.host_piece() == kChromeUIHistogramHost) + return new HistogramsInternalsUI(web_ui); if (url.host_piece() == kChromeUIIndexedDBInternalsHost) return new IndexedDBInternalsUI(web_ui); if (url.host_piece() == kChromeUIMediaInternalsHost) @@ -79,11 +82,10 @@ WebUIController* ContentWebUIControllerFactory::CreateWebUIControllerForURL( if (url.host_piece() == kChromeUITracingHost) return new TracingUI(web_ui); #endif - -#if BUILDFLAG(ENABLE_WEBRTC) if (url.host_piece() == kChromeUIWebRTCInternalsHost) return new WebRTCInternalsUI(web_ui); -#endif + if (url.host_piece() == kChromeUIProcessInternalsHost) + return new ProcessInternalsUI(web_ui); return nullptr; } diff --git a/chromium/content/browser/webui/url_data_manager.cc b/chromium/content/browser/webui/url_data_manager.cc index a2d542f817e..ce87d5984c0 100644 --- a/chromium/content/browser/webui/url_data_manager.cc +++ b/chromium/content/browser/webui/url_data_manager.cc @@ -12,7 +12,6 @@ #include "base/bind_helpers.h" #include "base/lazy_instance.h" #include "base/memory/ref_counted_memory.h" -#include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "base/synchronization/lock.h" #include "content/browser/resource_context_impl.h" diff --git a/chromium/content/browser/webui/url_data_manager_backend.cc b/chromium/content/browser/webui/url_data_manager_backend.cc index 8ecdc60b491..96d91340510 100644 --- a/chromium/content/browser/webui/url_data_manager_backend.cc +++ b/chromium/content/browser/webui/url_data_manager_backend.cc @@ -25,7 +25,6 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" -#include "content/browser/histogram_internals_request_job.h" #include "content/browser/net/view_blob_internals_job_factory.h" #include "content/browser/resource_context_impl.h" #include "content/browser/webui/shared_resources_data_source.h" @@ -358,12 +357,6 @@ class ChromeProtocolHandler request, network_delegate, blob_storage_context_->context()); } - // Next check for chrome://histograms/, which uses its own job type. - if (request->url().SchemeIs(kChromeUIScheme) && - request->url().host_piece() == kChromeUIHistogramHost) { - return new HistogramInternalsRequestJob(request, network_delegate); - } - // Check for chrome://network-error/, which uses its own job type. if (request->url().SchemeIs(kChromeUIScheme) && request->url().host_piece() == kChromeUINetworkErrorHost) { 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 ab6ccbba485..5dad37af6f2 100644 --- a/chromium/content/browser/webui/web_ui_data_source_impl.cc +++ b/chromium/content/browser/webui/web_ui_data_source_impl.cc @@ -43,6 +43,15 @@ void WebUIDataSource::Update(BrowserContext* browser_context, std::move(update)); } +namespace { + +std::string CleanUpPath(const std::string& path) { + // Remove the query string for named resource lookups. + return path.substr(0, path.find_first_of('?')); +} + +} // namespace + // Internal class to hide the fact that WebUIDataSourceImpl implements // URLDataSource. class WebUIDataSourceImpl::InternalDataSource : public URLDataSource { @@ -88,8 +97,7 @@ class WebUIDataSourceImpl::InternalDataSource : public URLDataSource { return parent_->deny_xframe_options_; } bool IsGzipped(const std::string& path) const override { - return parent_->use_gzip_ && - parent_->excluded_paths_.find(path) == parent_->excluded_paths_.end(); + return parent_->IsGzipped(path); } private: @@ -275,8 +283,7 @@ void WebUIDataSourceImpl::StartDataRequest( int resource_id = default_resource_; std::map::iterator result; // Remove the query string for named resource lookups. - std::string file_path = path.substr(0, path.find_first_of('?')); - result = path_to_idr_map_.find(file_path); + result = path_to_idr_map_.find(CleanUpPath(path)); if (result != path_to_idr_map_.end()) resource_id = result->second; DCHECK_NE(resource_id, -1); @@ -292,4 +299,8 @@ void WebUIDataSourceImpl::SendLocalizedStringsAsJSON( callback.Run(base::RefCountedString::TakeString(&template_data)); } +bool WebUIDataSourceImpl::IsGzipped(const std::string& path) const { + return use_gzip_ && excluded_paths_.count(CleanUpPath(path)) == 0; +} + } // namespace content 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 18c48754437..06c1bd9af05 100644 --- a/chromium/content/browser/webui/web_ui_data_source_impl.h +++ b/chromium/content/browser/webui/web_ui_data_source_impl.h @@ -13,6 +13,7 @@ #include "base/callback.h" #include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/values.h" #include "content/browser/webui/url_data_manager.h" @@ -69,6 +70,8 @@ class CONTENT_EXPORT WebUIDataSourceImpl : public URLDataSourceImpl, friend class WebUIDataSource; friend class WebUIDataSourceTest; + FRIEND_TEST_ALL_PREFIXES(WebUIDataSourceTest, IsGzipped); + explicit WebUIDataSourceImpl(const std::string& source_name); // Methods that match URLDataSource which are called by @@ -85,6 +88,8 @@ class CONTENT_EXPORT WebUIDataSourceImpl : public URLDataSourceImpl, add_load_time_data_defaults_ = false; } + bool IsGzipped(const std::string& path) const; + // The name of this source. // E.g., for favicons, this could be "favicon", which results in paths for // specific resources like "favicon/34" getting sent to this source. 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 f2974ff7f75..6125eb9562e 100644 --- a/chromium/content/browser/webui/web_ui_data_source_unittest.cc +++ b/chromium/content/browser/webui/web_ui_data_source_unittest.cc @@ -212,4 +212,24 @@ TEST_F(WebUIDataSourceTest, MimeType) { EXPECT_EQ(GetMimeType("foo.js?abc?abc"), js); } +TEST_F(WebUIDataSourceTest, IsGzipped) { + EXPECT_FALSE(source()->IsGzipped("foobar")); + + source()->AddResourcePath("foobar", kDummyResourceId); + source()->SetDefaultResource(kDummyDefaultResourceId); + source()->SetJsonPath("strings.js"); + source()->UseGzip({"json/special/path"}); + + EXPECT_TRUE(source()->IsGzipped("foobar")); + EXPECT_TRUE(source()->IsGzipped("foobar?query")); + + EXPECT_TRUE(source()->IsGzipped("unknown_path")); + EXPECT_TRUE(source()->IsGzipped("unknown_path?query")); + + EXPECT_FALSE(source()->IsGzipped("json/special/path")); + EXPECT_FALSE(source()->IsGzipped("json/special/path?query")); + EXPECT_FALSE(source()->IsGzipped("strings.js")); + EXPECT_FALSE(source()->IsGzipped("strings.js?query")); +} + } // namespace content diff --git a/chromium/content/browser/webui/web_ui_impl.cc b/chromium/content/browser/webui/web_ui_impl.cc index d759aba3325..ceac82cbc8d 100644 --- a/chromium/content/browser/webui/web_ui_impl.cc +++ b/chromium/content/browser/webui/web_ui_impl.cc @@ -106,9 +106,14 @@ bool WebUIImpl::OnMessageReceived(const IPC::Message& message, } void WebUIImpl::OnWebUISend(RenderFrameHost* sender, - const GURL& source_url, const std::string& message, const base::ListValue& args) { + // Ignore IPCs from frames that are pending deletion. See also + // https://crbug.com/780920. + if (!sender->IsCurrent()) + return; + + const GURL& source_url = sender->GetLastCommittedURL(); if (!ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( sender->GetProcess()->GetID()) || !WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI( @@ -119,10 +124,6 @@ void WebUIImpl::OnWebUISend(RenderFrameHost* sender, return; } - // Ignore IPCs from swapped-out frames. See also https://crbug.com/780920. - if (!sender->IsCurrent()) - return; - if (base::EndsWith(message, "RequiringGesture", base::CompareCase::SENSITIVE) && !web_contents_->HasRecentInteractiveInputEvent()) { diff --git a/chromium/content/browser/webui/web_ui_impl.h b/chromium/content/browser/webui/web_ui_impl.h index 556102626b3..484f918c98c 100644 --- a/chromium/content/browser/webui/web_ui_impl.h +++ b/chromium/content/browser/webui/web_ui_impl.h @@ -86,7 +86,6 @@ class CONTENT_EXPORT WebUIImpl : public WebUI, // IPC message handling. void OnWebUISend(RenderFrameHost* sender, - const GURL& source_url, const std::string& message, const base::ListValue& args); diff --git a/chromium/content/browser/webui/web_ui_mojo_browsertest.cc b/chromium/content/browser/webui/web_ui_mojo_browsertest.cc index 12e4cdf43fc..cf09e884aed 100644 --- a/chromium/content/browser/webui/web_ui_mojo_browsertest.cc +++ b/chromium/content/browser/webui/web_ui_mojo_browsertest.cc @@ -48,7 +48,7 @@ base::FilePath GetFilePathForJSResource(const std::string& path) { base::ReplaceChars(binding_path, "//", "\\", &binding_path); #endif base::FilePath exe_dir; - PathService::Get(base::DIR_EXE, &exe_dir); + base::PathService::Get(base::DIR_EXE, &exe_dir); return exe_dir.AppendASCII(binding_path); } diff --git a/chromium/content/browser/webui/web_ui_url_loader_factory.cc b/chromium/content/browser/webui/web_ui_url_loader_factory.cc index fb7a2fe41f9..baa74f4c5e5 100644 --- a/chromium/content/browser/webui/web_ui_url_loader_factory.cc +++ b/chromium/content/browser/webui/web_ui_url_loader_factory.cc @@ -7,6 +7,7 @@ #include #include "base/bind.h" +#include "base/debug/crash_logging.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/ref_counted_memory.h" @@ -17,7 +18,6 @@ #include "content/browser/blob_storage/blob_internals_url_loader.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" #include "content/browser/frame_host/render_frame_host_impl.h" -#include "content/browser/histogram_internals_url_loader.h" #include "content/browser/resource_context_impl.h" #include "content/browser/storage_partition_impl.h" #include "content/browser/webui/network_error_url_loader.h" @@ -257,6 +257,11 @@ class WebUIURLLoaderFactory : public network::mojom::URLLoaderFactory, if (!allowed_hosts_.empty() && (!request.url.has_host() || allowed_hosts_.find(request.url.host()) == allowed_hosts_.end())) { + // Temporary reporting the bad WebUI host for for http://crbug.com/837328. + static auto* crash_key = base::debug::AllocateCrashKeyString( + "webui_url", base::debug::CrashKeySize::Size64); + base::debug::SetCrashKeyString(crash_key, request.url.spec()); + DVLOG(1) << "Bad host: \"" << request.url.host() << '"'; ReceivedBadMessage(render_frame_host_->GetProcess(), bad_message::WEBUI_BAD_HOST_ACCESS); @@ -280,11 +285,6 @@ class WebUIURLLoaderFactory : public network::mojom::URLLoaderFactory, return; } - if (request.url.host_piece() == kChromeUIHistogramHost) { - StartHistogramInternalsURLLoader(request, std::move(client)); - return; - } - // We pass the FrameTreeNode ID to get to the WebContents because requests // from frames can happen while the RFH is changed for a cross-process // navigation. The URLDataSources just need the WebContents; the specific diff --git a/chromium/content/browser/zygote_host/zygote_communication_linux.cc b/chromium/content/browser/zygote_host/zygote_communication_linux.cc deleted file mode 100644 index f389abaf2fc..00000000000 --- a/chromium/content/browser/zygote_host/zygote_communication_linux.cc +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/zygote_host/zygote_communication_linux.h" - -#include -#include - -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/i18n/unicodestring.h" -#include "base/logging.h" -#include "base/metrics/histogram_functions.h" -#include "base/path_service.h" -#include "base/pickle.h" -#include "base/posix/eintr_wrapper.h" -#include "base/posix/unix_domain_socket.h" -#include "content/common/zygote_commands_linux.h" -#include "content/public/common/content_switches.h" -#include "content/public/common/result_codes.h" -#include "services/service_manager/embedder/switches.h" -#include "services/service_manager/sandbox/switches.h" -#include "third_party/icu/source/i18n/unicode/timezone.h" - -namespace content { - -ZygoteCommunication::ZygoteCommunication() - : control_fd_(), - control_lock_(), - pid_(), - list_of_running_zygote_children_(), - child_tracking_lock_(), - sandbox_status_(0), - have_read_sandbox_status_word_(false), - init_(false) {} - -ZygoteCommunication::~ZygoteCommunication() {} - -bool ZygoteCommunication::SendMessage(const base::Pickle& data, - const std::vector* fds) { - DCHECK(control_fd_.is_valid()); - CHECK(data.size() <= kZygoteMaxMessageLength) - << "Trying to send too-large message to zygote (sending " << data.size() - << " bytes, max is " << kZygoteMaxMessageLength << ")"; - CHECK(!fds || fds->size() <= base::UnixDomainSocket::kMaxFileDescriptors) - << "Trying to send message with too many file descriptors to zygote " - << "(sending " << fds->size() << ", max is " - << base::UnixDomainSocket::kMaxFileDescriptors << ")"; - - return base::UnixDomainSocket::SendMsg(control_fd_.get(), data.data(), - data.size(), - fds ? *fds : std::vector()); -} - -ssize_t ZygoteCommunication::ReadSandboxStatus() { - DCHECK(control_fd_.is_valid()); - // At startup we send a kZygoteCommandGetSandboxStatus request to the zygote, - // but don't wait for the reply. Thus, the first time that we read from the - // zygote, we get the reply to that request. - ssize_t bytes_read = HANDLE_EINTR( - read(control_fd_.get(), &sandbox_status_, sizeof(sandbox_status_))); - if (bytes_read != sizeof(sandbox_status_)) { - return -1; - } - return bytes_read; -} - -ssize_t ZygoteCommunication::ReadReply(void* buf, size_t buf_len) { - DCHECK(control_fd_.is_valid()); - if (!have_read_sandbox_status_word_) { - if (ReadSandboxStatus() == -1) { - return -1; - } - have_read_sandbox_status_word_ = true; - base::UmaHistogramSparse("Linux.SandboxStatus", sandbox_status_); - } - - return HANDLE_EINTR(read(control_fd_.get(), buf, buf_len)); -} - -pid_t ZygoteCommunication::ForkRequest( - const std::vector& argv, - const base::FileHandleMappingVector& mapping, - const std::string& process_type) { - DCHECK(init_); - - base::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(base::UnixDomainSocket::EnableReceiveProcessId(my_sock.get())); - - pickle.WriteInt(kZygoteCommandFork); - pickle.WriteString(process_type); - pickle.WriteInt(argv.size()); - for (std::vector::const_iterator i = argv.begin(); - i != argv.end(); ++i) - pickle.WriteString(*i); - if (process_type == switches::kRendererProcess) { - std::unique_ptr timezone(icu::TimeZone::createDefault()); - icu::UnicodeString timezone_id; - pickle.WriteString16( - base::i18n::UnicodeStringToString16(timezone->getID(timezone_id))); - } - - // 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 fds; - - // First FD to send is peer_sock. - // TODO(morrita): Ideally, this should be part of the mapping so that - // PosixFileDescriptorInfo can manages its lifetime. - fds.push_back(peer_sock.get()); - - // The rest come from mapping. - for (const auto& item : mapping) { - fds.push_back(item.first); - pickle.WriteUInt32(item.second); - } - - // 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; - peer_sock.reset(); - - { - char buf[sizeof(kZygoteChildPingMessage) + 1]; - std::vector recv_fds; - base::ProcessId real_pid; - - ssize_t n = base::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. - base::Pickle pid_pickle; - pid_pickle.WriteInt(kZygoteCommandForkRealPID); - pid_pickle.WriteInt(real_pid); - if (!SendMessage(pid_pickle, nullptr)) - return base::kNullProcessHandle; - } - - // Read the reply, which pickles the PID and an optional UMA enumeration. - static const unsigned kMaxReplyLength = 2048; - char buf[kMaxReplyLength]; - const ssize_t len = ReadReply(buf, sizeof(buf)); - - base::Pickle reply_pickle(buf, len); - base::PickleIterator iter(reply_pickle); - if (len <= 0 || !iter.ReadInt(&pid)) - return base::kNullProcessHandle; - - // If there is a nonempty UMA name string, then there is a UMA - // enumeration to record. - std::string uma_name; - int uma_sample; - int uma_boundary_value; - if (iter.ReadString(&uma_name) && !uma_name.empty() && - iter.ReadInt(&uma_sample) && iter.ReadInt(&uma_boundary_value)) { - // We cannot use the UMA_HISTOGRAM_ENUMERATION macro here, - // because that's only for when the name is the same every time. - // Here we're using whatever name we got from the other side. - // But since it's likely that the same one will be used repeatedly - // (even though it's not guaranteed), we cache it here. - static base::HistogramBase* uma_histogram; - if (!uma_histogram || uma_histogram->histogram_name() != uma_name) { - uma_histogram = base::LinearHistogram::FactoryGet( - uma_name, 1, uma_boundary_value, uma_boundary_value + 1, - base::HistogramBase::kUmaTargetedHistogramFlag); - } - uma_histogram->Add(uma_sample); - } - - if (pid <= 0) - return base::kNullProcessHandle; - } - - ZygoteChildBorn(pid); - return pid; -} - -void ZygoteCommunication::EnsureProcessTerminated(pid_t process) { - DCHECK(init_); - base::Pickle pickle; - - pickle.WriteInt(kZygoteCommandReap); - pickle.WriteInt(process); - if (!SendMessage(pickle, nullptr)) - LOG(ERROR) << "Failed to send Reap message to zygote"; - ZygoteChildDied(process); -} - -void ZygoteCommunication::ZygoteChildBorn(pid_t process) { - base::AutoLock lock(child_tracking_lock_); - bool new_element_inserted = - list_of_running_zygote_children_.insert(process).second; - DCHECK(new_element_inserted); -} - -void ZygoteCommunication::ZygoteChildDied(pid_t process) { - base::AutoLock lock(child_tracking_lock_); - size_t num_erased = list_of_running_zygote_children_.erase(process); - DCHECK_EQ(1U, num_erased); -} - -void ZygoteCommunication::Init( - base::OnceCallback launcher) { - CHECK(!init_); - - base::FilePath chrome_path; - CHECK(PathService::Get(base::FILE_EXE, &chrome_path)); - - base::CommandLine cmd_line(chrome_path); - cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess); - - const base::CommandLine& browser_command_line = - *base::CommandLine::ForCurrentProcess(); - if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) { - cmd_line.PrependWrapper( - browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix)); - } - // Append any switches from the service manager that need to be forwarded on - // to the zygote/renderers. - static const char* const kForwardSwitches[] = { - service_manager::switches::kAllowSandboxDebugging, - service_manager::switches::kDisableInProcessStackTraces, - service_manager::switches::kDisableSeccompFilterSandbox, - service_manager::switches::kNoSandbox, - }; - cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches, - arraysize(kForwardSwitches)); - - pid_ = std::move(launcher).Run(&cmd_line, &control_fd_); - - base::Pickle pickle; - pickle.WriteInt(kZygoteCommandGetSandboxStatus); - if (!SendMessage(pickle, nullptr)) - LOG(FATAL) << "Cannot communicate with zygote"; - - init_ = true; -} - -base::TerminationStatus ZygoteCommunication::GetTerminationStatus( - base::ProcessHandle handle, - bool known_dead, - int* exit_code) { - DCHECK(init_); - base::Pickle pickle; - pickle.WriteInt(kZygoteCommandGetTerminationStatus); - pickle.WriteBool(known_dead); - pickle.WriteInt(handle); - - static const unsigned kMaxMessageLength = 128; - char buf[kMaxMessageLength]; - ssize_t len; - { - base::AutoLock lock(control_lock_); - if (!SendMessage(pickle, nullptr)) - LOG(ERROR) << "Failed to send GetTerminationStatus message to zygote"; - len = ReadReply(buf, sizeof(buf)); - } - - // Set this now to handle the error cases. - if (exit_code) - *exit_code = RESULT_CODE_NORMAL_EXIT; - int status = base::TERMINATION_STATUS_NORMAL_TERMINATION; - - if (len == -1) { - LOG(WARNING) << "Error reading message from zygote: " << errno; - } else if (len == 0) { - LOG(WARNING) << "Socket closed prematurely."; - } else { - base::Pickle read_pickle(buf, len); - int tmp_status, tmp_exit_code; - base::PickleIterator iter(read_pickle); - if (!iter.ReadInt(&tmp_status) || !iter.ReadInt(&tmp_exit_code)) { - LOG(WARNING) - << "Error parsing GetTerminationStatus response from zygote."; - } else { - if (exit_code) - *exit_code = tmp_exit_code; - status = tmp_status; - } - } - - if (status != base::TERMINATION_STATUS_STILL_RUNNING) { - ZygoteChildDied(handle); - } - return static_cast(status); -} - -int ZygoteCommunication::GetSandboxStatus() { - if (have_read_sandbox_status_word_) { - return sandbox_status_; - } - if (ReadSandboxStatus() == -1) { - return 0; - } - have_read_sandbox_status_word_ = true; - base::UmaHistogramSparse("Linux.SandboxStatus", sandbox_status_); - return sandbox_status_; -} - -} // namespace content diff --git a/chromium/content/browser/zygote_host/zygote_communication_linux.h b/chromium/content/browser/zygote_host/zygote_communication_linux.h deleted file mode 100644 index 13185255fdc..00000000000 --- a/chromium/content/browser/zygote_host/zygote_communication_linux.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_ZYGOTE_HOST_ZYGOTE_COMMUNICATION_LINUX_H_ -#define CONTENT_BROWSER_ZYGOTE_HOST_ZYGOTE_COMMUNICATION_LINUX_H_ - -#include -#include -#include -#include - -#include - -#include "base/callback.h" -#include "base/files/scoped_file.h" -#include "base/process/kill.h" -#include "base/process/launch.h" -#include "base/process/process_handle.h" -#include "base/synchronization/lock.h" -#include "content/common/content_export.h" - -namespace base { -class Pickle; -} // namespace base - -namespace content { - -// Handles interprocess communication with the Linux zygote process. The zygote -// does not use standard Chrome IPC or mojo, see: -// https://chromium.googlesource.com/chromium/src/+/master/docs/linux_sandbox_ipc.md -class CONTENT_EXPORT ZygoteCommunication { - public: - ZygoteCommunication(); - ~ZygoteCommunication(); - - void Init( - base::OnceCallback launcher); - - // Tries to start a process of type indicated by process_type. - // Returns its pid on success, otherwise base::kNullProcessHandle; - pid_t ForkRequest(const std::vector& command_line, - const base::FileHandleMappingVector& mapping, - const std::string& process_type); - - void EnsureProcessTerminated(pid_t process); - - // Should be called every time a Zygote child died. - void ZygoteChildDied(pid_t process); - - // Get the termination status (and, optionally, the exit code) of - // the process. |exit_code| is set to the exit code of the child - // process. (|exit_code| may be NULL.) - // Unfortunately the Zygote can not accurately figure out if a process - // is already dead without waiting synchronously for it. - // |known_dead| should be set to true when we already know that the process - // is dead. When |known_dead| is false, processes could be seen as - // still running, even when they're not. When |known_dead| is true, the - // process will be SIGKILL-ed first (which should have no effect if it was - // really dead). This is to prevent a waiting waitpid() from blocking in - // a single-threaded Zygote. See crbug.com/157458. - base::TerminationStatus GetTerminationStatus(base::ProcessHandle handle, - bool known_dead, - int* exit_code); - - // Returns the sandbox status of this zygote. - int GetSandboxStatus(); - - private: - // Should be called every time a Zygote child is born. - void ZygoteChildBorn(pid_t process); - - // Read the reply from the zygote. - ssize_t ReadReply(void* buf, size_t buf_len); - - // Sends |data| to the zygote via |control_fd_|. If |fds| is non-NULL, the - // included file descriptors will also be passed. The caller is responsible - // for acquiring |control_lock_|. - bool SendMessage(const base::Pickle& data, const std::vector* fds); - - // Get the sandbox status from the zygote. - ssize_t ReadSandboxStatus(); - - base::ScopedFD control_fd_; // the socket to the zygote. - // A lock protecting all communication with the zygote. This lock must be - // acquired before sending a command and released after the result has been - // received. - base::Lock control_lock_; - // The pid of the zygote. - pid_t pid_; - // The list of running zygote children. - std::set list_of_running_zygote_children_; - // The lock to guard the list of running zygote children. - base::Lock child_tracking_lock_; - int sandbox_status_; - bool have_read_sandbox_status_word_; - // Set to true when the zygote is initialized successfully. - bool init_; -}; - -} // namespace content - -#endif // CONTENT_BROWSER_ZYGOTE_HOST_ZYGOTE_COMMUNICATION_LINUX_H_ diff --git a/chromium/content/browser/zygote_host/zygote_handle_linux.cc b/chromium/content/browser/zygote_host/zygote_handle_linux.cc deleted file mode 100644 index 14eabaec1d0..00000000000 --- a/chromium/content/browser/zygote_host/zygote_handle_linux.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/public/common/zygote_handle.h" - -#include "content/browser/zygote_host/zygote_communication_linux.h" - -namespace content { -namespace { - -// Intentionally leaked. -ZygoteHandle g_generic_zygote = nullptr; - -} // namespace - -ZygoteHandle CreateGenericZygote( - base::OnceCallback launcher) { - CHECK(!g_generic_zygote); - g_generic_zygote = new ZygoteCommunication(); - g_generic_zygote->Init(std::move(launcher)); - return g_generic_zygote; -} - -ZygoteHandle GetGenericZygote() { - CHECK(g_generic_zygote); - return g_generic_zygote; -} - -} // 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 deleted file mode 100644 index 0b764578eca..00000000000 --- a/chromium/content/browser/zygote_host/zygote_host_impl_linux.cc +++ /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. - -#include "content/browser/zygote_host/zygote_host_impl_linux.h" - -#include -#include -#include - -#include "base/allocator/allocator_extension.h" -#include "base/files/file_enumerator.h" -#include "base/posix/unix_domain_socket.h" -#include "base/process/kill.h" -#include "base/process/memory.h" -#include "base/strings/string_number_conversions.h" -#include "content/common/zygote_commands_linux.h" -#include "content/public/common/content_switches.h" -#include "sandbox/linux/services/credentials.h" -#include "sandbox/linux/services/namespace_sandbox.h" -#include "sandbox/linux/suid/client/setuid_sandbox_host.h" -#include "sandbox/linux/suid/common/sandbox.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" -#include "services/service_manager/sandbox/switches.h" - -namespace content { - -namespace { - -// Receive a fixed message on fd and return the sender's PID. -// Returns true if the message received matches the expected message. -bool ReceiveFixedMessage(int fd, - const char* expect_msg, - size_t expect_len, - base::ProcessId* sender_pid) { - // Allocate an extra byte of buffer space so we can check that we received - // exactly |expect_len| bytes, and the message wasn't just truncated to fit. - char buf[expect_len + 1]; - std::vector fds_vec; - - const ssize_t len = base::UnixDomainSocket::RecvMsgWithPid( - fd, buf, sizeof(buf), &fds_vec, sender_pid); - if (static_cast(len) != expect_len) - return false; - if (memcmp(buf, expect_msg, expect_len) != 0) - return false; - if (!fds_vec.empty()) - return false; - return true; -} - -} // namespace - -// static -ZygoteHost* ZygoteHost::GetInstance() { - return ZygoteHostImpl::GetInstance(); -} - -ZygoteHostImpl::ZygoteHostImpl() - : use_namespace_sandbox_(false), - use_suid_sandbox_(false), - use_suid_sandbox_for_adj_oom_score_(false), - sandbox_binary_(), - zygote_pids_lock_(), - zygote_pids_() {} - -ZygoteHostImpl::~ZygoteHostImpl() {} - -// static -ZygoteHostImpl* ZygoteHostImpl::GetInstance() { - return base::Singleton::get(); -} - -void ZygoteHostImpl::Init(const base::CommandLine& command_line) { - if (command_line.HasSwitch(switches::kNoSandbox)) { - return; - } - - // Exit early if running as root without --no-sandbox. See crbug.com/638180. - // When running as root with the sandbox enabled, the browser process - // crashes on zygote initialization. Running as root with the sandbox - // is not supported, and if Chrome were able to display UI it would be showing - // an error message. With the zygote crashing it doesn't even get to that, - // so print an error message on the console. - uid_t uid = 0; - gid_t gid = 0; - if (!sandbox::Credentials::GetRESIds(&uid, &gid) || uid == 0) { - LOG(ERROR) << "Running as root without --" << switches::kNoSandbox - << " is not supported. See https://crbug.com/638180."; - exit(EXIT_FAILURE); - } - - { - std::unique_ptr setuid_sandbox_host( - sandbox::SetuidSandboxHost::Create()); - sandbox_binary_ = setuid_sandbox_host->GetSandboxBinaryPath().value(); - } - - if (!command_line.HasSwitch(switches::kDisableNamespaceSandbox) && - sandbox::Credentials::CanCreateProcessInNewUserNS()) { - use_namespace_sandbox_ = true; - -#if defined(OS_CHROMEOS) - // Chrome OS has a kernel patch that restricts oom_score_adj. See - // crbug.com/576409 for details. - if (!sandbox_binary_.empty()) { - use_suid_sandbox_for_adj_oom_score_ = true; - } else { - LOG(ERROR) << "SUID sandbox binary is missing. Won't be able to adjust " - "OOM scores."; - } -#endif - } else if (!command_line.HasSwitch( - service_manager::switches::kDisableSetuidSandbox) && - !sandbox_binary_.empty()) { - use_suid_sandbox_ = true; - - // Use the SUID sandbox for adjusting OOM scores when we are using - // the setuid sandbox. This is needed beacuse the processes are - // non-dumpable, so /proc/pid/oom_score_adj can only be written by - // root. - use_suid_sandbox_for_adj_oom_score_ = use_suid_sandbox_; - } else { - LOG(FATAL) - << "No usable sandbox! Update your kernel or see " - "https://chromium.googlesource.com/chromium/src/+/master/" - "docs/linux_suid_sandbox_development.md for more information on " - "developing with the SUID sandbox. " - "If you want to live dangerously and need an immediate workaround, " - "you can try using --" - << switches::kNoSandbox << "."; - } -} - -void ZygoteHostImpl::AddZygotePid(pid_t pid) { - base::AutoLock lock(zygote_pids_lock_); - zygote_pids_.insert(pid); -} - -bool ZygoteHostImpl::IsZygotePid(pid_t pid) { - base::AutoLock lock(zygote_pids_lock_); - return zygote_pids_.find(pid) != zygote_pids_.end(); -} - -void ZygoteHostImpl::SetRendererSandboxStatus(int status) { - renderer_sandbox_status_ = status; -} - -int ZygoteHostImpl::GetRendererSandboxStatus() const { - return renderer_sandbox_status_; -} - -pid_t ZygoteHostImpl::LaunchZygote( - base::CommandLine* cmd_line, - base::ScopedFD* control_fd, - base::FileHandleMappingVector additional_remapped_fds) { - int fds[2]; - CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); - CHECK(base::UnixDomainSocket::EnableReceiveProcessId(fds[0])); - - base::LaunchOptions options; - options.fds_to_remap = std::move(additional_remapped_fds); - options.fds_to_remap.emplace_back(fds[1], kZygoteSocketPairFd); - - base::ScopedFD dummy_fd; - if (use_suid_sandbox_) { - std::unique_ptr sandbox_host( - sandbox::SetuidSandboxHost::Create()); - sandbox_host->PrependWrapper(cmd_line); - sandbox_host->SetupLaunchOptions(&options, &dummy_fd); - sandbox_host->SetupLaunchEnvironment(); - } - - base::Process process = - use_namespace_sandbox_ - ? sandbox::NamespaceSandbox::LaunchProcess(*cmd_line, options) - : base::LaunchProcess(*cmd_line, options); - CHECK(process.IsValid()) << "Failed to launch zygote process"; - - dummy_fd.reset(); - close(fds[1]); - control_fd->reset(fds[0]); - - pid_t pid = process.Pid(); - - if (use_namespace_sandbox_ || use_suid_sandbox_) { - // The namespace and 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::kNoSandbox << " to workaround."; - - // Now receive the message that the zygote's ready to go, along with the - // main zygote process's ID. - pid_t real_pid; - CHECK(ReceiveFixedMessage(fds[0], kZygoteHelloMessage, - sizeof(kZygoteHelloMessage), &real_pid)); - CHECK_GT(real_pid, 1); - - if (real_pid != pid) { - // Reap the sandbox. - base::EnsureProcessGetsReaped(std::move(process)); - } - pid = real_pid; - } - - AddZygotePid(pid); - return pid; -} - -#if !defined(OS_OPENBSD) -void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid, - int score) { - // 1) You can't change the oom_score_adj of a non-dumpable process - // (EPERM) unless you're root. Because of this, we can't set the - // oom_adj from the browser process. - // - // 2) We can't set the oom_score_adj before entering the sandbox - // because the zygote is in the sandbox and the zygote is as - // critical as the browser process. Its oom_adj value shouldn't - // be changed. - // - // 3) A non-dumpable process can't even change its own oom_score_adj - // because it's root owned 0644. The sandboxed processes don't - // even have /proc, but one could imagine passing in a descriptor - // from outside. - // - // So, in the normal case, we use the SUID binary to change it for us. - // However, Fedora (and other SELinux systems) don't like us touching other - // process's oom_score_adj (or oom_adj) values - // (https://bugzilla.redhat.com/show_bug.cgi?id=581256). - // - // The offical way to get the SELinux mode is selinux_getenforcemode, but I - // don't want to add another library to the build as it's sure to cause - // problems with other, non-SELinux distros. - // - // So we just check for files in /selinux. This isn't foolproof, but it's not - // bad and it's easy. - - static bool selinux; - static bool selinux_valid = false; - - if (!selinux_valid) { - const base::FilePath kSelinuxPath("/selinux"); - base::FileEnumerator en(kSelinuxPath, false, base::FileEnumerator::FILES); - bool has_selinux_files = !en.Next().empty(); - - selinux = - has_selinux_files && access(kSelinuxPath.value().c_str(), X_OK) == 0; - selinux_valid = true; - } - - if (!use_suid_sandbox_for_adj_oom_score_) { - if (!base::AdjustOOMScore(pid, score)) - PLOG(ERROR) << "Failed to adjust OOM score of renderer with pid " << pid; - return; - } - - if (selinux) - return; - - // If heap profiling is running, these processes are not exiting, at least - // on ChromeOS. The easiest thing to do is not launch them when profiling. - // TODO(stevenjb): Investigate further and fix. - if (base::allocator::IsHeapProfilerRunning()) - return; - - std::vector adj_oom_score_cmdline; - adj_oom_score_cmdline.push_back(sandbox_binary_); - adj_oom_score_cmdline.push_back(sandbox::kAdjustOOMScoreSwitch); - adj_oom_score_cmdline.push_back(base::Int64ToString(pid)); - adj_oom_score_cmdline.push_back(base::IntToString(score)); - - // sandbox_helper_process is a setuid binary. - base::LaunchOptions options; - options.allow_new_privs = true; - - base::Process sandbox_helper_process = - base::LaunchProcess(adj_oom_score_cmdline, options); - if (sandbox_helper_process.IsValid()) - base::EnsureProcessGetsReaped(std::move(sandbox_helper_process)); -} -#endif - -} // namespace content diff --git a/chromium/content/browser/zygote_host/zygote_host_impl_linux.h b/chromium/content/browser/zygote_host/zygote_host_impl_linux.h deleted file mode 100644 index c527996ad37..00000000000 --- a/chromium/content/browser/zygote_host/zygote_host_impl_linux.h +++ /dev/null @@ -1,71 +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_ZYGOTE_HOST_ZYGOTE_HOST_IMPL_LINUX_H_ -#define CONTENT_BROWSER_ZYGOTE_HOST_ZYGOTE_HOST_IMPL_LINUX_H_ - -#include - -#include -#include - -#include "base/command_line.h" -#include "base/files/scoped_file.h" -#include "base/process/launch.h" -#include "base/process/process_handle.h" -#include "base/synchronization/lock.h" -#include "content/public/browser/zygote_host_linux.h" - -namespace base { -template -struct DefaultSingletonTraits; -} // namespace base - -namespace content { - -class CONTENT_EXPORT ZygoteHostImpl : public ZygoteHost { - public: - // Returns the singleton instance. - static ZygoteHostImpl* GetInstance(); - - void Init(const base::CommandLine& cmd_line); - - // Returns whether or not this pid is the pid of a zygote. - bool IsZygotePid(pid_t pid) override; - - void SetRendererSandboxStatus(int status); - int GetRendererSandboxStatus() const override; - - pid_t LaunchZygote(base::CommandLine* cmd_line, - base::ScopedFD* control_fd, - base::FileHandleMappingVector additional_remapped_fds); - - void AdjustRendererOOMScore(base::ProcessHandle process_handle, - int score) override; - - private: - friend struct base::DefaultSingletonTraits; - - ZygoteHostImpl(); - ~ZygoteHostImpl() override; - - // Tells the ZygoteHost the PIDs of all the zygotes. - void AddZygotePid(pid_t pid); - - int renderer_sandbox_status_; - - bool use_namespace_sandbox_; - bool use_suid_sandbox_; - bool use_suid_sandbox_for_adj_oom_score_; - std::string sandbox_binary_; - - // This lock protects the |zygote_pids_| set. - base::Lock zygote_pids_lock_; - // This is a set of PIDs representing all the running zygotes. - std::set zygote_pids_; -}; - -} // namespace content - -#endif // CONTENT_BROWSER_ZYGOTE_HOST_ZYGOTE_HOST_IMPL_LINUX_H_ diff --git a/chromium/content/child/BUILD.gn b/chromium/content/child/BUILD.gn index fbb110d2d40..7c110b354fa 100644 --- a/chromium/content/child/BUILD.gn +++ b/chromium/content/child/BUILD.gn @@ -72,8 +72,6 @@ target(link_target_type, "child") { "service_factory.h", "thread_safe_sender.cc", "thread_safe_sender.h", - "webfallbackthemeengine_impl.cc", - "webfallbackthemeengine_impl.h", "webthemeengine_impl_android.cc", "webthemeengine_impl_android.h", "webthemeengine_impl_default.cc", @@ -106,7 +104,6 @@ target(link_target_type, "child") { "//gpu/command_buffer/client", "//media", "//media/blink", - "//mojo/common", "//mojo/edk", "//net", "//services/device/public/cpp:device_features", @@ -148,6 +145,10 @@ target(link_target_type, "child") { deps += [ "//third_party/android_tools:cpu_features" ] } + if (is_linux) { + deps += [ "//services/service_manager/zygote" ] + } + if (is_win) { libs = [ "dwrite.lib" ] } diff --git a/chromium/content/child/OWNERS b/chromium/content/child/OWNERS index e9510bd4d17..5d33001f6a8 100644 --- a/chromium/content/child/OWNERS +++ b/chromium/content/child/OWNERS @@ -1,12 +1 @@ haraken@chromium.org - -# AppCache -per-file appcache*=michaeln@chromium.org - -# WebSQL -per-file database_*=jsbell@chromium.org -per-file database_*=michaeln@chromium.org -per-file db_*=jsbell@chromium.org -per-file db_*=michaeln@chromium.org -per-file web_database_*=jsbell@chromium.org -per-file web_database_*=michaeln@chromium.org diff --git a/chromium/content/child/assert_matching_enums.cc b/chromium/content/child/assert_matching_enums.cc index b2b3db58b71..be579b005c8 100644 --- a/chromium/content/child/assert_matching_enums.cc +++ b/chromium/content/child/assert_matching_enums.cc @@ -8,7 +8,7 @@ #include "base/macros.h" #include "content/public/common/screen_orientation_values.h" #include "media/base/mime_util.h" -#include "third_party/blink/public/platform/modules/screen_orientation/web_screen_orientation_lock_type.h" +#include "third_party/blink/public/common/screen_orientation/web_screen_orientation_lock_type.h" #include "third_party/blink/public/platform/web_menu_source_type.h" #include "third_party/blink/public/platform/web_text_input_mode.h" #include "third_party/blink/public/platform/web_text_input_type.h" diff --git a/chromium/content/child/blink_platform_impl.cc b/chromium/content/child/blink_platform_impl.cc index aa67eb82782..d15a8c88c11 100644 --- a/chromium/content/child/blink_platform_impl.cc +++ b/chromium/content/child/blink_platform_impl.cc @@ -59,7 +59,6 @@ #include "ui/events/keycodes/dom/keycode_converter.h" using blink::WebData; -using blink::WebFallbackThemeEngine; using blink::WebLocalizedString; using blink::WebString; using blink::WebThemeEngine; @@ -366,10 +365,6 @@ void BlinkPlatformImpl::UpdateWebThreadTLS(blink::WebThread* thread, BlinkPlatformImpl::~BlinkPlatformImpl() { } -WebString BlinkPlatformImpl::UserAgent() { - return blink::WebString::FromUTF8(GetContentClient()->GetUserAgent()); -} - std::unique_ptr BlinkPlatformImpl::CreateThread( const blink::WebThreadCreationParams& params) { std::unique_ptr thread = @@ -702,16 +697,12 @@ WebThemeEngine* BlinkPlatformImpl::ThemeEngine() { return &native_theme_engine_; } -WebFallbackThemeEngine* BlinkPlatformImpl::FallbackThemeEngine() { - return &fallback_theme_engine_; -} - blink::Platform::FileHandle BlinkPlatformImpl::DatabaseOpenFile( const blink::WebString& vfs_file_name, int desired_flags) { #if defined(OS_WIN) return INVALID_HANDLE_VALUE; -#elif defined(OS_POSIX) +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) return -1; #endif } @@ -742,10 +733,6 @@ bool BlinkPlatformImpl::DatabaseSetFileSize( return false; } -size_t BlinkPlatformImpl::NumberOfProcessors() { - return static_cast(base::SysInfo::NumberOfProcessors()); -} - size_t BlinkPlatformImpl::MaxDecodedImageBytes() { const int kMB = 1024 * 1024; const int kMaxNumberOfBytesPerPixel = 4; @@ -781,12 +768,6 @@ bool BlinkPlatformImpl::IsLowEndDevice() { return base::SysInfo::IsLowEndDevice(); } -uint32_t BlinkPlatformImpl::GetUniqueIdForProcess() { - // TODO(rickyz): Replace this with base::GetUniqueIdForProcess when that's - // ready. - return base::trace_event::TraceLog::GetInstance()->process_id(); -} - bool BlinkPlatformImpl::IsMainThread() const { return main_thread_task_runner_.get() && main_thread_task_runner_->BelongsToCurrentThread(); diff --git a/chromium/content/child/blink_platform_impl.h b/chromium/content/child/blink_platform_impl.h index f9d13cf958a..4cbfab92679 100644 --- a/chromium/content/child/blink_platform_impl.h +++ b/chromium/content/child/blink_platform_impl.h @@ -15,7 +15,6 @@ #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "components/webcrypto/webcrypto_impl.h" -#include "content/child/webfallbackthemeengine_impl.h" #include "content/common/content_export.h" #include "media/blink/webmediacapabilitiesclient_impl.h" #include "third_party/blink/public/platform/platform.h" @@ -58,7 +57,6 @@ class CONTENT_EXPORT BlinkPlatformImpl : public blink::Platform { // Platform methods (partial implementation): blink::WebThemeEngine* ThemeEngine() override; - blink::WebFallbackThemeEngine* FallbackThemeEngine() override; blink::Platform::FileHandle DatabaseOpenFile( const blink::WebString& vfs_file_name, int desired_flags) override; @@ -71,12 +69,9 @@ class CONTENT_EXPORT BlinkPlatformImpl : public blink::Platform { const blink::WebSecurityOrigin& origin) override; bool DatabaseSetFileSize(const blink::WebString& vfs_file_name, long long size) override; - size_t NumberOfProcessors() override; size_t MaxDecodedImageBytes() override; bool IsLowEndDevice() override; - uint32_t GetUniqueIdForProcess() override; - blink::WebString UserAgent() override; std::unique_ptr CreateThread( const blink::WebThreadCreationParams& params) override; std::unique_ptr CreateWebAudioThread() override; @@ -127,7 +122,6 @@ class CONTENT_EXPORT BlinkPlatformImpl : public blink::Platform { scoped_refptr main_thread_task_runner_; scoped_refptr io_thread_task_runner_; WebThemeEngineImpl native_theme_engine_; - WebFallbackThemeEngineImpl fallback_theme_engine_; base::ThreadLocalStorage::Slot current_thread_slot_; webcrypto::WebCryptoImpl web_crypto_; media::WebMediaCapabilitiesClientImpl media_capabilities_client_; diff --git a/chromium/content/child/child_histogram_fetcher_impl.cc b/chromium/content/child/child_histogram_fetcher_impl.cc index 36dbd27bb6f..97283203509 100644 --- a/chromium/content/child/child_histogram_fetcher_impl.cc +++ b/chromium/content/child/child_histogram_fetcher_impl.cc @@ -37,7 +37,7 @@ void ChildHistogramFetcherFactoryImpl::CreateFetcher( const MojoResult result = mojo::UnwrapSharedMemoryHandle( std::move(buffer_handle), &memory_handle, &memory_size, nullptr); - if (result == MOJO_RESULT_OK) { + if (result == MOJO_RESULT_OK && memory_handle.IsValid()) { // This message must be received only once. Multiple calls to create a // global allocator will cause a CHECK() failure. base::GlobalHistogramAllocator::CreateWithSharedMemoryHandle(memory_handle, diff --git a/chromium/content/child/child_process.cc b/chromium/content/child/child_process.cc index 835a59e685a..7f70a3408c6 100644 --- a/chromium/content/child/child_process.cc +++ b/chromium/content/child/child_process.cc @@ -101,13 +101,13 @@ void ChildProcess::set_main_thread(ChildThreadImpl* thread) { void ChildProcess::AddRefProcess() { DCHECK(!main_thread_.get() || // null in unittests. - main_thread_->message_loop()->task_runner()->BelongsToCurrentThread()); + main_thread_->main_thread_runner()->BelongsToCurrentThread()); ref_count_++; } void ChildProcess::ReleaseProcess() { DCHECK(!main_thread_.get() || // null in unittests. - main_thread_->message_loop()->task_runner()->BelongsToCurrentThread()); + main_thread_->main_thread_runner()->BelongsToCurrentThread()); DCHECK(ref_count_); if (--ref_count_) return; diff --git a/chromium/content/child/child_process_sandbox_support_impl_linux.cc b/chromium/content/child/child_process_sandbox_support_impl_linux.cc index 587ddeab58e..7899935f659 100644 --- a/chromium/content/child/child_process_sandbox_support_impl_linux.cc +++ b/chromium/content/child/child_process_sandbox_support_impl_linux.cc @@ -15,8 +15,8 @@ #include "base/posix/unix_domain_socket.h" #include "base/sys_byteorder.h" #include "base/trace_event/trace_event.h" -#include "content/public/common/common_sandbox_support_linux.h" #include "services/service_manager/sandbox/linux/sandbox_linux.h" +#include "services/service_manager/zygote/common/common_sandbox_support_linux.h" #include "third_party/blink/public/platform/linux/web_fallback_font.h" #include "third_party/blink/public/platform/web_font_render_style.h" #include "third_party/blink/public/platform/web_string.h" @@ -37,7 +37,7 @@ void GetFallbackFontForCharacter(int32_t character, uint8_t buf[512]; const ssize_t n = base::UnixDomainSocket::SendRecvMsg( - GetSandboxFD(), buf, sizeof(buf), nullptr, request); + service_manager::GetSandboxFD(), buf, sizeof(buf), nullptr, request); std::string family_name; std::string filename; @@ -88,7 +88,7 @@ void GetRenderStyleForStrike(const char* family, uint8_t buf[512]; const ssize_t n = base::UnixDomainSocket::SendRecvMsg( - GetSandboxFD(), buf, sizeof(buf), nullptr, request); + service_manager::GetSandboxFD(), buf, sizeof(buf), nullptr, request); if (n == -1) return; @@ -127,8 +127,9 @@ int MatchFontWithFallback(const std::string& face, request.WriteUInt32(fallback_family); uint8_t reply_buf[64]; int fd = -1; - base::UnixDomainSocket::SendRecvMsg(GetSandboxFD(), reply_buf, - sizeof(reply_buf), &fd, request); + base::UnixDomainSocket::SendRecvMsg(service_manager::GetSandboxFD(), + reply_buf, sizeof(reply_buf), &fd, + request); return fd; } diff --git a/chromium/content/child/child_thread_impl.cc b/chromium/content/child/child_thread_impl.cc index 2663268bce3..d6898e941c8 100644 --- a/chromium/content/child/child_thread_impl.cc +++ b/chromium/content/child/child_thread_impl.cc @@ -17,7 +17,6 @@ #include "base/location.h" #include "base/logging.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" #include "base/message_loop/timer_slack.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram_macros.h" @@ -44,7 +43,6 @@ #include "content/public/common/connection_filter.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" -#include "content/public/common/mojo_channel_switches.h" #include "content/public/common/service_manager_connection.h" #include "content/public/common/service_names.mojom.h" #include "content/public/common/simple_connection_filter.h" @@ -64,6 +62,7 @@ #include "services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.h" #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h" #include "services/resource_coordinator/public/mojom/service_constants.mojom.h" +#include "services/service_manager/embedder/switches.h" #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "services/service_manager/runner/common/client_util.h" @@ -74,13 +73,19 @@ #include "content/public/common/content_descriptors.h" #endif -#if defined(OS_MACOSX) -#include "base/allocator/allocator_interception_mac.h" +#if defined(CLANG_COVERAGE) +extern "C" int __llvm_profile_write_file(void); #endif namespace content { namespace { +void WriteClangCoverageProfile() { +#if defined(CLANG_COVERAGE) + __llvm_profile_write_file(); +#endif +} + // How long to wait for a connection to the browser process before giving up. const int kConnectionTimeoutS = 15; @@ -104,6 +109,7 @@ class WaitAndExitDelegate : public base::PlatformThread::Delegate { void ThreadMain() override { base::PlatformThread::Sleep(duration_); + WriteClangCoverageProfile(); _exit(0); } @@ -164,6 +170,7 @@ class SuicideOnChannelErrorFilter : public IPC::MessageFilter { __lsan_do_leak_check(); #endif #else + WriteClangCoverageProfile(); _exit(0); #endif } @@ -212,7 +219,7 @@ void QuitClosure::BindToMainThread() { scoped_refptr task_runner( base::ThreadTaskRunnerHandle::Get()); base::Closure quit_closure = - base::MessageLoop::current()->QuitWhenIdleClosure(); + base::RunLoop::QuitCurrentWhenIdleClosureDeprecated(); closure_ = base::Bind(&QuitClosure::PostClosure, task_runner, quit_closure); cond_var_.Signal(); } @@ -232,7 +239,7 @@ base::LazyInstance::DestructorAtExit g_quit_closure = std::unique_ptr InitializeMojoIPCChannel() { TRACE_EVENT0("startup", "InitializeMojoIPCChannel"); - mojo::edk::ScopedPlatformHandle platform_channel; + mojo::edk::ScopedInternalPlatformHandle platform_channel; #if defined(OS_WIN) if (base::CommandLine::ForCurrentProcess()->HasSwitch( mojo::edk::PlatformChannelPair::kMojoPlatformChannelHandleSwitch)) { @@ -251,8 +258,9 @@ InitializeMojoIPCChannel() { mojo::edk::PlatformChannelPair::PassClientHandleFromParentProcess( *base::CommandLine::ForCurrentProcess()); #elif defined(OS_POSIX) - platform_channel.reset(mojo::edk::PlatformHandle( - base::GlobalDescriptors::GetInstance()->Get(kMojoIPCChannel))); + platform_channel.reset(mojo::edk::InternalPlatformHandle( + base::GlobalDescriptors::GetInstance()->Get( + service_manager::kMojoIPCChannel))); #endif // Mojo isn't supported on all child process types. // TODO(crbug.com/604282): Support Mojo in the remaining processes. @@ -432,7 +440,7 @@ void ChildThreadImpl::Init(const Options& options) { TRACE_EVENT0("startup", "ChildThreadImpl::Init"); g_lazy_tls.Pointer()->Set(this); on_channel_error_called_ = false; - message_loop_ = base::MessageLoop::current(); + main_thread_runner_ = base::ThreadTaskRunnerHandle::Get(); #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED) // We must make sure to instantiate the IPC Logger *before* we create the // channel, otherwise we can get a callback on the IO thread which creates @@ -458,7 +466,7 @@ void ChildThreadImpl::Init(const Options& options) { std::string service_request_token = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kServiceRequestChannelToken); + service_manager::switches::kServiceRequestChannelToken); if (!service_request_token.empty() && invitation) { service_request_pipe = invitation->ExtractMessagePipe(service_request_token); @@ -476,8 +484,8 @@ void ChildThreadImpl::Init(const Options& options) { } sync_message_filter_ = channel_->CreateSyncMessageFilter(); - thread_safe_sender_ = new ThreadSafeSender( - message_loop_->task_runner(), sync_message_filter_.get()); + thread_safe_sender_ = + new ThreadSafeSender(main_thread_runner_, sync_message_filter_.get()); auto registry = std::make_unique(); registry->AddInterface(base::Bind(&ChildHistogramFetcherFactoryImpl::Create), @@ -562,15 +570,7 @@ void ChildThreadImpl::Init(const Options& options) { connection_timeout = temp; } -#if defined(OS_MACOSX) - if (base::CommandLine::InitializedForCurrentProcess() && - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableHeapProfiling)) { - base::allocator::PeriodicallyShimNewMallocZones(); - } -#endif - - message_loop_->task_runner()->PostDelayedTask( + main_thread_runner_->PostDelayedTask( FROM_HERE, base::BindOnce(&ChildThreadImpl::EnsureConnected, channel_connected_factory_->GetWeakPtr()), @@ -608,7 +608,7 @@ void ChildThreadImpl::InitTracing() { channel_->AddFilter(new tracing::ChildTraceMessageFilter( ChildProcess::current()->io_task_runner())); - chrome_trace_event_agent_ = std::make_unique( + trace_event_agent_ = tracing::TraceEventAgent::Create( GetConnector(), false /* request_clock_sync_marker_on_android */); } @@ -632,8 +632,10 @@ ChildThreadImpl::~ChildThreadImpl() { } void ChildThreadImpl::Shutdown() { - // Delete objects that hold references to blink so derived classes can - // safely shutdown blink in their Shutdown implementation. + // The renderer process (and others) can to fast shutdown by calling _exit(0), + // in which case the clang-coverage profile does not get written to the file. + // So force write the profile here before shutting down. + WriteClangCoverageProfile(); } bool ChildThreadImpl::ShouldBeDestroyed() { @@ -653,7 +655,7 @@ void ChildThreadImpl::OnChannelError() { } bool ChildThreadImpl::Send(IPC::Message* msg) { - DCHECK(message_loop_->task_runner()->BelongsToCurrentThread()); + DCHECK(main_thread_runner_->BelongsToCurrentThread()); if (!channel_) { delete msg; return false; @@ -713,7 +715,7 @@ service_manager::Connector* ChildThreadImpl::GetConnector() { } IPC::MessageRouter* ChildThreadImpl::GetRouter() { - DCHECK(message_loop_->task_runner()->BelongsToCurrentThread()); + DCHECK(main_thread_runner_->BelongsToCurrentThread()); return &router_; } diff --git a/chromium/content/child/child_thread_impl.h b/chromium/content/child/child_thread_impl.h index 523af416a39..9f4155dfdcf 100644 --- a/chromium/content/child/child_thread_impl.h +++ b/chromium/content/child/child_thread_impl.h @@ -12,6 +12,7 @@ #include #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "base/memory/shared_memory.h" #include "base/memory/weak_ptr.h" #include "base/metrics/field_trial.h" @@ -29,7 +30,7 @@ #include "ipc/message_router.h" #include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/associated_binding_set.h" -#include "services/tracing/public/cpp/chrome_trace_event_agent.h" +#include "services/tracing/public/cpp/trace_event_agent.h" #if defined(OS_WIN) #include "content/public/common/font_cache_win.mojom.h" @@ -37,10 +38,6 @@ #include "content/common/font_loader_mac.mojom.h" #endif -namespace base { -class MessageLoop; -} // namespace base - namespace IPC { class MessageFilter; class SyncChannel; @@ -132,7 +129,9 @@ class CONTENT_EXPORT ChildThreadImpl return thread_safe_sender_.get(); } - base::MessageLoop* message_loop() const { return message_loop_; } + scoped_refptr main_thread_runner() const { + return main_thread_runner_; + } // Returns the one child thread. Can only be called on the main thread. static ChildThreadImpl* current(); @@ -248,13 +247,14 @@ class CONTENT_EXPORT ChildThreadImpl // attempt to communicate. bool on_channel_error_called_; - base::MessageLoop* message_loop_; + // TaskRunner to post tasks to the main thread. + scoped_refptr main_thread_runner_; std::unique_ptr power_monitor_; scoped_refptr browser_process_io_runner_; - std::unique_ptr chrome_trace_event_agent_; + std::unique_ptr trace_event_agent_; std::unique_ptr field_trial_syncer_; diff --git a/chromium/content/child/runtime_features.cc b/chromium/content/child/runtime_features.cc index 03a522caa66..e999f7b68a5 100644 --- a/chromium/content/child/runtime_features.cc +++ b/chromium/content/child/runtime_features.cc @@ -90,14 +90,17 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( if (enableExperimentalWebPlatformFeatures) WebRuntimeFeatures::EnableExperimentalFeatures(true); + SetRuntimeFeatureDefaultsForPlatform(); + + // Begin individual features. + // Do not add individual features above this line. + WebRuntimeFeatures::EnableOriginTrials( base::FeatureList::IsEnabled(features::kOriginTrials)); if (!base::FeatureList::IsEnabled(features::kWebUsb)) WebRuntimeFeatures::EnableWebUsb(false); - SetRuntimeFeatureDefaultsForPlatform(); - if (command_line.HasSwitch(switches::kDisableDatabases)) WebRuntimeFeatures::EnableDatabase(false); @@ -138,15 +141,12 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( } #if defined(OS_MACOSX) - bool enable_canvas_2d_image_chromium = command_line.HasSwitch( - switches::kEnableGpuMemoryBufferCompositorResources) && + const bool enable_canvas_2d_image_chromium = + command_line.HasSwitch( + switches::kEnableGpuMemoryBufferCompositorResources) && !command_line.HasSwitch(switches::kDisable2dCanvasImageChromium) && - !command_line.HasSwitch(switches::kDisableGpu); - - if (enable_canvas_2d_image_chromium) { - enable_canvas_2d_image_chromium = - base::FeatureList::IsEnabled(features::kCanvas2DImageChromium); - } + !command_line.HasSwitch(switches::kDisableGpu) && + base::FeatureList::IsEnabled(features::kCanvas2DImageChromium); #else bool enable_canvas_2d_image_chromium = false; #endif @@ -189,9 +189,9 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( if (command_line.HasSwitch(switches::kReducedReferrerGranularity)) WebRuntimeFeatures::EnableReducedReferrerGranularity(true); - WebRuntimeFeatures::EnableRootLayerScrolling( - base::FeatureList::IsEnabled(features::kRootLayerScrolling) || - enableExperimentalWebPlatformFeatures); + WebRuntimeFeatures::EnableIntersectionObserverGeometryMapper( + base::FeatureList::IsEnabled( + features::kIntersectionObserverGeometryMapper)); if (command_line.HasSwitch(switches::kDisablePermissionsAPI)) WebRuntimeFeatures::EnablePermissionsAPI(false); @@ -210,14 +210,17 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( if (base::FeatureList::IsEnabled(features::kWebXrGamepadSupport)) WebRuntimeFeatures::EnableWebXRGamepadSupport(true); + if (base::FeatureList::IsEnabled(features::kWebXrHitTest)) + WebRuntimeFeatures::EnableWebXRHitTest(true); + if (command_line.HasSwitch(switches::kDisablePresentationAPI)) WebRuntimeFeatures::EnablePresentationAPI(false); if (command_line.HasSwitch(switches::kDisableRemotePlaybackAPI)) WebRuntimeFeatures::EnableRemotePlaybackAPI(false); - WebRuntimeFeatures::EnableScrollAnchoring( - base::FeatureList::IsEnabled(features::kScrollAnchoring) || + WebRuntimeFeatures::EnableSecMetadata( + base::FeatureList::IsEnabled(features::kSecMetadata) || enableExperimentalWebPlatformFeatures); WebRuntimeFeatures::EnableUserActivationV2( @@ -232,6 +235,10 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( command_line.HasSwitch(switches::kEnableSlimmingPaintV175) || enableExperimentalWebPlatformFeatures); + WebRuntimeFeatures::EnableFeatureFromString( + "BlinkGenPropertyTrees", + command_line.HasSwitch(switches::kEnableBlinkGenPropertyTrees)); + if (command_line.HasSwitch(switches::kEnableSlimmingPaintV2)) WebRuntimeFeatures::EnableSlimmingPaintV2(true); @@ -297,6 +304,9 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( if (base::FeatureList::IsEnabled(features::kGamepadExtensions)) WebRuntimeFeatures::EnableGamepadExtensions(true); + if (base::FeatureList::IsEnabled(features::kGamepadVibration)) + WebRuntimeFeatures::EnableGamepadVibration(true); + if (base::FeatureList::IsEnabled(features::kCompositeOpaqueFixedPosition)) WebRuntimeFeatures::EnableFeatureFromString("CompositeOpaqueFixedPosition", true); @@ -313,9 +323,6 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( WebRuntimeFeatures::EnableGenericSensorExtraClasses(true); } - if (base::FeatureList::IsEnabled(features::kNotificationsWithMojo)) - WebRuntimeFeatures::EnableNotificationsWithMojo(true); - if (base::FeatureList::IsEnabled(network::features::kOutOfBlinkCORS)) WebRuntimeFeatures::EnableOutOfBlinkCORS(true); @@ -330,10 +337,6 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( false); } - if (base::FeatureList::IsEnabled( - features::kTurnOff2DAndOpacityCompositorAnimations)) - WebRuntimeFeatures::EnableTurnOff2DAndOpacityCompositorAnimations(true); - if (base::FeatureList::IsEnabled(features::kRasterInducingScroll)) WebRuntimeFeatures::EnableRasterInducingScroll(true); @@ -362,10 +365,8 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( WebRuntimeFeatures::EnableResourceLoadScheduler( base::FeatureList::IsEnabled(features::kResourceLoadScheduler)); - if (command_line.HasSwitch( - switches::kDisableOriginTrialControlledBlinkFeatures)) { - WebRuntimeFeatures::EnableOriginTrialControlledFeatures(false); - } + if (base::FeatureList::IsEnabled(features::kLayeredAPI)) + WebRuntimeFeatures::EnableLayeredAPI(true); WebRuntimeFeatures::EnableLazyInitializeMediaControls( base::FeatureList::IsEnabled(features::kLazyInitializeMediaControls)); @@ -402,17 +403,6 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( if (base::FeatureList::IsEnabled(features::kLazyFrameLoading)) WebRuntimeFeatures::EnableLazyFrameLoading(true); - // Enable explicitly enabled features, and then disable explicitly disabled - // ones. - for (const std::string& feature : - FeaturesFromSwitch(command_line, switches::kEnableBlinkFeatures)) { - WebRuntimeFeatures::EnableFeatureFromString(feature, true); - } - for (const std::string& feature : - FeaturesFromSwitch(command_line, switches::kDisableBlinkFeatures)) { - WebRuntimeFeatures::EnableFeatureFromString(feature, false); - } - WebRuntimeFeatures::EnableV8ContextSnapshot( base::FeatureList::IsEnabled(features::kV8ContextSnapshot)); @@ -434,21 +424,47 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( WebRuntimeFeatures::EnablePictureInPicture( base::FeatureList::IsEnabled(media::kPictureInPicture)); - WebRuntimeFeatures::EnableCodeCacheAfterExecute( - base::FeatureList::IsEnabled(features::kCodeCacheAfterExecute)); - WebRuntimeFeatures::EnableCacheInlineScriptCode( base::FeatureList::IsEnabled(features::kCacheInlineScriptCode)); - if (base::FeatureList::IsEnabled(features::kUnifiedTouchAdjustment)) - WebRuntimeFeatures::EnableUnifiedTouchAdjustment(true); - // Make srcset on link rel=preload work with SignedHTTPExchange flag too. if (base::FeatureList::IsEnabled(features::kSignedHTTPExchange)) WebRuntimeFeatures::EnablePreloadImageSrcSetEnabled(true); WebRuntimeFeatures::EnableOffMainThreadWebSocket( base::FeatureList::IsEnabled(features::kOffMainThreadWebSocket)); + + if (base::FeatureList::IsEnabled( + features::kExperimentalProductivityFeatures)) { + WebRuntimeFeatures::EnableExperimentalProductivityFeatures(true); + } + +#if defined(OS_ANDROID) + if (base::FeatureList::IsEnabled(features::kDisplayCutoutAPI)) + WebRuntimeFeatures::EnableDisplayCutoutViewportFit(true); +#endif + + // End individual features. + // Do not add individual features below this line. + + if (command_line.HasSwitch( + switches::kDisableOriginTrialControlledBlinkFeatures)) { + WebRuntimeFeatures::EnableOriginTrialControlledFeatures(false); + } + + WebRuntimeFeatures::EnableAutoplayIgnoresWebAudio( + base::FeatureList::IsEnabled(media::kAutoplayIgnoreWebAudio)); + + // Enable explicitly enabled features, and then disable explicitly disabled + // ones. + for (const std::string& feature : + FeaturesFromSwitch(command_line, switches::kEnableBlinkFeatures)) { + WebRuntimeFeatures::EnableFeatureFromString(feature, true); + } + for (const std::string& feature : + FeaturesFromSwitch(command_line, switches::kDisableBlinkFeatures)) { + WebRuntimeFeatures::EnableFeatureFromString(feature, false); + } }; } // namespace content diff --git a/chromium/content/child/webfallbackthemeengine_impl.cc b/chromium/content/child/webfallbackthemeengine_impl.cc deleted file mode 100644 index 5c091bc0a75..00000000000 --- a/chromium/content/child/webfallbackthemeengine_impl.cc +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/child/webfallbackthemeengine_impl.h" - -#include "base/macros.h" -#include "skia/ext/platform_canvas.h" -#include "third_party/blink/public/platform/web_rect.h" -#include "third_party/blink/public/platform/web_size.h" -#include "ui/native_theme/native_theme_base.h" - -using blink::WebCanvas; -using blink::WebColor; -using blink::WebRect; -using blink::WebFallbackThemeEngine; - -namespace content { - -class WebFallbackThemeEngineImpl::WebFallbackNativeTheme - : public ui::NativeThemeBase { - public: - WebFallbackNativeTheme() {} - ~WebFallbackNativeTheme() override {} - - // NativeTheme: - SkColor GetSystemColor(ColorId color_id) const override { - // The paint routines in NativeThemeBase only use GetSystemColor for - // button focus colors and the fallback theme is not used for buttons. - NOTREACHED(); - return SK_ColorRED; - } - - private: - DISALLOW_COPY_AND_ASSIGN(WebFallbackNativeTheme); -}; - -static ui::NativeTheme::Part NativeThemePart( - WebFallbackThemeEngine::Part part) { - switch (part) { - case WebFallbackThemeEngine::kPartScrollbarDownArrow: - return ui::NativeTheme::kScrollbarDownArrow; - case WebFallbackThemeEngine::kPartScrollbarLeftArrow: - return ui::NativeTheme::kScrollbarLeftArrow; - case WebFallbackThemeEngine::kPartScrollbarRightArrow: - return ui::NativeTheme::kScrollbarRightArrow; - case WebFallbackThemeEngine::kPartScrollbarUpArrow: - return ui::NativeTheme::kScrollbarUpArrow; - case WebFallbackThemeEngine::kPartScrollbarHorizontalThumb: - return ui::NativeTheme::kScrollbarHorizontalThumb; - case WebFallbackThemeEngine::kPartScrollbarVerticalThumb: - return ui::NativeTheme::kScrollbarVerticalThumb; - case WebFallbackThemeEngine::kPartScrollbarHorizontalTrack: - return ui::NativeTheme::kScrollbarHorizontalTrack; - case WebFallbackThemeEngine::kPartScrollbarVerticalTrack: - return ui::NativeTheme::kScrollbarVerticalTrack; - case WebFallbackThemeEngine::kPartScrollbarCorner: - return ui::NativeTheme::kScrollbarCorner; - case WebFallbackThemeEngine::kPartCheckbox: - return ui::NativeTheme::kCheckbox; - case WebFallbackThemeEngine::kPartRadio: - return ui::NativeTheme::kRadio; - case WebFallbackThemeEngine::kPartButton: - return ui::NativeTheme::kPushButton; - case WebFallbackThemeEngine::kPartTextField: - return ui::NativeTheme::kTextField; - case WebFallbackThemeEngine::kPartMenuList: - return ui::NativeTheme::kMenuList; - case WebFallbackThemeEngine::kPartSliderTrack: - return ui::NativeTheme::kSliderTrack; - case WebFallbackThemeEngine::kPartSliderThumb: - return ui::NativeTheme::kSliderThumb; - case WebFallbackThemeEngine::kPartInnerSpinButton: - return ui::NativeTheme::kInnerSpinButton; - case WebFallbackThemeEngine::kPartProgressBar: - return ui::NativeTheme::kProgressBar; - default: - return ui::NativeTheme::kScrollbarDownArrow; - } -} - -static ui::NativeTheme::State NativeThemeState( - WebFallbackThemeEngine::State state) { - switch (state) { - case WebFallbackThemeEngine::kStateDisabled: - return ui::NativeTheme::kDisabled; - case WebFallbackThemeEngine::kStateHover: - return ui::NativeTheme::kHovered; - case WebFallbackThemeEngine::kStateNormal: - return ui::NativeTheme::kNormal; - case WebFallbackThemeEngine::kStatePressed: - return ui::NativeTheme::kPressed; - default: - return ui::NativeTheme::kDisabled; - } -} - -static void GetNativeThemeExtraParams( - WebFallbackThemeEngine::Part part, - WebFallbackThemeEngine::State state, - const WebFallbackThemeEngine::ExtraParams* extra_params, - ui::NativeTheme::ExtraParams* native_theme_extra_params) { - switch (part) { - case WebFallbackThemeEngine::kPartScrollbarHorizontalTrack: - case WebFallbackThemeEngine::kPartScrollbarVerticalTrack: - native_theme_extra_params->scrollbar_track.track_x = - extra_params->scrollbar_track.track_x; - native_theme_extra_params->scrollbar_track.track_y = - extra_params->scrollbar_track.track_y; - native_theme_extra_params->scrollbar_track.track_width = - extra_params->scrollbar_track.track_width; - native_theme_extra_params->scrollbar_track.track_height = - extra_params->scrollbar_track.track_height; - break; - case WebFallbackThemeEngine::kPartCheckbox: - native_theme_extra_params->button.checked = extra_params->button.checked; - native_theme_extra_params->button.indeterminate = - extra_params->button.indeterminate; - break; - case WebFallbackThemeEngine::kPartRadio: - native_theme_extra_params->button.checked = extra_params->button.checked; - break; - case WebFallbackThemeEngine::kPartButton: - native_theme_extra_params->button.is_default = - extra_params->button.is_default; - native_theme_extra_params->button.has_border = - extra_params->button.has_border; - // Native buttons have a different focus style. - native_theme_extra_params->button.is_focused = false; - native_theme_extra_params->button.background_color = - extra_params->button.background_color; - break; - case WebFallbackThemeEngine::kPartTextField: - native_theme_extra_params->text_field.is_text_area = - extra_params->text_field.is_text_area; - native_theme_extra_params->text_field.is_listbox = - extra_params->text_field.is_listbox; - native_theme_extra_params->text_field.background_color = - extra_params->text_field.background_color; - break; - case WebFallbackThemeEngine::kPartMenuList: - native_theme_extra_params->menu_list.has_border = - extra_params->menu_list.has_border; - native_theme_extra_params->menu_list.has_border_radius = - extra_params->menu_list.has_border_radius; - native_theme_extra_params->menu_list.arrow_x = - extra_params->menu_list.arrow_x; - native_theme_extra_params->menu_list.arrow_y = - extra_params->menu_list.arrow_y; - native_theme_extra_params->menu_list.arrow_size = - extra_params->menu_list.arrow_size; - native_theme_extra_params->menu_list.arrow_color = - extra_params->menu_list.arrow_color; - native_theme_extra_params->menu_list.background_color = - extra_params->menu_list.background_color; - break; - case WebFallbackThemeEngine::kPartSliderTrack: - case WebFallbackThemeEngine::kPartSliderThumb: - native_theme_extra_params->slider.vertical = - extra_params->slider.vertical; - native_theme_extra_params->slider.in_drag = extra_params->slider.in_drag; - break; - case WebFallbackThemeEngine::kPartInnerSpinButton: - native_theme_extra_params->inner_spin.spin_up = - extra_params->inner_spin.spin_up; - native_theme_extra_params->inner_spin.read_only = - extra_params->inner_spin.read_only; - break; - case WebFallbackThemeEngine::kPartProgressBar: - native_theme_extra_params->progress_bar.determinate = - extra_params->progress_bar.determinate; - native_theme_extra_params->progress_bar.value_rect_x = - extra_params->progress_bar.value_rect_x; - native_theme_extra_params->progress_bar.value_rect_y = - extra_params->progress_bar.value_rect_y; - native_theme_extra_params->progress_bar.value_rect_width = - extra_params->progress_bar.value_rect_width; - native_theme_extra_params->progress_bar.value_rect_height = - extra_params->progress_bar.value_rect_height; - break; - default: - break; // Parts that have no extra params get here. - } -} - -WebFallbackThemeEngineImpl::WebFallbackThemeEngineImpl() - : theme_(new WebFallbackNativeTheme()) {} - -WebFallbackThemeEngineImpl::~WebFallbackThemeEngineImpl() {} - -blink::WebSize WebFallbackThemeEngineImpl::GetSize( - WebFallbackThemeEngine::Part part) { - ui::NativeTheme::ExtraParams extra; - return theme_->GetPartSize(NativeThemePart(part), - ui::NativeTheme::kNormal, - extra); -} - -void WebFallbackThemeEngineImpl::Paint( - blink::WebCanvas* canvas, - WebFallbackThemeEngine::Part part, - WebFallbackThemeEngine::State state, - const blink::WebRect& rect, - const WebFallbackThemeEngine::ExtraParams* extra_params) { - ui::NativeTheme::ExtraParams native_theme_extra_params; - GetNativeThemeExtraParams( - part, state, extra_params, &native_theme_extra_params); - theme_->Paint(canvas, - NativeThemePart(part), - NativeThemeState(state), - gfx::Rect(rect), - native_theme_extra_params); -} - -} // namespace content diff --git a/chromium/content/child/webfallbackthemeengine_impl.h b/chromium/content/child/webfallbackthemeengine_impl.h deleted file mode 100644 index 53f7b83c2df..00000000000 --- a/chromium/content/child/webfallbackthemeengine_impl.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_CHILD_WEBFALLBACKTHEMEENGINE_IMPL_H_ -#define CONTENT_CHILD_WEBFALLBACKTHEMEENGINE_IMPL_H_ - -#include - -#include "base/macros.h" -#include "third_party/blink/public/platform/web_fallback_theme_engine.h" - -namespace content { - -// This theme should only be used in layout tests in cases the mock theme can't -// handle (such as zoomed controls). -class WebFallbackThemeEngineImpl : public blink::WebFallbackThemeEngine { - public: - WebFallbackThemeEngineImpl(); - ~WebFallbackThemeEngineImpl(); - - // WebFallbackThemeEngine methods: - blink::WebSize GetSize(blink::WebFallbackThemeEngine::Part) override; - void Paint( - blink::WebCanvas* canvas, - blink::WebFallbackThemeEngine::Part part, - blink::WebFallbackThemeEngine::State state, - const blink::WebRect& rect, - const blink::WebFallbackThemeEngine::ExtraParams* extra_params) override; - - private: - class WebFallbackNativeTheme; - std::unique_ptr theme_; - - DISALLOW_COPY_AND_ASSIGN(WebFallbackThemeEngineImpl); -}; - -} // namespace content - -#endif // CONTENT_CHILD_WEBFALLBACKTHEMEENGINE_IMPL_H_ diff --git a/chromium/content/child/webthemeengine_impl_android.cc b/chromium/content/child/webthemeengine_impl_android.cc index a66f7fbce54..6b14c3e23d7 100644 --- a/chromium/content/child/webthemeengine_impl_android.cc +++ b/chromium/content/child/webthemeengine_impl_android.cc @@ -12,7 +12,6 @@ #include "ui/native_theme/native_theme.h" using blink::WebCanvas; -using blink::WebColor; using blink::WebRect; using blink::WebThemeEngine; diff --git a/chromium/content/child/webthemeengine_impl_default.cc b/chromium/content/child/webthemeengine_impl_default.cc index 6d898cc534c..13e0a6278c9 100644 --- a/chromium/content/child/webthemeengine_impl_default.cc +++ b/chromium/content/child/webthemeengine_impl_default.cc @@ -12,7 +12,6 @@ #include "ui/native_theme/overlay_scrollbar_constants_aura.h" using blink::WebCanvas; -using blink::WebColor; using blink::WebRect; using blink::WebThemeEngine; using blink::WebScrollbarOverlayColorTheme; diff --git a/chromium/content/common/BUILD.gn b/chromium/content/common/BUILD.gn index 397d9172812..de4c4d2af49 100644 --- a/chromium/content/common/BUILD.gn +++ b/chromium/content/common/BUILD.gn @@ -7,7 +7,6 @@ import("//build/buildflag_header.gni") import("//build/config/features.gni") import("//build/config/ui.gni") import("//ipc/features.gni") -import("//media/media_options.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//ppapi/buildflags/buildflags.gni") import("//sandbox/features.gni") @@ -65,8 +64,6 @@ source_set("common") { "browser_plugin/browser_plugin_constants.cc", "browser_plugin/browser_plugin_constants.h", "browser_plugin/browser_plugin_messages.h", - "cache_storage/cache_storage_types.cc", - "cache_storage/cache_storage_types.h", "child_process_host_impl.cc", "child_process_host_impl.h", "common_sandbox_support_linux.cc", @@ -138,8 +135,8 @@ source_set("common") { "frame_owner_properties.h", "frame_replication_state.cc", "frame_replication_state.h", - "frame_resize_params.cc", - "frame_resize_params.h", + "frame_visual_properties.cc", + "frame_visual_properties.h", "gin_java_bridge_messages.h", "in_process_child_thread_params.cc", "in_process_child_thread_params.h", @@ -205,7 +202,6 @@ source_set("common") { "mac/font_loader.h", "mac/font_loader.mm", "media/aec_dump_messages.h", - "media/audio_messages.h", "media/cdm_info.cc", "media/media_devices.cc", "media/media_devices.h", @@ -231,9 +227,9 @@ source_set("common") { "net/url_request_user_data.h", "notifications/notification_struct_traits.cc", "notifications/notification_struct_traits.h", - "origin_trials/trial_policy_impl.cc", - "origin_trials/trial_policy_impl.h", "origin_util.cc", + "p2p_messages.h", + "p2p_socket_type.h", "page_message_enums.h", "page_messages.h", "page_state_serialization.cc", @@ -250,13 +246,12 @@ source_set("common") { "plugin_list.h", "possibly_associated_interface_ptr.h", "possibly_associated_interface_ptr_info.h", + "possibly_associated_wrapper_shared_url_loader_factory.h", "presentation/presentation_struct_traits.cc", "presentation/presentation_struct_traits.h", "process_type.cc", "render_widget_surface_properties.cc", "render_widget_surface_properties.h", - "resize_params.cc", - "resize_params.h", "resource_messages.h", "resource_timing_info.cc", "resource_timing_info.h", @@ -264,7 +259,6 @@ source_set("common") { "sandbox_init_mac.cc", "sandbox_init_win.cc", "savable_subframe.h", - "send_zygote_child_ping_linux.cc", "service_manager/service_manager_connection_impl.cc", "service_manager/service_manager_connection_impl.h", "service_worker/service_worker_loader_helpers.cc", @@ -280,7 +274,6 @@ source_set("common") { "service_worker/service_worker_utils.h", "single_request_url_loader_factory.cc", "single_request_url_loader_factory.h", - "speech_recognition_messages.h", "swapped_out_messages.cc", "swapped_out_messages.h", "task_scheduler.cc", @@ -299,9 +292,8 @@ source_set("common") { "user_agent.cc", "view_message_enums.h", "view_messages.h", - "wrapper_shared_url_loader_factory.cc", - "wrapper_shared_url_loader_factory.h", - "zygote_commands_linux.h", + "visual_properties.cc", + "visual_properties.h", ] configs += [ @@ -326,7 +318,6 @@ source_set("common") { "//base", "//base/third_party/dynamic_annotations", "//build/util:webkit_version", - "//cc/ipc", "//components/discardable_memory/common", "//components/services/filesystem/public/interfaces", "//components/tracing", @@ -334,9 +325,9 @@ source_set("common") { "//components/viz/service", "//content:resources", "//content/app/resources", + "//content/common/service_worker:service_worker_types_proto", "//content/public/common:interfaces", "//content/public/common:service_names", - "//content/public/common:zygote_buildflags", "//device/base/synchronization", "//device/bluetooth", "//gpu", @@ -372,6 +363,7 @@ source_set("common") { "//services/service_manager/public/cpp", "//services/service_manager/public/mojom", "//services/service_manager/runner/common", + "//services/service_manager/zygote:zygote_buildflags", "//services/ui/public/interfaces", "//services/video_capture/public/mojom", "//services/viz/public/interfaces", @@ -380,6 +372,8 @@ source_set("common") { "//third_party/angle:angle_gpu_info_util", "//third_party/boringssl", "//third_party/icu", + "//third_party/webrtc/rtc_base:rtc_base", + "//third_party/webrtc_overrides", "//ui/base", "//ui/base/ime", "//ui/display", @@ -437,17 +431,6 @@ source_set("common") { deps += [ "//ppapi/proxy:ipc_sources" ] } - if (enable_webrtc) { - sources += [ - "p2p_messages.h", - "p2p_socket_type.h", - ] - deps += [ - "//third_party/webrtc/rtc_base:rtc_base", - "//third_party/webrtc_overrides", - ] - } - if (use_ozone) { deps += [ "//ui/ozone" ] } else { @@ -510,6 +493,8 @@ source_set("common") { "sandbox_policy_fuchsia.cc", "sandbox_policy_fuchsia.h", ] + + deps += [ "//third_party/fuchsia-sdk:launchpad" ] } } @@ -535,6 +520,8 @@ mojom("mojo_bindings") { # both a direct and an indirect dependency on the same target skip_deps_check = true + disable_variants = true + sources = [ "appcache.mojom", "associated_interfaces.mojom", @@ -542,7 +529,6 @@ mojom("mojo_bindings") { "child_control.mojom", "child_memory_coordinator.mojom", "field_trial_recorder.mojom", - "file_utilities.mojom", "frame.mojom", "frame_sink_provider.mojom", "histogram_fetcher.mojom", @@ -583,6 +569,7 @@ mojom("mojo_bindings") { "shared_worker/shared_worker_factory.mojom", "shared_worker/shared_worker_host.mojom", "shared_worker/shared_worker_info.mojom", + "speech_recognizer.mojom", "storage_partition_service.mojom", "url_loader_factory_bundle.mojom", "widget.mojom", diff --git a/chromium/content/common/DEPS b/chromium/content/common/DEPS index 17e3223f618..c6649bd23f8 100644 --- a/chromium/content/common/DEPS +++ b/chromium/content/common/DEPS @@ -23,7 +23,6 @@ include_rules = [ "+third_party/blink/public/mojom", "+third_party/blink/public/platform/WebAddressSpace.h", "+third_party/blink/public/platform/web_content_security_policy.h", - "+third_party/blink/public/platform/web_display_mode.h", "+third_party/blink/public/platform/web_drag_operation.h", "+third_party/blink/public/platform/web_float_point.h", "+third_party/blink/public/platform/web_float_rect.h", @@ -45,6 +44,7 @@ include_rules = [ "+third_party/blink/public/platform/web_screen_info.h", "+third_party/blink/public/platform/web_scrollbar_buttons_placement.h", "+third_party/blink/public/platform/web_scroll_into_view_params.h", + "+third_party/blink/public/platform/web_scroll_types.h", "+third_party/blink/public/platform/web_storage_area.h", "+third_party/blink/public/platform/web_sudden_termination_disabler_type.h", "+third_party/blink/public/platform/web_touch_event.h", @@ -64,9 +64,6 @@ include_rules = [ "+third_party/blink/public/platform/modules/presentation/presentation.mojom.h", "+third_party/blink/public/platform/modules/push_messaging/web_push_error.h", "+third_party/blink/public/platform/modules/remoteplayback/web_remote_playback_availability.h", - "+third_party/blink/public/platform/modules/screen_orientation/web_lock_orientation_error.h", - "+third_party/blink/public/platform/modules/screen_orientation/web_screen_orientation_lock_type.h", - "+third_party/blink/public/platform/modules/screen_orientation/web_screen_orientation_type.h", "+third_party/blink/public/platform/modules/payments/WebPaymentAppRequest.h", "+third_party/blink/public/platform/modules/serviceworker/WebServiceWorkerClientType.h", "+third_party/blink/public/platform/modules/serviceworker/web_service_worker_error.h", @@ -76,6 +73,7 @@ include_rules = [ "+third_party/blink/public/web/web_find_options.h", "+third_party/blink/public/web/web_frame_owner_properties.h", "+third_party/blink/public/web/web_frame_serializer_cache_control_policy.h", + "+third_party/blink/public/web/web_fullscreen_options.h", "+third_party/blink/public/web/web_ime_text_span.h", "+third_party/blink/public/web/web_media_player_action.h", "+third_party/blink/public/web/web_plugin_action.h", diff --git a/chromium/content/common/OWNERS b/chromium/content/common/OWNERS index 5baee77702b..c3390635291 100644 --- a/chromium/content/common/OWNERS +++ b/chromium/content/common/OWNERS @@ -7,10 +7,14 @@ per-file sandbox_init_win.cc=set noparent per-file sandbox_init_win.cc=file://sandbox/win/OWNERS # Mac Sandbox. -per-file sandbox_init_mac.*=rsesek@chromium.org -per-file sandbox_mac*=rsesek@chromium.org +per-file sandbox_init_mac.cc=set noparent +per-file sandbox_init_mac.cc=file://sandbox/mac/OWNERS + +per-file sandbox_mac*=set noparent +per-file sandbox_mac*=file://sandbox/mac/OWNERS + per-file *.sb=set noparent -per-file *.sb=rsesek@chromium.org +per-file *.sb=file://sandbox/mac/OWNERS per-file pepper*=bauerb@chromium.org per-file plugin*=bauerb@chromium.org diff --git a/chromium/content/common/associated_interface_provider_impl.h b/chromium/content/common/associated_interface_provider_impl.h index 91d196c4c90..a46b6575ca5 100644 --- a/chromium/content/common/associated_interface_provider_impl.h +++ b/chromium/content/common/associated_interface_provider_impl.h @@ -11,6 +11,7 @@ #include #include "base/macros.h" +#include "base/single_thread_task_runner.h" #include "content/common/associated_interfaces.mojom.h" namespace content { diff --git a/chromium/content/common/browser_plugin/browser_plugin_messages.h b/chromium/content/common/browser_plugin/browser_plugin_messages.h index 87991910cd3..bef070d8e16 100644 --- a/chromium/content/common/browser_plugin/browser_plugin_messages.h +++ b/chromium/content/common/browser_plugin/browser_plugin_messages.h @@ -9,13 +9,13 @@ #include "base/process/process.h" #include "base/unguessable_token.h" -#include "cc/ipc/cc_param_traits.h" +#include "cc/trees/render_frame_metadata.h" #include "components/viz/common/surfaces/surface_info.h" #include "content/common/content_export.h" #include "content/common/content_param_traits.h" #include "content/common/cursors/webcursor.h" #include "content/common/edit_command.h" -#include "content/common/frame_resize_params.h" +#include "content/common/frame_visual_properties.h" #include "content/public/common/drop_data.h" #include "content/public/common/screen_info.h" #include "ipc/ipc_channel_handle.h" @@ -156,10 +156,10 @@ IPC_MESSAGE_CONTROL1(BrowserPluginHostMsg_UnlockMouse_ACK, int /* browser_plugin_instance_id */) // Sent when plugin's position has changed. -IPC_MESSAGE_CONTROL3(BrowserPluginHostMsg_UpdateResizeParams, +IPC_MESSAGE_CONTROL3(BrowserPluginHostMsg_SynchronizeVisualProperties, int /* browser_plugin_instance_id */, viz::LocalSurfaceId /* local_surface_id */, - content::FrameResizeParams /* resize_params */) + content::FrameVisualProperties /* resize_params */) // ----------------------------------------------------------------------------- // These messages are from the browser process to the embedder. @@ -187,11 +187,10 @@ IPC_MESSAGE_CONTROL2(BrowserPluginMsg_AdvanceFocus, int /* browser_plugin_instance_id */, bool /* reverse */) -// When a guest resizes due to auto-resize, this message informs the -// BrowserPlugin to request a new viz::LocalSurfaceId. -IPC_MESSAGE_CONTROL2(BrowserPluginMsg_ResizeDueToAutoResize, +// Informs the BrowserPlugin that the guest's visual properties have changed. +IPC_MESSAGE_CONTROL2(BrowserPluginMsg_DidUpdateVisualProperties, int /* browser_plugin_instance_id */, - uint64_t /* sequence_number */) + cc::RenderFrameMetadata /* metadata */) // Requests a viz::LocalSurfaceId to enable auto-resize mode from the parent // renderer. diff --git a/chromium/content/common/cache_storage/OWNERS b/chromium/content/common/cache_storage/OWNERS index d55e59d60d9..5e0c72bf2bb 100644 --- a/chromium/content/common/cache_storage/OWNERS +++ b/chromium/content/common/cache_storage/OWNERS @@ -1,4 +1,3 @@ -michaeln@chromium.org nhiroki@chromium.org jkarlin@chromium.org jsbell@chromium.org diff --git a/chromium/content/common/cache_storage/cache_storage.typemap b/chromium/content/common/cache_storage/cache_storage.typemap deleted file mode 100644 index b04b8896324..00000000000 --- a/chromium/content/common/cache_storage/cache_storage.typemap +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2018 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -mojom = "//third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom" -public_headers = [ - "//content/common/cache_storage/cache_storage_types.h", - "//content/common/service_worker/service_worker_types.h", -] -traits_headers = - [ "//content/common/cache_storage/cache_storage_mojom_traits.h" ] -sources = [ - "//content/common/cache_storage/cache_storage_mojom_traits.cc", -] -type_mappings = [ - "blink.mojom.BatchOperation=content::CacheStorageBatchOperation", - "blink.mojom.OperationType=content::CacheStorageCacheOperationType", - "blink.mojom.QueryParams=content::CacheStorageCacheQueryParams", -] diff --git a/chromium/content/common/cache_storage/cache_storage_mojom_traits.cc b/chromium/content/common/cache_storage/cache_storage_mojom_traits.cc deleted file mode 100644 index 0face783f87..00000000000 --- a/chromium/content/common/cache_storage/cache_storage_mojom_traits.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/cache_storage/cache_storage_mojom_traits.h" -#include "base/logging.h" -#include "content/public/common/referrer_struct_traits.h" - -namespace mojo { - -using blink::mojom::CacheStorageError; -using blink::mojom::OperationType; - -OperationType -EnumTraits::ToMojom( - content::CacheStorageCacheOperationType input) { - switch (input) { - case content::CACHE_STORAGE_CACHE_OPERATION_TYPE_UNDEFINED: - return OperationType::kUndefined; - case content::CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT: - return OperationType::kPut; - case content::CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE: - return OperationType::kDelete; - } - NOTREACHED(); - return OperationType::kUndefined; -} - -bool EnumTraits:: - FromMojom(OperationType input, - content::CacheStorageCacheOperationType* out) { - switch (input) { - case OperationType::kUndefined: - *out = content::CACHE_STORAGE_CACHE_OPERATION_TYPE_UNDEFINED; - return true; - case OperationType::kPut: - *out = content::CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT; - return true; - case OperationType::kDelete: - *out = content::CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE; - return true; - } - return false; -} - -bool StructTraits:: - Read(blink::mojom::QueryParamsDataView data, - content::CacheStorageCacheQueryParams* out) { - base::Optional cache_name; - if (!data.ReadCacheName(&cache_name)) - return false; - out->cache_name = base::NullableString16(std::move(cache_name)); - out->ignore_search = data.ignore_search(); - out->ignore_method = data.ignore_method(); - out->ignore_vary = data.ignore_vary(); - return true; -} - -bool StructTraits:: - Read(blink::mojom::BatchOperationDataView data, - content::CacheStorageBatchOperation* out) { - if (!data.ReadRequest(&out->request)) - return false; - if (!data.ReadResponse(&out->response)) - return false; - if (!data.ReadMatchParams(&out->match_params)) - return false; - if (!data.ReadOperationType(&out->operation_type)) - return false; - return true; -} - -} // namespace mojo diff --git a/chromium/content/common/cache_storage/cache_storage_types.cc b/chromium/content/common/cache_storage/cache_storage_types.cc deleted file mode 100644 index 198e6eaf300..00000000000 --- a/chromium/content/common/cache_storage/cache_storage_types.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/cache_storage/cache_storage_types.h" - -namespace content { - -CacheStorageCacheQueryParams::CacheStorageCacheQueryParams() - : ignore_search(false), ignore_method(false), ignore_vary(false) { -} - -CacheStorageBatchOperation::CacheStorageBatchOperation() { -} - -CacheStorageBatchOperation::CacheStorageBatchOperation( - const CacheStorageBatchOperation& other) = default; - -} // namespace content diff --git a/chromium/content/common/cache_storage/cache_storage_types.h b/chromium/content/common/cache_storage/cache_storage_types.h deleted file mode 100644 index 61d8b769170..00000000000 --- a/chromium/content/common/cache_storage/cache_storage_types.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_COMMON_CACHE_STORAGE_CACHE_STORAGE_TYPES_H_ -#define CONTENT_COMMON_CACHE_STORAGE_CACHE_STORAGE_TYPES_H_ - -#include -#include - -#include "base/strings/nullable_string16.h" -#include "content/common/content_export.h" -#include "content/common/service_worker/service_worker_types.h" - -// This file is to have common definitions that are to be shared by -// browser and child process. - -namespace content { - -// Controls how requests are matched in the Cache API. -struct CONTENT_EXPORT CacheStorageCacheQueryParams { - CacheStorageCacheQueryParams(); - - bool ignore_search = false; - bool ignore_method = false; - bool ignore_vary = false; - base::NullableString16 cache_name; -}; - -// The type of a single batch operation in the Cache API. -enum CacheStorageCacheOperationType { - CACHE_STORAGE_CACHE_OPERATION_TYPE_UNDEFINED, - CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT, - CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE, - CACHE_STORAGE_CACHE_OPERATION_TYPE_LAST = - CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE -}; - -// A single batch operation for the Cache API. -struct CONTENT_EXPORT CacheStorageBatchOperation { - CacheStorageBatchOperation(); - CacheStorageBatchOperation(const CacheStorageBatchOperation& other); - - CacheStorageCacheOperationType operation_type; - ServiceWorkerFetchRequest request; - ServiceWorkerResponse response; - CacheStorageCacheQueryParams match_params; -}; - -} // namespace content - -#endif // CONTENT_COMMON_CACHE_STORAGE_CACHE_STORAGE_TYPES_H_ diff --git a/chromium/content/common/child_process_host_impl.cc b/chromium/content/common/child_process_host_impl.cc index d2b99832168..bc78aba80ac 100644 --- a/chromium/content/common/child_process_host_impl.cc +++ b/chromium/content/common/child_process_host_impl.cc @@ -11,7 +11,6 @@ #include "base/files/file_path.h" #include "base/hash.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/safe_math.h" #include "base/path_service.h" @@ -68,7 +67,7 @@ base::FilePath ChildProcessHost::GetChildPath(int flags) { // On most platforms, the child executable is the same as the current // executable. if (child_path.empty()) - PathService::Get(CHILD_PROCESS_EXE, &child_path); + base::PathService::Get(CHILD_PROCESS_EXE, &child_path); return child_path; } diff --git a/chromium/content/common/child_process_host_impl.h b/chromium/content/common/child_process_host_impl.h index 659cffaf25d..99644ea03dd 100644 --- a/chromium/content/common/child_process_host_impl.h +++ b/chromium/content/common/child_process_host_impl.h @@ -71,6 +71,8 @@ class CONTENT_EXPORT ChildProcessHostImpl : public ChildProcessHost, void BindInterface(const std::string& interface_name, mojo::ScopedMessagePipeHandle interface_pipe) override; + base::Process& peer_process() { return peer_process_; } + private: friend class ChildProcessHost; diff --git a/chromium/content/common/common_param_traits_unittest.cc b/chromium/content/common/common_param_traits_unittest.cc index 71b30f65c4d..4d4ee04d13b 100644 --- a/chromium/content/common/common_param_traits_unittest.cc +++ b/chromium/content/common/common_param_traits_unittest.cc @@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/values.h" +#include "components/viz/common/surfaces/surface_info.h" #include "content/common/resource_messages.h" #include "content/public/common/content_constants.h" #include "ipc/ipc_message.h" @@ -285,3 +286,24 @@ TEST(IPCMessageTest, RenderWidgetSurfaceProperties) { output.has_transparent_background); #endif } + +static constexpr viz::FrameSinkId kArbitraryFrameSinkId(1, 1); + +TEST(IPCMessageTest, SurfaceInfo) { + IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); + const viz::SurfaceId kArbitrarySurfaceId( + kArbitraryFrameSinkId, + viz::LocalSurfaceId(3, base::UnguessableToken::Create())); + constexpr float kArbitraryDeviceScaleFactor = 0.9f; + const gfx::Size kArbitrarySize(65, 321); + const viz::SurfaceInfo surface_info_in( + kArbitrarySurfaceId, kArbitraryDeviceScaleFactor, kArbitrarySize); + IPC::ParamTraits::Write(&msg, surface_info_in); + + viz::SurfaceInfo surface_info_out; + base::PickleIterator iter(msg); + EXPECT_TRUE( + IPC::ParamTraits::Read(&msg, &iter, &surface_info_out)); + + ASSERT_EQ(surface_info_in, surface_info_out); +} diff --git a/chromium/content/common/common_sandbox_support_linux.cc b/chromium/content/common/common_sandbox_support_linux.cc index ed806b7f998..c80e44a6d2b 100644 --- a/chromium/content/common/common_sandbox_support_linux.cc +++ b/chromium/content/common/common_sandbox_support_linux.cc @@ -4,21 +4,14 @@ #include "content/public/common/common_sandbox_support_linux.h" -#include #include #include #include #include "base/numerics/safe_conversions.h" -#include "base/pickle.h" #include "base/posix/eintr_wrapper.h" -#include "base/posix/global_descriptors.h" -#include "base/posix/unix_domain_socket.h" #include "base/sys_byteorder.h" -#include "base/trace_event/trace_event.h" -#include "content/public/common/content_descriptors.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" namespace content { @@ -97,23 +90,4 @@ bool GetFontTable(int fd, return true; } -int MakeSharedMemorySegmentViaIPC(size_t length, bool executable) { - base::Pickle request; - request.WriteInt( - service_manager::SandboxLinux::METHOD_MAKE_SHARED_MEMORY_SEGMENT); - request.WriteUInt32(length); - request.WriteBool(executable); - uint8_t reply_buf[10]; - int result_fd; - ssize_t result = base::UnixDomainSocket::SendRecvMsg( - GetSandboxFD(), reply_buf, sizeof(reply_buf), &result_fd, request); - if (result == -1) - return -1; - return result_fd; -} - -int GetSandboxFD() { - return kSandboxIPCChannel + base::GlobalDescriptors::kBaseDescriptor; -} - -} // namespace content +} // namespace content \ No newline at end of file diff --git a/chromium/content/common/content_message_generator.h b/chromium/content/common/content_message_generator.h index ab5609447eb..84e0f51ad8d 100644 --- a/chromium/content/common/content_message_generator.h +++ b/chromium/content/common/content_message_generator.h @@ -52,11 +52,6 @@ #ifndef CONTENT_COMMON_MEDIA_AEC_DUMP_MESSAGES_H_ #error "Failed to include content/common/media/aec_dump_messages.h" #endif -#undef CONTENT_COMMON_MEDIA_AUDIO_MESSAGES_H_ -#include "content/common/media/audio_messages.h" -#ifndef CONTENT_COMMON_MEDIA_AUDIO_MESSAGES_H_ -#error "Failed to include content/common/media/audio_messages.h" -#endif #undef CONTENT_COMMON_MEDIA_MEDIA_PLAYER_DELEGATE_MESSAGES_H_ #include "content/common/media/media_player_delegate_messages.h" #ifndef CONTENT_COMMON_MEDIA_MEDIA_PLAYER_DELEGATE_MESSAGES_H_ @@ -94,11 +89,6 @@ #error \ "Failed to include content/common/service_worker/service_worker_messages.h" #endif -#undef CONTENT_COMMON_SPEECH_RECOGNITION_MESSAGES_H_ -#include "content/common/speech_recognition_messages.h" -#ifndef CONTENT_COMMON_SPEECH_RECOGNITION_MESSAGES_H_ -#error "Failed to include content/common/speech_recognition_messages.h" -#endif #undef CONTENT_COMMON_TEXT_INPUT_CLIENT_MESSAGES_H_ #include "content/common/text_input_client_messages.h" #ifndef CONTENT_COMMON_TEXT_INPUT_CLIENT_MESSAGES_H_ @@ -117,13 +107,11 @@ #error "Failed to include content/common/input/sync_compositor_messages.h" #endif -#if BUILDFLAG(ENABLE_WEBRTC) #undef CONTENT_COMMON_P2P_MESSAGES_H_ #include "content/common/p2p_messages.h" #ifndef CONTENT_COMMON_P2P_MESSAGES_H_ #error "Failed to include content/common/p2p_messages.h" #endif -#endif #if defined(OS_ANDROID) #undef CONTENT_COMMON_GIN_JAVA_BRIDGE_MESSAGES_H_ diff --git a/chromium/content/common/content_param_traits.cc b/chromium/content/common/content_param_traits.cc index be32e05f410..310cd7f7c6f 100644 --- a/chromium/content/common/content_param_traits.cc +++ b/chromium/content/common/content_param_traits.cc @@ -7,6 +7,11 @@ #include #include "base/strings/string_number_conversions.h" +#include "base/unguessable_token.h" +#include "components/viz/common/surfaces/frame_sink_id.h" +#include "components/viz/common/surfaces/local_surface_id.h" +#include "components/viz/common/surfaces/surface_id.h" +#include "components/viz/common/surfaces/surface_info.h" #include "content/common/frame_message_structs.h" #include "ipc/ipc_mojo_message_helper.h" #include "ipc/ipc_mojo_param_traits.h" @@ -17,6 +22,7 @@ #include "ui/accessibility/ax_modes.h" #include "ui/base/ui_base_features.h" #include "ui/events/blink/web_input_event_traits.h" +// #include "ui/gfx/ipc/geometry/gfx_param_traits.h" namespace IPC { @@ -61,7 +67,7 @@ void ParamTraits::Log(const param_type& p, l->append(", "); LogParam(p->GetType(), l); l->append(", "); - LogParam(p->TimeStampSeconds(), l); + LogParam(p->TimeStamp(), l); l->append(")"); } @@ -240,6 +246,139 @@ void ParamTraitsappend(""); } +void ParamTraits::Write(base::Pickle* m, + const param_type& p) { + DCHECK(p.is_valid()); + WriteParam(m, p.client_id()); + WriteParam(m, p.sink_id()); +} + +bool ParamTraits::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p) { + uint32_t client_id; + if (!ReadParam(m, iter, &client_id)) + return false; + + uint32_t sink_id; + if (!ReadParam(m, iter, &sink_id)) + return false; + + *p = viz::FrameSinkId(client_id, sink_id); + return p->is_valid(); +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + l->append("viz::FrameSinkId("); + LogParam(p.client_id(), l); + l->append(", "); + LogParam(p.sink_id(), l); + l->append(")"); +} + +void ParamTraits::Write(base::Pickle* m, + const param_type& p) { + DCHECK(p.is_valid()); + WriteParam(m, p.parent_sequence_number()); + WriteParam(m, p.child_sequence_number()); + WriteParam(m, p.embed_token()); +} + +bool ParamTraits::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p) { + uint32_t parent_sequence_number; + if (!ReadParam(m, iter, &parent_sequence_number)) + return false; + + uint32_t child_sequence_number; + if (!ReadParam(m, iter, &child_sequence_number)) + return false; + + base::UnguessableToken embed_token; + if (!ReadParam(m, iter, &embed_token)) + return false; + + *p = viz::LocalSurfaceId(parent_sequence_number, child_sequence_number, + embed_token); + return p->is_valid(); +} + +void ParamTraits::Log(const param_type& p, + std::string* l) { + l->append("viz::LocalSurfaceId("); + LogParam(p.parent_sequence_number(), l); + l->append(", "); + LogParam(p.child_sequence_number(), l); + l->append(", "); + LogParam(p.embed_token(), l); + l->append(")"); +} + +void ParamTraits::Write(base::Pickle* m, const param_type& p) { + WriteParam(m, p.frame_sink_id()); + WriteParam(m, p.local_surface_id()); +} + +bool ParamTraits::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p) { + viz::FrameSinkId frame_sink_id; + if (!ReadParam(m, iter, &frame_sink_id)) + return false; + + viz::LocalSurfaceId local_surface_id; + if (!ReadParam(m, iter, &local_surface_id)) + return false; + + *p = viz::SurfaceId(frame_sink_id, local_surface_id); + return true; +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + l->append("viz::SurfaceId("); + LogParam(p.frame_sink_id(), l); + l->append(", "); + LogParam(p.local_surface_id(), l); + l->append(")"); +} + +void ParamTraits::Write(base::Pickle* m, + const param_type& p) { + WriteParam(m, p.id()); + WriteParam(m, p.device_scale_factor()); + WriteParam(m, p.size_in_pixels()); +} + +bool ParamTraits::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* p) { + viz::SurfaceId surface_id; + if (!ReadParam(m, iter, &surface_id)) + return false; + + float device_scale_factor; + if (!ReadParam(m, iter, &device_scale_factor)) + return false; + + gfx::Size size_in_pixels; + if (!ReadParam(m, iter, &size_in_pixels)) + return false; + + *p = viz::SurfaceInfo(surface_id, device_scale_factor, size_in_pixels); + return p->is_valid(); +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + l->append("viz::SurfaceInfo("); + LogParam(p.id(), l); + l->append(", "); + LogParam(p.device_scale_factor(), l); + l->append(", "); + LogParam(p.size_in_pixels(), l); + l->append(")"); +} + } // namespace IPC // Generate param traits write methods. diff --git a/chromium/content/common/content_param_traits.h b/chromium/content/common/content_param_traits.h index ffe7fc9edc1..e9a29ff4075 100644 --- a/chromium/content/common/content_param_traits.h +++ b/chromium/content/common/content_param_traits.h @@ -30,6 +30,13 @@ namespace content { struct FrameMsg_ViewChanged_Params; } +namespace viz { +class FrameSinkId; +class LocalSurfaceId; +class SurfaceId; +class SurfaceInfo; +} // namespace viz + namespace IPC { template <> @@ -109,6 +116,46 @@ struct CONTENT_EXPORT ParamTraits { static void Log(const param_type& p, std::string* l); }; +template <> +struct CONTENT_EXPORT ParamTraits { + typedef viz::FrameSinkId param_type; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct CONTENT_EXPORT ParamTraits { + typedef viz::LocalSurfaceId param_type; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct CONTENT_EXPORT ParamTraits { + typedef viz::SurfaceId param_type; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct CONTENT_EXPORT ParamTraits { + typedef viz::SurfaceInfo param_type; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + } // namespace IPC #endif // CONTENT_COMMON_CONTENT_PARAM_TRAITS_H_ diff --git a/chromium/content/common/content_param_traits_macros.h b/chromium/content/common/content_param_traits_macros.h index 9dd58cf6618..65199c2f005 100644 --- a/chromium/content/common/content_param_traits_macros.h +++ b/chromium/content/common/content_param_traits_macros.h @@ -8,8 +8,8 @@ #ifndef CONTENT_COMMON_CONTENT_PARAM_TRAITS_MACROS_H_ #define CONTENT_COMMON_CONTENT_PARAM_TRAITS_MACROS_H_ -#include "cc/ipc/cc_param_traits.h" #include "content/common/content_export.h" +#include "content/common/content_param_traits.h" #include "content/common/download/mhtml_save_status.h" #include "content/common/render_widget_surface_properties.h" #include "content/public/common/input_event_ack_state.h" @@ -22,6 +22,7 @@ #include "third_party/blink/public/web/web_ime_text_span.h" #include "ui/gfx/gpu_memory_buffer.h" #include "ui/gfx/ipc/geometry/gfx_param_traits.h" +#include "ui/gfx/ipc/gfx_param_traits.h" #undef IPC_MESSAGE_EXPORT #define IPC_MESSAGE_EXPORT CONTENT_EXPORT @@ -48,6 +49,11 @@ IPC_ENUM_TRAITS_MAX_VALUE(blink::WebImeTextSpan::Type, IPC_ENUM_TRAITS_MAX_VALUE(ui::mojom::ImeTextSpanThickness, ui::mojom::ImeTextSpanThickness::kThick) +IPC_STRUCT_TRAITS_BEGIN(viz::Selection) + IPC_STRUCT_TRAITS_MEMBER(start) + IPC_STRUCT_TRAITS_MEMBER(end) +IPC_STRUCT_TRAITS_END() + IPC_STRUCT_TRAITS_BEGIN(blink::WebImeTextSpan) IPC_STRUCT_TRAITS_MEMBER(type) IPC_STRUCT_TRAITS_MEMBER(start_offset) diff --git a/chromium/content/common/content_paths.cc b/chromium/content/common/content_paths.cc index 60ba02e3a6d..ac594ed5f3c 100644 --- a/chromium/content/common/content_paths.cc +++ b/chromium/content/common/content_paths.cc @@ -17,10 +17,10 @@ namespace content { bool PathProvider(int key, base::FilePath* result) { switch (key) { case CHILD_PROCESS_EXE: - return PathService::Get(base::FILE_EXE, result); + return base::PathService::Get(base::FILE_EXE, result); case DIR_TEST_DATA: { base::FilePath cur; - if (!PathService::Get(base::DIR_SOURCE_ROOT, &cur)) + if (!base::PathService::Get(base::DIR_SOURCE_ROOT, &cur)) return false; cur = cur.Append(FILE_PATH_LITERAL("content")); cur = cur.Append(FILE_PATH_LITERAL("test")); @@ -37,7 +37,7 @@ bool PathProvider(int key, base::FilePath* result) { *result = result->Append("Libraries"); return true; #else - return PathService::Get(base::DIR_MODULE, result); + return base::PathService::Get(base::DIR_MODULE, result); #endif } default: @@ -48,7 +48,7 @@ bool PathProvider(int key, base::FilePath* result) { // This cannot be done as a static initializer sadly since Visual Studio will // eliminate this object file if there is no direct entry point into it. void RegisterPathProvider() { - PathService::RegisterProvider(PathProvider, PATH_START, PATH_END); + base::PathService::RegisterProvider(PathProvider, PATH_START, PATH_END); } } // namespace content diff --git a/chromium/content/common/content_security_policy/content_security_policy.cc b/chromium/content/common/content_security_policy/content_security_policy.cc index c0b29bb9466..0a5565c573c 100644 --- a/chromium/content/common/content_security_policy/content_security_policy.cc +++ b/chromium/content/common/content_security_policy/content_security_policy.cc @@ -16,6 +16,7 @@ static CSPDirective::Name CSPFallback(CSPDirective::Name directive) { case CSPDirective::DefaultSrc: case CSPDirective::FormAction: case CSPDirective::UpgradeInsecureRequests: + case CSPDirective::NavigateTo: return CSPDirective::Unknown; case CSPDirective::FrameSrc: @@ -32,6 +33,19 @@ static CSPDirective::Name CSPFallback(CSPDirective::Name directive) { return CSPDirective::Unknown; } +// Looks by name for a directive in a list of directives. +// If it is not found, returns nullptr. +static const CSPDirective* FindDirective( + const CSPDirective::Name name, + const std::vector& directives) { + for (const CSPDirective& directive : directives) { + if (directive.name == name) { + return &directive; + } + } + return nullptr; +} + std::string ElideURLForReportViolation(const GURL& url) { // TODO(arthursonzogni): the url length should be limited to 1024 char. Find // a function that will not break the utf8 encoding while eliding the string. @@ -69,6 +83,8 @@ void ReportViolation(CSPContext* context, message << "Refused to send form data to '"; else if (directive_name == CSPDirective::FrameSrc) message << "Refused to frame '"; + else if (directive_name == CSPDirective::NavigateTo) + message << "Refused to navigate to '"; message << ElideURLForReportViolation(safe_url) << "' because it violates the following Content Security Policy " @@ -97,9 +113,12 @@ bool AllowDirective(CSPContext* context, CSPDirective::Name directive_name, const GURL& url, bool is_redirect, + bool is_response_check, const SourceLocation& source_location) { - if (CSPSourceList::Allow(directive.source_list, url, context, is_redirect)) + if (CSPSourceList::Allow(directive.source_list, url, context, is_redirect, + is_response_check)) { return true; + } ReportViolation(context, policy, directive, directive_name, url, is_redirect, source_location); @@ -146,20 +165,30 @@ bool ContentSecurityPolicy::Allow(const ContentSecurityPolicy& policy, CSPDirective::Name directive_name, const GURL& url, bool is_redirect, + bool is_response_check, CSPContext* context, - const SourceLocation& source_location) { - if (ShouldBypassContentSecurityPolicy(context, url)) return true; + const SourceLocation& source_location, + bool is_form_submission) { + if (ShouldBypassContentSecurityPolicy(context, url)) + return true; + + // 'navigate-to' has no effect when doing a form submission and a + // 'form-action' directive is present. + if (is_form_submission && directive_name == CSPDirective::Name::NavigateTo && + FindDirective(CSPDirective::Name::FormAction, policy.directives)) { + return true; + } CSPDirective::Name current_directive_name = directive_name; do { - for (const CSPDirective& directive : policy.directives) { - if (directive.name == current_directive_name) { - bool allowed = - AllowDirective(context, policy, directive, directive_name, url, - is_redirect, source_location); - return allowed || - policy.header.type == blink::kWebContentSecurityPolicyTypeReport; - } + const CSPDirective* current_directive = + FindDirective(current_directive_name, policy.directives); + if (current_directive) { + bool allowed = + AllowDirective(context, policy, *current_directive, directive_name, + url, is_redirect, is_response_check, source_location); + return allowed || + policy.header.type == blink::kWebContentSecurityPolicyTypeReport; } current_directive_name = CSPFallback(current_directive_name); } while (current_directive_name != CSPDirective::Unknown); diff --git a/chromium/content/common/content_security_policy/content_security_policy.h b/chromium/content/common/content_security_policy/content_security_policy.h index 7366f00e2ce..abac1dead71 100644 --- a/chromium/content/common/content_security_policy/content_security_policy.h +++ b/chromium/content/common/content_security_policy/content_security_policy.h @@ -45,8 +45,10 @@ struct CONTENT_EXPORT ContentSecurityPolicy { CSPDirective::Name directive, const GURL& url, bool is_redirect, + bool is_response_check, CSPContext* context, - const SourceLocation& source_location); + const SourceLocation& source_location, + bool is_form_submission); // Returns true if |policy| specifies that an insecure HTTP request should be // upgraded to HTTPS. diff --git a/chromium/content/common/content_security_policy/content_security_policy_unittest.cc b/chromium/content/common/content_security_policy/content_security_policy_unittest.cc index a8596b39fac..a7e57115ffc 100644 --- a/chromium/content/common/content_security_policy/content_security_policy_unittest.cc +++ b/chromium/content/common/content_security_policy/content_security_policy_unittest.cc @@ -50,9 +50,9 @@ TEST(ContentSecurityPolicy, NoDirective) { ContentSecurityPolicy policy(EmptyCspHeader(), std::vector(), report_end_points, false); - EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FormAction, - GURL("http://www.example.com"), - false, &context, SourceLocation())); + EXPECT_TRUE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FormAction, GURL("http://www.example.com"), false, + false, &context, SourceLocation(), true)); ASSERT_EQ(0u, context.violations().size()); } @@ -62,15 +62,15 @@ TEST(ContentSecurityPolicy, ReportViolation) { // source = "www.example.com" CSPSource source("", "www.example.com", false, url::PORT_UNSPECIFIED, false, ""); - CSPSourceList source_list(false, false, {source}); + CSPSourceList source_list(false, false, false, {source}); CSPDirective directive(CSPDirective::FormAction, source_list); std::vector report_end_points; // empty ContentSecurityPolicy policy(EmptyCspHeader(), {directive}, report_end_points, false); - EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FormAction, - GURL("http://www.not-example.com"), - false, &context, SourceLocation())); + EXPECT_FALSE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FormAction, GURL("http://www.not-example.com"), + false, false, &context, SourceLocation(), true)); ASSERT_EQ(1u, context.violations().size()); const char console_message[] = @@ -83,8 +83,8 @@ TEST(ContentSecurityPolicy, ReportViolation) { TEST(ContentSecurityPolicy, DirectiveFallback) { CSPSource source_a("http", "a.com", false, url::PORT_UNSPECIFIED, false, ""); CSPSource source_b("http", "b.com", false, url::PORT_UNSPECIFIED, false, ""); - CSPSourceList source_list_a(false, false, {source_a}); - CSPSourceList source_list_b(false, false, {source_b}); + CSPSourceList source_list_a(false, false, false, {source_a}); + CSPSourceList source_list_b(false, false, false, {source_b}); std::vector report_end_points; // Empty. @@ -94,9 +94,9 @@ TEST(ContentSecurityPolicy, DirectiveFallback) { EmptyCspHeader(), {CSPDirective(CSPDirective::DefaultSrc, source_list_a)}, report_end_points, false); - EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("http://b.com"), false, - &context, SourceLocation())); + EXPECT_FALSE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("http://b.com"), false, false, + &context, SourceLocation(), false)); ASSERT_EQ(1u, context.violations().size()); const char console_message[] = "Refused to frame 'http://b.com/' because it violates " @@ -104,18 +104,18 @@ TEST(ContentSecurityPolicy, DirectiveFallback) { "http://a.com\". Note that 'frame-src' was not explicitly " "set, so 'default-src' is used as a fallback.\n"; EXPECT_EQ(console_message, context.violations()[0].console_message); - EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("http://a.com"), false, - &context, SourceLocation())); + EXPECT_TRUE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("http://a.com"), false, false, + &context, SourceLocation(), false)); } { CSPContextTest context; ContentSecurityPolicy policy( EmptyCspHeader(), {CSPDirective(CSPDirective::ChildSrc, source_list_a)}, report_end_points, false); - EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("http://b.com"), false, - &context, SourceLocation())); + EXPECT_FALSE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("http://b.com"), false, false, + &context, SourceLocation(), false)); ASSERT_EQ(1u, context.violations().size()); const char console_message[] = "Refused to frame 'http://b.com/' because it violates " @@ -123,24 +123,24 @@ TEST(ContentSecurityPolicy, DirectiveFallback) { "http://a.com\". Note that 'frame-src' was not explicitly " "set, so 'child-src' is used as a fallback.\n"; EXPECT_EQ(console_message, context.violations()[0].console_message); - EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("http://a.com"), false, - &context, SourceLocation())); + EXPECT_TRUE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("http://a.com"), false, false, + &context, SourceLocation(), false)); } { CSPContextTest context; - CSPSourceList source_list(false, false, {source_a, source_b}); + CSPSourceList source_list(false, false, false, {source_a, source_b}); ContentSecurityPolicy policy( EmptyCspHeader(), {CSPDirective(CSPDirective::FrameSrc, {source_list_a}), CSPDirective(CSPDirective::ChildSrc, {source_list_b})}, report_end_points, false); - EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("http://a.com"), false, - &context, SourceLocation())); - EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("http://b.com"), false, - &context, SourceLocation())); + EXPECT_TRUE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("http://a.com"), false, false, + &context, SourceLocation(), false)); + EXPECT_FALSE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("http://b.com"), false, false, + &context, SourceLocation(), false)); ASSERT_EQ(1u, context.violations().size()); const char console_message[] = "Refused to frame 'http://b.com/' because it violates " @@ -155,27 +155,27 @@ TEST(ContentSecurityPolicy, RequestsAllowedWhenBypassingCSP) { std::vector report_end_points; // empty CSPSource source("https", "example.com", false, url::PORT_UNSPECIFIED, false, ""); - CSPSourceList source_list(false, false, {source}); + CSPSourceList source_list(false, false, false, {source}); ContentSecurityPolicy policy( EmptyCspHeader(), {CSPDirective(CSPDirective::DefaultSrc, source_list)}, report_end_points, false); - EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("https://example.com/"), false, - &context, SourceLocation())); - EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("https://not-example.com/"), - false, &context, SourceLocation())); + EXPECT_TRUE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("https://example.com/"), false, + false, &context, SourceLocation(), false)); + EXPECT_FALSE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("https://not-example.com/"), false, + false, &context, SourceLocation(), false)); // Register 'https' as bypassing CSP, which should now bypass is entirely. context.AddSchemeToBypassCSP("https"); - EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("https://example.com/"), false, - &context, SourceLocation())); - EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("https://not-example.com/"), - false, &context, SourceLocation())); + EXPECT_TRUE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("https://example.com/"), false, + false, &context, SourceLocation(), false)); + EXPECT_TRUE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("https://not-example.com/"), false, + false, &context, SourceLocation(), false)); } TEST(ContentSecurityPolicy, FilesystemAllowedWhenBypassingCSP) { @@ -183,31 +183,31 @@ TEST(ContentSecurityPolicy, FilesystemAllowedWhenBypassingCSP) { std::vector report_end_points; // empty CSPSource source("https", "example.com", false, url::PORT_UNSPECIFIED, false, ""); - CSPSourceList source_list(false, false, {source}); + CSPSourceList source_list(false, false, false, {source}); ContentSecurityPolicy policy( EmptyCspHeader(), {CSPDirective(CSPDirective::DefaultSrc, source_list)}, report_end_points, false); EXPECT_FALSE(ContentSecurityPolicy::Allow( policy, CSPDirective::FrameSrc, - GURL("filesystem:https://example.com/file.txt"), false, &context, - SourceLocation())); + GURL("filesystem:https://example.com/file.txt"), false, false, &context, + SourceLocation(), false)); EXPECT_FALSE(ContentSecurityPolicy::Allow( policy, CSPDirective::FrameSrc, - GURL("filesystem:https://not-example.com/file.txt"), false, &context, - SourceLocation())); + GURL("filesystem:https://not-example.com/file.txt"), false, false, + &context, SourceLocation(), false)); // Register 'https' as bypassing CSP, which should now bypass is entirely. context.AddSchemeToBypassCSP("https"); EXPECT_TRUE(ContentSecurityPolicy::Allow( policy, CSPDirective::FrameSrc, - GURL("filesystem:https://example.com/file.txt"), false, &context, - SourceLocation())); + GURL("filesystem:https://example.com/file.txt"), false, false, &context, + SourceLocation(), false)); EXPECT_TRUE(ContentSecurityPolicy::Allow( policy, CSPDirective::FrameSrc, - GURL("filesystem:https://not-example.com/file.txt"), false, &context, - SourceLocation())); + GURL("filesystem:https://not-example.com/file.txt"), false, false, + &context, SourceLocation(), false)); } TEST(ContentSecurityPolicy, BlobAllowedWhenBypassingCSP) { @@ -215,34 +215,34 @@ TEST(ContentSecurityPolicy, BlobAllowedWhenBypassingCSP) { std::vector report_end_points; // empty CSPSource source("https", "example.com", false, url::PORT_UNSPECIFIED, false, ""); - CSPSourceList source_list(false, false, {source}); + CSPSourceList source_list(false, false, false, {source}); ContentSecurityPolicy policy( EmptyCspHeader(), {CSPDirective(CSPDirective::DefaultSrc, source_list)}, report_end_points, false); - EXPECT_FALSE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("blob:https://example.com/"), - false, &context, SourceLocation())); + EXPECT_FALSE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("blob:https://example.com/"), false, + false, &context, SourceLocation(), false)); EXPECT_FALSE(ContentSecurityPolicy::Allow( policy, CSPDirective::FrameSrc, GURL("blob:https://not-example.com/"), - false, &context, SourceLocation())); + false, false, &context, SourceLocation(), false)); // Register 'https' as bypassing CSP, which should now bypass is entirely. context.AddSchemeToBypassCSP("https"); - EXPECT_TRUE(ContentSecurityPolicy::Allow(policy, CSPDirective::FrameSrc, - GURL("blob:https://example.com/"), - false, &context, SourceLocation())); + EXPECT_TRUE(ContentSecurityPolicy::Allow( + policy, CSPDirective::FrameSrc, GURL("blob:https://example.com/"), false, + false, &context, SourceLocation(), false)); EXPECT_TRUE(ContentSecurityPolicy::Allow( policy, CSPDirective::FrameSrc, GURL("blob:https://not-example.com/"), - false, &context, SourceLocation())); + false, false, &context, SourceLocation(), false)); } TEST(ContentSecurityPolicy, ShouldUpgradeInsecureRequest) { std::vector report_end_points; // empty CSPSource source("https", "example.com", false, url::PORT_UNSPECIFIED, false, ""); - CSPSourceList source_list(false, false, {source}); + CSPSourceList source_list(false, false, false, {source}); ContentSecurityPolicy policy( EmptyCspHeader(), {CSPDirective(CSPDirective::DefaultSrc, source_list)}, report_end_points, false); @@ -254,4 +254,91 @@ TEST(ContentSecurityPolicy, ShouldUpgradeInsecureRequest) { EXPECT_TRUE(ContentSecurityPolicy::ShouldUpgradeInsecureRequest(policy)); } +TEST(ContentSecurityPolicy, NavigateToChecks) { + CSPContextTest context; + std::vector report_end_points; // empty + CSPSource example("https", "example.test", false, url::PORT_UNSPECIFIED, + false, ""); + CSPSourceList none_source_list(false, false, false, {}); + CSPSourceList example_source_list(false, false, false, {example}); + CSPSourceList self_source_list(true, false, false, {}); + CSPSourceList redirects_source_list(false, false, true, {}); + CSPSourceList redirects_example_source_list(false, false, true, {example}); + context.SetSelf(example); + + struct TestCase { + const CSPSourceList& navigate_to_list; + const GURL& url; + bool is_redirect; + bool is_response_check; + bool expected; + bool is_form_submission; + const CSPSourceList* form_action_list; + } cases[] = { + // Basic source matching. + {none_source_list, GURL("https://example.test"), false, false, false, + false, nullptr}, + {example_source_list, GURL("https://example.test"), false, false, true, + false, nullptr}, + {example_source_list, GURL("https://not-example.test"), false, false, + false, false, nullptr}, + {self_source_list, GURL("https://example.test"), false, false, true, + false, nullptr}, + + // Checking allow_redirect flag interactions. + {redirects_source_list, GURL("https://example.test"), false, false, true, + false, nullptr}, + {redirects_source_list, GURL("https://example.test"), true, false, true, + false, nullptr}, + {redirects_source_list, GURL("https://example.test"), true, true, true, + false, nullptr}, + {redirects_source_list, GURL("https://example.test"), false, true, false, + false, nullptr}, + {redirects_example_source_list, GURL("https://example.test"), false, true, + true, false, nullptr}, + + // Interaction with form-action + + // Form submission without form-action present + {none_source_list, GURL("https://example.test"), false, false, false, + true, nullptr}, + {example_source_list, GURL("https://example.test"), false, false, true, + true, nullptr}, + {example_source_list, GURL("https://not-example.test"), false, false, + false, true, nullptr}, + {self_source_list, GURL("https://example.test"), false, false, true, true, + nullptr}, + + // Form submission with form-action present + {none_source_list, GURL("https://example.test"), false, false, true, true, + &example_source_list}, + {example_source_list, GURL("https://example.test"), false, false, true, + true, &example_source_list}, + {example_source_list, GURL("https://not-example.test"), false, false, + true, true, &example_source_list}, + {self_source_list, GURL("https://example.test"), false, false, true, true, + &example_source_list}, + + }; + + for (const auto& test : cases) { + std::vector directives; + directives.push_back( + CSPDirective(CSPDirective::NavigateTo, test.navigate_to_list)); + + if (test.form_action_list) + directives.push_back( + CSPDirective(CSPDirective::FormAction, *(test.form_action_list))); + + ContentSecurityPolicy policy(EmptyCspHeader(), directives, + report_end_points, false); + + EXPECT_EQ(test.expected, + ContentSecurityPolicy::Allow( + policy, CSPDirective::NavigateTo, test.url, test.is_redirect, + test.is_response_check, &context, SourceLocation(), + test.is_form_submission)); + } +} + } // namespace content diff --git a/chromium/content/common/content_security_policy/csp_context.cc b/chromium/content/common/content_security_policy/csp_context.cc index 99b8ef4d48a..7791627ad19 100644 --- a/chromium/content/common/content_security_policy/csp_context.cc +++ b/chromium/content/common/content_security_policy/csp_context.cc @@ -32,41 +32,48 @@ CSPContext::~CSPContext() {} bool CSPContext::IsAllowedByCsp(CSPDirective::Name directive_name, const GURL& url, bool is_redirect, + bool is_response_check, const SourceLocation& source_location, - CheckCSPDisposition check_csp_disposition) { + CheckCSPDisposition check_csp_disposition, + bool is_form_submission) { if (SchemeShouldBypassCSP(url.scheme_piece())) return true; bool allow = true; for (const auto& policy : policies_) { if (ShouldCheckPolicy(policy, check_csp_disposition)) { - allow &= ContentSecurityPolicy::Allow(policy, directive_name, url, - is_redirect, this, source_location); + allow &= ContentSecurityPolicy::Allow( + policy, directive_name, url, is_redirect, is_response_check, this, + source_location, is_form_submission); } } + + DCHECK(allow || check_csp_disposition != CSPContext::CHECK_REPORT_ONLY_CSP); + return allow; } bool CSPContext::ShouldModifyRequestUrlForCsp( - const GURL& url, - bool is_subresource_or_form_submission, - GURL* new_url) { + bool is_subresource_or_form_submission) { for (const auto& policy : policies_) { - if (url.scheme() == "http" && - ContentSecurityPolicy::ShouldUpgradeInsecureRequest(policy) && + if (ContentSecurityPolicy::ShouldUpgradeInsecureRequest(policy) && is_subresource_or_form_submission) { - *new_url = url; - GURL::Replacements replacements; - replacements.SetSchemeStr("https"); - if (url.port() == "80") - replacements.SetPortStr("443"); - *new_url = new_url->ReplaceComponents(replacements); return true; } } return false; } +void CSPContext::ModifyRequestUrlForCsp(GURL* url) { + if (url->scheme() == "http") { + GURL::Replacements replacements; + replacements.SetSchemeStr("https"); + if (url->port() == "80") + replacements.SetPortStr("443"); + *url = url->ReplaceComponents(replacements); + } +} + void CSPContext::SetSelf(const url::Origin origin) { self_source_.reset(); @@ -88,6 +95,10 @@ void CSPContext::SetSelf(const url::Origin origin) { DCHECK_NE("", self_source_->scheme); } +void CSPContext::SetSelf(const CSPSource& self_source) { + self_source_ = self_source; +} + bool CSPContext::SchemeShouldBypassCSP(const base::StringPiece& scheme) { return false; } diff --git a/chromium/content/common/content_security_policy/csp_context.h b/chromium/content/common/content_security_policy/csp_context.h index 3697e14c713..e7f1ceedbe9 100644 --- a/chromium/content/common/content_security_policy/csp_context.h +++ b/chromium/content/common/content_security_policy/csp_context.h @@ -49,17 +49,21 @@ class CONTENT_EXPORT CSPContext { bool IsAllowedByCsp(CSPDirective::Name directive_name, const GURL& url, bool is_redirect, + bool is_response_check, const SourceLocation& source_location, - CheckCSPDisposition check_csp_disposition); + CheckCSPDisposition check_csp_disposition, + bool is_form_submission); // Returns true if the request URL needs to be modified (e.g. upgraded to - // HTTPS) according to the CSP. If true, |new_url| will contain the new URL - // that should be used instead of |url|. - bool ShouldModifyRequestUrlForCsp(const GURL& url, - bool is_suresource_or_form_submssion, - GURL* new_url); + // HTTPS) according to the CSP. + bool ShouldModifyRequestUrlForCsp(bool is_suresource_or_form_submssion); + + // If the scheme of |url| is HTTP, this upgrades it to HTTPS, otherwise it + // doesn't modify it. + void ModifyRequestUrlForCsp(GURL* url); void SetSelf(const url::Origin origin); + void SetSelf(const CSPSource& self_source); // When a CSPSourceList contains 'self', the url is allowed when it match the // CSPSource returned by this function. diff --git a/chromium/content/common/content_security_policy/csp_context_unittest.cc b/chromium/content/common/content_security_policy/csp_context_unittest.cc index d5cf5780947..49ce33e13e1 100644 --- a/chromium/content/common/content_security_policy/csp_context_unittest.cc +++ b/chromium/content/common/content_security_policy/csp_context_unittest.cc @@ -60,7 +60,8 @@ ContentSecurityPolicy BuildPolicy(CSPDirective::Name directive_name, ContentSecurityPolicyHeader(std::string(), // header blink::kWebContentSecurityPolicyTypeEnforce, blink::kWebContentSecurityPolicySourceHTTP), - {CSPDirective(directive_name, CSPSourceList(false, false, sources))}, + {CSPDirective(directive_name, + CSPSourceList(false, false, false, sources))}, std::vector(), false); // report_end_points } @@ -74,13 +75,13 @@ TEST(CSPContextTest, SchemeShouldBypassCSP) { EXPECT_FALSE(context.IsAllowedByCsp( CSPDirective::FrameSrc, GURL("data:text/html,"), false, - SourceLocation(), CSPContext::CHECK_ALL_CSP)); + false, SourceLocation(), CSPContext::CHECK_ALL_CSP, false)); context.AddSchemeToBypassCSP("data"); EXPECT_TRUE(context.IsAllowedByCsp( CSPDirective::FrameSrc, GURL("data:text/html,"), false, - SourceLocation(), CSPContext::CHECK_ALL_CSP)); + false, SourceLocation(), CSPContext::CHECK_ALL_CSP, false)); } TEST(CSPContextTest, MultiplePolicies) { @@ -97,17 +98,17 @@ TEST(CSPContextTest, MultiplePolicies) { BuildPolicy(CSPDirective::FrameSrc, {source_a, source_c})); EXPECT_TRUE(context.IsAllowedByCsp( - CSPDirective::FrameSrc, GURL("http://a.com"), false, SourceLocation(), - CSPContext::CHECK_ALL_CSP)); + CSPDirective::FrameSrc, GURL("http://a.com"), false, false, + SourceLocation(), CSPContext::CHECK_ALL_CSP, false)); EXPECT_FALSE(context.IsAllowedByCsp( - CSPDirective::FrameSrc, GURL("http://b.com"), false, SourceLocation(), - CSPContext::CHECK_ALL_CSP)); + CSPDirective::FrameSrc, GURL("http://b.com"), false, false, + SourceLocation(), CSPContext::CHECK_ALL_CSP, false)); EXPECT_FALSE(context.IsAllowedByCsp( - CSPDirective::FrameSrc, GURL("http://c.com"), false, SourceLocation(), - CSPContext::CHECK_ALL_CSP)); + CSPDirective::FrameSrc, GURL("http://c.com"), false, false, + SourceLocation(), CSPContext::CHECK_ALL_CSP, false)); EXPECT_FALSE(context.IsAllowedByCsp( - CSPDirective::FrameSrc, GURL("http://d.com"), false, SourceLocation(), - CSPContext::CHECK_ALL_CSP)); + CSPDirective::FrameSrc, GURL("http://d.com"), false, false, + SourceLocation(), CSPContext::CHECK_ALL_CSP, false)); } TEST(CSPContextTest, SanitizeDataForUseInCspViolation) { @@ -126,8 +127,8 @@ TEST(CSPContextTest, SanitizeDataForUseInCspViolation) { // When the |blocked_url| and |source_location| aren't sensitive information. { EXPECT_FALSE(context.IsAllowedByCsp(CSPDirective::FrameSrc, blocked_url, - false, source_location, - CSPContext::CHECK_ALL_CSP)); + false, false, source_location, + CSPContext::CHECK_ALL_CSP, false)); ASSERT_EQ(1u, context.violations().size()); EXPECT_EQ(context.violations()[0].blocked_url, blocked_url); EXPECT_EQ(context.violations()[0].source_location.url, @@ -145,8 +146,8 @@ TEST(CSPContextTest, SanitizeDataForUseInCspViolation) { // When the |blocked_url| and |source_location| are sensitive information. { EXPECT_FALSE(context.IsAllowedByCsp(CSPDirective::FrameSrc, blocked_url, - false, source_location, - CSPContext::CHECK_ALL_CSP)); + false, false, source_location, + CSPContext::CHECK_ALL_CSP, false)); ASSERT_EQ(2u, context.violations().size()); EXPECT_EQ(context.violations()[1].blocked_url, blocked_url.GetOrigin()); EXPECT_EQ(context.violations()[1].source_location.url, "http://a.com/"); @@ -176,8 +177,8 @@ TEST(CSPContextTest, MultipleInfringement) { BuildPolicy(CSPDirective::FrameSrc, {source_c})); EXPECT_FALSE(context.IsAllowedByCsp( - CSPDirective::FrameSrc, GURL("http://c.com"), false, SourceLocation(), - CSPContext::CHECK_ALL_CSP)); + CSPDirective::FrameSrc, GURL("http://c.com"), false, false, + SourceLocation(), CSPContext::CHECK_ALL_CSP, false)); ASSERT_EQ(2u, context.violations().size()); const char console_message_a[] = "Refused to frame 'http://c.com/' because it violates the following " @@ -207,8 +208,8 @@ TEST(CSPContextTest, CheckCSPDisposition) { // With CHECK_ALL_CSP, both policies should be checked and violations should // be reported. EXPECT_FALSE(context.IsAllowedByCsp( - CSPDirective::FrameSrc, GURL("https://not-example.com"), false, - SourceLocation(), CSPContext::CHECK_ALL_CSP)); + CSPDirective::FrameSrc, GURL("https://not-example.com"), false, false, + SourceLocation(), CSPContext::CHECK_ALL_CSP, false)); ASSERT_EQ(2u, context.violations().size()); const char console_message_a[] = "Refused to frame 'https://not-example.com/' because it violates the " @@ -229,8 +230,8 @@ TEST(CSPContextTest, CheckCSPDisposition) { // With CHECK_REPORT_ONLY_CSP, the request should be allowed but reported. context.ClearViolations(); EXPECT_TRUE(context.IsAllowedByCsp( - CSPDirective::FrameSrc, GURL("https://not-example.com"), false, - SourceLocation(), CSPContext::CHECK_REPORT_ONLY_CSP)); + CSPDirective::FrameSrc, GURL("https://not-example.com"), false, false, + SourceLocation(), CSPContext::CHECK_REPORT_ONLY_CSP, false)); ASSERT_EQ(1u, context.violations().size()); EXPECT_EQ(console_message_b, context.violations()[0].console_message); @@ -238,8 +239,8 @@ TEST(CSPContextTest, CheckCSPDisposition) { // enforced policy violation should be reported. context.ClearViolations(); EXPECT_FALSE(context.IsAllowedByCsp( - CSPDirective::FrameSrc, GURL("https://not-example.com"), false, - SourceLocation(), CSPContext::CHECK_ENFORCED_CSP)); + CSPDirective::FrameSrc, GURL("https://not-example.com"), false, false, + SourceLocation(), CSPContext::CHECK_ENFORCED_CSP, false)); ASSERT_EQ(1u, context.violations().size()); EXPECT_EQ(console_message_a, context.violations()[0].console_message); } @@ -250,27 +251,40 @@ TEST(CSPContextTest, ShouldModifyRequestUrlForCsp) { CSPContextTest context; context.AddContentSecurityPolicy(BuildPolicy( CSPDirective::UpgradeInsecureRequests, std::vector())); - GURL new_url; // An HTTP subresource or form submission should be upgraded. - EXPECT_TRUE(context.ShouldModifyRequestUrlForCsp(GURL("http://example.com"), - true, &new_url)); - EXPECT_EQ(GURL("https://example.com"), new_url); - EXPECT_TRUE(context.ShouldModifyRequestUrlForCsp( - GURL("http://example.com:80"), true, &new_url)); - EXPECT_EQ(GURL("https://example.com:443"), new_url); + EXPECT_TRUE(context.ShouldModifyRequestUrlForCsp(true)); + + // Main-frame navigation requests should not be modified. + EXPECT_FALSE(context.ShouldModifyRequestUrlForCsp(false)); +} + +// Tests that URLs passed to ModifyRequestUrlForCsp are modified according to +// the spec for upgrades. +TEST(CSPContextTest, ModifyRequestUrlForCsp) { + CSPContextTest context; + GURL test_url; + + test_url = GURL("http://example.com"); + context.ModifyRequestUrlForCsp(&test_url); + EXPECT_EQ(GURL("https://example.com"), test_url); + + test_url = GURL("http://example.com:80"); + context.ModifyRequestUrlForCsp(&test_url); + EXPECT_EQ(GURL("https://example.com:443"), test_url); + // Non-standard ports should not be modified. - EXPECT_TRUE(context.ShouldModifyRequestUrlForCsp( - GURL("http://example-weird-port.com:8088"), true, &new_url)); - EXPECT_EQ(GURL("https://example-weird-port.com:8088"), new_url); + test_url = GURL("http://example-weird-port.com:8088"); + context.ModifyRequestUrlForCsp(&test_url); + EXPECT_EQ(GURL("https://example-weird-port.com:8088"), test_url); // Non-HTTP URLs don't need to be modified. - EXPECT_FALSE(context.ShouldModifyRequestUrlForCsp(GURL("https://example.com"), - true, &new_url)); - EXPECT_FALSE(context.ShouldModifyRequestUrlForCsp( - GURL("data:text/html,"), true, &new_url)); - // Nor do main-frame navigation requests. - EXPECT_FALSE(context.ShouldModifyRequestUrlForCsp(GURL("http://example.com"), - false, &new_url)); + test_url = GURL("https://example.com"); + context.ModifyRequestUrlForCsp(&test_url); + EXPECT_EQ(GURL("https://example.com"), test_url); + + test_url = GURL("data:text/html,"); + context.ModifyRequestUrlForCsp(&test_url); + EXPECT_EQ(GURL("data:text/html,"), test_url); } } // namespace content diff --git a/chromium/content/common/content_security_policy/csp_directive.cc b/chromium/content/common/content_security_policy/csp_directive.cc index d518a4b5f91..51a6cb23672 100644 --- a/chromium/content/common/content_security_policy/csp_directive.cc +++ b/chromium/content/common/content_security_policy/csp_directive.cc @@ -30,6 +30,8 @@ std::string CSPDirective::NameToString(CSPDirective::Name name) { return "form-action"; case UpgradeInsecureRequests: return "upgrade-insecure-requests"; + case NavigateTo: + return "navigate-to"; case Unknown: return ""; } @@ -49,6 +51,8 @@ CSPDirective::Name CSPDirective::StringToName(const std::string& name) { return CSPDirective::FormAction; if (name == "upgrade-insecure-requests") return CSPDirective::UpgradeInsecureRequests; + if (name == "navigate-to") + return CSPDirective::NavigateTo; return CSPDirective::Unknown; } diff --git a/chromium/content/common/content_security_policy/csp_directive.h b/chromium/content/common/content_security_policy/csp_directive.h index 914ab181392..289e5fba4c0 100644 --- a/chromium/content/common/content_security_policy/csp_directive.h +++ b/chromium/content/common/content_security_policy/csp_directive.h @@ -27,6 +27,7 @@ struct CONTENT_EXPORT CSPDirective { FrameSrc, FormAction, UpgradeInsecureRequests, + NavigateTo, Unknown, NameLast = Unknown, diff --git a/chromium/content/common/content_security_policy/csp_source_list.cc b/chromium/content/common/content_security_policy/csp_source_list.cc index 905aaae5fdd..4bf358daa1d 100644 --- a/chromium/content/common/content_security_policy/csp_source_list.cc +++ b/chromium/content/common/content_security_policy/csp_source_list.cc @@ -22,12 +22,16 @@ bool AllowFromSources(const GURL& url, }; // namespace CSPSourceList::CSPSourceList() - : allow_self(false), allow_star(false), sources() {} + : allow_self(false), allow_star(false), allow_redirects(false), sources() {} CSPSourceList::CSPSourceList(bool allow_self, bool allow_star, + bool allow_redirects, std::vector sources) - : allow_self(allow_self), allow_star(allow_star), sources(sources) {} + : allow_self(allow_self), + allow_star(allow_star), + allow_redirects(allow_redirects), + sources(sources) {} CSPSourceList::CSPSourceList(const CSPSourceList&) = default; CSPSourceList::~CSPSourceList() = default; @@ -36,7 +40,23 @@ CSPSourceList::~CSPSourceList() = default; bool CSPSourceList::Allow(const CSPSourceList& source_list, const GURL& url, CSPContext* context, - bool is_redirect) { + bool is_redirect, + bool is_response_check) { + // If the source list allows all redirects, the decision can't be made until + // the response is received. + if (source_list.allow_redirects && !is_response_check) + return true; + + // If the source list does not allow all redirects, the decision has already + // been made when checking the request. + if (!source_list.allow_redirects && is_response_check) + return true; + + // If the source list allows all redirects, all responses that are a redirect + // are allowed. + if (source_list.allow_redirects && is_response_check && is_redirect) + return true; + // Wildcards match network schemes ('http', 'https', 'ftp', 'ws', 'wss'), and // the scheme of the protected resource: // https://w3c.github.io/webappsec-csp/#match-url-to-source-expression. Other diff --git a/chromium/content/common/content_security_policy/csp_source_list.h b/chromium/content/common/content_security_policy/csp_source_list.h index 4fafb439a0f..94af77554dd 100644 --- a/chromium/content/common/content_security_policy/csp_source_list.h +++ b/chromium/content/common/content_security_policy/csp_source_list.h @@ -18,6 +18,7 @@ struct CONTENT_EXPORT CSPSourceList { CSPSourceList(); CSPSourceList(bool allow_self, bool allow_star, + bool allow_redirects, std::vector source_list); CSPSourceList(const CSPSourceList&); ~CSPSourceList(); @@ -26,6 +27,7 @@ struct CONTENT_EXPORT CSPSourceList { // on the source list itself. bool allow_self; bool allow_star; + bool allow_redirects; std::vector sources; std::string ToString() const; @@ -37,7 +39,8 @@ struct CONTENT_EXPORT CSPSourceList { static bool Allow(const CSPSourceList& source_list, const GURL& url, CSPContext* context, - bool is_redirect = false); + bool is_redirect = false, + bool is_response_check = false); }; } // namespace content diff --git a/chromium/content/common/content_security_policy/csp_source_list_unittest.cc b/chromium/content/common/content_security_policy/csp_source_list_unittest.cc index 2e0838d298c..826bf429062 100644 --- a/chromium/content/common/content_security_policy/csp_source_list_unittest.cc +++ b/chromium/content/common/content_security_policy/csp_source_list_unittest.cc @@ -15,8 +15,10 @@ namespace { bool Allow(const CSPSourceList& source_list, const GURL& url, CSPContext* context, - bool is_redirect = false) { - return CSPSourceList::Allow(source_list, url, context, is_redirect); + bool is_redirect = false, + bool is_response_check = false) { + return CSPSourceList::Allow(source_list, url, context, is_redirect, + is_response_check); } } // namespace @@ -26,7 +28,8 @@ TEST(CSPSourceList, MultipleSource) { context.SetSelf(url::Origin::Create(GURL("http://example.com"))); CSPSourceList source_list( false, // allow_self - false, // allow_star: + false, // allow_star + false, // allow_redirects {CSPSource("", "a.com", false, url::PORT_UNSPECIFIED, false, ""), CSPSource("", "b.com", false, url::PORT_UNSPECIFIED, false, "")}); EXPECT_TRUE(Allow(source_list, GURL("http://a.com"), &context)); @@ -38,7 +41,8 @@ TEST(CSPSourceList, AllowStar) { CSPContext context; context.SetSelf(url::Origin::Create(GURL("http://example.com"))); CSPSourceList source_list(false, // allow_self - true, // allow_star: + true, // allow_star + false, // allow_redirects std::vector()); // source_list EXPECT_TRUE(Allow(source_list, GURL("http://not-example.com"), &context)); EXPECT_TRUE(Allow(source_list, GURL("https://not-example.com"), &context)); @@ -59,7 +63,8 @@ TEST(CSPSourceList, AllowSelf) { CSPContext context; context.SetSelf(url::Origin::Create(GURL("http://example.com"))); CSPSourceList source_list(true, // allow_self - false, // allow_star: + false, // allow_star + false, // allow_redirects std::vector()); // source_list EXPECT_TRUE(Allow(source_list, GURL("http://example.com"), &context)); EXPECT_FALSE(Allow(source_list, GURL("http://not-example.com"), &context)); @@ -72,6 +77,7 @@ TEST(CSPSourceList, AllowStarAndSelf) { context.SetSelf(url::Origin::Create(GURL("https://a.com"))); CSPSourceList source_list(false, // allow_self false, // allow_star + false, // allow_redirects std::vector()); // If the request is allowed by {*} and not by {'self'} then it should be @@ -91,7 +97,8 @@ TEST(CSPSourceList, AllowSelfWithUnspecifiedPort) { CSPContext context; context.SetSelf(url::Origin::Create(GURL("chrome://print"))); CSPSourceList source_list(true, // allow_self - false, // allow_star: + false, // allow_star + false, // allow_redirects std::vector()); // source_list EXPECT_TRUE( @@ -104,7 +111,8 @@ TEST(CSPSourceList, AllowNone) { CSPContext context; context.SetSelf(url::Origin::Create(GURL("http://example.com"))); CSPSourceList source_list(false, // allow_self - false, // allow_star: + false, // allow_star + false, // allow_redirects std::vector()); // source_list EXPECT_FALSE(Allow(source_list, GURL("http://example.com"), &context)); EXPECT_FALSE(Allow(source_list, GURL("https://example.test/"), &context)); @@ -113,7 +121,8 @@ TEST(CSPSourceList, AllowNone) { TEST(CSPSourceTest, SelfIsUnique) { // Policy: 'self' CSPSourceList source_list(true, // allow_self - false, // allow_star: + false, // allow_star + false, // allow_redirects std::vector()); // source_list CSPContext context; diff --git a/chromium/content/common/cursors/webcursor_aura.cc b/chromium/content/common/cursors/webcursor_aura.cc index 9a3939ecfc7..73e110f8823 100644 --- a/chromium/content/common/cursors/webcursor_aura.cc +++ b/chromium/content/common/cursors/webcursor_aura.cc @@ -104,6 +104,11 @@ gfx::NativeCursor WebCursor::GetNativeCursor() { case WebCursorInfo::kTypeCustom: { ui::Cursor cursor(ui::CursorType::kCustom); cursor.SetPlatformCursor(GetPlatformCursor()); + SkBitmap bitmap; + gfx::Point hotspot; + CreateScaledBitmapAndHotspotFromCustomData(&bitmap, &hotspot); + cursor.set_custom_bitmap(bitmap); + cursor.set_custom_hotspot(hotspot); return cursor; } default: diff --git a/chromium/content/common/dom_storage/OWNERS b/chromium/content/common/dom_storage/OWNERS index 39d46142947..74dc2f93524 100644 --- a/chromium/content/common/dom_storage/OWNERS +++ b/chromium/content/common/dom_storage/OWNERS @@ -1,9 +1,6 @@ dmurph@chromium.org mek@chromium.org -# OOO until this comment is removed. -michaeln@chromium.org - per-file *_messages*.h=set noparent per-file *_messages*.h=file://ipc/SECURITY_OWNERS diff --git a/chromium/content/common/external_ipc_dumper.cc b/chromium/content/common/external_ipc_dumper.cc index 907ebfd9c52..f9ab7e69bd7 100644 --- a/chromium/content/common/external_ipc_dumper.cc +++ b/chromium/content/common/external_ipc_dumper.cc @@ -27,10 +27,11 @@ const char kSetDumpDirectoryEntryName[] = "SetDumpDirectory"; namespace content { +NO_SANITIZE("cfi-icall") IPC::ChannelProxy::OutgoingMessageFilter* LoadExternalIPCDumper( const base::FilePath& dump_directory) { base::FilePath module_path; - if (!PathService::Get(base::DIR_MODULE, &module_path)) { + if (!base::PathService::Get(base::DIR_MODULE, &module_path)) { LOG(ERROR) << "Unable to get message dump module directory."; return NULL; } diff --git a/chromium/content/common/file_utilities.mojom b/chromium/content/common/file_utilities.mojom deleted file mode 100644 index 7ab8354141b..00000000000 --- a/chromium/content/common/file_utilities.mojom +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -module content.mojom; - -import "mojo/public/mojom/base/file_info.mojom"; -import "mojo/public/mojom/base/file_path.mojom"; - -// File utilities messages sent from the renderer to the browser. -interface FileUtilitiesHost { - [Sync] - GetFileInfo(mojo_base.mojom.FilePath path) => ( - mojo_base.mojom.FileInfo? result); -}; diff --git a/chromium/content/common/fileapi/OWNERS b/chromium/content/common/fileapi/OWNERS index 77d0e9de90c..3a69eb7de67 100644 --- a/chromium/content/common/fileapi/OWNERS +++ b/chromium/content/common/fileapi/OWNERS @@ -1,4 +1,4 @@ -michaeln@chromium.org +mek@chromium.org jianli@chromium.org tzik@chromium.org nhiroki@chromium.org diff --git a/chromium/content/common/frame.mojom b/chromium/content/common/frame.mojom index 99ec7e8d799..09b5766c079 100644 --- a/chromium/content/common/frame.mojom +++ b/chromium/content/common/frame.mojom @@ -38,7 +38,8 @@ interface Frame { // Extracts the data at the given rect. [EnableIf=is_android] ExtractSmartClipData(gfx.mojom.Rect rect) - => (mojo_base.mojom.String16 text, mojo_base.mojom.String16 html); + => (mojo_base.mojom.String16 text, mojo_base.mojom.String16 html, + gfx.mojom.Rect clip_rect); }; // See src/content/common/navigation_params.h @@ -54,20 +55,18 @@ struct RequestNavigationParams; interface FrameNavigationControl { // Tells the renderer that a navigation is ready to commit. // - // The renderer should request |body_url| to get access to the stream - // containing the body of the response. When the Network Service or - // NavigationMojoResponse is enabled, |body_url| is not used and instead - // |url_loader_client_endpoints| provides a way to continue the navigation. + // The renderer should bind the |url_loader_client_endpoints| to an + // URLLoaderClient implementation to continue loading the document that will + // be the result of the committed navigation. // - // Note: |body_url| and |url_loader_client_endpoints| will be empty iff the - // navigation URL wasn't handled by the network stack (i.e. JavaScript URLs, - // renderer debug URLs, same document navigations, about:blank, ...) + // Note: |url_loader_client_endpoints| will be empty iff the navigation URL + // wasn't handled by the network stack (i.e. JavaScript URLs, renderer debug + // URLs, same document navigations, about:blank, ...) // // When the Network Service is enabled, |subresource_loader_factories| may // also be provided by the browser as a a means for the renderer to load // subresources where applicable. // - // When S13nServiceWorker/NavigationMojoResponse is enabled, // |controller_service_worker_info| may also be provided by the browser if the // frame that is being navigated is supposed to be controlled by a Service // Worker. @@ -84,7 +83,6 @@ interface FrameNavigationControl { // navigation token that can be passed from renderer to the browser. CommitNavigation( network.mojom.URLResponseHead head, - url.mojom.Url body_url, CommonNavigationParams common_params, RequestNavigationParams request_params, network.mojom.URLLoaderClientEndpoints? url_loader_client_endpoints, @@ -327,13 +325,4 @@ interface FrameHost { // correctly set the initial size of the frame in case of a cross-process // navigation. FrameSizeChanged(gfx.mojom.Size size); - - // Sent by the renderer to update Picture-in-Picture with SurfaceId and video - // size to be used to show content in the Picture-in-Picture window. - OnUpdatePictureInPictureSurfaceId( - viz.mojom.SurfaceId surface_id, - gfx.mojom.Size natural_size); - - // Sent by the renderer to signal that Picture-in-Picture mode has ended. - OnExitPictureInPicture(); }; diff --git a/chromium/content/common/frame_messages.h b/chromium/content/common/frame_messages.h index cf96a19a4e0..98bc62f6c93 100644 --- a/chromium/content/common/frame_messages.h +++ b/chromium/content/common/frame_messages.h @@ -17,6 +17,8 @@ #include "base/optional.h" #include "build/build_config.h" +#include "cc/input/touch_action.h" +#include "cc/trees/render_frame_metadata.h" #include "components/viz/common/surfaces/surface_id.h" #include "components/viz/common/surfaces/surface_info.h" #include "content/common/buildflags.h" @@ -29,7 +31,7 @@ #include "content/common/frame_message_structs.h" #include "content/common/frame_owner_properties.h" #include "content/common/frame_replication_state.h" -#include "content/common/frame_resize_params.h" +#include "content/common/frame_visual_properties.h" #include "content/common/navigation_gesture.h" #include "content/common/navigation_params.h" #include "content/common/resource_timing_info.h" @@ -62,10 +64,12 @@ #include "third_party/blink/public/platform/web_insecure_request_policy.h" #include "third_party/blink/public/platform/web_intrinsic_sizing_info.h" #include "third_party/blink/public/platform/web_scroll_into_view_params.h" +#include "third_party/blink/public/platform/web_scroll_types.h" #include "third_party/blink/public/platform/web_sudden_termination_disabler_type.h" #include "third_party/blink/public/web/web_find_options.h" #include "third_party/blink/public/web/web_frame_owner_properties.h" #include "third_party/blink/public/web/web_frame_serializer_cache_control_policy.h" +#include "third_party/blink/public/web/web_fullscreen_options.h" #include "third_party/blink/public/web/web_tree_scope_type.h" #include "third_party/blink/public/web/web_triggering_event_info.h" #include "ui/gfx/geometry/rect.h" @@ -134,6 +138,12 @@ IPC_ENUM_TRAITS_MAX_VALUE(content::CSPDisposition, content::CSPDisposition::LAST) IPC_ENUM_TRAITS_MAX_VALUE(blink::WebTriggeringEventInfo, blink::WebTriggeringEventInfo::kLast) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::WebScrollDirection, + blink::kFirstScrollDirection, + blink::kLastScrollDirection) +IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::WebScrollGranularity, + blink::kFirstScrollGranularity, + blink::kLastScrollGranularity) IPC_STRUCT_TRAITS_BEGIN(blink::WebFloatSize) IPC_STRUCT_TRAITS_MEMBER(width) @@ -154,6 +164,10 @@ IPC_STRUCT_TRAITS_BEGIN(blink::WebFindOptions) IPC_STRUCT_TRAITS_MEMBER(force) IPC_STRUCT_TRAITS_END() +IPC_STRUCT_TRAITS_BEGIN(blink::WebFullscreenOptions) + IPC_STRUCT_TRAITS_MEMBER(prefers_navigation_bar) +IPC_STRUCT_TRAITS_END() + IPC_STRUCT_TRAITS_BEGIN(blink::WebScrollIntoViewParams::Alignment) IPC_STRUCT_TRAITS_MEMBER(rect_visible) IPC_STRUCT_TRAITS_MEMBER(rect_hidden) @@ -230,14 +244,25 @@ IPC_STRUCT_TRAITS_BEGIN(content::FrameOwnerProperties) IPC_STRUCT_TRAITS_MEMBER(required_csp) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(content::FrameResizeParams) +IPC_STRUCT_TRAITS_BEGIN(content::FrameVisualProperties) IPC_STRUCT_TRAITS_MEMBER(screen_info) IPC_STRUCT_TRAITS_MEMBER(auto_resize_enabled) IPC_STRUCT_TRAITS_MEMBER(min_size_for_auto_resize) IPC_STRUCT_TRAITS_MEMBER(max_size_for_auto_resize) - IPC_STRUCT_TRAITS_MEMBER(auto_resize_sequence_number) IPC_STRUCT_TRAITS_MEMBER(screen_space_rect) IPC_STRUCT_TRAITS_MEMBER(local_frame_size) + IPC_STRUCT_TRAITS_MEMBER(capture_sequence_number) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(cc::RenderFrameMetadata) + IPC_STRUCT_TRAITS_MEMBER(is_scroll_offset_at_top) + IPC_STRUCT_TRAITS_MEMBER(root_background_color) + IPC_STRUCT_TRAITS_MEMBER(root_scroll_offset) + IPC_STRUCT_TRAITS_MEMBER(selection) + IPC_STRUCT_TRAITS_MEMBER(is_mobile_optimized) + IPC_STRUCT_TRAITS_MEMBER(device_scale_factor) + IPC_STRUCT_TRAITS_MEMBER(viewport_size_in_pixels) + IPC_STRUCT_TRAITS_MEMBER(local_surface_id) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(blink::FramePolicy) @@ -426,11 +451,6 @@ IPC_STRUCT_BEGIN_WITH_PARENT(FrameHostMsg_DidCommitProvisionalLoad_Params, IPC_STRUCT_END() IPC_STRUCT_BEGIN(FrameMsg_PostMessage_Params) - // Whether the data format is supplied as serialized script value, or as - // a simple string. If it is a raw string, must be converted from string to a - // WebSerializedScriptValue in the renderer process. - IPC_STRUCT_MEMBER(bool, is_data_raw_string) - // When sent to the browser, this is the routing ID of the source frame in // the source process. The browser replaces it with the routing ID of the // equivalent frame proxy in the destination process. @@ -473,7 +493,8 @@ IPC_STRUCT_TRAITS_BEGIN(content::CommonNavigationParams) IPC_STRUCT_TRAITS_MEMBER(should_check_main_world_csp) IPC_STRUCT_TRAITS_MEMBER(has_user_gesture) IPC_STRUCT_TRAITS_MEMBER(started_from_context_menu) - IPC_STRUCT_TRAITS_MEMBER(suggested_filename) + IPC_STRUCT_TRAITS_MEMBER(initiator_csp) + IPC_STRUCT_TRAITS_MEMBER(initiator_self_source) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(content::NavigationTiming) @@ -550,7 +571,7 @@ IPC_STRUCT_BEGIN(FrameHostMsg_OpenURL_Params) IPC_STRUCT_MEMBER(bool, user_gesture) IPC_STRUCT_MEMBER(bool, is_history_navigation_in_new_child) IPC_STRUCT_MEMBER(blink::WebTriggeringEventInfo, triggering_event_info) - IPC_STRUCT_MEMBER(base::Optional, suggested_filename) + IPC_STRUCT_MEMBER(mojo::MessagePipeHandle, blob_url_token) IPC_STRUCT_END() IPC_STRUCT_BEGIN(FrameHostMsg_DownloadUrl_Params) @@ -560,6 +581,7 @@ IPC_STRUCT_BEGIN(FrameHostMsg_DownloadUrl_Params) IPC_STRUCT_MEMBER(content::Referrer, referrer) IPC_STRUCT_MEMBER(url::Origin, initiator_origin) IPC_STRUCT_MEMBER(base::string16, suggested_name) + IPC_STRUCT_MEMBER(mojo::MessagePipeHandle, blob_url_token) IPC_STRUCT_END() IPC_STRUCT_BEGIN(FrameMsg_TextTrackSettings_Params) @@ -764,9 +786,6 @@ IPC_MESSAGE_ROUTED4(FrameHostMsg_ShowCreatedWindow, gfx::Rect /* initial_rect */, bool /* opened_by_user_gesture */) -// Let the browser know a StreamHandle has been consumed and can be released. -IPC_MESSAGE_ROUTED1(FrameHostMsg_StreamHandleConsumed, GURL /* stream_url */) - #if BUILDFLAG(ENABLE_PLUGINS) IPC_STRUCT_TRAITS_BEGIN(content::PepperRendererInstanceData) IPC_STRUCT_TRAITS_MEMBER(render_process_id) @@ -990,10 +1009,10 @@ IPC_MESSAGE_ROUTED1(FrameMsg_PostMessageEvent, FrameMsg_PostMessage_Params) // Tells the RenderFrame to clear the focused element (if any). IPC_MESSAGE_ROUTED0(FrameMsg_ClearFocusedElement) -// Informs the parent renderer that the child would like a new -// viz::LocalSurfaceId in response to an auto-resize. -IPC_MESSAGE_ROUTED1(FrameMsg_ResizeDueToAutoResize, - uint64_t /* sequence_number */) +// Informs the parent renderer that the child has completed an autoresize +// transaction and should update with the provided viz::LocalSurfaceId. +IPC_MESSAGE_ROUTED1(FrameMsg_DidUpdateVisualProperties, + cc::RenderFrameMetadata /* metadata */) // Requests a viz::LocalSurfaceId to enable auto-resize mode from the parent // renderer. @@ -1005,38 +1024,6 @@ IPC_MESSAGE_ROUTED2(FrameMsg_EnableAutoResize, // renderer. IPC_MESSAGE_ROUTED0(FrameMsg_DisableAutoResize) -#if defined(OS_ANDROID) -// Request the distance to the nearest find result in a frame from the point at -// (x, y), defined in fractions of the content document's width and height. The -// distance will be returned via FrameHostMsg_GetNearestFindResult_Reply. Note -// that |nfr_request_id| is a completely seperate ID from the |request_id| used -// in other find-related IPCs. It is specifically used to uniquely identify a -// nearest find result request, rather than a find request. -IPC_MESSAGE_ROUTED3(FrameMsg_GetNearestFindResult, - int /* nfr_request_id */, - float /* x */, - float /* y */) - -// Activates a find result. The point (x,y) is in fractions of the content -// document's width and height. -IPC_MESSAGE_ROUTED3(FrameMsg_ActivateNearestFindResult, - int /* request_id */, - float /* x */, - float /* y */) - -// Sent when the browser wants the bounding boxes of the current find matches. -// -// If match rects are already cached on the browser side, |current_version| -// should be the version number from the FrameHostMsg_FindMatchRects_Reply -// they came in, so the renderer can tell if it needs to send updated rects. -// Otherwise just pass -1 to always receive the list of rects. -// -// There must be an active search string (it is probably most useful to call -// this immediately after a FrameHostMsg_Find_Reply message arrives with -// final_update set to true). -IPC_MESSAGE_ROUTED1(FrameMsg_FindMatchRects, int /* current_version */) -#endif - #if BUILDFLAG(USE_EXTERNAL_POPUP_MENU) #if defined(OS_MACOSX) IPC_MESSAGE_ROUTED1(FrameMsg_SelectPopupMenuItem, @@ -1094,11 +1081,6 @@ IPC_MESSAGE_ROUTED3(FrameMsg_Find, base::string16 /* search_text */, blink::WebFindOptions) -// This message notifies the frame that it is no longer the active frame in the -// current find session, and so it should clear its active find match (and no -// longer highlight it with special coloring). -IPC_MESSAGE_ROUTED0(FrameMsg_ClearActiveFindMatch) - // This message notifies the frame that the user has closed the find-in-page // window (and what action to take regarding the selection). IPC_MESSAGE_ROUTED1(FrameMsg_StopFinding, content::StopFindAction /* action */) @@ -1171,6 +1153,12 @@ IPC_MESSAGE_ROUTED2(FrameMsg_ScrollRectToVisible, gfx::Rect /* rect_to_scroll */, blink::WebScrollIntoViewParams /* properties */) +// Sent to the parent process of a cross-process frame to continue bubbling +// a logical scroll. +IPC_MESSAGE_ROUTED2(FrameMsg_BubbleLogicalScroll, + blink::WebScrollDirection /* direction */, + blink::WebScrollGranularity /* granularity */) + // ----------------------------------------------------------------------------- // Messages sent from the renderer to the browser. @@ -1497,10 +1485,10 @@ IPC_MESSAGE_ROUTED3(FrameHostMsg_BeforeUnload_ACK, // Indicates that the current frame has swapped out, after a SwapOut message. IPC_MESSAGE_ROUTED0(FrameHostMsg_SwapOut_ACK) -// Tells the browser that a child's resize parameters have changed. -IPC_MESSAGE_ROUTED2(FrameHostMsg_UpdateResizeParams, +// Tells the browser that a child's visual properties have changed. +IPC_MESSAGE_ROUTED2(FrameHostMsg_SynchronizeVisualProperties, viz::SurfaceId /* surface_id */, - content::FrameResizeParams) + content::FrameVisualProperties) // Sent by a parent frame to update its child's viewport intersection rect for // use by the IntersectionObserver API. @@ -1516,6 +1504,10 @@ IPC_MESSAGE_ROUTED1(FrameHostMsg_VisibilityChanged, bool /* visible */) // Sets or unsets the inert bit on a remote frame. IPC_MESSAGE_ROUTED1(FrameHostMsg_SetIsInert, bool /* inert */) +// Sets the inherited effective touch action on a remote frame. +IPC_MESSAGE_ROUTED1(FrameHostMsg_SetInheritedEffectiveTouchAction, + cc::TouchAction) + // Toggles render throttling on a remote frame. |is_throttled| indicates // whether the current frame should be throttled based on its viewport // visibility, and |subtree_throttled| indicates that an ancestor frame has @@ -1622,7 +1614,11 @@ IPC_MESSAGE_ROUTED1(FrameHostMsg_VisualStateResponse, uint64_t /* id */) // Puts the browser into "tab fullscreen" mode for the sending renderer. // See the comment in chrome/browser/ui/browser.h for more details. -IPC_MESSAGE_ROUTED1(FrameHostMsg_ToggleFullscreen, bool /* enter_fullscreen */) +IPC_MESSAGE_ROUTED1(FrameHostMsg_EnterFullscreen, blink::WebFullscreenOptions) + +// Exits the browser from "tab fullscreen" mode for the sending renderer. +// See the comment in chrome/browser/ui/browser.h for more details. +IPC_MESSAGE_ROUTED0(FrameHostMsg_ExitFullscreen) // Sent when a new sudden termination disabler condition is either introduced or // removed. @@ -1735,8 +1731,7 @@ IPC_MESSAGE_ROUTED1(FrameHostMsg_UpdateFaviconURL, // A message from HTML-based UI. When (trusted) Javascript calls // send(message, args), this message is sent to the browser. -IPC_MESSAGE_ROUTED3(FrameHostMsg_WebUISend, - GURL /* source_url */, +IPC_MESSAGE_ROUTED2(FrameHostMsg_WebUISend, std::string /* message */, base::ListValue /* args */) @@ -1745,6 +1740,12 @@ IPC_MESSAGE_ROUTED2(FrameHostMsg_ScrollRectToVisibleInParentFrame, gfx::Rect /* rect_to_scroll */, blink::WebScrollIntoViewParams /* properties */) +// Sent by a local root to continue bubbling a logical scroll in its parent +// process. +IPC_MESSAGE_ROUTED2(FrameHostMsg_BubbleLogicalScrollInParentFrame, + blink::WebScrollDirection /* direction */, + blink::WebScrollGranularity /* granularity */) + // Sent to notify that a frame called |window.focus()|. IPC_MESSAGE_ROUTED0(FrameHostMsg_FrameDidCallFocus) @@ -1767,34 +1768,6 @@ IPC_MESSAGE_ROUTED0(FrameHostMsg_HidePopup) #endif -#if defined(OS_ANDROID) -// Response to FrameMsg_FindMatchRects. -// -// |version| will contain the current version number of the renderer's find -// match list (incremented whenever they change), which should be passed in the -// next call to FrameMsg_FindMatchRects. -// -// |rects| will either contain a list of the enclosing rects of all matches -// found by the most recent Find operation, or will be empty if |version| is not -// greater than the |current_version| passed to FrameMsg_FindMatchRects (hence -// your locally cached rects should still be valid). The rect coords will be -// custom normalized fractions of the document size. The rects will be sorted by -// frame traversal order starting in the main frame, then by dom order. -// -// |active_rect| will contain the bounding box of the active find-in-page match -// marker, in similarly normalized coords (or an empty rect if there isn't one). -IPC_MESSAGE_ROUTED3(FrameHostMsg_FindMatchRects_Reply, - int /* version */, - std::vector /* rects */, - gfx::RectF /* active_rect */) - -// Response to FrameMsg_GetNearestFindResult. |distance| is the distance to the -// nearest find result in the sending frame. -IPC_MESSAGE_ROUTED2(FrameHostMsg_GetNearestFindResult_Reply, - int /* nfr_request_id */, - float /* distance */) -#endif - // Adding a new message? Stick to the sort order above: first platform // independent FrameMsg, then ifdefs for platform specific FrameMsg, then // platform independent FrameHostMsg, then ifdefs for platform specific diff --git a/chromium/content/common/frame_resize_params.cc b/chromium/content/common/frame_resize_params.cc deleted file mode 100644 index fc5059d0fc6..00000000000 --- a/chromium/content/common/frame_resize_params.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/frame_resize_params.h" - -namespace content { - -FrameResizeParams::FrameResizeParams() - : auto_resize_enabled(false), auto_resize_sequence_number(0u) {} - -FrameResizeParams::FrameResizeParams(const FrameResizeParams& other) = default; - -FrameResizeParams::~FrameResizeParams() {} - -FrameResizeParams& FrameResizeParams::operator=( - const FrameResizeParams& other) = default; - -} // namespace content diff --git a/chromium/content/common/frame_resize_params.h b/chromium/content/common/frame_resize_params.h deleted file mode 100644 index 425590722c0..00000000000 --- a/chromium/content/common/frame_resize_params.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_COMMON_FRAME_RESIZE_PARAMS_H_ -#define CONTENT_COMMON_FRAME_RESIZE_PARAMS_H_ - -#include "content/common/content_export.h" -#include "content/public/common/screen_info.h" -#include "third_party/blink/public/platform/web_display_mode.h" -#include "ui/gfx/geometry/size.h" - -namespace content { - -// TODO(fsamuel): We might want to unify this with content::ResizeParams. -struct CONTENT_EXPORT FrameResizeParams { - FrameResizeParams(); - FrameResizeParams(const FrameResizeParams& other); - ~FrameResizeParams(); - - FrameResizeParams& operator=(const FrameResizeParams& other); - - // Information about the screen (dpi, depth, etc..). - ScreenInfo screen_info; - - // Whether or not blink should be in auto-resize mode. - bool auto_resize_enabled; - - // The minimum size for Blink if auto-resize is enabled. - gfx::Size min_size_for_auto_resize; - - // The maximum size for Blink if auto-resize is enabled. - gfx::Size max_size_for_auto_resize; - - // This variable is increased after each auto-resize. If the - // renderer receives a ResizeParams with stale auto_resize_seqence_number, - // then the resize request is dropped. - uint64_t auto_resize_sequence_number; - - gfx::Rect screen_space_rect; - - gfx::Size local_frame_size; -}; - -} // namespace content - -#endif // CONTENT_COMMON_FRAME_RESIZE_PARAMS_H_ diff --git a/chromium/content/common/frame_sink_provider.mojom b/chromium/content/common/frame_sink_provider.mojom index ad84346d80b..003f761e458 100644 --- a/chromium/content/common/frame_sink_provider.mojom +++ b/chromium/content/common/frame_sink_provider.mojom @@ -1,3 +1,7 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + module content.mojom; import "content/common/render_frame_metadata.mojom"; @@ -11,7 +15,10 @@ interface FrameSinkProvider { CreateForWidget( int32 widget_id, viz.mojom.CompositorFrameSink& compositor_frame_sink_request, - viz.mojom.CompositorFrameSinkClient compositor_frame_sink_client, + viz.mojom.CompositorFrameSinkClient compositor_frame_sink_client); + + RegisterRenderFrameMetadataObserver( + int32 widget_id, RenderFrameMetadataObserverClient& render_frame_metadata_observer_client_request, RenderFrameMetadataObserver render_frame_metadata_observer); diff --git a/chromium/content/common/frame_visual_properties.cc b/chromium/content/common/frame_visual_properties.cc new file mode 100644 index 00000000000..1f1d4f1dbed --- /dev/null +++ b/chromium/content/common/frame_visual_properties.cc @@ -0,0 +1,19 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/common/frame_visual_properties.h" + +namespace content { + +FrameVisualProperties::FrameVisualProperties() = default; + +FrameVisualProperties::FrameVisualProperties( + const FrameVisualProperties& other) = default; + +FrameVisualProperties::~FrameVisualProperties() = default; + +FrameVisualProperties& FrameVisualProperties::operator=( + const FrameVisualProperties& other) = default; + +} // namespace content diff --git a/chromium/content/common/frame_visual_properties.h b/chromium/content/common/frame_visual_properties.h new file mode 100644 index 00000000000..6dc8da06d92 --- /dev/null +++ b/chromium/content/common/frame_visual_properties.h @@ -0,0 +1,43 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_COMMON_FRAME_VISUAL_PROPERTIES_H_ +#define CONTENT_COMMON_FRAME_VISUAL_PROPERTIES_H_ + +#include "content/common/content_export.h" +#include "content/public/common/screen_info.h" +#include "ui/gfx/geometry/size.h" + +namespace content { + +// TODO(fsamuel): We might want to unify this with content::ResizeParams. +struct CONTENT_EXPORT FrameVisualProperties { + FrameVisualProperties(); + FrameVisualProperties(const FrameVisualProperties& other); + ~FrameVisualProperties(); + + FrameVisualProperties& operator=(const FrameVisualProperties& other); + + // Information about the screen (dpi, depth, etc..). + ScreenInfo screen_info; + + // Whether or not blink should be in auto-resize mode. + bool auto_resize_enabled = false; + + // The minimum size for Blink if auto-resize is enabled. + gfx::Size min_size_for_auto_resize; + + // The maximum size for Blink if auto-resize is enabled. + gfx::Size max_size_for_auto_resize; + + gfx::Rect screen_space_rect; + + gfx::Size local_frame_size; + + uint32_t capture_sequence_number = 0u; +}; + +} // namespace content + +#endif // CONTENT_COMMON_FRAME_VISUAL_PROPERTIES_H_ diff --git a/chromium/content/common/input/event_with_latency_info.h b/chromium/content/common/input/event_with_latency_info.h index 7dc0e8113ce..0b97bdd35ad 100644 --- a/chromium/content/common/input/event_with_latency_info.h +++ b/chromium/content/common/input/event_with_latency_info.h @@ -30,9 +30,9 @@ class EventWithLatencyInfo { EventWithLatencyInfo(blink::WebInputEvent::Type type, int modifiers, - double timeStampSeconds, + base::TimeTicks time_stamp, const ui::LatencyInfo& l) - : event(type, modifiers, timeStampSeconds), latency(l) {} + : event(type, modifiers, time_stamp), latency(l) {} EventWithLatencyInfo() {} @@ -54,9 +54,9 @@ class EventWithLatencyInfo { // New events get coalesced into older events, and the newer timestamp // should always be preserved. - const double time_stamp_seconds = other.event.TimeStampSeconds(); + const base::TimeTicks time_stamp = other.event.TimeStamp(); ui::Coalesce(other.event, &event); - event.SetTimeStampSeconds(time_stamp_seconds); + event.SetTimeStamp(time_stamp); // When coalescing two input events, we keep the oldest LatencyInfo // for Telemetry latency tests, since it will represent the longest diff --git a/chromium/content/common/input/event_with_latency_info_unittest.cc b/chromium/content/common/input/event_with_latency_info_unittest.cc index 640e42e71fd..2c135593a3e 100644 --- a/chromium/content/common/input/event_with_latency_info_unittest.cc +++ b/chromium/content/common/input/event_with_latency_info_unittest.cc @@ -25,16 +25,20 @@ using EventWithLatencyInfoTest = testing::Test; TouchEventWithLatencyInfo CreateTouchEvent(WebInputEvent::Type type, double timestamp, unsigned touch_count = 1) { - TouchEventWithLatencyInfo touch(type, WebInputEvent::kNoModifiers, timestamp, - ui::LatencyInfo()); + TouchEventWithLatencyInfo touch( + type, WebInputEvent::kNoModifiers, + base::TimeTicks() + base::TimeDelta::FromSecondsD(timestamp), + ui::LatencyInfo()); touch.event.touches_length = touch_count; return touch; } MouseEventWithLatencyInfo CreateMouseEvent(WebInputEvent::Type type, double timestamp) { - return MouseEventWithLatencyInfo(type, WebInputEvent::kNoModifiers, timestamp, - ui::LatencyInfo()); + return MouseEventWithLatencyInfo( + type, WebInputEvent::kNoModifiers, + base::TimeTicks() + base::TimeDelta::FromSecondsD(timestamp), + ui::LatencyInfo()); } MouseWheelEventWithLatencyInfo CreateMouseWheelEvent( @@ -43,7 +47,9 @@ MouseWheelEventWithLatencyInfo CreateMouseWheelEvent( float deltaY = 0.0f, int modifiers = WebInputEvent::kNoModifiers) { MouseWheelEventWithLatencyInfo mouse_wheel( - WebInputEvent::kMouseWheel, modifiers, timestamp, ui::LatencyInfo()); + WebInputEvent::kMouseWheel, modifiers, + base::TimeTicks() + base::TimeDelta::FromSecondsD(timestamp), + ui::LatencyInfo()); mouse_wheel.event.delta_x = deltaX; mouse_wheel.event.delta_y = deltaY; return mouse_wheel; @@ -53,8 +59,10 @@ GestureEventWithLatencyInfo CreateGestureEvent(WebInputEvent::Type type, double timestamp, float x = 0.0f, float y = 0.0f) { - GestureEventWithLatencyInfo gesture(type, WebInputEvent::kNoModifiers, - timestamp, ui::LatencyInfo()); + GestureEventWithLatencyInfo gesture( + type, WebInputEvent::kNoModifiers, + base::TimeTicks() + base::TimeDelta::FromSecondsD(timestamp), + ui::LatencyInfo()); gesture.event.SetPositionInWidget(gfx::PointF(x, y)); return gesture; } @@ -68,7 +76,7 @@ TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForMouseEvent) { ASSERT_TRUE(mouse_0.CanCoalesceWith(mouse_1)); mouse_0.CoalesceWith(mouse_1); // Coalescing WebMouseEvent preserves newer timestamp. - EXPECT_EQ(10.0, mouse_0.event.TimeStampSeconds()); + EXPECT_EQ(10.0, mouse_0.event.TimeStamp().since_origin().InSecondsF()); } TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForMouseWheelEvent) { @@ -78,7 +86,7 @@ TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForMouseWheelEvent) { ASSERT_TRUE(mouse_wheel_0.CanCoalesceWith(mouse_wheel_1)); mouse_wheel_0.CoalesceWith(mouse_wheel_1); // Coalescing WebMouseWheelEvent preserves newer timestamp. - EXPECT_EQ(10.0, mouse_wheel_0.event.TimeStampSeconds()); + EXPECT_EQ(10.0, mouse_wheel_0.event.TimeStamp().since_origin().InSecondsF()); } TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForTouchEvent) { @@ -90,7 +98,7 @@ TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForTouchEvent) { ASSERT_TRUE(touch_0.CanCoalesceWith(touch_1)); touch_0.CoalesceWith(touch_1); // Coalescing WebTouchEvent preserves newer timestamp. - EXPECT_EQ(10.0, touch_0.event.TimeStampSeconds()); + EXPECT_EQ(10.0, touch_0.event.TimeStamp().since_origin().InSecondsF()); } TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForGestureEvent) { @@ -102,14 +110,14 @@ TEST_F(EventWithLatencyInfoTest, TimestampCoalescingForGestureEvent) { ASSERT_TRUE(scroll_0.CanCoalesceWith(scroll_1)); scroll_0.CoalesceWith(scroll_1); // Coalescing WebGestureEvent preserves newer timestamp. - EXPECT_EQ(10.0, scroll_0.event.TimeStampSeconds()); + EXPECT_EQ(10.0, scroll_0.event.TimeStamp().since_origin().InSecondsF()); } TEST_F(EventWithLatencyInfoTest, LatencyInfoCoalescing) { MouseEventWithLatencyInfo mouse_0 = CreateMouseEvent(WebInputEvent::kMouseMove, 5.0); mouse_0.latency.AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, base::TimeTicks(), 1); + ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, base::TimeTicks(), 1); MouseEventWithLatencyInfo mouse_1 = CreateMouseEvent(WebInputEvent::kMouseMove, 10.0); @@ -360,7 +368,7 @@ TEST_F(EventWithLatencyInfoTest, TimestampCoalescing) { EXPECT_TRUE(CanCoalesce(mouse_wheel_0, mouse_wheel_1)); Coalesce(mouse_wheel_1, &mouse_wheel_0); - EXPECT_EQ(10.0, mouse_wheel_0.event.TimeStampSeconds()); + EXPECT_EQ(10.0, mouse_wheel_0.event.TimeStamp().since_origin().InSecondsF()); } } // namespace diff --git a/chromium/content/common/input/input_event_struct_traits.cc b/chromium/content/common/input/input_event_struct_traits.cc index c1e18caff8c..afa97f2064f 100644 --- a/chromium/content/common/input/input_event_struct_traits.cc +++ b/chromium/content/common/input/input_event_struct_traits.cc @@ -6,6 +6,7 @@ #include "base/i18n/char_iterator.h" #include "content/common/input_messages.h" +#include "mojo/public/cpp/base/time_mojom_traits.h" #include "third_party/blink/public/platform/web_keyboard_event.h" #include "ui/latency/mojo/latency_info_struct_traits.h" @@ -88,13 +89,17 @@ bool StructTraits::Read( if (!event.ReadType(&type)) return false; + base::TimeTicks timestamp; + if (!event.ReadTimestamp(×tamp)) + return false; + if (blink::WebInputEvent::IsKeyboardEventType(type)) { content::mojom::KeyDataPtr key_data; if (!event.ReadKeyData(&key_data)) return false; - (*out)->web_event.reset(new blink::WebKeyboardEvent( - type, event.modifiers(), event.timestamp_seconds())); + (*out)->web_event.reset( + new blink::WebKeyboardEvent(type, event.modifiers(), timestamp)); blink::WebKeyboardEvent* key_event = static_cast((*out)->web_event.get()); @@ -111,8 +116,7 @@ bool StructTraits::Read( if (!event.ReadGestureData(&gesture_data)) return false; (*out)->web_event.reset(new blink::WebGestureEvent( - type, event.modifiers(), event.timestamp_seconds(), - gesture_data->source_device)); + type, event.modifiers(), timestamp, gesture_data->source_device)); blink::WebGestureEvent* gesture_event = static_cast((*out)->web_event.get()); @@ -258,8 +262,8 @@ bool StructTraits::Read( if (!event.ReadTouchData(&touch_data)) return false; - (*out)->web_event.reset(new blink::WebTouchEvent( - type, event.modifiers(), event.timestamp_seconds())); + (*out)->web_event.reset( + new blink::WebTouchEvent(type, event.modifiers(), timestamp)); blink::WebTouchEvent* touch_event = static_cast((*out)->web_event.get()); @@ -287,11 +291,11 @@ bool StructTraits::Read( return false; if (blink::WebInputEvent::IsMouseEventType(type)) { - (*out)->web_event.reset(new blink::WebMouseEvent( - type, event.modifiers(), event.timestamp_seconds())); + (*out)->web_event.reset( + new blink::WebMouseEvent(type, event.modifiers(), timestamp)); } else { - (*out)->web_event.reset(new blink::WebMouseWheelEvent( - type, event.modifiers(), event.timestamp_seconds())); + (*out)->web_event.reset( + new blink::WebMouseWheelEvent(type, event.modifiers(), timestamp)); } blink::WebMouseEvent* mouse_event = diff --git a/chromium/content/common/input/input_event_struct_traits.h b/chromium/content/common/input/input_event_struct_traits.h index d63ca68bea7..ca8ea062713 100644 --- a/chromium/content/common/input/input_event_struct_traits.h +++ b/chromium/content/common/input/input_event_struct_traits.h @@ -25,8 +25,8 @@ struct StructTraits { return event->web_event->GetModifiers(); } - static double timestamp_seconds(const InputEventUniquePtr& event) { - return event->web_event->TimeStampSeconds(); + static base::TimeTicks timestamp(const InputEventUniquePtr& event) { + return event->web_event->TimeStamp(); } static const ui::LatencyInfo& latency(const InputEventUniquePtr& event) { diff --git a/chromium/content/common/input/input_handler.mojom b/chromium/content/common/input/input_handler.mojom index b5725e1dbf9..9f8a63e8b72 100644 --- a/chromium/content/common/input/input_handler.mojom +++ b/chromium/content/common/input/input_handler.mojom @@ -7,6 +7,7 @@ module content.mojom; import "content/common/input/synchronous_compositor.mojom"; import "content/common/native_types.mojom"; import "mojo/public/mojom/base/string16.mojom"; +import "mojo/public/mojom/base/time.mojom"; import "services/ui/public/interfaces/ime/ime.mojom"; import "third_party/blink/public/web/selection_menu_behavior.mojom"; import "ui/events/mojo/event.mojom"; @@ -139,7 +140,7 @@ struct TouchData { struct Event { EventType type; int32 modifiers; - double timestamp_seconds; + mojo_base.mojom.TimeTicks timestamp; ui.mojom.LatencyInfo latency; KeyData? key_data; PointerData? pointer_data; @@ -187,6 +188,13 @@ interface WidgetInputHandlerHost { // to always have correct bound info. ImeCompositionRangeChanged(gfx.mojom.Range range, array bounds); + + // Updates the mouse capture state of this widget. While capture is enabled, + // all mouse events, including those that don't hittest to this widget, will + // be targeted to this widget. This enables Blink to behave correctly when + // a scrollbar is being dragged, or text is being drag-highlighted, even + // when the mouse passes across different RenderWidget areas. + SetMouseCapture(bool capture); }; // Interface exposed by the renderer to the browser. This class represents @@ -234,7 +242,7 @@ interface WidgetInputHandler { RequestTextInputStateUpdate(); // Request from browser to update the cursor and composition information which - // will be sent through InputHostMsg_ImeCompositionRangeChanged. Setting + // will be sent through ImeCompositionRangeChanged. Setting // |immediate_request| to true will lead to an immediate update. If // |monitor_updates| is set to true then changes to text selection or regular // updates in each compositor frame (when there is a change in composition diff --git a/chromium/content/common/input/input_param_traits_unittest.cc b/chromium/content/common/input/input_param_traits_unittest.cc deleted file mode 100644 index 81d1d69cd85..00000000000 --- a/chromium/content/common/input/input_param_traits_unittest.cc +++ /dev/null @@ -1,121 +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/common/input/input_param_traits.h" - -#include - -#include -#include -#include - -#include "content/common/input/input_event.h" -#include "content/common/input_messages.h" -#include "ipc/ipc_message.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/platform/web_gesture_event.h" -#include "third_party/blink/public/platform/web_input_event.h" -#include "third_party/blink/public/platform/web_keyboard_event.h" -#include "third_party/blink/public/platform/web_mouse_wheel_event.h" -#include "third_party/blink/public/platform/web_touch_event.h" - -namespace content { -namespace { - -typedef std::vector> InputEvents; - -class InputParamTraitsTest : public testing::Test { - protected: - static void Compare(const InputEvent* a, const InputEvent* b) { - EXPECT_EQ(!!a->web_event, !!b->web_event); - if (a->web_event && b->web_event) { - const size_t a_size = a->web_event->size(); - ASSERT_EQ(a_size, b->web_event->size()); - EXPECT_EQ(0, memcmp(a->web_event.get(), b->web_event.get(), a_size)); - } - EXPECT_EQ(a->latency_info.latency_components().size(), - b->latency_info.latency_components().size()); - } - - static void Compare(const InputEvents* a, const InputEvents* b) { - for (size_t i = 0; i < a->size(); ++i) - Compare((*a)[i].get(), (*b)[i].get()); - } - - static void Verify(const InputEvents& events_in) { - IPC::Message msg; - IPC::ParamTraits::Write(&msg, events_in); - - InputEvents events_out; - base::PickleIterator iter(msg); - EXPECT_TRUE(IPC::ParamTraits::Read(&msg, &iter, &events_out)); - - Compare(&events_in, &events_out); - - // Perform a sanity check that logging doesn't explode. - std::string events_in_string; - IPC::ParamTraits::Log(events_in, &events_in_string); - std::string events_out_string; - IPC::ParamTraits::Log(events_out, &events_out_string); - ASSERT_FALSE(events_in_string.empty()); - EXPECT_EQ(events_in_string, events_out_string); - } -}; - -TEST_F(InputParamTraitsTest, UninitializedEvents) { - InputEvent event; - - IPC::Message msg; - IPC::WriteParam(&msg, event); - - InputEvent event_out; - base::PickleIterator iter(msg); - EXPECT_FALSE(IPC::ReadParam(&msg, &iter, &event_out)); -} - -TEST_F(InputParamTraitsTest, InitializedEvents) { - InputEvents events; - - ui::LatencyInfo latency; - latency.set_trace_id(5); - - blink::WebKeyboardEvent key_event( - blink::WebInputEvent::kRawKeyDown, blink::WebInputEvent::kNoModifiers, - blink::WebInputEvent::GetStaticTimeStampForTests()); - key_event.native_key_code = 5; - events.push_back(std::make_unique(key_event, latency)); - - blink::WebMouseWheelEvent wheel_event( - blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers, - blink::WebInputEvent::GetStaticTimeStampForTests()); - wheel_event.delta_x = 10; - latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 1, 1); - events.push_back(std::make_unique(wheel_event, latency)); - - blink::WebMouseEvent mouse_event( - blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers, - blink::WebInputEvent::GetStaticTimeStampForTests()); - mouse_event.SetPositionInWidget(10, 0); - latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_UI_COMPONENT, 2, 2); - events.push_back(std::make_unique(mouse_event, latency)); - - blink::WebGestureEvent gesture_event( - blink::WebInputEvent::kGestureScrollBegin, - blink::WebInputEvent::kNoModifiers, - blink::WebInputEvent::GetStaticTimeStampForTests()); - gesture_event.SetPositionInWidget(gfx::PointF(-1, 0)); - events.push_back(std::make_unique(gesture_event, latency)); - - blink::WebTouchEvent touch_event( - blink::WebInputEvent::kTouchStart, blink::WebInputEvent::kNoModifiers, - blink::WebInputEvent::GetStaticTimeStampForTests()); - touch_event.touches_length = 1; - touch_event.touches[0].radius_x = 1; - events.push_back(std::make_unique(touch_event, latency)); - - Verify(events); -} - -} // namespace -} // namespace content diff --git a/chromium/content/common/input/sync_compositor_messages.h b/chromium/content/common/input/sync_compositor_messages.h index fcfd89e58d0..a608f7eb160 100644 --- a/chromium/content/common/input/sync_compositor_messages.h +++ b/chromium/content/common/input/sync_compositor_messages.h @@ -8,14 +8,9 @@ #include #include "base/memory/shared_memory_handle.h" -#include "base/optional.h" -#include "components/viz/common/frame_sinks/begin_frame_args.h" -#include "components/viz/common/quads/compositor_frame.h" #include "content/common/content_export.h" #include "content/common/content_param_traits.h" -#include "content/public/common/input_event_ack_state.h" #include "ipc/ipc_message_macros.h" -#include "third_party/blink/public/platform/web_input_event.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/scroll_offset.h" @@ -80,10 +75,6 @@ struct SyncCompositorCommonRendererParams { #endif // INTERNAL_CONTENT_COMMON_SYNC_COMPOSITOR_MESSAGES_H_ -#undef IPC_MESSAGE_EXPORT -#define IPC_MESSAGE_EXPORT CONTENT_EXPORT -#define IPC_MESSAGE_START SyncCompositorMsgStart - IPC_STRUCT_TRAITS_BEGIN(content::SyncCompositorDemandDrawHwParams) IPC_STRUCT_TRAITS_MEMBER(viewport_size) IPC_STRUCT_TRAITS_MEMBER(viewport_rect_for_tile_priority) @@ -114,77 +105,4 @@ IPC_STRUCT_TRAITS_BEGIN(content::SyncCompositorCommonRendererParams) IPC_STRUCT_TRAITS_MEMBER(did_activate_pending_tree_count) IPC_STRUCT_TRAITS_END() -// Messages sent from the browser to the renderer. -// Synchronous IPCs are allowed here to the renderer compositor thread. See -// design doc https://goo.gl/Tn81FW and https://crbug.com/526842 for details. - -IPC_MESSAGE_ROUTED1(SyncCompositorMsg_ComputeScroll, base::TimeTicks) - -IPC_MESSAGE_ROUTED1(SyncCompositorMsg_DemandDrawHwAsync, - content::SyncCompositorDemandDrawHwParams) - -IPC_SYNC_MESSAGE_ROUTED1_4(SyncCompositorMsg_DemandDrawHw, - content::SyncCompositorDemandDrawHwParams, - content::SyncCompositorCommonRendererParams, - uint32_t /* layer_tree_frame_sink_id */, - uint32_t /* metadata_version */, - base::Optional) - -IPC_SYNC_MESSAGE_ROUTED1_2(SyncCompositorMsg_SetSharedMemory, - content::SyncCompositorSetSharedMemoryParams, - bool /* success */, - content::SyncCompositorCommonRendererParams) - -IPC_MESSAGE_ROUTED0(SyncCompositorMsg_ZeroSharedMemory) - -IPC_SYNC_MESSAGE_ROUTED1_3(SyncCompositorMsg_DemandDrawSw, - content::SyncCompositorDemandDrawSwParams, - content::SyncCompositorCommonRendererParams, - uint32_t /* metadata_version */, - base::Optional) - -IPC_SYNC_MESSAGE_ROUTED2_1(SyncCompositorMsg_ZoomBy, - float /* delta */, - gfx::Point /* anchor */, - content::SyncCompositorCommonRendererParams) - -IPC_MESSAGE_ROUTED1(SyncCompositorMsg_SetMemoryPolicy, - uint32_t /* bytes_limit */) - -IPC_MESSAGE_ROUTED2(SyncCompositorMsg_ReclaimResources, - uint32_t /* layer_tree_frame_sink_id */, - std::vector /* resources */) - -IPC_MESSAGE_ROUTED1(SyncCompositorMsg_SetScroll, gfx::ScrollOffset) - -// Let renderer know begin frame messages won't be sent even if requested. -IPC_MESSAGE_ROUTED1(SyncCompositorMsg_SetBeginFramePaused, bool /* paused */) - -// Sent by the browser when the renderer should generate a new frame. -IPC_MESSAGE_ROUTED1(SyncCompositorMsg_BeginFrame, - viz::BeginFrameArgs /* args */) - -// ----------------------------------------------------------------------------- -// Messages sent from the renderer to the browser. - -IPC_MESSAGE_ROUTED0(SyncCompositorHostMsg_LayerTreeFrameSinkCreated) - -IPC_MESSAGE_ROUTED1(SyncCompositorHostMsg_UpdateState, - content::SyncCompositorCommonRendererParams) - -// Response to a begin frame request. -IPC_MESSAGE_ROUTED1(SyncCompositorHostMsg_BeginFrameResponse, - content::SyncCompositorCommonRendererParams) - -IPC_MESSAGE_ROUTED3(SyncCompositorHostMsg_ReturnFrame, - uint32_t /* layer_tree_frame_sink_id */, - uint32_t /* metadata_version */, - base::Optional) - -// Sent by renderer to request a SyncCompositorMsg_BeginFrame message for -// upcoming display events. If |enabled| is true, the BeginFrame message will -// continue to be be delivered until the notification is disabled. -IPC_MESSAGE_ROUTED1(SyncCompositorHostMsg_SetNeedsBeginFrames, - bool /* enabled */) - #endif // CONTENT_COMMON_SYNC_COMPOSITOR_MESSAGES_H_ diff --git a/chromium/content/common/input/synchronous_compositor.typemap b/chromium/content/common/input/synchronous_compositor.typemap index 6adbf1659d1..cb07fb33228 100644 --- a/chromium/content/common/input/synchronous_compositor.typemap +++ b/chromium/content/common/input/synchronous_compositor.typemap @@ -6,7 +6,6 @@ mojom = "//content/common/input/synchronous_compositor.mojom" public_headers = [ "//content/common/input/sync_compositor_messages.h" ] traits_headers = [ "//content/common/input/sync_compositor_messages.h" ] deps = [ - "//cc/ipc", "//ui/gfx/ipc", ] type_mappings = [ diff --git a/chromium/content/common/input/synthetic_web_input_event_builders.cc b/chromium/content/common/input/synthetic_web_input_event_builders.cc index 617d0ea12b5..5cf6591d784 100644 --- a/chromium/content/common/input/synthetic_web_input_event_builders.cc +++ b/chromium/content/common/input/synthetic_web_input_event_builders.cc @@ -23,7 +23,7 @@ using blink::WebTouchPoint; WebMouseEvent SyntheticWebMouseEventBuilder::Build( blink::WebInputEvent::Type type) { return WebMouseEvent(type, WebInputEvent::kNoModifiers, - ui::EventTimeStampToSeconds(ui::EventTimeForNow())); + ui::EventTimeForNow()); } WebMouseEvent SyntheticWebMouseEventBuilder::Build( @@ -33,9 +33,9 @@ WebMouseEvent SyntheticWebMouseEventBuilder::Build( int modifiers, blink::WebPointerProperties::PointerType pointer_type) { DCHECK(WebInputEvent::IsMouseEventType(type)); - WebMouseEvent result(type, modifiers, - ui::EventTimeStampToSeconds(ui::EventTimeForNow())); + WebMouseEvent result(type, modifiers, ui::EventTimeForNow()); result.SetPositionInWidget(window_x, window_y); + result.SetPositionInScreen(window_x, window_y); result.SetModifiers(modifiers); result.pointer_type = pointer_type; result.id = ui::MouseEvent::kMousePointerId; @@ -45,8 +45,7 @@ WebMouseEvent SyntheticWebMouseEventBuilder::Build( WebMouseWheelEvent SyntheticWebMouseWheelEventBuilder::Build( WebMouseWheelEvent::Phase phase) { WebMouseWheelEvent result(WebInputEvent::kMouseWheel, - WebInputEvent::kNoModifiers, - ui::EventTimeStampToSeconds(ui::EventTimeForNow())); + WebInputEvent::kNoModifiers, ui::EventTimeForNow()); result.phase = phase; return result; } @@ -69,7 +68,7 @@ WebMouseWheelEvent SyntheticWebMouseWheelEventBuilder::Build(float x, int modifiers, bool precise) { WebMouseWheelEvent result(WebInputEvent::kMouseWheel, modifiers, - ui::EventTimeStampToSeconds(ui::EventTimeForNow())); + ui::EventTimeForNow()); result.SetPositionInScreen(global_x, global_y); result.SetPositionInWidget(x, y); result.delta_x = dx; @@ -86,7 +85,7 @@ WebKeyboardEvent SyntheticWebKeyboardEventBuilder::Build( WebInputEvent::Type type) { DCHECK(WebInputEvent::IsKeyboardEventType(type)); WebKeyboardEvent result(type, WebInputEvent::kNoModifiers, - ui::EventTimeStampToSeconds(ui::EventTimeForNow())); + ui::EventTimeForNow()); result.windows_key_code = ui::VKEY_L; // non-null made up value. return result; } @@ -96,9 +95,7 @@ WebGestureEvent SyntheticWebGestureEventBuilder::Build( blink::WebGestureDevice source_device, int modifiers) { DCHECK(WebInputEvent::IsGestureEventType(type)); - WebGestureEvent result(type, modifiers, - ui::EventTimeStampToSeconds(ui::EventTimeForNow()), - source_device); + WebGestureEvent result(type, modifiers, ui::EventTimeForNow(), source_device); if (type == WebInputEvent::kGestureTap || type == WebInputEvent::kGestureTapUnconfirmed || type == WebInputEvent::kGestureDoubleTap) { @@ -202,9 +199,9 @@ int SyntheticWebTouchEvent::PressPoint(float x, float y) { point.rotation_angle = 1.f; point.force = 1.f; point.tilt_x = point.tilt_y = 0; + point.pointer_type = blink::WebPointerProperties::PointerType::kTouch; ++touches_length; - WebTouchEventTraits::ResetType(WebInputEvent::kTouchStart, TimeStampSeconds(), - this); + WebTouchEventTraits::ResetType(WebInputEvent::kTouchStart, TimeStamp(), this); return point.id; } @@ -218,8 +215,7 @@ void SyntheticWebTouchEvent::MovePoint(int index, float x, float y) { point.SetPositionInWidget(x, y); point.SetPositionInScreen(x, y); touches[index].state = WebTouchPoint::kStateMoved; - WebTouchEventTraits::ResetType(WebInputEvent::kTouchMove, TimeStampSeconds(), - this); + WebTouchEventTraits::ResetType(WebInputEvent::kTouchMove, TimeStamp(), this); } void SyntheticWebTouchEvent::ReleasePoint(int index) { @@ -227,20 +223,19 @@ void SyntheticWebTouchEvent::ReleasePoint(int index) { CHECK_LT(index, kTouchesLengthCap); touches[index].state = WebTouchPoint::kStateReleased; touches[index].force = 0.f; - WebTouchEventTraits::ResetType(WebInputEvent::kTouchEnd, TimeStampSeconds(), - this); + WebTouchEventTraits::ResetType(WebInputEvent::kTouchEnd, TimeStamp(), this); } void SyntheticWebTouchEvent::CancelPoint(int index) { CHECK_GE(index, 0); CHECK_LT(index, kTouchesLengthCap); touches[index].state = WebTouchPoint::kStateCancelled; - WebTouchEventTraits::ResetType(WebInputEvent::kTouchCancel, - TimeStampSeconds(), this); + WebTouchEventTraits::ResetType(WebInputEvent::kTouchCancel, TimeStamp(), + this); } void SyntheticWebTouchEvent::SetTimestamp(base::TimeTicks timestamp) { - SetTimeStampSeconds(ui::EventTimeStampToSeconds(timestamp)); + SetTimeStamp(timestamp); } int SyntheticWebTouchEvent::FirstFreeIndex() { diff --git a/chromium/content/common/input/touch_action_optional_struct_traits.cc b/chromium/content/common/input/touch_action_optional_struct_traits.cc index b2b4c33e638..3b57659bb1d 100644 --- a/chromium/content/common/input/touch_action_optional_struct_traits.cc +++ b/chromium/content/common/input/touch_action_optional_struct_traits.cc @@ -4,7 +4,7 @@ #include "content/common/input/touch_action_optional_struct_traits.h" -#include "content/common/input_messages.h" +#include "content/public/common/common_param_traits_macros.h" namespace mojo { bool StructTraits< diff --git a/chromium/content/common/input/web_touch_event_traits.cc b/chromium/content/common/input/web_touch_event_traits.cc index 897d78e5b58..06972d03654 100644 --- a/chromium/content/common/input/web_touch_event_traits.cc +++ b/chromium/content/common/input/web_touch_event_traits.cc @@ -50,7 +50,7 @@ bool WebTouchEventTraits::IsTouchSequenceEnd(const WebTouchEvent& event) { } void WebTouchEventTraits::ResetType(WebInputEvent::Type type, - double timestamp_sec, + base::TimeTicks timestamp, WebTouchEvent* event) { DCHECK(WebInputEvent::IsTouchEventType(type)); DCHECK(type != WebInputEvent::kTouchScrollStarted); @@ -59,13 +59,13 @@ void WebTouchEventTraits::ResetType(WebInputEvent::Type type, event->dispatch_type = type == WebInputEvent::kTouchCancel ? WebInputEvent::kEventNonBlocking : WebInputEvent::kBlocking; - event->SetTimeStampSeconds(timestamp_sec); + event->SetTimeStamp(timestamp); } void WebTouchEventTraits::ResetTypeAndTouchStates(WebInputEvent::Type type, - double timestamp_sec, + base::TimeTicks timestamp, WebTouchEvent* event) { - ResetType(type, timestamp_sec, event); + ResetType(type, timestamp, event); WebTouchPoint::State newState = WebTouchPoint::kStateUndefined; switch (event->GetType()) { diff --git a/chromium/content/common/input/web_touch_event_traits.h b/chromium/content/common/input/web_touch_event_traits.h index eac4718d4ff..ff27930e937 100644 --- a/chromium/content/common/input/web_touch_event_traits.h +++ b/chromium/content/common/input/web_touch_event_traits.h @@ -5,6 +5,7 @@ #ifndef CONTENT_COMMON_INPUT_WEB_TOUCH_EVENT_TRAITS_H_ #define CONTENT_COMMON_INPUT_WEB_TOUCH_EVENT_TRAITS_H_ +#include "base/time/time.h" #include "content/common/content_export.h" #include "third_party/blink/public/platform/web_input_event.h" @@ -32,14 +33,14 @@ class CONTENT_EXPORT WebTouchEventTraits { // Sets the type of |event| to |type|, resetting any other type-specific // properties and updating the timestamp. static void ResetType(blink::WebInputEvent::Type type, - double timestamp_sec, + base::TimeTicks timestamp, blink::WebTouchEvent* event); // Like ResetType but also resets the state of all active touches // to match the event type. This is particularly useful, for example, // in sending a touchcancel for all active touches. static void ResetTypeAndTouchStates(blink::WebInputEvent::Type type, - double timestamp_sec, + base::TimeTicks timestamp, blink::WebTouchEvent* event); }; diff --git a/chromium/content/common/input_messages.h b/chromium/content/common/input_messages.h index 89df9cc4f40..81f0759d605 100644 --- a/chromium/content/common/input_messages.h +++ b/chromium/content/common/input_messages.h @@ -11,7 +11,6 @@ #include "base/strings/string16.h" #include "build/build_config.h" #include "cc/input/overscroll_behavior.h" -#include "cc/input/touch_action.h" #include "content/common/content_export.h" #include "content/common/content_param_traits.h" #include "content/common/edit_command.h" @@ -66,7 +65,6 @@ IPC_ENUM_TRAITS_MAX_VALUE( content::SyntheticPointerActionParams::Button::BUTTON_MAX) IPC_ENUM_TRAITS_MAX_VALUE(content::InputEventDispatchType, content::InputEventDispatchType::DISPATCH_TYPE_MAX) -IPC_ENUM_TRAITS_MAX_VALUE(cc::TouchAction, cc::kTouchActionMax) IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::WebPointerProperties::Button, blink::WebPointerProperties::Button::kNoButton, blink::WebPointerProperties::Button::kLastEntry) @@ -105,11 +103,6 @@ IPC_STRUCT_TRAITS_BEGIN(content::EditCommand) IPC_STRUCT_TRAITS_MEMBER(value) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(content::InputEvent) - IPC_STRUCT_TRAITS_MEMBER(web_event) - IPC_STRUCT_TRAITS_MEMBER(latency_info) -IPC_STRUCT_TRAITS_END() - IPC_STRUCT_TRAITS_BEGIN(content::SyntheticGestureParams) IPC_STRUCT_TRAITS_MEMBER(gesture_source_type) IPC_STRUCT_TRAITS_END() @@ -154,229 +147,8 @@ IPC_STRUCT_TRAITS_BEGIN(content::SyntheticPointerActionListParams) IPC_STRUCT_TRAITS_MEMBER(params) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(content::InputEventAck) - IPC_STRUCT_TRAITS_MEMBER(source) - IPC_STRUCT_TRAITS_MEMBER(type) - IPC_STRUCT_TRAITS_MEMBER(state) - IPC_STRUCT_TRAITS_MEMBER(latency) - IPC_STRUCT_TRAITS_MEMBER(overscroll) - IPC_STRUCT_TRAITS_MEMBER(unique_touch_event_id) - IPC_STRUCT_TRAITS_MEMBER(touch_action) -IPC_STRUCT_TRAITS_END() - -// Sends an input event to the render widget. The input event in general -// contains a list of coalesced events and one event that is representative of -// all those events (https://w3c.github.io/pointerevents/extension.html). -IPC_MESSAGE_ROUTED4( - InputMsg_HandleInputEvent, - IPC::WebInputEventPointer /* event */, - std::vector /* coalesced events */, - ui::LatencyInfo /* latency_info */, - content::InputEventDispatchType) - -// Sends the cursor visibility state to the render widget. -IPC_MESSAGE_ROUTED1(InputMsg_CursorVisibilityChange, - bool /* is_visible */) - -// Sets the text composition to be between the given start and end offsets in -// the currently focused editable field. -IPC_MESSAGE_ROUTED3(InputMsg_SetCompositionFromExistingText, - int /* start */, - int /* end */, - std::vector /* ime_text_spans */) - -// Deletes the current selection plus the specified number of characters before -// and after the selection or caret. -IPC_MESSAGE_ROUTED2(InputMsg_ExtendSelectionAndDelete, - int /* before */, - int /* after */) - -// Deletes text before and after the current cursor position, excluding the -// selection. The lengths are supplied in Java chars (UTF-16 Code Unit), not in -// code points or in glyphs. -IPC_MESSAGE_ROUTED2(InputMsg_DeleteSurroundingText, - int /* before */, - int /* after */) - -// Deletes text before and after the current cursor position, excluding the -// selection. The lengths are supplied in code points, not in Java chars (UTF-16 -// Code Unit) or in glyphs. Does nothing if there are one or more invalid -// surrogate pairs in the requested range -IPC_MESSAGE_ROUTED2(InputMsg_DeleteSurroundingTextInCodePoints, - int /* before */, - int /* after */) - -// Selects between the given start and end offsets in the currently focused -// editable field. -IPC_MESSAGE_ROUTED2(InputMsg_SetEditableSelectionOffsets, - int /* start */, - int /* end */) - -// This message sends a string being composed with an input method. -IPC_MESSAGE_ROUTED5(InputMsg_ImeSetComposition, - base::string16, /* text */ - std::vector, /* ime_text_spans */ - gfx::Range /* replacement_range */, - int, /* selectiont_start */ - int /* selection_end */) - -// This message deletes the current composition, inserts specified text, and -// moves the cursor. -IPC_MESSAGE_ROUTED4(InputMsg_ImeCommitText, - base::string16 /* text */, - std::vector, /* ime_text_spans */ - gfx::Range /* replacement_range */, - int /* relative_cursor_pos */) - -// This message inserts the ongoing composition. -IPC_MESSAGE_ROUTED1(InputMsg_ImeFinishComposingText, bool /* keep_selection */) - -// This message notifies the renderer that the next key event is bound to one -// or more pre-defined edit commands. If the next key event is not handled -// by webkit, the specified edit commands shall be executed against current -// focused frame. -// Parameters -// * edit_commands (see chrome/common/edit_command_types.h) -// Contains one or more edit commands. -// See third_party/WebKit/Source/WebCore/editing/EditorCommand.cpp for detailed -// definition of webkit edit commands. -// -// This message must be sent just before sending a key event. -IPC_MESSAGE_ROUTED1(InputMsg_SetEditCommandsForNextKeyEvent, - std::vector /* edit_commands */) - -// Message payload is the name of a WebCore edit command to execute. -IPC_MESSAGE_ROUTED1(InputMsg_ExecuteNoValueEditCommand, std::string /* name */) - -IPC_MESSAGE_ROUTED0(InputMsg_MouseCaptureLost) - -// TODO(darin): figure out how this meshes with RestoreFocus +// TODO(dtapuska): Remove this as only OOPIF uses this IPC_MESSAGE_ROUTED1(InputMsg_SetFocus, bool /* enable */) -// Tells the renderer to scroll the currently focused node into rect only if -// the currently focused node is a Text node (textfield, text area or content -// editable divs). -IPC_MESSAGE_ROUTED1(InputMsg_ScrollFocusedEditableNodeIntoRect, gfx::Rect) - -// These messages are typically generated from context menus and request the -// renderer to apply the specified operation to the current selection. -IPC_MESSAGE_ROUTED0(InputMsg_Undo) -IPC_MESSAGE_ROUTED0(InputMsg_Redo) -IPC_MESSAGE_ROUTED0(InputMsg_Cut) -IPC_MESSAGE_ROUTED0(InputMsg_Copy) -#if defined(OS_MACOSX) -IPC_MESSAGE_ROUTED0(InputMsg_CopyToFindPboard) -#endif -IPC_MESSAGE_ROUTED0(InputMsg_Paste) -IPC_MESSAGE_ROUTED0(InputMsg_PasteAndMatchStyle) -// Replaces the selected region or a word around the cursor with the -// specified string. -IPC_MESSAGE_ROUTED1(InputMsg_Replace, - base::string16) -// Replaces the misspelling in the selected region with the specified string. -IPC_MESSAGE_ROUTED1(InputMsg_ReplaceMisspelling, - base::string16) -IPC_MESSAGE_ROUTED0(InputMsg_Delete) -IPC_MESSAGE_ROUTED0(InputMsg_SelectAll) - -IPC_MESSAGE_ROUTED0(InputMsg_CollapseSelection) - -// Requests the renderer to select the region between two points. -// Expects a SelectRange_ACK message when finished. -IPC_MESSAGE_ROUTED2(InputMsg_SelectRange, - gfx::Point /* base */, - gfx::Point /* extent */) - -// Sent by the browser to ask the renderer to adjust the selection start and -// end points by the given amounts. A negative amount moves the selection -// towards the beginning of the document, a positive amount moves the selection -// towards the end of the document. Will send show selection menu event when -// needed. -IPC_MESSAGE_ROUTED3(InputMsg_AdjustSelectionByCharacterOffset, - int /* start_adjust*/, - int /* end_adjust */, - bool /* show_selection_menu */) - -// Requests the renderer to move the selection extent point to a new position. -// Expects a MoveRangeSelectionExtent_ACK message when finished. -IPC_MESSAGE_ROUTED1(InputMsg_MoveRangeSelectionExtent, - gfx::Point /* extent */) - -// Requests the renderer to move the caret selection toward the point. -// Expects a MoveCaret_ACK message when finished. -IPC_MESSAGE_ROUTED1(InputMsg_MoveCaret, - gfx::Point /* location */) - -#if defined(OS_ANDROID) -// Request from browser to update text input state. -IPC_MESSAGE_ROUTED0(InputMsg_RequestTextInputStateUpdate) -#endif - -// Request from browser to update the cursor and composition information which -// will be sent through InputHostMsg_ImeCompositionRangeChanged. Setting -// |immediate_request| to true will lead to an immediate update. If -// |monitor_updates| is set to true then changes to text selection or regular -// updates in each compositor frame (when there is a change in composition info) -// will lead to updates being sent to the browser. -IPC_MESSAGE_ROUTED2(InputMsg_RequestCompositionUpdates, - bool /* immediate_request */, - bool /* monitor_updates */) - -// ----------------------------------------------------------------------------- -// Messages sent from the renderer to the browser. - -// Acknowledges receipt of a InputMsg_HandleInputEvent message. -IPC_MESSAGE_ROUTED1(InputHostMsg_HandleInputEvent_ACK, - content::InputEventAck /* ack */) - -// Notifies the allowed touch actions for a new touch point. -IPC_MESSAGE_ROUTED1(InputHostMsg_SetTouchAction, - cc::TouchAction /* touch_action */) - -// The whitelisted touch action and the associated unique touch event id -// for a new touch point sent by the compositor. The unique touch event id is -// only needed to verify that the whitelisted touch action is being associated -// with the correct touch event. The input event ack state is needed when -// the touchstart message was not sent to the renderer and the touch -// actions need to be reset and the touch ack timeout needs to be started. -IPC_MESSAGE_ROUTED3(InputHostMsg_SetWhiteListedTouchAction, - cc::TouchAction /* white_listed_touch_action */, - uint32_t /* unique_touch_event_id */, - content::InputEventAckState /* ack_result */) - -// Sent by the compositor when input scroll events are dropped due to bounds -// restrictions on the root scroll offset. -IPC_MESSAGE_ROUTED1(InputHostMsg_DidOverscroll, - ui::DidOverscrollParams /* params */) - -// Sent by the compositor when a fling animation is stopped. -IPC_MESSAGE_ROUTED0(InputHostMsg_DidStopFlinging) - -// Sent by the compositor when a GSB has started scrolling the viewport. -IPC_MESSAGE_ROUTED0(InputHostMsg_DidStartScrollingViewport) - -// Acknowledges receipt of a InputMsg_MoveCaret message. -IPC_MESSAGE_ROUTED0(InputHostMsg_MoveCaret_ACK) - -// Acknowledges receipt of a InputMsg_MoveRangeSelectionExtent message. -IPC_MESSAGE_ROUTED0(InputHostMsg_MoveRangeSelectionExtent_ACK) - -// Acknowledges receipt of a InputMsg_SelectRange message. -IPC_MESSAGE_ROUTED0(InputHostMsg_SelectRange_ACK) - -// Required for cancelling an ongoing input method composition. -IPC_MESSAGE_ROUTED0(InputHostMsg_ImeCancelComposition) - -// This IPC message sends the character bounds after every composition change -// to always have correct bound info. -IPC_MESSAGE_ROUTED2(InputHostMsg_ImeCompositionRangeChanged, - gfx::Range /* composition range */, - std::vector /* character bounds */) - -// Adding a new message? Stick to the sort order above: first platform -// independent InputMsg, then ifdefs for platform specific InputMsg, then -// platform independent InputHostMsg, then ifdefs for platform specific -// InputHostMsg. - #endif // CONTENT_COMMON_INPUT_MESSAGES_H_ diff --git a/chromium/content/common/inter_process_time_ticks_converter.cc b/chromium/content/common/inter_process_time_ticks_converter.cc index be36224a314..128abab37eb 100644 --- a/chromium/content/common/inter_process_time_ticks_converter.cc +++ b/chromium/content/common/inter_process_time_ticks_converter.cc @@ -4,84 +4,78 @@ #include "content/common/inter_process_time_ticks_converter.h" +#include + #include "base/logging.h" #include "base/strings/string_number_conversions.h" namespace content { InterProcessTimeTicksConverter::InterProcessTimeTicksConverter( - const LocalTimeTicks& local_lower_bound, - const LocalTimeTicks& local_upper_bound, - const RemoteTimeTicks& remote_lower_bound, - const RemoteTimeTicks& remote_upper_bound) - : remote_lower_bound_(remote_lower_bound.value_), - remote_upper_bound_(remote_upper_bound.value_) { - int64_t target_range = local_upper_bound.value_ - local_lower_bound.value_; - int64_t source_range = remote_upper_bound.value_ - remote_lower_bound.value_; - DCHECK_GE(target_range, 0); - DCHECK_GE(source_range, 0); - if (source_range <= target_range) { + LocalTimeTicks local_lower_bound, + LocalTimeTicks local_upper_bound, + RemoteTimeTicks remote_lower_bound, + RemoteTimeTicks remote_upper_bound) + : local_range_(local_upper_bound - local_lower_bound), + remote_lower_bound_(remote_lower_bound), + remote_upper_bound_(remote_upper_bound) { + RemoteTimeDelta remote_range = remote_upper_bound - remote_lower_bound; + + DCHECK_LE(LocalTimeDelta(), local_range_); + DCHECK_LE(RemoteTimeDelta(), remote_range); + + if (remote_range.ToTimeDelta() <= local_range_.ToTimeDelta()) { // We fit! Center the source range on the target range. - numerator_ = 1; - denominator_ = 1; + range_conversion_rate_ = 1.0; + base::TimeDelta diff = + local_range_.ToTimeDelta() - remote_range.ToTimeDelta(); + local_base_time_ = - local_lower_bound.value_ + (target_range - source_range) / 2; + local_lower_bound + LocalTimeDelta::FromTimeDelta(diff / 2); // When converting times, remote bounds should fall within local bounds. - DCHECK_LE(local_lower_bound.value_, - ToLocalTimeTicks(remote_lower_bound).value_); - DCHECK_GE(local_upper_bound.value_, - ToLocalTimeTicks(remote_upper_bound).value_); + DCHECK_LE(local_lower_bound, ToLocalTimeTicks(remote_lower_bound)); + DCHECK_LE(ToLocalTimeTicks(remote_upper_bound), local_upper_bound); return; } // Interpolate values so that remote range will be will exactly fit into the // local range, if possible. - numerator_ = target_range; - denominator_ = source_range; - local_base_time_ = local_lower_bound.value_; - // When converting times, remote bounds should equal local bounds. - DCHECK_EQ(local_lower_bound.value_, - ToLocalTimeTicks(remote_lower_bound).value_); - DCHECK_EQ(local_upper_bound.value_, - ToLocalTimeTicks(remote_upper_bound).value_); - DCHECK_EQ(target_range, Convert(source_range)); + DCHECK_GT(remote_range.ToTimeDelta().InMicroseconds(), 0); + range_conversion_rate_ = + static_cast(local_range_.ToTimeDelta().InMicroseconds()) / + remote_range.ToTimeDelta().InMicroseconds(); + local_base_time_ = local_lower_bound; } LocalTimeTicks InterProcessTimeTicksConverter::ToLocalTimeTicks( - const RemoteTimeTicks& remote_ms) const { + RemoteTimeTicks remote_time_ticks) const { // If input time is "null", return another "null" time. - if (remote_ms.value_ == 0) - return LocalTimeTicks(0); + if (remote_time_ticks.is_null()) + return LocalTimeTicks(); - RemoteTimeDelta remote_delta = remote_ms - remote_lower_bound_; - - DCHECK_LE(remote_ms.value_, remote_upper_bound_); - // For remote times that come before remote time range, apply just time - // offset and ignore scaling, so as to avoid extrapolation error for values - // long in the past. - if (remote_ms.value_ < remote_lower_bound_) - return LocalTimeTicks(local_base_time_ + remote_delta.value_); + RemoteTimeDelta remote_delta = remote_time_ticks - remote_lower_bound_; - return LocalTimeTicks(local_base_time_ + - ToLocalTimeDelta(remote_delta).value_); + DCHECK_LE(remote_time_ticks, remote_upper_bound_); + return local_base_time_ + ToLocalTimeDelta(remote_delta); } LocalTimeDelta InterProcessTimeTicksConverter::ToLocalTimeDelta( - const RemoteTimeDelta& remote_delta) const { - DCHECK_GE(remote_upper_bound_, remote_lower_bound_ + remote_delta.value_); - return LocalTimeDelta(Convert(remote_delta.value_)); -} + RemoteTimeDelta remote_delta) const { + DCHECK_LE(remote_lower_bound_ + remote_delta, remote_upper_bound_); -int64_t InterProcessTimeTicksConverter::Convert(int64_t value) const { - if (value <= 0) { - return value; - } - return numerator_ * value / denominator_; + // For remote times that come before remote time range, apply just time + // offset and ignore scaling, so as to avoid extrapolation error for values + // long in the past. + if (remote_delta <= RemoteTimeDelta()) + return LocalTimeDelta::FromTimeDelta(remote_delta.ToTimeDelta()); + + return std::min(local_range_, + LocalTimeDelta::FromTimeDelta(remote_delta.ToTimeDelta() * + range_conversion_rate_)); } base::TimeDelta InterProcessTimeTicksConverter::GetSkewForMetrics() const { - return base::TimeTicks::FromInternalValue(remote_lower_bound_) - - base::TimeTicks::FromInternalValue(local_base_time_); + return remote_lower_bound_.ToTimeTicks() - local_base_time_.ToTimeTicks(); } } // namespace content diff --git a/chromium/content/common/inter_process_time_ticks_converter.h b/chromium/content/common/inter_process_time_ticks_converter.h index bbefaa6aa31..cc2952e4164 100644 --- a/chromium/content/common/inter_process_time_ticks_converter.h +++ b/chromium/content/common/inter_process_time_ticks_converter.h @@ -12,10 +12,95 @@ namespace content { -class LocalTimeDelta; -class LocalTimeTicks; -class RemoteTimeDelta; -class RemoteTimeTicks; +// SiteSpecificTimeDelta is base::TimeDelta with a type tag. It it +// essentially base::TimeDelta, but SiteSpecificTimeDelta is different from +// SiteSpecificTimeDelta if T is different from U. +template +class SiteSpecificTimeDelta final { + public: + SiteSpecificTimeDelta() = default; + static SiteSpecificTimeDelta FromTimeDelta(base::TimeDelta delta) { + return SiteSpecificTimeDelta(delta); + } + static SiteSpecificTimeDelta FromMicroseconds(int64_t usec) { + return SiteSpecificTimeDelta(base::TimeDelta::FromMicroseconds(usec)); + } + + base::TimeDelta ToTimeDelta() const { return delta_; } + bool operator==(const SiteSpecificTimeDelta rhs) const { + return delta_ == rhs.delta_; + } + bool operator<(const SiteSpecificTimeDelta rhs) const { + return delta_ < rhs.delta_; + } + bool operator<=(const SiteSpecificTimeDelta rhs) const { + return delta_ <= rhs.delta_; + } + + private: + explicit SiteSpecificTimeDelta(base::TimeDelta delta) : delta_(delta) {} + + base::TimeDelta delta_; +}; + +// For logging use only. +template +std::ostream& operator<<(std::ostream& os, SiteSpecificTimeDelta delta) { + return os << delta.ToTimeDelta(); +} + +// SiteSpecificTimeTicks is base::TimeTicks with a type tag. It is +// essentially base::TimeTicks, but SiteSpecificTimeTicks is different from +// SiteSpecificTimeTicks if T is different from U. +template +class SiteSpecificTimeTicks final { + public: + SiteSpecificTimeTicks() = default; + static SiteSpecificTimeTicks FromTimeTicks(base::TimeTicks time_ticks) { + return SiteSpecificTimeTicks(time_ticks); + } + + base::TimeTicks ToTimeTicks() const { return time_ticks_; } + bool is_null() const { return time_ticks_.is_null(); } + + SiteSpecificTimeTicks operator+(SiteSpecificTimeDelta delta) const { + return SiteSpecificTimeTicks(time_ticks_ + delta.ToTimeDelta()); + } + SiteSpecificTimeDelta operator-(SiteSpecificTimeTicks rhs) const { + return SiteSpecificTimeDelta::FromTimeDelta(time_ticks_ - + rhs.time_ticks_); + } + bool operator<(const SiteSpecificTimeTicks rhs) const { + return time_ticks_ < rhs.time_ticks_; + } + bool operator==(const SiteSpecificTimeTicks rhs) const { + return time_ticks_ == rhs.time_ticks_; + } + bool operator<=(const SiteSpecificTimeTicks rhs) const { + return time_ticks_ <= rhs.time_ticks_; + } + + private: + explicit SiteSpecificTimeTicks(base::TimeTicks time_ticks) + : time_ticks_(time_ticks) {} + + base::TimeTicks time_ticks_; +}; + +// For logging use only. +template +std::ostream& operator<<(std::ostream& os, + SiteSpecificTimeTicks time_ticks) { + return os << time_ticks.ToTimeTicks(); +} + +class SiteSpecificTimeLocalTag; +using LocalTimeTicks = SiteSpecificTimeTicks; +using LocalTimeDelta = SiteSpecificTimeDelta; + +class SiteSpecificTimeRemoteTag; +using RemoteTimeTicks = SiteSpecificTimeTicks; +using RemoteTimeDelta = SiteSpecificTimeDelta; // On Windows, TimeTicks are not always consistent between processes as // indicated by |TimeTicks::IsConsistentAcrossProcesses()|. Often, the values on @@ -51,18 +136,18 @@ class RemoteTimeTicks; // local's range. Any values converted will be shifted the same amount. class CONTENT_EXPORT InterProcessTimeTicksConverter { public: - InterProcessTimeTicksConverter(const LocalTimeTicks& local_lower_bound, - const LocalTimeTicks& local_upper_bound, - const RemoteTimeTicks& remote_lower_bound, - const RemoteTimeTicks& remote_upper_bound); + InterProcessTimeTicksConverter(LocalTimeTicks local_lower_bound, + LocalTimeTicks local_upper_bound, + RemoteTimeTicks remote_lower_bound, + RemoteTimeTicks remote_upper_bound); // Returns the value within the local's bounds that correlates to // |remote_ms|. - LocalTimeTicks ToLocalTimeTicks(const RemoteTimeTicks& remote_ms) const; + LocalTimeTicks ToLocalTimeTicks(RemoteTimeTicks remote_ms) const; // Returns the equivalent delta after applying remote-to-local scaling to // |remote_delta|. - LocalTimeDelta ToLocalTimeDelta(const RemoteTimeDelta& remote_delta) const; + LocalTimeDelta ToLocalTimeDelta(RemoteTimeDelta remote_delta) const; // Returns the (remote time) - (local time) difference estimated by the // converter. This is the constant that is subtracted from remote TimeTicks to @@ -70,84 +155,14 @@ class CONTENT_EXPORT InterProcessTimeTicksConverter { base::TimeDelta GetSkewForMetrics() const; private: - int64_t Convert(int64_t value) const; - // The local time which |remote_lower_bound_| is mapped to. - int64_t local_base_time_; - - int64_t numerator_; - int64_t denominator_; - - int64_t remote_lower_bound_; - int64_t remote_upper_bound_; -}; - -class CONTENT_EXPORT LocalTimeDelta { - public: - int ToInt32() const { return value_; } - - private: - friend class InterProcessTimeTicksConverter; - friend class LocalTimeTicks; - - LocalTimeDelta(int value) : value_(value) {} - - int value_; -}; - -class CONTENT_EXPORT LocalTimeTicks { - public: - static LocalTimeTicks FromTimeTicks(const base::TimeTicks& value) { - return LocalTimeTicks(value.ToInternalValue()); - } - - base::TimeTicks ToTimeTicks() { - return base::TimeTicks::FromInternalValue(value_); - } - - LocalTimeTicks operator+(const LocalTimeDelta& delta) { - return LocalTimeTicks(value_ + delta.value_); - } - - private: - friend class InterProcessTimeTicksConverter; - - LocalTimeTicks(int64_t value) : value_(value) {} - - int64_t value_; -}; - -class CONTENT_EXPORT RemoteTimeDelta { - public: - static RemoteTimeDelta FromRawDelta(int delta) { - return RemoteTimeDelta(delta); - } - - private: - friend class InterProcessTimeTicksConverter; - friend class RemoteTimeTicks; - - RemoteTimeDelta(int value) : value_(value) {} - - int value_; -}; - -class CONTENT_EXPORT RemoteTimeTicks { - public: - static RemoteTimeTicks FromTimeTicks(const base::TimeTicks& ticks) { - return RemoteTimeTicks(ticks.ToInternalValue()); - } - - RemoteTimeDelta operator-(const RemoteTimeTicks& rhs) const { - return RemoteTimeDelta(value_ - rhs.value_); - } - - private: - friend class InterProcessTimeTicksConverter; + LocalTimeTicks local_base_time_; + LocalTimeDelta local_range_; - RemoteTimeTicks(int64_t value) : value_(value) {} + double range_conversion_rate_; - int64_t value_; + RemoteTimeTicks remote_lower_bound_; + RemoteTimeTicks remote_upper_bound_; }; } // namespace content diff --git a/chromium/content/common/inter_process_time_ticks_converter_unittest.cc b/chromium/content/common/inter_process_time_ticks_converter_unittest.cc index 0a555e02214..8b8d72157b6 100644 --- a/chromium/content/common/inter_process_time_ticks_converter_unittest.cc +++ b/chromium/content/common/inter_process_time_ticks_converter_unittest.cc @@ -16,43 +16,38 @@ namespace content { namespace { struct TestParams { - int64_t local_lower_bound; - int64_t remote_lower_bound; - int64_t remote_upper_bound; - int64_t local_upper_bound; - int64_t test_time; - int64_t test_delta; + LocalTimeTicks local_lower_bound; + RemoteTimeTicks remote_lower_bound; + RemoteTimeTicks remote_upper_bound; + LocalTimeTicks local_upper_bound; + RemoteTimeTicks test_time; + RemoteTimeDelta test_delta; }; struct TestResults { - int64_t result_time; - int32_t result_delta; + LocalTimeTicks result_time; + LocalTimeDelta result_delta; int64_t skew; }; -TestResults RunTest(const TestParams& params) { - TimeTicks local_lower_bound = TimeTicks::FromInternalValue( - params.local_lower_bound); - TimeTicks local_upper_bound = TimeTicks::FromInternalValue( - params.local_upper_bound); - TimeTicks remote_lower_bound = TimeTicks::FromInternalValue( - params.remote_lower_bound); - TimeTicks remote_upper_bound = TimeTicks::FromInternalValue( - params.remote_upper_bound); - TimeTicks test_time = TimeTicks::FromInternalValue(params.test_time); +LocalTimeTicks GetLocalTimeTicks(int64_t value) { + return LocalTimeTicks::FromTimeTicks( + base::TimeTicks() + base::TimeDelta::FromMicroseconds(value)); +} + +RemoteTimeTicks GetRemoteTimeTicks(int64_t value) { + return RemoteTimeTicks::FromTimeTicks( + base::TimeTicks() + base::TimeDelta::FromMicroseconds(value)); +} +TestResults RunTest(const TestParams& params) { InterProcessTimeTicksConverter converter( - LocalTimeTicks::FromTimeTicks(local_lower_bound), - LocalTimeTicks::FromTimeTicks(local_upper_bound), - RemoteTimeTicks::FromTimeTicks(remote_lower_bound), - RemoteTimeTicks::FromTimeTicks(remote_upper_bound)); + params.local_lower_bound, params.local_upper_bound, + params.remote_lower_bound, params.remote_upper_bound); TestResults results; - results.result_time = converter.ToLocalTimeTicks( - RemoteTimeTicks::FromTimeTicks( - test_time)).ToTimeTicks().ToInternalValue(); - results.result_delta = converter.ToLocalTimeDelta( - RemoteTimeDelta::FromRawDelta(params.test_delta)).ToInt32(); + results.result_time = converter.ToLocalTimeTicks(params.test_time); + results.result_delta = converter.ToLocalTimeDelta(params.test_delta); results.skew = converter.GetSkewForMetrics().ToInternalValue(); return results; } @@ -60,29 +55,29 @@ TestResults RunTest(const TestParams& params) { TEST(InterProcessTimeTicksConverterTest, NullTime) { // Null / zero times should remain null. TestParams p; - p.local_lower_bound = 1; - p.remote_lower_bound = 2; - p.remote_upper_bound = 5; - p.local_upper_bound = 6; - p.test_time = 0; - p.test_delta = 0; + p.local_lower_bound = GetLocalTimeTicks(1); + p.remote_lower_bound = GetRemoteTimeTicks(2); + p.remote_upper_bound = GetRemoteTimeTicks(5); + p.local_upper_bound = GetLocalTimeTicks(6); + p.test_time = GetRemoteTimeTicks(0); + p.test_delta = RemoteTimeDelta(); TestResults results = RunTest(p); - EXPECT_EQ(0, results.result_time); - EXPECT_EQ(0, results.result_delta); + EXPECT_EQ(GetLocalTimeTicks(0), results.result_time); + EXPECT_EQ(LocalTimeDelta(), results.result_delta); } TEST(InterProcessTimeTicksConverterTest, NoSkew) { // All times are monotonic and centered, so no adjustment should occur. TestParams p; - p.local_lower_bound = 1; - p.remote_lower_bound = 2; - p.remote_upper_bound = 5; - p.local_upper_bound = 6; - p.test_time = 3; - p.test_delta = 1; + p.local_lower_bound = GetLocalTimeTicks(1); + p.remote_lower_bound = GetRemoteTimeTicks(2); + p.remote_upper_bound = GetRemoteTimeTicks(5); + p.local_upper_bound = GetLocalTimeTicks(6); + p.test_time = GetRemoteTimeTicks(3); + p.test_delta = RemoteTimeDelta::FromMicroseconds(1); TestResults results = RunTest(p); - EXPECT_EQ(3, results.result_time); - EXPECT_EQ(1, results.result_delta); + EXPECT_EQ(GetLocalTimeTicks(3), results.result_time); + EXPECT_EQ(LocalTimeDelta::FromMicroseconds(1), results.result_delta); EXPECT_EQ(0, results.skew); } @@ -90,15 +85,15 @@ TEST(InterProcessTimeTicksConverterTest, OffsetMidpoints) { // All times are monotonic, but not centered. Adjust the |remote_*| times so // they are centered within the |local_*| times. TestParams p; - p.local_lower_bound = 1; - p.remote_lower_bound = 3; - p.remote_upper_bound = 6; - p.local_upper_bound = 6; - p.test_time = 4; - p.test_delta = 1; + p.local_lower_bound = GetLocalTimeTicks(1); + p.remote_lower_bound = GetRemoteTimeTicks(3); + p.remote_upper_bound = GetRemoteTimeTicks(6); + p.local_upper_bound = GetLocalTimeTicks(6); + p.test_time = GetRemoteTimeTicks(4); + p.test_delta = RemoteTimeDelta::FromMicroseconds(1); TestResults results = RunTest(p); - EXPECT_EQ(3, results.result_time); - EXPECT_EQ(1, results.result_delta); + EXPECT_EQ(GetLocalTimeTicks(3), results.result_time); + EXPECT_EQ(LocalTimeDelta::FromMicroseconds(1), results.result_delta); EXPECT_EQ(1, results.skew); } @@ -109,15 +104,15 @@ TEST(InterProcessTimeTicksConverterTest, DoubleEndedSkew) { // doesn't change. The ratio of local time to network time is 1:2, so we scale // |test_delta| to half. TestParams p; - p.local_lower_bound = 3; - p.remote_lower_bound = 1; - p.remote_upper_bound = 9; - p.local_upper_bound = 7; - p.test_time = 5; - p.test_delta = 2; + p.local_lower_bound = GetLocalTimeTicks(3); + p.remote_lower_bound = GetRemoteTimeTicks(1); + p.remote_upper_bound = GetRemoteTimeTicks(9); + p.local_upper_bound = GetLocalTimeTicks(7); + p.test_time = GetRemoteTimeTicks(5); + p.test_delta = RemoteTimeDelta::FromMicroseconds(2); TestResults results = RunTest(p); - EXPECT_EQ(5, results.result_time); - EXPECT_EQ(1, results.result_delta); + EXPECT_EQ(GetLocalTimeTicks(5), results.result_time); + EXPECT_EQ(LocalTimeDelta::FromMicroseconds(1), results.result_delta); } TEST(InterProcessTimeTicksConverterTest, FrontEndSkew) { @@ -126,30 +121,30 @@ TEST(InterProcessTimeTicksConverterTest, FrontEndSkew) { // but since we use integers, the numbers truncate from 3.33 to 3 and 1.33 // to 1. TestParams p; - p.local_lower_bound = 3; - p.remote_lower_bound = 1; - p.remote_upper_bound = 7; - p.local_upper_bound = 7; - p.test_time = 3; - p.test_delta = 2; + p.local_lower_bound = GetLocalTimeTicks(3); + p.remote_lower_bound = GetRemoteTimeTicks(1); + p.remote_upper_bound = GetRemoteTimeTicks(7); + p.local_upper_bound = GetLocalTimeTicks(7); + p.test_time = GetRemoteTimeTicks(3); + p.test_delta = RemoteTimeDelta::FromMicroseconds(2); TestResults results = RunTest(p); - EXPECT_EQ(4, results.result_time); - EXPECT_EQ(1, results.result_delta); + EXPECT_EQ(GetLocalTimeTicks(4), results.result_time); + EXPECT_EQ(LocalTimeDelta::FromMicroseconds(1), results.result_delta); } TEST(InterProcessTimeTicksConverterTest, BackEndSkew) { // Like the previous test, but |remote_lower_bound| is coherent and // |remote_upper_bound| is skewed. TestParams p; - p.local_lower_bound = 1; - p.remote_lower_bound = 1; - p.remote_upper_bound = 7; - p.local_upper_bound = 5; - p.test_time = 3; - p.test_delta = 2; + p.local_lower_bound = GetLocalTimeTicks(1); + p.remote_lower_bound = GetRemoteTimeTicks(1); + p.remote_upper_bound = GetRemoteTimeTicks(7); + p.local_upper_bound = GetLocalTimeTicks(5); + p.test_time = GetRemoteTimeTicks(3); + p.test_delta = RemoteTimeDelta::FromMicroseconds(2); TestResults results = RunTest(p); - EXPECT_EQ(2, results.result_time); - EXPECT_EQ(1, results.result_delta); + EXPECT_EQ(GetLocalTimeTicks(2), results.result_time); + EXPECT_EQ(LocalTimeDelta::FromMicroseconds(1), results.result_delta); } TEST(InterProcessTimeTicksConverterTest, Instantaneous) { @@ -157,15 +152,15 @@ TEST(InterProcessTimeTicksConverterTest, Instantaneous) { // |remote_upper_bound| have the same value. No adjustments should be made and // no divide-by-zero errors should occur. TestParams p; - p.local_lower_bound = 1; - p.remote_lower_bound = 2; - p.remote_upper_bound = 2; - p.local_upper_bound = 3; - p.test_time = 2; - p.test_delta = 0; + p.local_lower_bound = GetLocalTimeTicks(1); + p.remote_lower_bound = GetRemoteTimeTicks(2); + p.remote_upper_bound = GetRemoteTimeTicks(2); + p.local_upper_bound = GetLocalTimeTicks(3); + p.test_time = GetRemoteTimeTicks(2); + p.test_delta = RemoteTimeDelta(); TestResults results = RunTest(p); - EXPECT_EQ(2, results.result_time); - EXPECT_EQ(0, results.result_delta); + EXPECT_EQ(GetLocalTimeTicks(2), results.result_time); + EXPECT_EQ(LocalTimeDelta(), results.result_delta); } TEST(InterProcessTimeTicksConverterTest, OffsetInstantaneous) { @@ -174,15 +169,15 @@ TEST(InterProcessTimeTicksConverterTest, OffsetInstantaneous) { // of |local_lower_bound| and |local_upper_bound|. An offset should be applied // to make the midpoints line up. TestParams p; - p.local_lower_bound = 1; - p.remote_lower_bound = 3; - p.remote_upper_bound = 3; - p.local_upper_bound = 3; - p.test_time = 3; - p.test_delta = 0; + p.local_lower_bound = GetLocalTimeTicks(1); + p.remote_lower_bound = GetRemoteTimeTicks(3); + p.remote_upper_bound = GetRemoteTimeTicks(3); + p.local_upper_bound = GetLocalTimeTicks(3); + p.test_time = GetRemoteTimeTicks(3); + p.test_delta = RemoteTimeDelta(); TestResults results = RunTest(p); - EXPECT_EQ(2, results.result_time); - EXPECT_EQ(0, results.result_delta); + EXPECT_EQ(GetLocalTimeTicks(2), results.result_time); + EXPECT_EQ(LocalTimeDelta(), results.result_delta); } TEST(InterProcessTimeTicksConverterTest, DisjointInstantaneous) { @@ -191,15 +186,15 @@ TEST(InterProcessTimeTicksConverterTest, DisjointInstantaneous) { // local_upper_bound]. So, all of the values should be adjusted so they are // exactly that value. TestParams p; - p.local_lower_bound = 1; - p.remote_lower_bound = 2; - p.remote_upper_bound = 2; - p.local_upper_bound = 1; - p.test_time = 2; - p.test_delta = 0; + p.local_lower_bound = GetLocalTimeTicks(1); + p.remote_lower_bound = GetRemoteTimeTicks(2); + p.remote_upper_bound = GetRemoteTimeTicks(2); + p.local_upper_bound = GetLocalTimeTicks(1); + p.test_time = GetRemoteTimeTicks(2); + p.test_delta = RemoteTimeDelta(); TestResults results = RunTest(p); - EXPECT_EQ(1, results.result_time); - EXPECT_EQ(0, results.result_delta); + EXPECT_EQ(GetLocalTimeTicks(1), results.result_time); + EXPECT_EQ(LocalTimeDelta(), results.result_delta); } TEST(InterProcessTimeTicksConverterTest, RoundingNearEdges) { @@ -209,37 +204,69 @@ TEST(InterProcessTimeTicksConverterTest, RoundingNearEdges) { for (int i = 1; i < kMaxRange; ++i) { for (int j = 1; j < kMaxRange; ++j) { TestParams p; - p.local_lower_bound = 1; - p.remote_lower_bound = 1; - p.remote_upper_bound = j; - p.local_upper_bound = i; + p.local_lower_bound = GetLocalTimeTicks(1); + p.remote_lower_bound = GetRemoteTimeTicks(1); + p.remote_upper_bound = GetRemoteTimeTicks(j); + p.local_upper_bound = GetLocalTimeTicks(i); - p.test_time = 1; - p.test_delta = 0; + p.test_time = GetRemoteTimeTicks(1); + p.test_delta = RemoteTimeDelta(); TestResults results = RunTest(p); - EXPECT_LE(1, results.result_time); - EXPECT_EQ(0, results.result_delta); + EXPECT_LE(GetLocalTimeTicks(1), results.result_time); + EXPECT_EQ(LocalTimeDelta(), results.result_delta); - p.test_time = j; - p.test_delta = j - 1; + p.test_time = GetRemoteTimeTicks(j); + p.test_delta = RemoteTimeDelta::FromMicroseconds(j - 1); results = RunTest(p); - EXPECT_GE(i, results.result_time); - EXPECT_GE(i - 1, results.result_delta); + EXPECT_LE(results.result_time, GetLocalTimeTicks(i)); + EXPECT_LE(results.result_delta, LocalTimeDelta::FromMicroseconds(i - 1)); } } } TEST(InterProcessTimeTicksConverterTest, DisjointRanges) { TestParams p; - p.local_lower_bound = 10; - p.remote_lower_bound = 30; - p.remote_upper_bound = 41; - p.local_upper_bound = 20; - p.test_time = 41; - p.test_delta = 0; + p.local_lower_bound = GetLocalTimeTicks(10); + p.remote_lower_bound = GetRemoteTimeTicks(30); + p.remote_upper_bound = GetRemoteTimeTicks(41); + p.local_upper_bound = GetLocalTimeTicks(20); + p.test_time = GetRemoteTimeTicks(41); + p.test_delta = RemoteTimeDelta(); + TestResults results = RunTest(p); + EXPECT_EQ(GetLocalTimeTicks(20), results.result_time); + EXPECT_EQ(LocalTimeDelta(), results.result_delta); +} + +TEST(InterProcessTimeTicksConverterTest, LargeValue_LocalIsLargetThanRemote) { + constexpr auto kWeek = base::TimeTicks::kMicrosecondsPerWeek; + constexpr auto kHour = base::TimeTicks::kMicrosecondsPerHour; + TestParams p; + p.local_lower_bound = GetLocalTimeTicks(4 * kWeek); + p.remote_lower_bound = GetRemoteTimeTicks(4 * kWeek + 2 * kHour); + p.remote_upper_bound = GetRemoteTimeTicks(4 * kWeek + 4 * kHour); + p.local_upper_bound = GetLocalTimeTicks(4 * kWeek + 8 * kHour); + + p.test_time = GetRemoteTimeTicks(4 * kWeek + 3 * kHour); + p.test_delta = RemoteTimeDelta(); + TestResults results = RunTest(p); + EXPECT_EQ(GetLocalTimeTicks(4 * kWeek + 4 * kHour), results.result_time); + EXPECT_EQ(LocalTimeDelta(), results.result_delta); +} + +TEST(InterProcessTimeTicksConverterTest, LargeValue_RemoteIsLargetThanLocal) { + constexpr auto kWeek = base::TimeTicks::kMicrosecondsPerWeek; + constexpr auto kHour = base::TimeTicks::kMicrosecondsPerHour; + TestParams p; + p.local_lower_bound = GetLocalTimeTicks(4 * kWeek); + p.remote_lower_bound = GetRemoteTimeTicks(5 * kWeek); + p.remote_upper_bound = GetRemoteTimeTicks(5 * kWeek + 2 * kHour); + p.local_upper_bound = GetLocalTimeTicks(4 * kWeek + kHour); + + p.test_time = GetRemoteTimeTicks(5 * kWeek + kHour); + p.test_delta = RemoteTimeDelta(); TestResults results = RunTest(p); - EXPECT_EQ(20, results.result_time); - EXPECT_EQ(0, results.result_delta); + EXPECT_EQ(GetLocalTimeTicks(4 * kWeek + kHour / 2), results.result_time); + EXPECT_EQ(LocalTimeDelta(), results.result_delta); } TEST(InterProcessTimeTicksConverterTest, ValuesOutsideOfRange) { diff --git a/chromium/content/common/manifest_share_target_util_unittest.cc b/chromium/content/common/manifest_share_target_util_unittest.cc deleted file mode 100644 index edfc6767ea7..00000000000 --- a/chromium/content/common/manifest_share_target_util_unittest.cc +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "content/public/common/manifest_share_target_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" - -namespace content { -namespace { - -constexpr char kTitle[] = "My title"; -constexpr char kText[] = "My text"; -constexpr char kUrlSpec[] = "https://www.google.com/"; - -} // namespace - -TEST(ManifestShareTargetUtilTest, ReplaceUrlPlaceholdersInvalidTemplate) { - // Badly nested placeholders. - GURL url_template = GURL("http://example.com/?q={"); - EXPECT_FALSE(ValidateWebShareUrlTemplate(url_template)); - EXPECT_FALSE( - ReplaceWebShareUrlPlaceholders(url_template, "", "", GURL(), nullptr)); - - url_template = GURL("http://example.com/?q={title"); - EXPECT_FALSE(ValidateWebShareUrlTemplate(url_template)); - EXPECT_FALSE( - ReplaceWebShareUrlPlaceholders(url_template, "", "", GURL(), nullptr)); - - url_template = GURL("http://example.com/?q={title{text}}"); - EXPECT_FALSE(ValidateWebShareUrlTemplate(url_template)); - EXPECT_FALSE( - ReplaceWebShareUrlPlaceholders(url_template, "", "", GURL(), nullptr)); - - url_template = GURL("http://example.com/?q={title{}"); - EXPECT_FALSE(ValidateWebShareUrlTemplate(url_template)); - EXPECT_FALSE( - ReplaceWebShareUrlPlaceholders(url_template, "", "", GURL(), nullptr)); - - url_template = GURL("http://example.com/?q={{title}}"); - EXPECT_FALSE(ValidateWebShareUrlTemplate(url_template)); - EXPECT_FALSE( - ReplaceWebShareUrlPlaceholders(url_template, "", "", GURL(), nullptr)); - - // Placeholder with non-identifier character. - url_template = GURL("http://example.com/?q={title?}"); - EXPECT_FALSE(ValidateWebShareUrlTemplate(url_template)); - EXPECT_FALSE( - ReplaceWebShareUrlPlaceholders(url_template, "", "", GURL(), nullptr)); - - // Placeholder with digit character. - url_template = GURL("http://example.com/?q={title1}"); - EXPECT_TRUE(ValidateWebShareUrlTemplate(url_template)); - EXPECT_TRUE( - ReplaceWebShareUrlPlaceholders(url_template, "", "", GURL(), nullptr)); - - // Empty placeholder. - url_template = GURL("http://example.com/?q={}"); - EXPECT_TRUE(ValidateWebShareUrlTemplate(url_template)); - EXPECT_TRUE( - ReplaceWebShareUrlPlaceholders(url_template, "", "", GURL(), nullptr)); - - // Invalid placeholder in URL fragment. - url_template = GURL("http://example.com/#{title?}"); - EXPECT_FALSE(ValidateWebShareUrlTemplate(url_template)); - EXPECT_FALSE( - ReplaceWebShareUrlPlaceholders(url_template, "", "", GURL(), nullptr)); - - // { in path. - url_template = GURL("http://example.com/subpath{/"); - EXPECT_TRUE(ValidateWebShareUrlTemplate(url_template)); - EXPECT_TRUE( - ReplaceWebShareUrlPlaceholders(url_template, "", "", GURL(), nullptr)); - - // Invalid placeholder. Non-empty title, text, share URL and non-empty output - // parameter. - GURL url_template_filled; - url_template = GURL("http://example.com/?q={"); - EXPECT_FALSE(ReplaceWebShareUrlPlaceholders(url_template, "text", "title", - GURL("http://www.google.com"), - &url_template_filled)); -} - -TEST(ManifestShareTargetUtilTest, ReplaceWebShareUrlPlaceholders) { - const GURL kUrl(kUrlSpec); - - // No placeholders. - GURL url_template = GURL("http://example.com/?q=a#a"); - GURL url_template_filled; - bool succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, - kUrl, &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ(url_template, url_template_filled); - - // One title placeholder. - url_template = GURL("http://example.com/#{title}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#My%20title", url_template_filled.spec()); - - // One text placeholder. - url_template = GURL("http://example.com/#{text}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#My%20text", url_template_filled.spec()); - - // One url placeholder. - url_template = GURL("http://example.com/#{url}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#https%3A%2F%2Fwww.google.com%2F", - url_template_filled.spec()); - - // One of each placeholder, in title, text, url order. - url_template = GURL("http://example.com/#{title}{text}{url}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ( - "http://example.com/#My%20titleMy%20texthttps%3A%2F%2Fwww.google.com%2F", - url_template_filled.spec()); - - // One of each placeholder, in url, text, title order. - url_template = GURL("http://example.com/#{url}{text}{title}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ( - "http://example.com/#https%3A%2F%2Fwww.google.com%2FMy%20textMy%20title", - url_template_filled.spec()); - - // Two of each placeholder, some next to each other, others not. - url_template = - GURL("http://example.com/#{title}{url}{text}{text}{title}{url}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ( - "http://example.com/" - "#My%20titlehttps%3A%2F%2Fwww.google.com%2FMy%20textMy%20textMy%" - "20titlehttps%3A%2F%2Fwww.google.com%2F", - url_template_filled.spec()); - - // Placeholders are in a query string, as values. The expected use case. - // Two of each placeholder, some next to each other, others not. - url_template = GURL( - "http://example.com?title={title}&url={url}&text={text}&text={text}&" - "title={title}&url={url}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ( - "http://" - "example.com/?title=My%20title&url=https%3A%2F%2Fwww.google.com%2F&" - "text=My%20text&" - "text=My%20text&title=My%20title&url=https%3A%2F%2Fwww.google.com%2F", - url_template_filled.spec()); - - // Empty placeholder. - url_template = GURL("http://example.com/#{}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#", url_template_filled.spec()); - - // Unexpected placeholders. - url_template = GURL("http://example.com/#{nonexistentplaceholder}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#", url_template_filled.spec()); - - // Placeholders should only be replaced in query and fragment. - url_template = GURL("http://example.com/subpath{title}/?q={title}#{title}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/subpath%7Btitle%7D/?q=My%20title#My%20title", - url_template_filled.spec()); - - // Braces in the path, which would be invalid, but should parse fine as they - // are escaped. - url_template = GURL("http://example.com/subpath{/?q={title}"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/subpath%7B/?q=My%20title", - url_template_filled.spec()); - - // |url_template| with % escapes. - url_template = GURL("http://example.com#%20{title}%20"); - succeeded = ReplaceWebShareUrlPlaceholders(url_template, kTitle, kText, kUrl, - &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#%20My%20title%20", url_template_filled.spec()); -} - -// Test URL escaping done by ReplaceWebShareUrlPlaceholders(). -TEST(ManifestShareTargetUtilTest, ReplaceWebShareUrlPlaceholders_Escaping) { - const GURL kUrl(kUrlSpec); - const GURL kUrlTemplate("http://example.com/#{title}"); - - // Share data that contains percent escapes. - GURL url_template_filled; - bool succeeded = ReplaceWebShareUrlPlaceholders( - kUrlTemplate, "My%20title", kText, kUrl, &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#My%2520title", url_template_filled.spec()); - - // Share data that contains placeholders. These should not be replaced. - succeeded = ReplaceWebShareUrlPlaceholders(kUrlTemplate, "{title}", kText, - kUrl, &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#%7Btitle%7D", url_template_filled.spec()); - - // All characters that shouldn't be escaped. - succeeded = ReplaceWebShareUrlPlaceholders(kUrlTemplate, - "-_.!~*'()0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz", - kText, kUrl, &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ( - "http://example.com/#-_.!~*'()0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz", - url_template_filled.spec()); - - // All characters that should be escaped. - succeeded = - ReplaceWebShareUrlPlaceholders(kUrlTemplate, " \"#$%&+,/:;<=>?@[\\]^`{|}", - kText, kUrl, &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ( - "http://example.com/" - "#%20%22%23%24%25%26%2B%2C%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%60%7B%7C%" - "7D", - url_template_filled.spec()); - - // Unicode chars. - // U+263B - succeeded = ReplaceWebShareUrlPlaceholders(kUrlTemplate, "\xe2\x98\xbb", - kText, kUrl, &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#%E2%98%BB", url_template_filled.spec()); - - // U+00E9 - succeeded = ReplaceWebShareUrlPlaceholders(kUrlTemplate, "\xc3\xa9", kText, - kUrl, &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#%C3%A9", url_template_filled.spec()); - - // U+1F4A9 - succeeded = ReplaceWebShareUrlPlaceholders(kUrlTemplate, "\xf0\x9f\x92\xa9", - kText, kUrl, &url_template_filled); - EXPECT_TRUE(succeeded); - EXPECT_EQ("http://example.com/#%F0%9F%92%A9", url_template_filled.spec()); -} - -} // namespace content diff --git a/chromium/content/common/media/audio_messages.h b/chromium/content/common/media/audio_messages.h deleted file mode 100644 index c4c2f3535af..00000000000 --- a/chromium/content/common/media/audio_messages.h +++ /dev/null @@ -1,136 +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_COMMON_MEDIA_AUDIO_MESSAGES_H_ -#define CONTENT_COMMON_MEDIA_AUDIO_MESSAGES_H_ - -// IPC messages for the audio. - -#include - -#include - -#include "base/memory/shared_memory.h" -#include "base/sync_socket.h" -#include "content/common/content_export.h" -#include "ipc/ipc_message_macros.h" -#include "media/audio/audio_input_ipc.h" -#include "media/audio/audio_output_ipc.h" -#include "media/base/ipc/media_param_traits.h" -#include "media/base/ipc/media_param_traits_macros.h" -#include "media/gpu/ipc/common/media_param_traits.h" -#include "url/origin.h" - -#undef IPC_MESSAGE_EXPORT -#define IPC_MESSAGE_EXPORT CONTENT_EXPORT -#define IPC_MESSAGE_START AudioMsgStart - -IPC_STRUCT_BEGIN(AudioInputHostMsg_CreateStream_Config) - IPC_STRUCT_MEMBER(media::AudioParameters, params) - IPC_STRUCT_MEMBER(bool, automatic_gain_control) - IPC_STRUCT_MEMBER(uint32_t, shared_memory_count) -IPC_STRUCT_END() - -// Messages sent from the browser to the renderer. - -// Tell the renderer process that an audio output device has been authorized -// for a given stream. The renderer is given the output parameters for the -// authorized device. -IPC_MESSAGE_CONTROL4(AudioMsg_NotifyDeviceAuthorized, - int /* stream id */, - media::OutputDeviceStatus /* device_status */, - media::AudioParameters /* output parameters */, - std::string /* matched_device_id */) - -// Tell the renderer process that an audio stream has been created. -// The renderer process is given a shared memory handle for the audio data -// buffer it shares with the browser process. It is also given a SyncSocket that -// it uses to communicate with the browser process about the state of the -// buffered audio data. -IPC_MESSAGE_CONTROL(AudioMsg_NotifyStreamCreated, - int /* stream id */, - base::SharedMemoryHandle /* handle */, - base::SyncSocket::TransitDescriptor /* socket descriptor */) - -// Tell the renderer process that an audio input stream has been created. -// The renderer process would be given a SyncSocket that it should read from -// from then on. It is also given whether the stream initially is muted. -IPC_MESSAGE_CONTROL(AudioInputMsg_NotifyStreamCreated, - int /* stream id */, - base::SharedMemoryHandle /* handle */, - base::SyncSocket::TransitDescriptor /* socket descriptor */, - bool /* initially muted */) - -// Notification message sent from AudioRendererHost to renderer for state -// update on error. -IPC_MESSAGE_CONTROL1(AudioMsg_NotifyStreamError, int /* stream id */) - -// Notification message sent from browser to renderer for state update. -IPC_MESSAGE_CONTROL1(AudioInputMsg_NotifyStreamError, int /* stream id */) - -// Notification message sent from browser to renderer when stream mutes or -// unmutes. -IPC_MESSAGE_CONTROL2(AudioInputMsg_NotifyStreamMuted, - int /* stream id */, - bool /* is muted? */) - -// Messages sent from the renderer to the browser. - -// Message sent to the browser to request the use of an audio output -// device. |render_frame_id| is the routing ID for the RenderFrame producing -// the audio data. -IPC_MESSAGE_CONTROL5(AudioHostMsg_RequestDeviceAuthorization, - int /* stream_id */, - int /* render_frame_id */, - int /* session_id */, - std::string /* device_id */, - url::Origin /* security_origin */) - -// Request that is sent to the browser for creating an audio output stream. -IPC_MESSAGE_CONTROL3(AudioHostMsg_CreateStream, - int /* stream_id */, - int /* render_frame_id */, - media::AudioParameters /* params */) - -// Request that is sent to the browser for creating an audio input stream. -// |render_frame_id| is the routing ID for the RenderFrame consuming the audio -// data. -IPC_MESSAGE_CONTROL4(AudioInputHostMsg_CreateStream, - int /* stream_id */, - int /* render_frame_id */, - int /* session_id */, - AudioInputHostMsg_CreateStream_Config) - -// Start buffering and play the audio stream specified by stream_id. -IPC_MESSAGE_CONTROL1(AudioHostMsg_PlayStream, - int /* stream_id */) - -// Start recording the audio input stream specified by stream_id. -IPC_MESSAGE_CONTROL1(AudioInputHostMsg_RecordStream, - int /* stream_id */) - -// Pause the audio stream specified by stream_id. -IPC_MESSAGE_CONTROL1(AudioHostMsg_PauseStream, - int /* stream_id */) - -// Close an audio stream specified by stream_id. -IPC_MESSAGE_CONTROL1(AudioHostMsg_CloseStream, - int /* stream_id */) - -// Close an audio input stream specified by stream_id. -IPC_MESSAGE_CONTROL1(AudioInputHostMsg_CloseStream, - int /* stream_id */) - -// Set audio volume of the stream specified by stream_id. -// TODO(hclam): change this to vector if we have channel numbers other than 2. -IPC_MESSAGE_CONTROL2(AudioHostMsg_SetVolume, - int /* stream_id */, - double /* volume */) - -// Set audio volume of the input stream specified by stream_id. -IPC_MESSAGE_CONTROL2(AudioInputHostMsg_SetVolume, - int /* stream_id */, - double /* volume */) - -#endif // CONTENT_COMMON_MEDIA_AUDIO_MESSAGES_H_ diff --git a/chromium/content/common/media/cdm_info.cc b/chromium/content/common/media/cdm_info.cc index 12b98dc56d1..4e3dbbc1e71 100644 --- a/chromium/content/common/media/cdm_info.cc +++ b/chromium/content/common/media/cdm_info.cc @@ -9,15 +9,17 @@ namespace content { -CdmInfo::CdmInfo(const std::string& name, - const std::string& guid, - const base::Version& version, - const base::FilePath& path, - const std::string& file_system_id, - const std::vector& supported_video_codecs, - bool supports_persistent_license, - const std::string& supported_key_system, - bool supports_sub_key_systems) +CdmInfo::CdmInfo( + const std::string& name, + const std::string& guid, + const base::Version& version, + const base::FilePath& path, + const std::string& file_system_id, + const std::vector& supported_video_codecs, + bool supports_persistent_license, + const base::flat_set& supported_encryption_schemes, + const std::string& supported_key_system, + bool supports_sub_key_systems) : name(name), guid(guid), version(version), @@ -25,13 +27,15 @@ CdmInfo::CdmInfo(const std::string& name, file_system_id(file_system_id), supported_video_codecs(supported_video_codecs), supports_persistent_license(supports_persistent_license), + supported_encryption_schemes(supported_encryption_schemes), supported_key_system(supported_key_system), supports_sub_key_systems(supports_sub_key_systems) { DCHECK(base::IsValidGUID(guid)); + DCHECK(!supported_encryption_schemes.empty()); } CdmInfo::CdmInfo(const CdmInfo& other) = default; -CdmInfo::~CdmInfo() {} +CdmInfo::~CdmInfo() = default; } // namespace content diff --git a/chromium/content/common/media/media_devices.cc b/chromium/content/common/media/media_devices.cc index 90488d037ad..b2ed9e4e016 100644 --- a/chromium/content/common/media/media_devices.cc +++ b/chromium/content/common/media/media_devices.cc @@ -17,11 +17,12 @@ MediaDeviceInfo::MediaDeviceInfo(MediaDeviceInfo&& other) = default; MediaDeviceInfo::MediaDeviceInfo(const std::string& device_id, const std::string& label, - const std::string& group_id) + const std::string& group_id, + media::VideoFacingMode video_facing) : device_id(device_id), label(label), group_id(group_id), - video_facing(media::VideoFacingMode::MEDIA_VIDEO_FACING_NONE) {} + video_facing(video_facing) {} MediaDeviceInfo::MediaDeviceInfo( const media::AudioDeviceDescription& device_description) @@ -44,9 +45,11 @@ MediaDeviceInfo& MediaDeviceInfo::operator=(const MediaDeviceInfo& other) = MediaDeviceInfo& MediaDeviceInfo::operator=(MediaDeviceInfo&& other) = default; bool operator==(const MediaDeviceInfo& first, const MediaDeviceInfo& second) { - return first.device_id == second.device_id && first.label == second.label && - first.group_id == second.group_id && - first.video_facing == second.video_facing; + // Do not use the |group_id| and |video_facing| fields for equality comparison + // since they are currently not fully supported by the video-capture layer. + // The modification of those fields by heuristics in upper layers does not + // result in a different device. + return first.device_id == second.device_id && first.label == second.label; } } // namespace content diff --git a/chromium/content/common/media/media_devices.h b/chromium/content/common/media/media_devices.h index c0cd354d258..0e3de71288f 100644 --- a/chromium/content/common/media/media_devices.h +++ b/chromium/content/common/media/media_devices.h @@ -14,7 +14,7 @@ namespace media { struct AudioDeviceDescription; struct VideoCaptureDeviceDescriptor; -} +} // namespace media namespace content { @@ -29,9 +29,11 @@ struct CONTENT_EXPORT MediaDeviceInfo { MediaDeviceInfo(); MediaDeviceInfo(const MediaDeviceInfo& other); MediaDeviceInfo(MediaDeviceInfo&& other); - MediaDeviceInfo(const std::string& device_id, - const std::string& label, - const std::string& group_id); + MediaDeviceInfo( + const std::string& device_id, + const std::string& label, + const std::string& group_id, + media::VideoFacingMode video_facing = media::MEDIA_VIDEO_FACING_NONE); explicit MediaDeviceInfo(const media::AudioDeviceDescription& description); explicit MediaDeviceInfo( const media::VideoCaptureDeviceDescriptor& descriptor); diff --git a/chromium/content/common/media/media_player_delegate_messages.h b/chromium/content/common/media/media_player_delegate_messages.h index 7fd75b9430d..451ecbd4fbb 100644 --- a/chromium/content/common/media/media_player_delegate_messages.h +++ b/chromium/content/common/media/media_player_delegate_messages.h @@ -13,6 +13,7 @@ #include +#include "components/viz/common/surfaces/surface_id.h" #include "content/common/content_export.h" #include "ipc/ipc_message_macros.h" #include "media/base/media_content_type.h" @@ -55,6 +56,26 @@ IPC_MESSAGE_ROUTED2(MediaPlayerDelegateMsg_BecamePersistentVideo, int /* delegate_id, distinguishes instances */, double /* is_persistent */) +IPC_MESSAGE_ROUTED1(MediaPlayerDelegateMsg_EndPictureInPictureMode, + int /* delegate_id, distinguishes instances */) + +IPC_MESSAGE_ROUTED2(MediaPlayerDelegateMsg_OnPictureInPictureWindowResize, + int /* delegate_id, distinguishes instances */, + gfx::Size /* window_size */) + +// ---------------------------------------------------------------------------- +// Messages from the browser to the renderer acknowledging changes happened. +// ---------------------------------------------------------------------------- + +IPC_MESSAGE_ROUTED3(MediaPlayerDelegateMsg_OnPictureInPictureModeStarted_ACK, + int /* delegate id */, + int /* request_id */, + gfx::Size /* window_size */) + +IPC_MESSAGE_ROUTED2(MediaPlayerDelegateMsg_OnPictureInPictureModeEnded_ACK, + int /* delegate id */, + int /* request_id */) + // ---------------------------------------------------------------------------- // Messages from the renderer notifying the browser of playback state changes. // ---------------------------------------------------------------------------- @@ -86,10 +107,19 @@ IPC_MESSAGE_ROUTED2(MediaPlayerDelegateHostMsg_OnMediaSizeChanged, int /* delegate_id, distinguishes instances */, gfx::Size /* new size of video */) -IPC_MESSAGE_ROUTED1(MediaPlayerDelegateHostMsg_OnPictureInPictureSourceChanged, - int /* delegate id */) +IPC_MESSAGE_ROUTED4(MediaPlayerDelegateHostMsg_OnPictureInPictureModeStarted, + int /* delegate id */, + viz::SurfaceId /* surface_id */, + gfx::Size /* natural_size */, + int /* request_id */) + +IPC_MESSAGE_ROUTED2(MediaPlayerDelegateHostMsg_OnPictureInPictureModeEnded, + int /* delegate id */, + int /* request_id */) -IPC_MESSAGE_ROUTED1(MediaPlayerDelegateHostMsg_OnPictureInPictureModeEnded, - int /* delegate id */) +IPC_MESSAGE_ROUTED3(MediaPlayerDelegateHostMsg_OnPictureInPictureSurfaceChanged, + int /* delegate id */, + viz::SurfaceId /* surface_id */, + gfx::Size /* natural_size */) #endif // CONTENT_COMMON_MEDIA_MEDIA_PLAYER_DELEGATE_MESSAGES_H_ diff --git a/chromium/content/common/media/media_stream_param_traits.h b/chromium/content/common/media/media_stream_param_traits.h index 146f5ceca9f..3b58a31792e 100644 --- a/chromium/content/common/media/media_stream_param_traits.h +++ b/chromium/content/common/media/media_stream_param_traits.h @@ -3,7 +3,7 @@ // found in the LICENSE file. // IPC messages for the media streaming. -// Multiply-included message file, hence no include guard. +// no-include-guard-because-multiply-included #include "content/common/content_export.h" #include "content/public/common/media_stream_request.h" @@ -23,6 +23,7 @@ IPC_ENUM_TRAITS_MAX_VALUE(media::VideoFacingMode, IPC_STRUCT_TRAITS_BEGIN(content::MediaStreamDevice) IPC_STRUCT_TRAITS_MEMBER(type) IPC_STRUCT_TRAITS_MEMBER(id) + IPC_STRUCT_TRAITS_MEMBER(group_id) IPC_STRUCT_TRAITS_MEMBER(video_facing) IPC_STRUCT_TRAITS_MEMBER(matched_output_device_id) IPC_STRUCT_TRAITS_MEMBER(name) diff --git a/chromium/content/common/media/renderer_audio_input_stream_factory.mojom b/chromium/content/common/media/renderer_audio_input_stream_factory.mojom index 01a060ff526..63594e29fa6 100644 --- a/chromium/content/common/media/renderer_audio_input_stream_factory.mojom +++ b/chromium/content/common/media/renderer_audio_input_stream_factory.mojom @@ -8,6 +8,7 @@ import "media/mojo/interfaces/audio_data_pipe.mojom"; import "media/mojo/interfaces/audio_input_stream.mojom"; import "media/mojo/interfaces/audio_parameters.mojom"; import "media/mojo/interfaces/media_types.mojom"; +import "mojo/public/mojom/base/unguessable_token.mojom"; // This interface is used by the renderer to ask the browser to create input // streams. The renderer supplies the desired audio parameters, and a client @@ -20,11 +21,22 @@ interface RendererAudioInputStreamFactory { media.mojom.AudioParameters params, bool automatic_gain_control, uint32 shared_memory_count); + + // Associates an output device with an input stream, so that the input knows + // which output device to cancel echo from. |input_stream_id| is the id + // returned when the stream was created. |output_device_id| is a device + // id HMAC. In case either of the parameters are invalid, the operation will + // silently fail. + AssociateInputAndOutputForAec( + mojo_base.mojom.UnguessableToken input_stream_id, + string output_device_id); }; interface RendererAudioInputStreamFactoryClient { // Called when a stream has been created. Will only be called once for every - // CreateStream call. + // CreateStream call. |stream_id| is a handle used to refer to the stream, + // specifically to be able to associate it with an output device for echo + // cancellation. Loopback streams don't have ids. // TODO(crbug.com/787806): There are plans to allow this function to be called // serveral times in the future. If the stream is terminated e.g. due to the // process hosting it crashing, this function should be called again with a @@ -33,5 +45,6 @@ interface RendererAudioInputStreamFactoryClient { media.mojom.AudioInputStream stream, media.mojom.AudioInputStreamClient& client_request, media.mojom.AudioDataPipe data_pipe, - bool initially_muted); + bool initially_muted, + mojo_base.mojom.UnguessableToken? stream_id); }; diff --git a/chromium/content/common/native_types.mojom b/chromium/content/common/native_types.mojom index 59aa3bf9d0f..08151055452 100644 --- a/chromium/content/common/native_types.mojom +++ b/chromium/content/common/native_types.mojom @@ -18,7 +18,7 @@ struct FrameReplicationState; struct RendererPreferences; [Native] -struct ResizeParams; +struct VisualProperties; // NOTE: This type is only mapped and usable on Mac. [Native] diff --git a/chromium/content/common/native_types.typemap b/chromium/content/common/native_types.typemap index 780ba7aa63b..35e7fbb50f7 100644 --- a/chromium/content/common/native_types.typemap +++ b/chromium/content/common/native_types.typemap @@ -8,8 +8,8 @@ public_headers = [ "//content/common/edit_command.h", "//content/common/frame_owner_properties.h", "//content/common/frame_replication_state.h", - "//content/common/resize_params.h", "//content/common/input/input_event.h", + "//content/common/visual_properties.h", "//content/public/common/input_event_ack_source.h", "//content/public/common/input_event_ack_state.h", "//content/common/input/synthetic_pinch_gesture_params.h", @@ -46,7 +46,6 @@ public_deps = [ # includes from //content/common and //content/public/common, this isn't a # transitive allowance, so those targets' own public_deps aren't included in # the set of implied dependencies. - "//cc/ipc", "//content/common:buildflags", "//media", "//media/base/ipc", @@ -78,7 +77,7 @@ type_mappings = [ "content.mojom.DidOverscrollParams=ui::DidOverscrollParams", "content.mojom.PointerType=blink::WebPointerProperties::PointerType", "content.mojom.RendererPreferences=content::RendererPreferences", - "content.mojom.ResizeParams=content::ResizeParams", + "content.mojom.VisualProperties=content::VisualProperties", "content.mojom.ScrollUnits=blink::WebGestureEvent::ScrollUnits", "content.mojom.SyntheticSmoothDrag=content::SyntheticSmoothDragGestureParams", "content.mojom.SyntheticSmoothScroll=content::SyntheticSmoothScrollGestureParams", diff --git a/chromium/content/common/native_types_mac.typemap b/chromium/content/common/native_types_mac.typemap index c162fc14e21..c35085535e7 100644 --- a/chromium/content/common/native_types_mac.typemap +++ b/chromium/content/common/native_types_mac.typemap @@ -16,7 +16,6 @@ deps = [ # includes from //content/common and //content/public/common, this isn't a # transitive allowance, so those targets' own public_deps aren't included in # the set of implied dependencies. - "//cc/ipc", "//media", "//media/base/ipc", "//net", diff --git a/chromium/content/common/navigation_params.cc b/chromium/content/common/navigation_params.cc index 6d18f9fefd8..e11e92e86c9 100644 --- a/chromium/content/common/navigation_params.cc +++ b/chromium/content/common/navigation_params.cc @@ -45,7 +45,8 @@ CommonNavigationParams::CommonNavigationParams( CSPDisposition should_check_main_world_csp, bool started_from_context_menu, bool has_user_gesture, - const base::Optional& suggested_filename) + const std::vector& initiator_csp, + const base::Optional& initiator_self_source) : url(url), referrer(referrer), transition(transition), @@ -64,7 +65,8 @@ CommonNavigationParams::CommonNavigationParams( should_check_main_world_csp(should_check_main_world_csp), started_from_context_menu(started_from_context_menu), has_user_gesture(has_user_gesture), - suggested_filename(suggested_filename) { + initiator_csp(initiator_csp), + initiator_self_source(initiator_self_source) { // |method != "POST"| should imply absence of |post_data|. if (method != "POST" && post_data) { NOTREACHED(); diff --git a/chromium/content/common/navigation_params.h b/chromium/content/common/navigation_params.h index 806969a8e25..e571195b1cb 100644 --- a/chromium/content/common/navigation_params.h +++ b/chromium/content/common/navigation_params.h @@ -15,6 +15,7 @@ #include "base/time/time.h" #include "build/build_config.h" #include "content/common/content_export.h" +#include "content/common/content_security_policy/content_security_policy.h" #include "content/common/content_security_policy/csp_disposition_enum.h" #include "content/common/frame_message_enums.h" #include "content/common/service_worker/service_worker_types.h" @@ -25,6 +26,7 @@ #include "content/public/common/request_context_type.h" #include "net/url_request/redirect_info.h" #include "services/network/public/cpp/resource_request_body.h" +#include "services/network/public/cpp/resource_response.h" #include "services/network/public/cpp/resource_response_info.h" #include "third_party/blink/public/platform/web_mixed_content_context_type.h" #include "ui/base/page_transition_types.h" @@ -76,7 +78,8 @@ struct CONTENT_EXPORT CommonNavigationParams { CSPDisposition should_check_main_world_csp, bool started_from_context_menu, bool has_user_gesture, - const base::Optional& suggested_filename); + const std::vector& initiator_csp, + const base::Optional& initiator_self_source); CommonNavigationParams(const CommonNavigationParams& other); ~CommonNavigationParams(); @@ -161,10 +164,9 @@ struct CONTENT_EXPORT CommonNavigationParams { // True if the request was user initiated. bool has_user_gesture = false; - // If the navigation started in response to a HTML anchor element with a - // download attribute, this is the (possible empty) value of the download - // attribute. - base::Optional suggested_filename; + // We require a copy of the relevant CSP to perform navigation checks. + std::vector initiator_csp; + base::Optional initiator_self_source; }; // Provided by the browser ----------------------------------------------------- @@ -211,7 +213,7 @@ struct CONTENT_EXPORT RequestNavigationParams { std::vector redirects; // The ResourceResponseInfos received during redirects. - std::vector redirect_response; + std::vector redirect_response; // PlzNavigate // The RedirectInfos received during redirects. diff --git a/chromium/content/common/navigation_subresource_loader_params.cc b/chromium/content/common/navigation_subresource_loader_params.cc index b465c0f73f8..7c08e6bdda4 100644 --- a/chromium/content/common/navigation_subresource_loader_params.cc +++ b/chromium/content/common/navigation_subresource_loader_params.cc @@ -19,6 +19,7 @@ SubresourceLoaderParams& SubresourceLoaderParams::operator=( loader_factory_info = std::move(other.loader_factory_info); controller_service_worker_info = std::move(other.controller_service_worker_info); + controller_service_worker_handle = other.controller_service_worker_handle; return *this; } diff --git a/chromium/content/common/navigation_subresource_loader_params.h b/chromium/content/common/navigation_subresource_loader_params.h index 2ffa1287594..bb3407579f7 100644 --- a/chromium/content/common/navigation_subresource_loader_params.h +++ b/chromium/content/common/navigation_subresource_loader_params.h @@ -5,11 +5,14 @@ #ifndef CONTENT_COMMON_NAVIGATION_SUBRESOURCE_LOADER_PARAMS_H_ #define CONTENT_COMMON_NAVIGATION_SUBRESOURCE_LOADER_PARAMS_H_ +#include "base/memory/weak_ptr.h" #include "content/common/service_worker/controller_service_worker.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" namespace content { +class ServiceWorkerHandle; + // For NetworkService glues: // Navigation parameters that are necessary to set-up a subresource loader // for the frame that is going to be created by the navigation. @@ -28,7 +31,13 @@ struct CONTENT_EXPORT SubresourceLoaderParams { // The controller service worker, non-null if the frame is to be // controlled by the service worker. + // + // |controller_service_worker_info->object_info| is "incomplete". It must be + // updated before being sent over Mojo and then registered with + // |controller_service_worker_handle|. See + // ServiceWorkerHandle::CreateIncompleteObjectInfo() for details. mojom::ControllerServiceWorkerInfoPtr controller_service_worker_info; + base::WeakPtr controller_service_worker_handle; }; } // namespace content diff --git a/chromium/content/common/notifications/notification_struct_traits.cc b/chromium/content/common/notifications/notification_struct_traits.cc index 3e639b63f1d..6c8fa6bb2b7 100644 --- a/chromium/content/common/notifications/notification_struct_traits.cc +++ b/chromium/content/common/notifications/notification_struct_traits.cc @@ -35,7 +35,7 @@ bool ValidateActions( bool ValidateData(const std::vector& data) { return data.size() <= - content::PlatformNotificationData::kMaximumDeveloperDataSize; + blink::mojom::NotificationData::kMaximumDeveloperDataSize; } bool ValidateImage(const SkBitmap& image) { diff --git a/chromium/content/common/origin_trials/OWNERS b/chromium/content/common/origin_trials/OWNERS deleted file mode 100644 index 47b509bfe3e..00000000000 --- a/chromium/content/common/origin_trials/OWNERS +++ /dev/null @@ -1,13 +0,0 @@ -# This file also covers ownership of the following directories: -# //chrome/common/origin_trials/ -# //content/renderer/origin_trials/ -# //third_party/blink/common/origin_trials/ -# //third_party/blink/public/common/origin_trials/ -# //tools/origin_trials/ - -chasej@chromium.org -iclelland@chromium.org -mek@chromium.org - -# TEAM: experimentation-dev@chromium.org -# COMPONENT: Internals>OriginTrials diff --git a/chromium/content/common/origin_trials/trial_policy_impl.cc b/chromium/content/common/origin_trials/trial_policy_impl.cc deleted file mode 100644 index e85ee25b245..00000000000 --- a/chromium/content/common/origin_trials/trial_policy_impl.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/origin_trials/trial_policy_impl.h" - -#include "base/feature_list.h" -#include "content/public/common/content_client.h" -#include "content/public/common/content_features.h" -#include "content/public/common/origin_trial_policy.h" -#include "content/public/common/origin_util.h" -#include "third_party/blink/public/common/origin_trials/trial_token_validator.h" - -namespace content { - -bool TrialPolicyImpl::IsOriginTrialsSupported() const { - // In order for the validator to work these are all required. - return base::FeatureList::IsEnabled(features::kOriginTrials) && policy() && - !GetPublicKey().empty(); -} -base::StringPiece TrialPolicyImpl::GetPublicKey() const { - return policy()->GetPublicKey(); -} -bool TrialPolicyImpl::IsFeatureDisabled(base::StringPiece feature) const { - return policy()->IsFeatureDisabled(feature); -} -bool TrialPolicyImpl::IsTokenDisabled(base::StringPiece token_signature) const { - return policy()->IsTokenDisabled(token_signature); -} -bool TrialPolicyImpl::IsOriginSecure(const GURL& url) const { - return ::content::IsOriginSecure(url); -} - -const OriginTrialPolicy* TrialPolicyImpl::policy() const { - return GetContentClient()->GetOriginTrialPolicy(); -} - -std::unique_ptr -TrialPolicyImpl::CreateValidatorForPolicy() { - return std::make_unique( - std::make_unique()); -} - -} // namespace content diff --git a/chromium/content/common/origin_trials/trial_policy_impl.h b/chromium/content/common/origin_trials/trial_policy_impl.h deleted file mode 100644 index 370c5067b31..00000000000 --- a/chromium/content/common/origin_trials/trial_policy_impl.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_POLICY_IMPL_H_ -#define CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_POLICY_IMPL_H_ - -#include "base/strings/string_piece.h" -#include "content/common/content_export.h" -#include "third_party/blink/public/common/origin_trials/trial_policy.h" - -namespace blink { -class TrialTokenValidator; -} // namespace blink - -namespace content { - -class OriginTrialPolicy; - -// TrialPolicyImpl is an adaptor to fit the policy provided by the content -// embedder via ContentClient to the interface allowed by the DEPS rules of -// third_party/WebKit/public/common -// TODO(avallee, kinuko): Plumb the the content embedder policy straight through -// to the users in third_party/WebKit/public/common/origin_trials. -class CONTENT_EXPORT TrialPolicyImpl : public blink::TrialPolicy { - public: - bool IsOriginTrialsSupported() const override; - - base::StringPiece GetPublicKey() const override; - bool IsFeatureDisabled(base::StringPiece feature) const override; - bool IsTokenDisabled(base::StringPiece token_signature) const override; - bool IsOriginSecure(const GURL& url) const override; - - static std::unique_ptr CreateValidatorForPolicy(); - - private: - const OriginTrialPolicy* policy() const; -}; - -} // namespace content - -#endif // CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_POLICY_IMPL_H_ diff --git a/chromium/content/common/origin_util.cc b/chromium/content/common/origin_util.cc index 66516d0fed2..6cac476bd4b 100644 --- a/chromium/content/common/origin_util.cc +++ b/chromium/content/common/origin_util.cc @@ -7,6 +7,7 @@ #include "base/lazy_instance.h" #include "base/macros.h" #include "base/stl_util.h" +#include "base/strings/pattern.h" #include "content/common/url_schemes.h" #include "net/base/url_util.h" #include "url/gurl.h" @@ -23,6 +24,17 @@ bool IsOriginUnique(const url::Origin& origin) { base::ContainsValue(url::GetNoAccessSchemes(), origin.scheme()); } +bool IsWhitelistedSecureOrigin(const url::Origin& origin) { + if (base::ContainsValue(content::GetSecureOriginsAndPatterns(), + origin.Serialize())) + return true; + for (const auto& origin_or_pattern : content::GetSecureOriginsAndPatterns()) { + if (base::MatchPattern(origin.host(), origin_or_pattern)) + return true; + } + return false; +} + } // namespace namespace content { @@ -42,10 +54,7 @@ bool IsOriginSecure(const GURL& url) { if (base::ContainsValue(url::GetSecureSchemes(), url.scheme())) return true; - if (base::ContainsValue(GetSecureOrigins(), url::Origin::Create(url))) - return true; - - return false; + return IsWhitelistedSecureOrigin(url::Origin::Create(url)); } bool OriginCanAccessServiceWorkers(const GURL& url) { @@ -74,10 +83,7 @@ bool IsPotentiallyTrustworthyOrigin(const url::Origin& origin) { return true; } - if (base::ContainsValue(GetSecureOrigins(), origin)) - return true; - - return false; + return IsWhitelistedSecureOrigin(origin); } } // namespace content diff --git a/chromium/content/common/page_state_serialization_unittest.cc b/chromium/content/common/page_state_serialization_unittest.cc index ffd1a65d0f8..79251f47f77 100644 --- a/chromium/content/common/page_state_serialization_unittest.cc +++ b/chromium/content/common/page_state_serialization_unittest.cc @@ -203,7 +203,7 @@ class PageStateSerializationTest : public testing::Test { int version, ExplodedPageState* page_state) { base::FilePath path; - PathService::Get(content::DIR_TEST_DATA, &path); + base::PathService::Get(content::DIR_TEST_DATA, &path); path = path.AppendASCII("page_state") .AppendASCII( base::StringPrintf("serialized_%s.dat", suffix.c_str())); @@ -396,7 +396,7 @@ TEST_F(PageStateSerializationTest, LegacyEncodePageStateFrozen) { LegacyEncodePageStateForTesting(actual_state, 25, &actual_encoded_state); base::FilePath path; - PathService::Get(content::DIR_TEST_DATA, &path); + base::PathService::Get(content::DIR_TEST_DATA, &path); path = path.AppendASCII("page_state").AppendASCII("serialized_v25.dat"); std::string file_contents; @@ -459,7 +459,7 @@ TEST_F(PageStateSerializationTest, DumpExpectedPageStateForBackwardsCompat) { base::Base64Encode(encoded, &base64); base::FilePath path; - PathService::Get(base::DIR_TEMP, &path); + base::PathService::Get(base::DIR_TEMP, &path); path = path.AppendASCII("expected.dat"); FILE* fp = base::OpenFile(path, "wb"); diff --git a/chromium/content/common/platform_notification_messages.h b/chromium/content/common/platform_notification_messages.h index 507b4fb0061..b925cdcd4fd 100644 --- a/chromium/content/common/platform_notification_messages.h +++ b/chromium/content/common/platform_notification_messages.h @@ -17,19 +17,10 @@ #include "content/public/common/platform_notification_data.h" #include "ipc/ipc_message_macros.h" -// Singly-included section for type definitions. -#ifndef INTERNAL_CONTENT_COMMON_PLATFORM_NOTIFICATION_MESSAGES_H_ -#define INTERNAL_CONTENT_COMMON_PLATFORM_NOTIFICATION_MESSAGES_H_ - -// Defines the pair of [notification id] => [notification data] used when -// getting the notifications for a given Service Worker registration. -using PersistentNotificationInfo = - std::pair; - -#endif // INTERNAL_CONTENT_COMMON_PLATFORM_NOTIFICATION_MESSAGES_H_ - #define IPC_MESSAGE_START PlatformNotificationMsgStart +// TODO(https://crbug.com/841329): Delete this legacy IPC code, use a pure +// mojo struct instead from ServiceWorkerEventDispatcher mojo interface. IPC_ENUM_TRAITS_MAX_VALUE( content::PlatformNotificationData::Direction, content::PlatformNotificationData::DIRECTION_LAST) @@ -63,48 +54,4 @@ IPC_STRUCT_TRAITS_BEGIN(content::PlatformNotificationData) IPC_STRUCT_TRAITS_MEMBER(actions) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(content::NotificationResources) - IPC_STRUCT_TRAITS_MEMBER(image) - IPC_STRUCT_TRAITS_MEMBER(notification_icon) - IPC_STRUCT_TRAITS_MEMBER(badge) - IPC_STRUCT_TRAITS_MEMBER(action_icons) -IPC_STRUCT_TRAITS_END() - -// Messages sent from the browser to the renderer. - -// Reply to PlatformNotificationHostMsg_ShowPersistent indicating that a -// persistent notification has been shown on the platform (if |success| is -// true), or that an unspecified error occurred. -IPC_MESSAGE_CONTROL2(PlatformNotificationMsg_DidShowPersistent, - int /* request_id */, - bool /* success */) - -// Reply to PlatformNotificationHostMsg_GetNotifications sharing a vector of -// available notifications per the request's constraints. -IPC_MESSAGE_CONTROL2(PlatformNotificationMsg_DidGetNotifications, - int /* request_id */, - std::vector - /* notifications */) - -// Messages sent from the renderer to the browser. - -IPC_MESSAGE_CONTROL5( - PlatformNotificationHostMsg_ShowPersistent, - int /* request_id */, - int64_t /* service_worker_registration_id */, - GURL /* origin */, - content::PlatformNotificationData /* notification_data */, - content::NotificationResources /* notification_resources */) - -IPC_MESSAGE_CONTROL4(PlatformNotificationHostMsg_GetNotifications, - int /* request_id */, - int64_t /* service_worker_registration_id */, - GURL /* origin */, - std::string /* filter_tag */) - -IPC_MESSAGE_CONTROL3(PlatformNotificationHostMsg_ClosePersistent, - GURL /* origin */, - std::string /* tag */, - std::string /* notification_id */) - #endif // CONTENT_COMMON_PLATFORM_NOTIFICATION_MESSAGES_H_ diff --git a/chromium/content/common/possibly_associated_interface_ptr.h b/chromium/content/common/possibly_associated_interface_ptr.h index f49afcef703..5c70a46e086 100644 --- a/chromium/content/common/possibly_associated_interface_ptr.h +++ b/chromium/content/common/possibly_associated_interface_ptr.h @@ -6,6 +6,7 @@ #define CONTENT_COMMON_POSSIBLY_ASSOCIATED_INTERFACE_PTR_H_ #include "base/macros.h" +#include "base/single_thread_task_runner.h" #include "content/common/possibly_associated_interface_ptr_info.h" #include "mojo/public/cpp/bindings/associated_interface_ptr.h" #include "mojo/public/cpp/bindings/interface_ptr.h" diff --git a/chromium/content/common/possibly_associated_wrapper_shared_url_loader_factory.h b/chromium/content/common/possibly_associated_wrapper_shared_url_loader_factory.h new file mode 100644 index 00000000000..d162a359c0d --- /dev/null +++ b/chromium/content/common/possibly_associated_wrapper_shared_url_loader_factory.h @@ -0,0 +1,18 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_COMMON_POSSIBLY_ASSOCIATED_WRAPPER_SHARED_URL_LOADER_FACTORY_H_ +#define CONTENT_COMMON_POSSIBLY_ASSOCIATED_WRAPPER_SHARED_URL_LOADER_FACTORY_H_ + +#include "content/common/possibly_associated_interface_ptr.h" +#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" + +namespace content { + +using PossiblyAssociatedWrapperSharedURLLoaderFactory = + network::WrapperSharedURLLoaderFactoryBase; + +} // namespace content + +#endif // CONTENT_COMMON_POSSIBLY_ASSOCIATED_WRAPPER_SHARED_URL_LOADER_FACTORY_H_ diff --git a/chromium/content/common/presentation/presentation.typemap b/chromium/content/common/presentation/presentation.typemap index e6d6a8c0f1e..f69f4453021 100644 --- a/chromium/content/common/presentation/presentation.typemap +++ b/chromium/content/common/presentation/presentation.typemap @@ -3,20 +3,10 @@ # found in the LICENSE file. mojom = "//third_party/blink/public/platform/modules/presentation/presentation.mojom" -public_headers = [ - "//content/public/common/presentation_connection_message.h", - "//content/public/common/presentation_info.h", -] +public_headers = [ "//content/public/common/presentation_connection_message.h" ] traits_headers = [ "//content/common/presentation/presentation_struct_traits.h" ] deps = [ "//url", ] -type_mappings = [ - "blink.mojom.PresentationConnectionCloseReason=content::PresentationConnectionCloseReason", - "blink.mojom.PresentationConnectionMessage=content::PresentationConnectionMessage[move_only]", - "blink.mojom.PresentationConnectionState=content::PresentationConnectionState", - "blink.mojom.PresentationError=content::PresentationError", - "blink.mojom.PresentationErrorType=content::PresentationErrorType", - "blink.mojom.PresentationInfo=content::PresentationInfo", -] +type_mappings = [ "blink.mojom.PresentationConnectionMessage=content::PresentationConnectionMessage[move_only]" ] diff --git a/chromium/content/common/presentation/presentation_struct_traits.cc b/chromium/content/common/presentation/presentation_struct_traits.cc index 1db986d74c4..484a772a84e 100644 --- a/chromium/content/common/presentation/presentation_struct_traits.cc +++ b/chromium/content/common/presentation/presentation_struct_traits.cc @@ -8,40 +8,6 @@ namespace mojo { -bool StructTraits:: - Read(blink::mojom::PresentationInfoDataView data, - content::PresentationInfo* out) { - if (!data.ReadUrl(&(out->presentation_url)) || - !data.ReadId(&(out->presentation_id))) { - return false; - } - - if (out->presentation_id.empty() || - !base::IsStringASCII(out->presentation_id) || - out->presentation_id.length() > content::PresentationInfo::kMaxIdLength) { - return false; - } - return true; -} - -bool StructTraits:: - Read(blink::mojom::PresentationErrorDataView data, - content::PresentationError* out) { - if (!data.ReadErrorType(&(out->error_type)) || - !data.ReadMessage(&(out->message))) { - return false; - } - - if (!base::IsStringUTF8(out->message) || - out->message.length() > content::PresentationError::kMaxMessageLength) { - return false; - } - - return true; -} - bool UnionTraits:: Read(blink::mojom::PresentationConnectionMessageDataView data, diff --git a/chromium/content/common/presentation/presentation_struct_traits.h b/chromium/content/common/presentation/presentation_struct_traits.h index 13d406efedf..01e5e1ab535 100644 --- a/chromium/content/common/presentation/presentation_struct_traits.h +++ b/chromium/content/common/presentation/presentation_struct_traits.h @@ -12,167 +12,11 @@ #include "base/optional.h" #include "base/strings/string_util.h" #include "content/public/common/presentation_connection_message.h" -#include "content/public/common/presentation_info.h" #include "third_party/blink/public/platform/modules/presentation/presentation.mojom.h" #include "url/mojom/url.mojom.h" namespace mojo { -template <> -struct EnumTraits { - static blink::mojom::PresentationErrorType ToMojom( - content::PresentationErrorType input) { - switch (input) { - case content::PRESENTATION_ERROR_NO_AVAILABLE_SCREENS: - return blink::mojom::PresentationErrorType::NO_AVAILABLE_SCREENS; - case content::PRESENTATION_ERROR_PRESENTATION_REQUEST_CANCELLED: - return blink::mojom::PresentationErrorType:: - PRESENTATION_REQUEST_CANCELLED; - case content::PRESENTATION_ERROR_NO_PRESENTATION_FOUND: - return blink::mojom::PresentationErrorType::NO_PRESENTATION_FOUND; - case content::PRESENTATION_ERROR_PREVIOUS_START_IN_PROGRESS: - return blink::mojom::PresentationErrorType::PREVIOUS_START_IN_PROGRESS; - case content::PRESENTATION_ERROR_UNKNOWN: - return blink::mojom::PresentationErrorType::UNKNOWN; - } - NOTREACHED() << "Unknown content::PresentationErrorType " - << static_cast(input); - return blink::mojom::PresentationErrorType::UNKNOWN; - } - - static bool FromMojom(blink::mojom::PresentationErrorType input, - content::PresentationErrorType* output) { - switch (input) { - case blink::mojom::PresentationErrorType::NO_AVAILABLE_SCREENS: - *output = content::PRESENTATION_ERROR_NO_AVAILABLE_SCREENS; - return true; - case blink::mojom::PresentationErrorType::PRESENTATION_REQUEST_CANCELLED: - *output = content::PRESENTATION_ERROR_PRESENTATION_REQUEST_CANCELLED; - return true; - case blink::mojom::PresentationErrorType::NO_PRESENTATION_FOUND: - *output = content::PRESENTATION_ERROR_NO_PRESENTATION_FOUND; - return true; - case blink::mojom::PresentationErrorType::PREVIOUS_START_IN_PROGRESS: - *output = content::PRESENTATION_ERROR_PREVIOUS_START_IN_PROGRESS; - return true; - case blink::mojom::PresentationErrorType::UNKNOWN: - *output = content::PRESENTATION_ERROR_UNKNOWN; - return true; - } - return false; - } -}; - -template <> -struct EnumTraits { - static blink::mojom::PresentationConnectionState ToMojom( - content::PresentationConnectionState input) { - switch (input) { - case content::PRESENTATION_CONNECTION_STATE_CONNECTING: - return blink::mojom::PresentationConnectionState::CONNECTING; - case content::PRESENTATION_CONNECTION_STATE_CONNECTED: - return blink::mojom::PresentationConnectionState::CONNECTED; - case content::PRESENTATION_CONNECTION_STATE_CLOSED: - return blink::mojom::PresentationConnectionState::CLOSED; - case content::PRESENTATION_CONNECTION_STATE_TERMINATED: - return blink::mojom::PresentationConnectionState::TERMINATED; - } - NOTREACHED() << "Unknown content::PresentationConnectionState " - << static_cast(input); - return blink::mojom::PresentationConnectionState::TERMINATED; - } - - static bool FromMojom(blink::mojom::PresentationConnectionState input, - content::PresentationConnectionState* output) { - switch (input) { - case blink::mojom::PresentationConnectionState::CONNECTING: - *output = content::PRESENTATION_CONNECTION_STATE_CONNECTING; - return true; - case blink::mojom::PresentationConnectionState::CONNECTED: - *output = content::PRESENTATION_CONNECTION_STATE_CONNECTED; - return true; - case blink::mojom::PresentationConnectionState::CLOSED: - *output = content::PRESENTATION_CONNECTION_STATE_CLOSED; - return true; - case blink::mojom::PresentationConnectionState::TERMINATED: - *output = content::PRESENTATION_CONNECTION_STATE_TERMINATED; - return true; - } - return false; - } -}; - -template <> -struct EnumTraits { - static blink::mojom::PresentationConnectionCloseReason ToMojom( - content::PresentationConnectionCloseReason input) { - switch (input) { - case content::PRESENTATION_CONNECTION_CLOSE_REASON_CONNECTION_ERROR: - return blink::mojom::PresentationConnectionCloseReason:: - CONNECTION_ERROR; - case content::PRESENTATION_CONNECTION_CLOSE_REASON_CLOSED: - return blink::mojom::PresentationConnectionCloseReason::CLOSED; - case content::PRESENTATION_CONNECTION_CLOSE_REASON_WENT_AWAY: - return blink::mojom::PresentationConnectionCloseReason::WENT_AWAY; - } - NOTREACHED() << "Unknown content::PresentationConnectionCloseReason " - << static_cast(input); - return blink::mojom::PresentationConnectionCloseReason::CONNECTION_ERROR; - } - - static bool FromMojom(blink::mojom::PresentationConnectionCloseReason input, - content::PresentationConnectionCloseReason* output) { - switch (input) { - case blink::mojom::PresentationConnectionCloseReason::CONNECTION_ERROR: - *output = - content::PRESENTATION_CONNECTION_CLOSE_REASON_CONNECTION_ERROR; - return true; - case blink::mojom::PresentationConnectionCloseReason::CLOSED: - *output = content::PRESENTATION_CONNECTION_CLOSE_REASON_CLOSED; - return true; - case blink::mojom::PresentationConnectionCloseReason::WENT_AWAY: - *output = content::PRESENTATION_CONNECTION_CLOSE_REASON_WENT_AWAY; - return true; - } - return false; - } -}; - -template <> -struct StructTraits { - static const GURL& url(const content::PresentationInfo& presentation_info) { - return presentation_info.presentation_url; - } - - static const std::string& id( - const content::PresentationInfo& presentation_info) { - return presentation_info.presentation_id; - } - - static bool Read(blink::mojom::PresentationInfoDataView data, - content::PresentationInfo* out); -}; - -template <> -struct StructTraits { - static content::PresentationErrorType error_type( - const content::PresentationError& error) { - return error.error_type; - } - - static const std::string& message(const content::PresentationError& error) { - return error.message; - } - - static bool Read(blink::mojom::PresentationErrorDataView data, - content::PresentationError* out); -}; - template <> struct UnionTraits { diff --git a/chromium/content/common/render_frame_metadata.mojom b/chromium/content/common/render_frame_metadata.mojom index a2283f9d152..28573c16b7b 100644 --- a/chromium/content/common/render_frame_metadata.mojom +++ b/chromium/content/common/render_frame_metadata.mojom @@ -4,6 +4,7 @@ module content.mojom; +import "services/viz/public/interfaces/compositing/local_surface_id.mojom"; import "services/viz/public/interfaces/compositing/selection.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; @@ -25,6 +26,32 @@ struct RenderFrameMetadata { // Selection region relative to the current viewport. If the selection is // empty or otherwise unused, the bound types will indicate such. viz.mojom.Selection selection; + + // Determines whether the page is mobile optimized or not, which means at + // least one of the following has to be true: + // - page has a width=device-width or narrower viewport. + // - page prevents zooming in or out (i.e. min and max page scale factors + // are the same). + bool is_mobile_optimized; + + // The device scale factor used to generate CompositorFrame. + float device_scale_factor; + + // The size of the viewport used to generate a CompositorFrame. + gfx.mojom.Size viewport_size_in_pixels; + + // The last viz::LocalSurfaceId used to submit a CompositorFrame. + viz.mojom.LocalSurfaceId? local_surface_id; + + // Used to position the Android location top bar and page content, whose + // precise position is computed by the renderer compositor. + float top_controls_height; + float top_controls_shown_ratio; + + // Used to position Android bottom bar, whose position is computed by the + // renderer compositor. + float bottom_controls_height; + float bottom_controls_shown_ratio; }; // This interface is provided by the renderer. It can optionally enable diff --git a/chromium/content/common/render_frame_metadata_struct_traits.cc b/chromium/content/common/render_frame_metadata_struct_traits.cc index e6129ebf056..f17bfdf991d 100644 --- a/chromium/content/common/render_frame_metadata_struct_traits.cc +++ b/chromium/content/common/render_frame_metadata_struct_traits.cc @@ -17,8 +17,16 @@ bool StructTraitsroot_background_color = data.root_background_color(); out->is_scroll_offset_at_top = data.is_scroll_offset_at_top(); + out->is_mobile_optimized = data.is_mobile_optimized(); + out->device_scale_factor = data.device_scale_factor(); + out->top_controls_height = data.top_controls_height(); + out->top_controls_shown_ratio = data.top_controls_shown_ratio(); + out->bottom_controls_height = data.bottom_controls_height(); + out->bottom_controls_shown_ratio = data.bottom_controls_shown_ratio(); return data.ReadRootScrollOffset(&out->root_scroll_offset) && - data.ReadSelection(&out->selection); + data.ReadSelection(&out->selection) && + data.ReadViewportSizeInPixels(&out->viewport_size_in_pixels) && + data.ReadLocalSurfaceId(&out->local_surface_id); } } // namespace mojo diff --git a/chromium/content/common/render_frame_metadata_struct_traits.h b/chromium/content/common/render_frame_metadata_struct_traits.h index de13e7a88c9..fbf17edfa65 100644 --- a/chromium/content/common/render_frame_metadata_struct_traits.h +++ b/chromium/content/common/render_frame_metadata_struct_traits.h @@ -8,6 +8,7 @@ #include "base/optional.h" #include "cc/trees/render_frame_metadata.h" #include "content/common/render_frame_metadata.mojom-shared.h" +#include "services/viz/public/cpp/compositing/local_surface_id_struct_traits.h" namespace mojo { @@ -33,6 +34,42 @@ struct StructTraits& local_surface_id( + const cc::RenderFrameMetadata& metadata) { + return metadata.local_surface_id; + } + + static float top_controls_height(const cc::RenderFrameMetadata& metadata) { + return metadata.top_controls_height; + } + + static float top_controls_shown_ratio( + const cc::RenderFrameMetadata& metadata) { + return metadata.top_controls_shown_ratio; + } + + static float bottom_controls_height(const cc::RenderFrameMetadata& metadata) { + return metadata.bottom_controls_height; + } + + static float bottom_controls_shown_ratio( + const cc::RenderFrameMetadata& metadata) { + return metadata.bottom_controls_shown_ratio; + } + static bool Read(content::mojom::RenderFrameMetadataDataView data, cc::RenderFrameMetadata* out); }; diff --git a/chromium/content/common/renderer.mojom b/chromium/content/common/renderer.mojom index 05886a72fce..2405622dde3 100644 --- a/chromium/content/common/renderer.mojom +++ b/chromium/content/common/renderer.mojom @@ -80,16 +80,7 @@ struct CreateViewParams { bool has_committed_real_load; // The initial renderer size. - ResizeParams initial_size; - - // Whether to enable auto-resize. - bool enable_auto_resize; - - // The minimum size to layout the page if auto-resize is enabled. - gfx.mojom.Size min_size; - - // The maximum size to layout the page if auto-resize is enabled. - gfx.mojom.Size max_size; + VisualProperties visual_properties; // The page zoom level. double page_zoom_level; @@ -190,13 +181,6 @@ interface Renderer { // Tells the renderer to create a new RenderFrame. CreateFrame(CreateFrameParams params); - // Tells the renderer to create an EmbeddedWorkerInstanceClient, which is what - // manages service worker startup and shutdown. - // TODO(shimazu): Create a service worker's execution context by this method - // instead of just creating an instance of EmbeddedWorkerInstanceClient. - SetUpEmbeddedWorkerChannelForServiceWorker( - associated EmbeddedWorkerInstanceClient& client_request); - // Tells the renderer to create a new RenderFrameProxy object with // |routing_id|. |render_view_routing_id| identifies the // RenderView to be associated with this proxy. The new proxy's opener should @@ -212,6 +196,13 @@ interface Renderer { FrameReplicationState replication_state, mojo_base.mojom.UnguessableToken devtools_frame_token); + // Tells the renderer to create an EmbeddedWorkerInstanceClient, which is what + // manages service worker startup and shutdown. + // TODO(shimazu): Send all params for starting service worker to reduce the + // number of IPCs. + SetUpEmbeddedWorkerChannelForServiceWorker( + EmbeddedWorkerInstanceClient& client_request); + // Tells the renderer that the network type has changed so that // navigator.onLine and navigator.connection can be updated. OnNetworkConnectionChanged(NetworkConnectionType connection_type, @@ -234,6 +225,12 @@ interface Renderer { // Android. SetWebKitSharedTimersSuspended(bool suspend); + // Sets the user-agent string. This is needed because getting the value in the + // renderer from the system leads to a wrong value due to sandboxing. This + // must be called as early as possible, during the renderer process + // initialization. + SetUserAgent(string user_agent); + // Tells the renderer about a scrollbar appearance change. Only for use on // OS X. UpdateScrollbarTheme(UpdateScrollbarThemeParams params); diff --git a/chromium/content/common/resize_params.cc b/chromium/content/common/resize_params.cc deleted file mode 100644 index 5e1fd102f66..00000000000 --- a/chromium/content/common/resize_params.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/resize_params.h" - -namespace content { - -ResizeParams::ResizeParams() - : auto_resize_enabled(false), - auto_resize_sequence_number(0u), - browser_controls_shrink_blink_size(false), - scroll_focused_node_into_view(false), - top_controls_height(0.f), - bottom_controls_height(0.f), - is_fullscreen_granted(false), - display_mode(blink::kWebDisplayModeUndefined), - needs_resize_ack(false), - content_source_id(0u) {} - -ResizeParams::ResizeParams(const ResizeParams& other) = default; - -ResizeParams::~ResizeParams() {} - -} // namespace content diff --git a/chromium/content/common/resize_params.h b/chromium/content/common/resize_params.h deleted file mode 100644 index 51c472dd522..00000000000 --- a/chromium/content/common/resize_params.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_COMMON_RESIZE_PARAMS_H_ -#define CONTENT_COMMON_RESIZE_PARAMS_H_ - -#include "base/optional.h" -#include "components/viz/common/surfaces/local_surface_id.h" -#include "content/common/content_export.h" -#include "content/public/common/screen_info.h" -#include "third_party/blink/public/platform/web_display_mode.h" -#include "ui/gfx/geometry/size.h" - -namespace content { - -struct CONTENT_EXPORT ResizeParams { - ResizeParams(); - ResizeParams(const ResizeParams& other); - ~ResizeParams(); - - // Information about the screen (dpi, depth, etc..). - ScreenInfo screen_info; - - // Whether or not blink should be in auto-resize mode. - bool auto_resize_enabled; - - // The minimum size for Blink if auto-resize is enabled. - gfx::Size min_size_for_auto_resize; - - // The maximum size for Blink if auto-resize is enabled. - gfx::Size max_size_for_auto_resize; - - // This variable is increased after each auto-resize. If the - // renderer receives a ResizeParams with stale auto_resize_seqence_number, - // then the resize request is dropped. - uint64_t auto_resize_sequence_number; - - // The size for the widget in DIPs. - gfx::Size new_size; - - // The size of compositor's viewport in pixels. Note that this may differ - // from a ScaleToCeiledSize of |new_size| due to Android's keyboard or due - // to rounding particulars. - gfx::Size compositor_viewport_pixel_size; - - // Whether or not Blink's viewport size should be shrunk by the height of the - // URL-bar (always false on platforms where URL-bar hiding isn't supported). - bool browser_controls_shrink_blink_size; - - // Whether or not the focused node should be scrolled into view after the - // resize. - bool scroll_focused_node_into_view; - - // The height of the top controls (always 0 on platforms where URL-bar hiding - // isn't supported). - float top_controls_height; - - // The height of the bottom controls. - float bottom_controls_height; - - // The local surface ID to use (if valid). - base::Optional local_surface_id; - - // 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; - - // Indicates whether tab-initiated fullscreen was granted. - bool is_fullscreen_granted; - - // The display mode. - blink::WebDisplayMode display_mode; - - // If set, requests the renderer to reply with a - // ViewHostMsg_ResizeOrRepaint_ACK with the - // ViewHostMsg_ResizeOrRepaint_ACK_Flags::IS_RESIZE_ACK bit set in flags. - bool needs_resize_ack; - - // This variable is increased after each cross-document navigation. If the - // renderer receives a ResizeParams with stale content_source_id, it still - // performs the resize but doesn't use the given LocalSurfaceId. - uint32_t content_source_id; -}; - -} // namespace content - -#endif // CONTENT_COMMON_RESIZE_PARAMS_H_ diff --git a/chromium/content/common/resource_messages.h b/chromium/content/common/resource_messages.h index edcf425d7ea..c31444de9a6 100644 --- a/chromium/content/common/resource_messages.h +++ b/chromium/content/common/resource_messages.h @@ -21,7 +21,7 @@ #include "net/base/request_priority.h" #include "net/http/http_response_info.h" #include "net/traffic_annotation/network_traffic_annotation.h" -#include "services/network/public/cpp/network_param_ipc_traits.h" +#include "services/network/public/cpp/network_ipc_param_traits.h" #include "third_party/blink/public/platform/web_mixed_content_context_type.h" #ifndef INTERNAL_CONTENT_COMMON_RESOURCE_MESSAGES_H_ diff --git a/chromium/content/common/resource_timing_info.h b/chromium/content/common/resource_timing_info.h index 78b9a61316f..ccac0850564 100644 --- a/chromium/content/common/resource_timing_info.h +++ b/chromium/content/common/resource_timing_info.h @@ -11,6 +11,7 @@ #include #include "base/optional.h" +#include "base/time/time.h" namespace content { @@ -36,22 +37,22 @@ struct ResourceLoadTiming { ResourceLoadTiming(const ResourceLoadTiming&); ~ResourceLoadTiming(); - double request_time = 0.0; - double proxy_start = 0.0; - double proxy_end = 0.0; - double dns_start = 0.0; - double dns_end = 0.0; - double connect_start = 0.0; - double connect_end = 0.0; - double worker_start = 0.0; - double worker_ready = 0.0; - double send_start = 0.0; - double send_end = 0.0; - double receive_headers_end = 0.0; - double ssl_start = 0.0; - double ssl_end = 0.0; - double push_start = 0.0; - double push_end = 0.0; + base::TimeTicks request_time; + base::TimeTicks proxy_start; + base::TimeTicks proxy_end; + base::TimeTicks dns_start; + base::TimeTicks dns_end; + base::TimeTicks connect_start; + base::TimeTicks connect_end; + base::TimeTicks worker_start; + base::TimeTicks worker_ready; + base::TimeTicks send_start; + base::TimeTicks send_end; + base::TimeTicks receive_headers_end; + base::TimeTicks ssl_start; + base::TimeTicks ssl_end; + base::TimeTicks push_start; + base::TimeTicks push_end; }; // TODO(dcheng): Migrate this struct over to Mojo so it doesn't need to be @@ -64,13 +65,13 @@ struct ResourceTimingInfo { ~ResourceTimingInfo(); std::string name; - double start_time = 0.0; + base::TimeTicks start_time; std::string initiator_type; std::string alpn_negotiated_protocol; std::string connection_info; base::Optional timing; - double last_redirect_end_time = 0.0; - double finish_time = 0.0; + base::TimeTicks last_redirect_end_time; + base::TimeTicks finish_time; uint64_t transfer_size = 0; uint64_t encoded_body_size = 0; uint64_t decoded_body_size = 0; diff --git a/chromium/content/common/sandbox_init_mac.cc b/chromium/content/common/sandbox_init_mac.cc index 583e283f276..ee6202e3178 100644 --- a/chromium/content/common/sandbox_init_mac.cc +++ b/chromium/content/common/sandbox_init_mac.cc @@ -51,16 +51,22 @@ base::OnceClosure MaybeWrapWithGPUSandboxHook( gpu::SwitchValueToGpuPreferences(value, &gpu_preferences); CHECK(success); } + bool needs_more_info = false; gpu::GpuFeatureInfo gpu_feature_info = gpu::ComputeGpuFeatureInfo( gpu_info, gpu_preferences.ignore_gpu_blacklist, gpu_preferences.disable_gpu_driver_bug_workarounds, gpu_preferences.log_gpu_control_list_decisions, command_line, - nullptr); + &needs_more_info); gpu::CacheGpuFeatureInfo(gpu_feature_info); if (gpu::SwitchableGPUsSupported(gpu_info, *command_line)) { gpu::InitializeSwitchableGPUs( gpu_feature_info.enabled_gpu_driver_bug_workarounds); } + // Calling ShouldEnableSwiftShader will append the proper command line + // switch in order to enable SwiftShader if required. + gpu::ShouldEnableSwiftShader( + command_line, gpu_feature_info, + gpu_preferences.disable_software_rasterizer, needs_more_info); // Preload either the desktop GL or the osmesa so, depending on the // --use-gl flag. gl::init::InitializeGLOneOff(); diff --git a/chromium/content/common/sandbox_policy_fuchsia.cc b/chromium/content/common/sandbox_policy_fuchsia.cc index acfbbfd12d9..26a202b648c 100644 --- a/chromium/content/common/sandbox_policy_fuchsia.cc +++ b/chromium/content/common/sandbox_policy_fuchsia.cc @@ -13,41 +13,32 @@ #include "base/process/launch.h" #include "base/process/process.h" #include "content/public/common/content_switches.h" +#include "services/service_manager/sandbox/switches.h" namespace content { void UpdateLaunchOptionsForSandbox(service_manager::SandboxType type, base::LaunchOptions* options) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoSandbox)) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + service_manager::switches::kNoSandbox)) { type = service_manager::SANDBOX_TYPE_NO_SANDBOX; } if (type != service_manager::SANDBOX_TYPE_NO_SANDBOX) { - auto package_root = base::GetPackageRoot(); - if (!package_root.empty()) { - // TODO(kmarshall): Build path mappings for each sandbox type. - - // Map /pkg (read-only files deployed from the package) and /tmp into the - // child's namespace. - options->paths_to_map.push_back(package_root.AsUTF8Unsafe()); - base::FilePath temp_dir; - base::GetTempDir(&temp_dir); - options->paths_to_map.push_back(temp_dir.AsUTF8Unsafe()); - - // Clear environmental variables to better isolate the child from - // this process. - options->clear_environ = true; - - // Propagate stdout/stderr/stdin to the child. - options->clone_flags = LP_CLONE_FDIO_STDIO; - return; - } - - // TODO(crbug.com/750938): Remove this once package deployments become - // mandatory. - LOG(ERROR) << "Sandboxing was requested but is not available because" - << "the parent process is not hosted within a package."; - type = service_manager::SANDBOX_TYPE_NO_SANDBOX; + // Map /pkg (read-only files deployed from the package) and /tmp into the + // child's namespace. + options->paths_to_map.push_back(base::GetPackageRoot()); + base::FilePath temp_dir; + base::GetTempDir(&temp_dir); + options->paths_to_map.push_back(temp_dir); + + // Clear environmental variables to better isolate the child from + // this process. + options->clear_environ = true; + + // Propagate stdout/stderr/stdin to the child. + options->clone_flags = LP_CLONE_FDIO_STDIO; + return; } DCHECK_EQ(type, service_manager::SANDBOX_TYPE_NO_SANDBOX); diff --git a/chromium/content/common/send_zygote_child_ping_linux.cc b/chromium/content/common/send_zygote_child_ping_linux.cc deleted file mode 100644 index f7f1685834b..00000000000 --- a/chromium/content/common/send_zygote_child_ping_linux.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/public/common/send_zygote_child_ping_linux.h" - -#include - -#include "base/posix/unix_domain_socket.h" -#include "content/common/zygote_commands_linux.h" - -namespace content { - -bool SendZygoteChildPing(int fd) { - return base::UnixDomainSocket::SendMsg(fd, - kZygoteChildPingMessage, - sizeof(kZygoteChildPingMessage), - std::vector()); -} - -} // namespace content diff --git a/chromium/content/common/service_manager/service_manager_connection_impl.cc b/chromium/content/common/service_manager/service_manager_connection_impl.cc index 49f0da67e49..dac316d7662 100644 --- a/chromium/content/common/service_manager/service_manager_connection_impl.cc +++ b/chromium/content/common/service_manager/service_manager_connection_impl.cc @@ -13,9 +13,10 @@ #include "base/callback_helpers.h" #include "base/lazy_instance.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_current.h" #include "base/threading/thread_checker.h" #include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" #include "content/common/child.mojom.h" #include "content/public/common/connection_filter.h" #include "content/public/common/service_names.mojom.h" @@ -28,6 +29,13 @@ #include "services/service_manager/public/mojom/service_factory.mojom.h" #include "services/service_manager/runner/common/client_util.h" +#if defined(OS_ANDROID) +#include "base/android/jni_android.h" +#include "jni/ServiceManagerConnectionImpl_jni.h" +#include "services/service_manager/public/cpp/connector.h" +#include "services/service_manager/public/mojom/connector.mojom.h" +#endif + namespace content { namespace { @@ -133,15 +141,16 @@ class ServiceManagerConnectionImpl::IOThreadContext private: friend class base::RefCountedThreadSafe; - class MessageLoopObserver : public base::MessageLoop::DestructionObserver { + class MessageLoopObserver + : public base::MessageLoopCurrent::DestructionObserver { public: explicit MessageLoopObserver(base::WeakPtr context) : context_(context) { - base::MessageLoop::current()->AddDestructionObserver(this); + base::MessageLoopCurrent::Get()->AddDestructionObserver(this); } ~MessageLoopObserver() override { - base::MessageLoop::current()->RemoveDestructionObserver(this); + base::MessageLoopCurrent::Get()->RemoveDestructionObserver(this); } void ShutDown() { @@ -350,6 +359,23 @@ class ServiceManagerConnectionImpl::IOThreadContext DISALLOW_COPY_AND_ASSIGN(IOThreadContext); }; +#if defined(OS_ANDROID) +// static +jint JNI_ServiceManagerConnectionImpl_GetConnectorMessagePipeHandle( + JNIEnv* env, + const base::android::JavaParamRef& jcaller) { + DCHECK(ServiceManagerConnection::GetForProcess()); + + service_manager::mojom::ConnectorPtrInfo connector_info; + ServiceManagerConnection::GetForProcess() + ->GetConnector() + ->BindConnectorRequest(mojo::MakeRequest(&connector_info)); + + return connector_info.PassHandle().release().value(); +} + +#endif + //////////////////////////////////////////////////////////////////////////////// // ServiceManagerConnection, public: diff --git a/chromium/content/common/service_worker/BUILD.gn b/chromium/content/common/service_worker/BUILD.gn new file mode 100644 index 00000000000..65c27c138fc --- /dev/null +++ b/chromium/content/common/service_worker/BUILD.gn @@ -0,0 +1,11 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/protobuf/proto_library.gni") + +proto_library("service_worker_types_proto") { + sources = [ + "service_worker_types.proto", + ] +} diff --git a/chromium/content/common/service_worker/dispatch_fetch_event_params.mojom b/chromium/content/common/service_worker/dispatch_fetch_event_params.mojom index 05d50cc0893..785d219b776 100644 --- a/chromium/content/common/service_worker/dispatch_fetch_event_params.mojom +++ b/chromium/content/common/service_worker/dispatch_fetch_event_params.mojom @@ -19,9 +19,23 @@ struct DispatchFetchEventParams { // FetchEvent#request. network.mojom.URLRequest request; - // For Non-S13nServiceWorker these |request_body_*| fields are used to create - // FetchEvent#request#body. For S13nServiceWorker, the body is provided in - // |request.request_body|, and these fields are not used. + // The following fields are used to create FetchEvent#request#body, depending + // on whether S13nServiceWorker/NetworkService are enabled. + + // (A) S13nServiceWorker with NetworkService on: + // All information about the request body is provided in + // |request.request_body|. + + // (B) S13nServiceWorker with NetworkService off: + // All information about the request body except for BlobPtrs is provided in + // |request.request_body|. These BlobPtrs need to be passed separately. + // Once the NetworkService is enabled, this will be no longer used since all + // Blobs are passed as data pipes which can live in |request.request_body|. + array request_body_blob_ptrs; + + // (C) non-S13nServiceWorker: + // All information to create the request body are packed into a blob. These + // params are for passing the blob. string request_body_blob_uuid; uint64 request_body_blob_size; blink.mojom.Blob? request_body_blob; diff --git a/chromium/content/common/service_worker/embedded_worker.mojom b/chromium/content/common/service_worker/embedded_worker.mojom index 1dbda4e6cbc..a9c5333ade0 100644 --- a/chromium/content/common/service_worker/embedded_worker.mojom +++ b/chromium/content/common/service_worker/embedded_worker.mojom @@ -12,7 +12,6 @@ import "mojo/public/mojom/base/string16.mojom"; import "mojo/public/mojom/base/time.mojom"; import "mojo/public/mojom/base/unguessable_token.mojom"; import "services/service_manager/public/mojom/interface_provider.mojom"; -import "third_party/blink/public/mojom/service_worker/service_worker.mojom"; import "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom"; import "third_party/blink/public/platform/web_feature.mojom"; import "third_party/blink/public/web/console_message.mojom"; @@ -58,9 +57,6 @@ struct EmbeddedWorkerStartParams { ControllerServiceWorker& controller_request; // Information to transfer installed scripts from the browser to the renderer. blink.mojom.ServiceWorkerInstalledScriptsInfo? installed_scripts_info; - // Interface for the renderer to ask the browser to do operations needed for - // ServiceWorkerGlobalScope functionalities. - associated blink.mojom.ServiceWorkerHost service_worker_host; // Interface for the renderer to send the status updates to the browser. associated EmbeddedWorkerInstanceHost instance_host; // Information for creating ServiceWorkerNetworkProvider on the renderer. diff --git a/chromium/content/common/service_worker/service_worker_container.mojom b/chromium/content/common/service_worker/service_worker_container.mojom index 07dc0255a65..ef8136d3e89 100644 --- a/chromium/content/common/service_worker/service_worker_container.mojom +++ b/chromium/content/common/service_worker/service_worker_container.mojom @@ -99,7 +99,7 @@ interface ServiceWorkerContainerHost { // to use this name. interface ServiceWorkerContainer { // Corresponds to setting ServiceWorkerContainer#controller. - // If |controller_info| is invalid (its |handle_id| is invalid), then + // If |controller_info| is invalid (its |object_info| is null), then // ServiceWorkerContainer#controller is cleared. // If |controller_info| is valid, |used_features| is the set of // features the controller has used, for UseCounter purposes. diff --git a/chromium/content/common/service_worker/service_worker_event_dispatcher.mojom b/chromium/content/common/service_worker/service_worker_event_dispatcher.mojom index 2d97943541e..dc52bf442f0 100644 --- a/chromium/content/common/service_worker/service_worker_event_dispatcher.mojom +++ b/chromium/content/common/service_worker/service_worker_event_dispatcher.mojom @@ -8,12 +8,15 @@ import "content/common/service_worker/dispatch_fetch_event_params.mojom"; import "content/common/service_worker/service_worker_fetch_response_callback.mojom"; import "mojo/public/mojom/base/string16.mojom"; import "mojo/public/mojom/base/time.mojom"; +import "services/network/public/mojom/cookie_manager.mojom"; import "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom"; import "third_party/blink/public/platform/modules/payments/payment_app.mojom"; import "third_party/blink/public/mojom/message_port/message_port.mojom"; +import "third_party/blink/public/mojom/service_worker/service_worker.mojom"; import "third_party/blink/public/mojom/service_worker/service_worker_client.mojom"; import "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom"; import "third_party/blink/public/mojom/service_worker/service_worker_object.mojom"; +import "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom"; import "url/mojom/origin.mojom"; import "url/mojom/url.mojom"; @@ -63,6 +66,17 @@ const int32 kPushEventTimeoutSeconds = 90; // 'simple events'. ServiceWorkerVersion::CreateSimpleEventCallback can be used // to create the callback for these. interface ServiceWorkerEventDispatcher { + // The first message sent on this interface. It is used to associate service + // worker-related interfaces together on the service worker thread, as + // ServiceWorkerEventDispatcher is the first interface available on the + // service worker thread. It establishes the |service_worker_host| connection + // and passes information used to populate + // ServiceWorkerGlobalScope#registration object. JavaScript execution of the + // service worker does not start until this message is received. + InitializeGlobalScope( + associated blink.mojom.ServiceWorkerHost service_worker_host, + blink.mojom.ServiceWorkerRegistrationObjectInfo registration_info); + DispatchInstallEvent() => (blink.mojom.ServiceWorkerEventStatus status, bool has_fetch_handler, @@ -76,7 +90,9 @@ interface ServiceWorkerEventDispatcher { // The callbacks are called once the event handler has run and waitUntil() // promise has settled. |developer_id| and |unique_id| are documented in // content::BackgroundFetchRegistrationId. - DispatchBackgroundFetchAbortEvent(string developer_id) + DispatchBackgroundFetchAbortEvent(string developer_id, + string unique_id, + array fetches) => (blink.mojom.ServiceWorkerEventStatus status, mojo_base.mojom.Time dispatch_event_time); DispatchBackgroundFetchClickEvent(string developer_id, @@ -84,6 +100,7 @@ interface ServiceWorkerEventDispatcher { => (blink.mojom.ServiceWorkerEventStatus status, mojo_base.mojom.Time dispatch_event_time); DispatchBackgroundFetchFailEvent(string developer_id, + string unique_id, array fetches) => (blink.mojom.ServiceWorkerEventStatus status, mojo_base.mojom.Time dispatch_event_time); @@ -93,6 +110,16 @@ interface ServiceWorkerEventDispatcher { => (blink.mojom.ServiceWorkerEventStatus status, mojo_base.mojom.Time dispatch_event_time); + // Dispatches the cookie change events in the Async Cookie API specification. + // https://github.com/WICG/cookie-store/ + // The callback is called once the event handler has run and the waitUntil() + // promise has settled. + DispatchCookieChangeEvent( + network.mojom.CanonicalCookie cookie, + network.mojom.CookieChangeCause cause) + => (blink.mojom.ServiceWorkerEventStatus status, + mojo_base.mojom.Time dispatch_event_time); + // The Dispatch*FetchEvent() callback is called once the event finishes, // which means the event handler ran and all outstanding respondWith() and // waitUntil() promises have settled. |response_callback| is called once the diff --git a/chromium/content/common/service_worker/service_worker_fetch_request_mojom_traits.cc b/chromium/content/common/service_worker/service_worker_fetch_request_mojom_traits.cc index 28378abbaf7..a6b3557e7e3 100644 --- a/chromium/content/common/service_worker/service_worker_fetch_request_mojom_traits.cc +++ b/chromium/content/common/service_worker/service_worker_fetch_request_mojom_traits.cc @@ -207,10 +207,12 @@ bool StructTraits headers; + blink::mojom::SerializedBlobPtr serialized_blob_ptr; if (!data.ReadMode(&out->mode) || !data.ReadRequestContextType(&out->request_context_type) || !data.ReadFrameType(&out->frame_type) || !data.ReadUrl(&out->url) || !data.ReadMethod(&out->method) || !data.ReadHeaders(&headers) || + !data.ReadBlob(&serialized_blob_ptr) || !data.ReadReferrer(&out->referrer) || !data.ReadCredentialsMode(&out->credentials_mode) || !data.ReadRedirectMode(&out->redirect_mode) || @@ -220,11 +222,7 @@ bool StructTraits blob_uuid; - if (data.ReadBlobUuid(&blob_uuid) && blob_uuid && !blob_uuid->empty()) - return false; - blink::mojom::BlobPtr blob = data.TakeBlob(); - if (blob) + if (serialized_blob_ptr) return false; out->is_main_resource_load = data.is_main_resource_load(); diff --git a/chromium/content/common/service_worker/service_worker_fetch_request_mojom_traits.h b/chromium/content/common/service_worker/service_worker_fetch_request_mojom_traits.h index 55862700585..fe8cc4e2e27 100644 --- a/chromium/content/common/service_worker/service_worker_fetch_request_mojom_traits.h +++ b/chromium/content/common/service_worker/service_worker_fetch_request_mojom_traits.h @@ -63,18 +63,7 @@ struct StructTraits:: Read(blink::mojom::FetchAPIResponseDataView data, content::ServiceWorkerResponse* out) { + blink::mojom::SerializedBlobPtr serialized_blob_ptr; + blink::mojom::SerializedBlobPtr serialized_side_data_blob_ptr; if (!data.ReadUrlList(&out->url_list) || !data.ReadStatusText(&out->status_text) || !data.ReadResponseType(&out->response_type) || - !data.ReadHeaders(&out->headers) || !data.ReadBlobUuid(&out->blob_uuid) || - !data.ReadError(&out->error) || + !data.ReadHeaders(&out->headers) || + !data.ReadBlob(&serialized_blob_ptr) || !data.ReadError(&out->error) || !data.ReadResponseTime(&out->response_time) || !data.ReadCacheStorageCacheName(&out->cache_storage_cache_name) || - !data.ReadCorsExposedHeaderNames(&out->cors_exposed_header_names)) { + !data.ReadCorsExposedHeaderNames(&out->cors_exposed_header_names) || + !data.ReadSideDataBlob(&serialized_side_data_blob_ptr)) { return false; } out->status_code = data.status_code(); - out->blob_size = data.blob_size(); out->is_in_cache_storage = data.is_in_cache_storage(); - if (!out->blob_uuid.empty()) { - blink::mojom::BlobPtr blob = data.TakeBlob(); - out->blob = base::MakeRefCounted(std::move(blob)); + if (serialized_blob_ptr) { + out->blob_uuid = serialized_blob_ptr->uuid; + out->blob_size = serialized_blob_ptr->size; + blink::mojom::BlobPtr blob_ptr; + blob_ptr.Bind(std::move(serialized_blob_ptr->blob)); + out->blob = base::MakeRefCounted(std::move(blob_ptr)); + } + + if (serialized_side_data_blob_ptr) { + out->side_data_blob_uuid = serialized_side_data_blob_ptr->uuid; + out->side_data_blob_size = serialized_side_data_blob_ptr->size; + blink::mojom::BlobPtr blob_ptr; + blob_ptr.Bind(std::move(serialized_side_data_blob_ptr->blob)); + out->side_data_blob = + base::MakeRefCounted(std::move(blob_ptr)); } return true; diff --git a/chromium/content/common/service_worker/service_worker_fetch_response_mojom_traits.h b/chromium/content/common/service_worker/service_worker_fetch_response_mojom_traits.h index 51d7d859813..287ac17b670 100644 --- a/chromium/content/common/service_worker/service_worker_fetch_response_mojom_traits.h +++ b/chromium/content/common/service_worker/service_worker_fetch_response_mojom_traits.h @@ -9,6 +9,7 @@ #include #include "base/numerics/safe_conversions.h" +#include "content/common/content_export.h" #include "content/common/service_worker/service_worker_types.h" #include "third_party/blink/public/mojom/blob/blob.mojom.h" #include "third_party/blink/public/platform/modules/fetch/fetch_api_response.mojom.h" @@ -16,8 +17,8 @@ namespace mojo { template <> -struct StructTraits { +struct CONTENT_EXPORT StructTraits { static const std::vector& url_list( const content::ServiceWorkerResponse& response) { return response.url_list; @@ -29,10 +30,15 @@ struct StructTraitsClone(); + blink::mojom::SerializedBlobPtr serialized_blob_ptr = + blink::mojom::SerializedBlob::New(); + serialized_blob_ptr->uuid = response.blob_uuid; + serialized_blob_ptr->size = response.blob_size; + serialized_blob_ptr->blob = response.blob->Clone().PassInterface(); + return serialized_blob_ptr; } return nullptr; } @@ -50,12 +56,6 @@ struct StructTraitsuuid = response.side_data_blob_uuid; + serialized_blob_ptr->size = response.side_data_blob_size; + serialized_blob_ptr->blob = + response.side_data_blob->Clone().PassInterface(); + return serialized_blob_ptr; + } + return nullptr; + } static bool Read(blink::mojom::FetchAPIResponseDataView, content::ServiceWorkerResponse* output); }; diff --git a/chromium/content/common/service_worker/service_worker_loader_helpers.cc b/chromium/content/common/service_worker/service_worker_loader_helpers.cc index 0b4912c8982..738ed311a07 100644 --- a/chromium/content/common/service_worker/service_worker_loader_helpers.cc +++ b/chromium/content/common/service_worker/service_worker_loader_helpers.cc @@ -156,47 +156,4 @@ int ServiceWorkerLoaderHelpers::ReadBlobResponseBody( return net::OK; } -// static -scoped_refptr -ServiceWorkerLoaderHelpers::CloneResourceRequestBody( - const network::ResourceRequestBody* body) { - auto clone = base::MakeRefCounted(); - - clone->set_identifier(body->identifier()); - clone->set_contains_sensitive_info(body->contains_sensitive_info()); - for (const network::DataElement& element : *body->elements()) { - switch (element.type()) { - case network::DataElement::TYPE_UNKNOWN: - NOTREACHED(); - break; - case network::DataElement::TYPE_DATA_PIPE: { - clone->AppendDataPipe(element.CloneDataPipeGetter()); - break; - } - case network::DataElement::TYPE_RAW_FILE: - clone->AppendRawFileRange(element.file().Duplicate(), element.path(), - element.offset(), element.length(), - element.expected_modification_time()); - break; - case network::DataElement::TYPE_CHUNKED_DATA_PIPE: - NOTREACHED() << "There should be no chunked data pipes going through " - "ServiceWorker"; - break; - case network::DataElement::TYPE_BLOB: - NOTREACHED() << "There should be no blob elements in NetworkService"; - break; - case network::DataElement::TYPE_FILE: - clone->AppendFileRange(element.path(), element.offset(), - element.length(), - element.expected_modification_time()); - break; - case network::DataElement::TYPE_BYTES: - clone->AppendBytes(element.bytes(), element.length()); - break; - } - } - - return clone; -} - } // namespace content diff --git a/chromium/content/common/service_worker/service_worker_loader_helpers.h b/chromium/content/common/service_worker/service_worker_loader_helpers.h index 3654e07fcfe..a709385b4f2 100644 --- a/chromium/content/common/service_worker/service_worker_loader_helpers.h +++ b/chromium/content/common/service_worker/service_worker_loader_helpers.h @@ -12,7 +12,6 @@ #include "third_party/blink/public/mojom/blob/blob.mojom.h" namespace network { -class ResourceRequestBody; struct ResourceRequest; struct ResourceResponseHead; } @@ -49,15 +48,6 @@ class ServiceWorkerLoaderHelpers { const net::HttpRequestHeaders& headers, base::OnceCallback on_blob_read_complete, mojo::ScopedDataPipeConsumerHandle* handle_out); - - // Returns a new copy of the given body. This is useful for service worker - // with NetworkService because it sends the ResourceRequestBody over Mojo IPC, - // which moves out the DataPipeGetter elements in the Pickle code in - // resources_messages.cc. We can't change the Pickle code to call - // DataPipeGetter's Clone method because that code can run on different thread - // than the DataPipeGetter. - static scoped_refptr CloneResourceRequestBody( - const network::ResourceRequestBody* body); }; } // namespace content diff --git a/chromium/content/common/service_worker/service_worker_messages.h b/chromium/content/common/service_worker/service_worker_messages.h index 9c6dd91051c..8cba139aba4 100644 --- a/chromium/content/common/service_worker/service_worker_messages.h +++ b/chromium/content/common/service_worker/service_worker_messages.h @@ -26,12 +26,8 @@ #define IPC_MESSAGE_START ServiceWorkerMsgStart -IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::ServiceWorkerErrorType, - blink::mojom::ServiceWorkerErrorType::kMaxValue) - -IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::ServiceWorkerState, - blink::mojom::ServiceWorkerState::kMaxValue) - +// TODO(leonhsl): Figure out what's the purpose of all these traits then +// eliminate this file finally. IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::ServiceWorkerResponseError, blink::mojom::ServiceWorkerResponseError::kMaxValue) @@ -76,18 +72,4 @@ IPC_STRUCT_TRAITS_BEGIN(content::PushEventPayload) IPC_STRUCT_TRAITS_MEMBER(is_null) IPC_STRUCT_TRAITS_END() -//--------------------------------------------------------------------------- -// Messages sent from the browser to the child process. -// -// NOTE: All ServiceWorkerMsg messages not sent via EmbeddedWorker must have -// a thread_id as their first field so that ServiceWorkerMessageFilter can -// extract it and dispatch the message to the correct ServiceWorkerDispatcher -// on the correct thread. - -// Informs the child process that the ServiceWorker's state has changed. -IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_ServiceWorkerStateChanged, - int /* thread_id */, - int /* handle_id */, - blink::mojom::ServiceWorkerState) - #endif // CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_MESSAGES_H_ diff --git a/chromium/content/common/service_worker/service_worker_provider.mojom b/chromium/content/common/service_worker/service_worker_provider.mojom index de8bda5821d..17fdfada95a 100644 --- a/chromium/content/common/service_worker/service_worker_provider.mojom +++ b/chromium/content/common/service_worker/service_worker_provider.mojom @@ -9,6 +9,7 @@ import "services/network/public/mojom/url_loader_factory.mojom"; import "services/service_manager/public/mojom/interface_provider.mojom"; import "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom"; import "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom"; +import "third_party/blink/public/platform/modules/cache_storage/cache_storage.mojom"; // The name of the InterfaceProviderSpec in service manifests used by the // frame tree to expose service-worker-specific interfaces between renderer @@ -30,9 +31,6 @@ struct ServiceWorkerProviderInfoForSharedWorker { // worker. struct ServiceWorkerProviderInfoForStartWorker { int32 provider_id; - // |registration| is information about the service worker's - // registration used to populate ServiceWorkerGlobalScope#registration. - blink.mojom.ServiceWorkerRegistrationObjectInfo registration; associated ServiceWorkerContainerHost host_ptr_info; associated ServiceWorkerContainer& client_request; @@ -42,6 +40,13 @@ struct ServiceWorkerProviderInfoForStartWorker { // importScripts(). associated network.mojom.URLLoaderFactory? script_loader_factory_ptr_info; + // |cache_storage| is an optional optimization so the service worker can use + // the Cache Storage API immediately without using InterfaceProvider. May be + // null for service workers created for update checks, as the optimization + // would be wasteful because these workers usually are aborted after the + // byte-to-byte update check before running. + blink.mojom.CacheStorage? cache_storage; + service_manager.mojom.InterfaceProvider interface_provider; }; diff --git a/chromium/content/common/service_worker/service_worker_types.cc b/chromium/content/common/service_worker/service_worker_types.cc index 4f93a652e54..97f523803f3 100644 --- a/chromium/content/common/service_worker/service_worker_types.cc +++ b/chromium/content/common/service_worker/service_worker_types.cc @@ -4,6 +4,7 @@ #include "content/common/service_worker/service_worker_types.h" +#include "content/common/service_worker/service_worker_types.pb.h" #include "net/base/load_flags.h" #include "storage/common/blob_storage/blob_handle.h" @@ -55,6 +56,28 @@ ServiceWorkerFetchRequest& ServiceWorkerFetchRequest::operator=( ServiceWorkerFetchRequest::~ServiceWorkerFetchRequest() {} +std::string ServiceWorkerFetchRequest::Serialize() const { + proto::internal::ServiceWorkerFetchRequest request_proto; + + request_proto.set_url(url.spec()); + request_proto.set_method(method); + request_proto.mutable_headers()->insert(headers.begin(), headers.end()); + request_proto.mutable_referrer()->set_url(referrer.url.spec()); + request_proto.mutable_referrer()->set_policy(referrer.policy); + request_proto.set_is_reload(is_reload); + request_proto.set_mode(static_cast(mode)); + request_proto.set_is_main_resource_load(is_main_resource_load); + request_proto.set_request_context_type(request_context_type); + request_proto.set_credentials_mode(static_cast(credentials_mode)); + request_proto.set_cache_mode(static_cast(cache_mode)); + request_proto.set_redirect_mode(static_cast(redirect_mode)); + request_proto.set_integrity(integrity); + request_proto.set_keepalive(keepalive); + request_proto.set_client_id(client_id); + + return request_proto.SerializeAsString(); +} + size_t ServiceWorkerFetchRequest::EstimatedStructSize() { size_t size = sizeof(ServiceWorkerFetchRequest); size += url.spec().size(); @@ -68,6 +91,40 @@ size_t ServiceWorkerFetchRequest::EstimatedStructSize() { return size; } +// static +ServiceWorkerFetchRequest ServiceWorkerFetchRequest::ParseFromString( + const std::string& serialized) { + proto::internal::ServiceWorkerFetchRequest request_proto; + if (!request_proto.ParseFromString(serialized)) { + return ServiceWorkerFetchRequest(); + } + + ServiceWorkerFetchRequest request( + GURL(request_proto.url()), request_proto.method(), + ServiceWorkerHeaderMap(request_proto.headers().begin(), + request_proto.headers().end()), + Referrer(GURL(request_proto.referrer().url()), + static_cast( + request_proto.referrer().policy())), + request_proto.is_reload()); + request.mode = + static_cast(request_proto.mode()); + request.is_main_resource_load = request_proto.is_main_resource_load(); + request.request_context_type = + static_cast(request_proto.request_context_type()); + request.credentials_mode = static_cast( + request_proto.credentials_mode()); + request.cache_mode = + static_cast(request_proto.cache_mode()); + request.redirect_mode = static_cast( + request_proto.redirect_mode()); + request.integrity = request_proto.integrity(); + request.keepalive = request_proto.keepalive(); + request.client_id = request_proto.client_id(); + + return request; +} + // static blink::mojom::FetchCacheMode ServiceWorkerFetchRequest::GetCacheModeFromLoadFlags(int load_flags) { diff --git a/chromium/content/common/service_worker/service_worker_types.h b/chromium/content/common/service_worker/service_worker_types.h index bf4983a15ce..15b83155507 100644 --- a/chromium/content/common/service_worker/service_worker_types.h +++ b/chromium/content/common/service_worker/service_worker_types.h @@ -90,10 +90,14 @@ struct CONTENT_EXPORT ServiceWorkerFetchRequest { ServiceWorkerFetchRequest& operator=(const ServiceWorkerFetchRequest& other); ~ServiceWorkerFetchRequest(); size_t EstimatedStructSize(); + std::string Serialize() const; static blink::mojom::FetchCacheMode GetCacheModeFromLoadFlags(int load_flags); + static ServiceWorkerFetchRequest ParseFromString( + const std::string& serialized); - // Be sure to update EstimatedStructSize() when adding members. + // Be sure to update EstimatedStructSize(), Serialize(), and ParseFromString() + // when adding members. network::mojom::FetchRequestMode mode = network::mojom::FetchRequestMode::kNoCORS; bool is_main_resource_load = false; diff --git a/chromium/content/common/service_worker/service_worker_types.proto b/chromium/content/common/service_worker/service_worker_types.proto new file mode 100644 index 00000000000..f4470b521f7 --- /dev/null +++ b/chromium/content/common/service_worker/service_worker_types.proto @@ -0,0 +1,41 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package content.proto.internal; + +// Serializable version of ServiceWorkerFetchRequest. +// +// Next Tag: 15 +message ServiceWorkerFetchRequest { + // Serializable version of the Referrer struct defined in + // https://cs.chromium.org/chromium/src/content/public/common/referrer.h + // + // Next Tag: 3 + message Referrer { + optional string url = 1; + optional int32 policy = 2; // blink::WebReferrerPolicy. + } + + // Constructor params. + optional string url = 1; + optional string method = 2; + map headers = 3; + optional Referrer referrer = 4; + optional bool is_reload = 5; + + // Other params. + optional int32 mode = 6; // network::mojom::FetchRequestMode. + optional bool is_main_resource_load = 7; + optional int32 request_context_type = 8; // content::RequestContextType. + optional int32 credentials_mode = 9; // network::mojom::FetchCredentialsMode. + optional int32 cache_mode = 10; // blink::mojom::FetchCacheMode. + optional int32 redirect_mode = 11; // network::mojom::FetchRedirectMode. + optional string integrity = 12; + optional bool keepalive = 13; + optional string client_id = 14; +} \ No newline at end of file diff --git a/chromium/content/common/service_worker/service_worker_types_unittest.cc b/chromium/content/common/service_worker/service_worker_types_unittest.cc index 9a5373ac91c..8a077d53982 100644 --- a/chromium/content/common/service_worker/service_worker_types_unittest.cc +++ b/chromium/content/common/service_worker/service_worker_types_unittest.cc @@ -3,7 +3,13 @@ // found in the LICENSE file. #include "content/common/service_worker/service_worker_types.h" +#include "base/guid.h" +#include "content/common/service_worker/service_worker_fetch_response_mojom_traits.h" +#include "mojo/public/cpp/base/time_mojom_traits.h" +#include "mojo/public/cpp/test_support/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/modules/fetch/fetch_api_response.mojom.h" +#include "url/mojom/url_gurl_mojom_traits.h" #include "net/base/load_flags.h" @@ -39,6 +45,65 @@ TEST(ServiceWorkerFetchRequestTest, CacheModeTest) { net::LOAD_ONLY_FROM_CACHE | net::LOAD_BYPASS_CACHE)); } +// Tests that mojo serialization/deserialization of ServiceWorkerResponse works. +TEST(ServiceWorkerResponseTest, StructTraits) { + ServiceWorkerResponse input; + ServiceWorkerResponse output; + + input.url_list = {GURL("https://www.google.ca/"), + GURL("https://www.google.com")}; + input.status_code = 200; + input.status_text = "status_text"; + input.response_type = network::mojom::FetchResponseType::kDefault; + input.headers.insert( + std::pair("header1", "value1")); + input.headers.insert( + std::pair("header2", "value2")); + input.error = blink::mojom::ServiceWorkerResponseError::kUnknown; + input.response_time = base::Time::Now(); + input.is_in_cache_storage = true; + input.cache_storage_cache_name = "cache_name"; + + mojo::test::SerializeAndDeserialize(&input, + &output); + + EXPECT_EQ(input.url_list, output.url_list); + EXPECT_EQ(input.status_code, output.status_code); + EXPECT_EQ(input.status_text, output.status_text); + EXPECT_EQ(input.response_type, output.response_type); + EXPECT_EQ(input.headers, output.headers); + EXPECT_EQ(input.blob, output.blob); + EXPECT_EQ(input.error, output.error); + EXPECT_EQ(input.response_time, output.response_time); + EXPECT_EQ(input.is_in_cache_storage, output.is_in_cache_storage); + EXPECT_EQ(input.cache_storage_cache_name, output.cache_storage_cache_name); + EXPECT_EQ(input.cors_exposed_header_names, output.cors_exposed_header_names); + EXPECT_EQ(input.side_data_blob, output.side_data_blob); +} + +TEST(ServiceWorkerRequestTest, SerialiazeDeserializeRoundTrip) { + ServiceWorkerFetchRequest request( + GURL("foo.com"), "GET", {{"User-Agent", "Chrome"}}, + Referrer( + GURL("bar.com"), + blink::WebReferrerPolicy::kWebReferrerPolicyNoReferrerWhenDowngrade), + true); + request.mode = network::mojom::FetchRequestMode::kSameOrigin; + request.is_main_resource_load = true; + request.request_context_type = + RequestContextType::REQUEST_CONTEXT_TYPE_IFRAME; + request.credentials_mode = network::mojom::FetchCredentialsMode::kSameOrigin; + request.cache_mode = blink::mojom::FetchCacheMode::kForceCache; + request.redirect_mode = network::mojom::FetchRedirectMode::kManual; + request.integrity = "integrity"; + request.keepalive = true; + request.client_id = "42"; + + EXPECT_EQ(request.Serialize(), + ServiceWorkerFetchRequest::ParseFromString(request.Serialize()) + .Serialize()); +} + } // namespace } // namespace content diff --git a/chromium/content/common/service_worker/service_worker_utils.cc b/chromium/content/common/service_worker/service_worker_utils.cc index 918b5a7dae1..0820aad1064 100644 --- a/chromium/content/common/service_worker/service_worker_utils.cc +++ b/chromium/content/common/service_worker/service_worker_utils.cc @@ -186,6 +186,12 @@ bool ServiceWorkerUtils::ExtractSinglePartHttpRange( const net::HttpByteRange& byte_range = ranges[0]; if (byte_range.first_byte_position() < 0) return false; + // Allow the range [0, -1] to be valid and specify the entire range. + if (byte_range.first_byte_position() == 0 && + byte_range.last_byte_position() == -1) { + *has_range_out = false; + return true; + } if (byte_range.last_byte_position() < 0) return false; @@ -206,6 +212,21 @@ bool ServiceWorkerUtils::ExtractSinglePartHttpRange( return true; } +bool ServiceWorkerUtils::ShouldBypassCacheDueToUpdateViaCache( + bool is_main_script, + blink::mojom::ServiceWorkerUpdateViaCache cache_mode) { + switch (cache_mode) { + case blink::mojom::ServiceWorkerUpdateViaCache::kImports: + return is_main_script; + case blink::mojom::ServiceWorkerUpdateViaCache::kNone: + return true; + case blink::mojom::ServiceWorkerUpdateViaCache::kAll: + return false; + } + NOTREACHED() << static_cast(cache_mode); + return false; +} + bool LongestScopeMatcher::MatchLongest(const GURL& scope) { if (!ServiceWorkerUtils::ScopeMatches(scope, url_)) return false; diff --git a/chromium/content/common/service_worker/service_worker_utils.h b/chromium/content/common/service_worker/service_worker_utils.h index 9df6f6668ae..e7ce9c975f1 100644 --- a/chromium/content/common/service_worker/service_worker_utils.h +++ b/chromium/content/common/service_worker/service_worker_utils.h @@ -51,7 +51,6 @@ class ServiceWorkerUtils { // Returns true if servicified service worker is enabled. CONTENT_EXPORT static bool IsServicificationEnabled(); - // PlzNavigate // Returns true if the |provider_id| was assigned by the browser process. static bool IsBrowserAssignedProviderId(int provider_id) { return provider_id < kInvalidServiceWorkerProviderId; @@ -71,6 +70,10 @@ class ServiceWorkerUtils { bool* has_range_out, uint64_t* offset_out, uint64_t* size_out); + + static bool ShouldBypassCacheDueToUpdateViaCache( + bool is_main_script, + blink::mojom::ServiceWorkerUpdateViaCache cache_mode); }; class CONTENT_EXPORT LongestScopeMatcher { diff --git a/chromium/content/common/single_request_url_loader_factory.cc b/chromium/content/common/single_request_url_loader_factory.cc index 1a3828903af..3c1a19b1bd6 100644 --- a/chromium/content/common/single_request_url_loader_factory.cc +++ b/chromium/content/common/single_request_url_loader_factory.cc @@ -88,6 +88,11 @@ void SingleRequestURLLoaderFactory::CreateLoaderAndStart( state_->HandleRequest(std::move(loader), std::move(client)); } +void SingleRequestURLLoaderFactory::Clone( + network::mojom::URLLoaderFactoryRequest request) { + NOTREACHED(); +} + std::unique_ptr SingleRequestURLLoaderFactory::Clone() { return std::make_unique(state_); diff --git a/chromium/content/common/single_request_url_loader_factory.h b/chromium/content/common/single_request_url_loader_factory.h index 0250e786be4..9069591c060 100644 --- a/chromium/content/common/single_request_url_loader_factory.h +++ b/chromium/content/common/single_request_url_loader_factory.h @@ -32,6 +32,7 @@ class SingleRequestURLLoaderFactory : public network::SharedURLLoaderFactory { network::mojom::URLLoaderClientPtr client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) override; + void Clone(network::mojom::URLLoaderFactoryRequest request) override; std::unique_ptr Clone() override; private: diff --git a/chromium/content/common/speech_recognition_messages.h b/chromium/content/common/speech_recognition_messages.h deleted file mode 100644 index a7843381fac..00000000000 --- a/chromium/content/common/speech_recognition_messages.h +++ /dev/null @@ -1,121 +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_COMMON_SPEECH_RECOGNITION_MESSAGES_H_ -#define CONTENT_COMMON_SPEECH_RECOGNITION_MESSAGES_H_ - -#include - -#include - -#include "content/public/common/speech_recognition_error.h" -#include "content/public/common/speech_recognition_grammar.h" -#include "content/public/common/speech_recognition_result.h" -#include "ipc/ipc_message_macros.h" -#include "ipc/ipc_param_traits.h" -#include "ui/gfx/geometry/rect.h" - -#define IPC_MESSAGE_START SpeechRecognitionMsgStart - -IPC_ENUM_TRAITS_MAX_VALUE(content::SpeechAudioErrorDetails, - content::SPEECH_AUDIO_ERROR_DETAILS_LAST) -IPC_ENUM_TRAITS_MAX_VALUE(content::SpeechRecognitionErrorCode, - content::SPEECH_RECOGNITION_ERROR_LAST) - -IPC_STRUCT_TRAITS_BEGIN(content::SpeechRecognitionError) - IPC_STRUCT_TRAITS_MEMBER(code) - IPC_STRUCT_TRAITS_MEMBER(details) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(content::SpeechRecognitionHypothesis) - IPC_STRUCT_TRAITS_MEMBER(utterance) - IPC_STRUCT_TRAITS_MEMBER(confidence) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(content::SpeechRecognitionResult) - IPC_STRUCT_TRAITS_MEMBER(is_provisional) - IPC_STRUCT_TRAITS_MEMBER(hypotheses) -IPC_STRUCT_TRAITS_END() - -IPC_STRUCT_TRAITS_BEGIN(content::SpeechRecognitionGrammar) - IPC_STRUCT_TRAITS_MEMBER(url) - IPC_STRUCT_TRAITS_MEMBER(weight) -IPC_STRUCT_TRAITS_END() - -// ------- Messages for Speech JS APIs (SpeechRecognitionDispatcher) ---------- - -// Renderer -> Browser messages. - -// Used to start a speech recognition session. -IPC_STRUCT_BEGIN(SpeechRecognitionHostMsg_StartRequest_Params) - // The render frame requesting speech recognition. - IPC_STRUCT_MEMBER(int, render_frame_id) - // Unique ID associated with the JS object making the calls. - IPC_STRUCT_MEMBER(int, request_id) - // Language to use for speech recognition. - IPC_STRUCT_MEMBER(std::string, language) - // Speech grammars to use. - IPC_STRUCT_MEMBER(content::SpeechRecognitionGrammarArray, grammars) - // URL of the page (or iframe if applicable). - IPC_STRUCT_MEMBER(std::string, origin_url) - // Maximum number of hypotheses allowed for each results. - IPC_STRUCT_MEMBER(uint32_t, max_hypotheses) - // Whether the user requested continuous recognition or not. - IPC_STRUCT_MEMBER(bool, continuous) - // Whether the user requested interim results or not. - IPC_STRUCT_MEMBER(bool, interim_results) -IPC_STRUCT_END() - - -// Requests the speech recognition service to start speech recognition. -IPC_MESSAGE_CONTROL1(SpeechRecognitionHostMsg_StartRequest, - SpeechRecognitionHostMsg_StartRequest_Params) - -// Requests the speech recognition service to abort speech recognition on -// behalf of the given |render_frame_id| and |request_id|. If there are no -// sessions associated with the |request_id| in the render frame, this call -// does nothing. -IPC_MESSAGE_CONTROL2(SpeechRecognitionHostMsg_AbortRequest, - int /* render_frame_id */, - int /* request_id */) - -// Requests the speech recognition service to abort all speech recognitions on -// behalf of the given |render_frame_id|. If speech recognition is not happening -// or is happening on behalf of some other render frame, this call does nothing. -IPC_MESSAGE_CONTROL1(SpeechRecognitionHostMsg_AbortAllRequests, - int /* render_frame_id */) - -// Requests the speech recognition service to stop audio capture on behalf of -// the given |render_frame_id|. Any audio recorded so far will be fed to the -// speech recognizer. If speech recognition is not happening nor or is -// happening on behalf of some other render frame, this call does nothing. -IPC_MESSAGE_CONTROL2(SpeechRecognitionHostMsg_StopCaptureRequest, - int /* render_frame_id */, - int /* request_id */) - -// Browser -> Renderer messages. - -// The messages below follow exactly the same semantic of the corresponding -// events defined in content/public/browser/speech_recognition_event_listener.h. -IPC_MESSAGE_ROUTED2(SpeechRecognitionMsg_ResultRetrieved, - int /* request_id */, - content::SpeechRecognitionResults /* results */) - -IPC_MESSAGE_ROUTED2(SpeechRecognitionMsg_ErrorOccurred, - int /* request_id */, - content::SpeechRecognitionError /* error */) - -IPC_MESSAGE_ROUTED1(SpeechRecognitionMsg_Started, int /* request_id */) - -IPC_MESSAGE_ROUTED1(SpeechRecognitionMsg_AudioStarted, int /* request_id */) - -IPC_MESSAGE_ROUTED1(SpeechRecognitionMsg_SoundStarted, int /* request_id */) - -IPC_MESSAGE_ROUTED1(SpeechRecognitionMsg_SoundEnded, int /* request_id */) - -IPC_MESSAGE_ROUTED1(SpeechRecognitionMsg_AudioEnded, int /* request_id */) - -IPC_MESSAGE_ROUTED1(SpeechRecognitionMsg_Ended, int /* request_id */) - -#endif // CONTENT_COMMON_SPEECH_RECOGNITION_MESSAGES_H_ diff --git a/chromium/content/common/speech_recognizer.mojom b/chromium/content/common/speech_recognizer.mojom new file mode 100644 index 00000000000..f790d4c93cb --- /dev/null +++ b/chromium/content/common/speech_recognizer.mojom @@ -0,0 +1,95 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module content.mojom; + +import "content/public/common/speech_recognition_grammar.mojom"; +import "content/public/common/speech_recognition_result.mojom"; +import "content/public/common/speech_recognition_error.mojom"; +import "url/mojom/origin.mojom"; + +// Created by the renderer and sent to the browser to start a speech recognition +// session. +struct StartSpeechRecognitionRequestParams { + // Used to create a connection with a SpeechRecognitionSession implementation + // that will be created when the session is created. + SpeechRecognitionSession& session_request; + + // Used by the browser to communicate with a SpeechRecognitionSessionClient + // implementation created for the new session. + SpeechRecognitionSessionClient client; + + // Language to use for speech recognition. + string language; + + // Speech grammars to use. + array grammars; + + // URL of the page (or iframe if applicable). + url.mojom.Origin origin; + + // Maximum number of hypotheses allowed for each results. + uint32 max_hypotheses; + + // Whether the user requested continuous recognition. + bool continuous; + + // Whether the user requested interim results. + bool interim_results; +}; + +// API for the renderer process to start a speech recognition session in the +// browser process. +interface SpeechRecognizer { + // Requests the speech recognition service to start speech recognition. + Start(StartSpeechRecognitionRequestParams params); +}; + +// API for the renderer process to stop or abort an existing speech recognition +// session. An InterfaceRequest is sent to the browser process via +// SpeechRecognizer::Start, and is bound to an implementation there. +// SpeechRecognitionSession and SpeechRecognitionSessionClient are 1:1 with each +// other and with WebSpeechRecognitionHandle. +interface SpeechRecognitionSession { + // Requests the speech recognition service to abort speech recognition for the + // associated session. + Abort(); + + // Requests the speech recognition service to stop audio capture for the + // associated session. + StopCapture(); +}; + +// API for the browser process to communicate speech recognition related updates +// with renderer and cause events to be dispatched to the appropriate speech +// recognition handle. An InterfacePtr for each handle is sent to the browser +// process via SpeechRecognizer::Start. SpeechRecognitionSession and +// SpeechRecognitionSessionClient are 1:1 with each other and with +// WebSpeechRecognitionHandle. +interface SpeechRecognitionSessionClient { + // Called to dispatch the "result" event. + ResultRetrieved(array results); + + // Called to dispatch the "nomatch" event if the error code passed is of types + // kNoMatch, otherwise dispatchers an "error" event. + ErrorOccurred(content.mojom.SpeechRecognitionError error); + + // Called to dispatch the "start" event. + Started(); + + // Called to dispatch the "audiostart" event. + AudioStarted(); + + // Called to dispatch the "soundstart" and "speechstart" events. + SoundStarted(); + + // Called to dispatch "soundend" and "speechend" events. + SoundEnded(); + + // Called to dispatch the "audioend" event. + AudioEnded(); + + // Called to dispatch the "end" event. + Ended(); +}; diff --git a/chromium/content/common/swapped_out_messages.cc b/chromium/content/common/swapped_out_messages.cc index d7747d332a2..65cbd051764 100644 --- a/chromium/content/common/swapped_out_messages.cc +++ b/chromium/content/common/swapped_out_messages.cc @@ -6,8 +6,6 @@ #include "content/common/accessibility_messages.h" #include "content/common/frame_messages.h" -#include "content/common/input/sync_compositor_messages.h" -#include "content/common/input_messages.h" #include "content/common/view_messages.h" #include "content/public/common/content_client.h" @@ -18,10 +16,6 @@ bool SwappedOutMessages::CanSendWhileSwappedOut(const IPC::Message* msg) { // important (e.g., ACKs) for keeping the browser and renderer state // consistent in case we later return to the same renderer. switch (msg->type()) { - // Handled by RenderWidgetHost. - case InputHostMsg_HandleInputEvent_ACK::ID: - case SyncCompositorHostMsg_SetNeedsBeginFrames::ID: - case ViewHostMsg_ResizeOrRepaint_ACK::ID: // Handled by RenderViewHost. case FrameHostMsg_RenderProcessGone::ID: case ViewHostMsg_ClosePage_ACK::ID: diff --git a/chromium/content/common/throttling_url_loader.cc b/chromium/content/common/throttling_url_loader.cc index 0d0ba0268bf..28a8b328015 100644 --- a/chromium/content/common/throttling_url_loader.cc +++ b/chromium/content/common/throttling_url_loader.cc @@ -181,7 +181,7 @@ ThrottlingURLLoader::~ThrottlingURLLoader() { void ThrottlingURLLoader::FollowRedirect() { if (url_loader_) - url_loader_->FollowRedirect(); + url_loader_->FollowRedirect(base::nullopt); } void ThrottlingURLLoader::SetPriority(net::RequestPriority priority, diff --git a/chromium/content/common/throttling_url_loader_unittest.cc b/chromium/content/common/throttling_url_loader_unittest.cc index b7a22c10b4f..3f6b1610048 100644 --- a/chromium/content/common/throttling_url_loader_unittest.cc +++ b/chromium/content/common/throttling_url_loader_unittest.cc @@ -9,8 +9,8 @@ #include "base/test/scoped_task_environment.h" #include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/url_loader_throttle.h" -#include "content/public/common/weak_wrapper_shared_url_loader_factory.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "services/network/public/mojom/url_loader.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "testing/gtest/include/gtest/gtest.h" @@ -26,8 +26,9 @@ class TestURLLoaderFactory : public network::mojom::URLLoaderFactory, public: TestURLLoaderFactory() : binding_(this), url_loader_binding_(this) { binding_.Bind(mojo::MakeRequest(&factory_ptr_)); - shared_factory_ = base::MakeRefCounted( - factory_ptr_.get()); + shared_factory_ = + base::MakeRefCounted( + factory_ptr_.get()); } ~TestURLLoaderFactory() override { shared_factory_->Detach(); } @@ -94,7 +95,8 @@ class TestURLLoaderFactory : public network::mojom::URLLoaderFactory, } // network::mojom::URLLoader implementation. - void FollowRedirect() override {} + void FollowRedirect(const base::Optional& + modified_request_headers) override {} void ProceedWithResponse() override {} void SetPriority(net::RequestPriority priority, @@ -116,7 +118,7 @@ class TestURLLoaderFactory : public network::mojom::URLLoaderFactory, mojo::Binding url_loader_binding_; network::mojom::URLLoaderFactoryPtr factory_ptr_; network::mojom::URLLoaderClientPtr client_ptr_; - scoped_refptr shared_factory_; + scoped_refptr shared_factory_; DISALLOW_COPY_AND_ASSIGN(TestURLLoaderFactory); }; diff --git a/chromium/content/common/typemaps.gni b/chromium/content/common/typemaps.gni index 27721793d20..f1da0657d12 100644 --- a/chromium/content/common/typemaps.gni +++ b/chromium/content/common/typemaps.gni @@ -4,7 +4,6 @@ typemaps = [ "//content/common/background_fetch/background_fetch_types.typemap", - "//content/common/cache_storage/cache_storage.typemap", "//content/common/frame.typemap", "//content/common/frame_messages.typemap", "//content/common/input/synchronous_compositor.typemap", diff --git a/chromium/content/common/url_loader_factory_bundle.cc b/chromium/content/common/url_loader_factory_bundle.cc index e3d31d68690..28fd78fde19 100644 --- a/chromium/content/common/url_loader_factory_bundle.cc +++ b/chromium/content/common/url_loader_factory_bundle.cc @@ -78,6 +78,11 @@ void URLLoaderFactoryBundle::CreateLoaderAndStart( traffic_annotation); } +void URLLoaderFactoryBundle::Clone( + network::mojom::URLLoaderFactoryRequest request) { + NOTREACHED(); +} + std::unique_ptr URLLoaderFactoryBundle::Clone() { network::mojom::URLLoaderFactoryPtrInfo default_factory_info; diff --git a/chromium/content/common/url_loader_factory_bundle.h b/chromium/content/common/url_loader_factory_bundle.h index 3db8f4fc096..1883f494ebc 100644 --- a/chromium/content/common/url_loader_factory_bundle.h +++ b/chromium/content/common/url_loader_factory_bundle.h @@ -81,7 +81,7 @@ class CONTENT_EXPORT URLLoaderFactoryBundle network::mojom::URLLoaderClientPtr client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) override; - + void Clone(network::mojom::URLLoaderFactoryRequest request) override; std::unique_ptr Clone() override; // The |info| contains replacement factories for a subset of the existing diff --git a/chromium/content/common/url_schemes.cc b/chromium/content/common/url_schemes.cc index a269e1de2cb..8e4a1eb7f38 100644 --- a/chromium/content/common/url_schemes.cc +++ b/chromium/content/common/url_schemes.cc @@ -37,10 +37,10 @@ std::vector& GetMutableSavableSchemes() { return *schemes; } -// Note we store url::Origins here instead of strings to deal with -// canonicalization. -std::vector& GetMutableSecureOrigins() { - static base::NoDestructor> origins; +// This set contains serialized canonicalized origins as well as hostname +// patterns. The latter are canonicalized by component. +std::vector& GetMutableSecureOriginsAndPatterns() { + static base::NoDestructor> origins; return *origins; } @@ -58,6 +58,7 @@ void RegisterContentSchemes(bool lock_schemes) { url::AddStandardScheme(kChromeDevToolsScheme, url::SCHEME_WITH_HOST); url::AddStandardScheme(kChromeUIScheme, url::SCHEME_WITH_HOST); url::AddStandardScheme(kGuestScheme, url::SCHEME_WITH_HOST); + url::AddStandardScheme(kChromeErrorScheme, url::SCHEME_WITH_HOST); for (auto& scheme : schemes.standard_schemes) url::AddStandardScheme(scheme.c_str(), url::SCHEME_WITH_HOST); @@ -106,16 +107,16 @@ void RegisterContentSchemes(bool lock_schemes) { GetMutableServiceWorkerSchemes() = std::move(schemes.service_worker_schemes); - GetMutableSecureOrigins() = std::move(schemes.secure_origins); - network::cors::legacy::RegisterSecureOrigins(GetSecureOrigins()); + GetMutableSecureOriginsAndPatterns() = std::move(schemes.secure_origins); + network::cors::legacy::RegisterSecureOrigins(GetSecureOriginsAndPatterns()); } const std::vector& GetSavableSchemes() { return GetMutableSavableSchemes(); } -const std::vector& GetSecureOrigins() { - return GetMutableSecureOrigins(); +const std::vector& GetSecureOriginsAndPatterns() { + return GetMutableSecureOriginsAndPatterns(); } const std::vector& GetServiceWorkerSchemes() { diff --git a/chromium/content/common/url_schemes.h b/chromium/content/common/url_schemes.h index 52ac01845df..802c80b241c 100644 --- a/chromium/content/common/url_schemes.h +++ b/chromium/content/common/url_schemes.h @@ -28,7 +28,11 @@ CONTENT_EXPORT void RegisterContentSchemes(bool lock_schemes); // See comment in ContentClient::AddAdditionalSchemes for explanations. These // getters can be invoked on any thread. const std::vector& GetSavableSchemes(); -const std::vector& GetSecureOrigins(); +// Contains serialized canonicalized origins as well as hostname patterns such +// as "*.foo.com". An origin should be considered secure if it matches an origin +// in this list or if its hostname matches one of the hostname patterns. The +// hostname patterns are canonicalized by component. +const std::vector& GetSecureOriginsAndPatterns(); const std::vector& GetServiceWorkerSchemes(); } // namespace content diff --git a/chromium/content/common/user_agent.cc b/chromium/content/common/user_agent.cc index b071e6ba90d..e4565757f88 100644 --- a/chromium/content/common/user_agent.cc +++ b/chromium/content/common/user_agent.cc @@ -13,12 +13,10 @@ #include "build/build_config.h" #include "build/util/webkit_version.h" -#if defined(OS_POSIX) && !defined(OS_MACOSX) -#include -#endif - #if defined(OS_WIN) #include "base/win/windows_version.h" +#elif (defined(OS_POSIX) && !defined(OS_MACOSX)) || defined(OS_FUCHSIA) +#include #endif namespace content { @@ -47,21 +45,6 @@ std::string BuildOSCpuInfo() { &os_bugfix_version); #endif -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) - // Should work on any Posix system. - struct utsname unixinfo; - uname(&unixinfo); - - std::string cputype; - // special case for biarch systems - if (strcmp(unixinfo.machine, "x86_64") == 0 && - sizeof(void*) == sizeof(int32_t)) { // NOLINT - cputype.assign("i686 (x86_64)"); - } else { - cputype.assign(unixinfo.machine); - } -#endif - #if defined(OS_WIN) std::string architecture_token; base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); @@ -75,9 +58,7 @@ std::string BuildOSCpuInfo() { else if (windows_architecture == base::win::OSInfo::IA64_ARCHITECTURE) architecture_token = "; Win64; IA64"; } -#endif - -#if defined(OS_ANDROID) +#elif defined(OS_ANDROID) std::string android_version_str = base::SysInfo::OperatingSystemVersion(); std::string android_info_str; @@ -99,6 +80,19 @@ std::string BuildOSCpuInfo() { } android_info_str += " Build/" + android_build_id; } +#elif (defined(OS_POSIX) && !defined(OS_MACOSX)) || defined(OS_FUCHSIA) + // Should work on any Posix system. + struct utsname unixinfo; + uname(&unixinfo); + + std::string cputype; + // special case for biarch systems + if (strcmp(unixinfo.machine, "x86_64") == 0 && + sizeof(void*) == sizeof(int32_t)) { // NOLINT + cputype.assign("i686 (x86_64)"); + } else { + cputype.assign(unixinfo.machine); + } #endif base::StringAppendF( @@ -124,10 +118,10 @@ std::string BuildOSCpuInfo() { "Android %s%s", android_version_str.c_str(), android_info_str.c_str() -#else - "%s %s", - unixinfo.sysname, // e.g. Linux - cputype.c_str() // e.g. i686 +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + "%s %s", + unixinfo.sysname, // e.g. Linux + cputype.c_str() // e.g. i686 #endif ); // NOLINT @@ -144,7 +138,7 @@ std::string getUserAgentPlatform() { "X11; "; // strange, but that's what Firefox uses #elif defined(OS_ANDROID) "Linux; "; -#else +#elif defined(OS_POSIX) || defined(OS_FUCHSIA) "Unknown; "; #endif } diff --git a/chromium/content/common/view_messages.h b/chromium/content/common/view_messages.h index 1f619836b51..7411a755dd7 100644 --- a/chromium/content/common/view_messages.h +++ b/chromium/content/common/view_messages.h @@ -15,10 +15,11 @@ #include #include "base/memory/shared_memory.h" +#include "base/optional.h" #include "base/process/process.h" #include "base/strings/string16.h" #include "build/build_config.h" -#include "cc/ipc/cc_param_traits.h" +#include "cc/input/touch_action.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/quads/shared_bitmap.h" @@ -27,9 +28,9 @@ #include "content/common/date_time_suggestion.h" #include "content/common/frame_replication_state.h" #include "content/common/navigation_gesture.h" -#include "content/common/resize_params.h" #include "content/common/text_input_state.h" #include "content/common/view_message_enums.h" +#include "content/common/visual_properties.h" #include "content/public/common/common_param_traits.h" #include "content/public/common/menu_item.h" #include "content/public/common/page_state.h" @@ -45,8 +46,8 @@ #include "media/capture/ipc/capture_param_traits.h" #include "net/base/network_change_notifier.h" #include "ppapi/buildflags/buildflags.h" -#include "third_party/blink/public/platform/modules/screen_orientation/web_screen_orientation_type.h" -#include "third_party/blink/public/platform/web_display_mode.h" +#include "third_party/blink/public/common/manifest/web_display_mode.h" +#include "third_party/blink/public/common/screen_orientation/web_screen_orientation_type.h" #include "third_party/blink/public/platform/web_float_point.h" #include "third_party/blink/public/platform/web_float_rect.h" #include "third_party/blink/public/platform/web_intrinsic_sizing_info.h" @@ -160,12 +161,11 @@ IPC_STRUCT_TRAITS_BEGIN(blink::WebDeviceEmulationParams) IPC_STRUCT_TRAITS_MEMBER(screen_orientation_type) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(content::ResizeParams) +IPC_STRUCT_TRAITS_BEGIN(content::VisualProperties) IPC_STRUCT_TRAITS_MEMBER(screen_info) IPC_STRUCT_TRAITS_MEMBER(auto_resize_enabled) IPC_STRUCT_TRAITS_MEMBER(min_size_for_auto_resize) IPC_STRUCT_TRAITS_MEMBER(max_size_for_auto_resize) - IPC_STRUCT_TRAITS_MEMBER(auto_resize_sequence_number) IPC_STRUCT_TRAITS_MEMBER(new_size) IPC_STRUCT_TRAITS_MEMBER(compositor_viewport_pixel_size) IPC_STRUCT_TRAITS_MEMBER(browser_controls_shrink_blink_size) @@ -176,8 +176,7 @@ IPC_STRUCT_TRAITS_BEGIN(content::ResizeParams) IPC_STRUCT_TRAITS_MEMBER(visible_viewport_size) IPC_STRUCT_TRAITS_MEMBER(is_fullscreen_granted) IPC_STRUCT_TRAITS_MEMBER(display_mode) - IPC_STRUCT_TRAITS_MEMBER(needs_resize_ack) - IPC_STRUCT_TRAITS_MEMBER(content_source_id) + IPC_STRUCT_TRAITS_MEMBER(capture_sequence_number) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(content::MenuItem) @@ -281,33 +280,6 @@ IPC_STRUCT_BEGIN(ViewHostMsg_SelectionBounds_Params) IPC_STRUCT_MEMBER(bool, is_anchor_first) IPC_STRUCT_END() -IPC_STRUCT_BEGIN(ViewHostMsg_ResizeOrRepaint_ACK_Params) - // The size of the RenderView when this message was generated. This is - // included so the host knows how large the view is from the perspective of - // the renderer process. This is necessary in case a resize operation is in - // progress. If auto-resize is enabled, this should update the corresponding - // view size. - IPC_STRUCT_MEMBER(gfx::Size, view_size) - - // The following describes the various bits that may be set in flags: - // - // ViewHostMsg_ResizeOrRepaint_ACK_Flags::IS_RESIZE_ACK - // Indicates that this is a response to a ViewMsg_Resize message. - // - // ViewHostMsg_ResizeOrRepaint_ACK_Flags::IS_REPAINT_ACK - // Indicates that this is a response to a ViewMsg_Repaint message. - // - // If flags is zero, then this message corresponds to an unsolicited paint - // request by the render view. Any of the above bits may be set in flags, - // which would indicate that this paint message is an ACK for multiple - // request messages. - IPC_STRUCT_MEMBER(int, flags) - - // A unique monotonically increasing sequence number used to identify this - // ACK. - IPC_STRUCT_MEMBER(uint64_t, sequence_number) -IPC_STRUCT_END() - // Messages sent from the browser to the renderer. #if defined(OS_ANDROID) @@ -340,12 +312,14 @@ IPC_MESSAGE_ROUTED1(ViewMsg_UpdateWebPreferences, // Expects a Close_ACK message when finished. IPC_MESSAGE_ROUTED0(ViewMsg_Close) -// Tells the render view to change its size. A ViewHostMsg_ResizeOrRepaint_ACK -// message is generated in response provided new_size is not empty and not equal -// to the view's current size. The generated ViewHostMsg_ResizeOrRepaint_ACK +// Tells the renderer to update visual properties. A +// ViewHostMsg_ResizeOrRepaint_ACK message is generated in response provided +// new_size is not empty and not equal to the view's current size. The +// generated ViewHostMsg_ResizeOrRepaint_ACK // message will have the IS_RESIZE_ACK flag set. It also receives the resizer // rect so that we don't have to fetch it every time WebKit asks for it. -IPC_MESSAGE_ROUTED1(ViewMsg_Resize, content::ResizeParams /* params */) +IPC_MESSAGE_ROUTED1(ViewMsg_SynchronizeVisualProperties, + content::VisualProperties /* params */) // Enables device emulation. See WebDeviceEmulationParams for description. IPC_MESSAGE_ROUTED1(ViewMsg_EnableDeviceEmulation, @@ -413,11 +387,6 @@ IPC_MESSAGE_ROUTED2(ViewMsg_EnumerateDirectoryResponse, // Expects a ClosePage_ACK message when finished. IPC_MESSAGE_ROUTED0(ViewMsg_ClosePage) -// Notifies the renderer that a paint is to be generated for the rectangle -// passed in. -IPC_MESSAGE_ROUTED1(ViewMsg_Repaint, - gfx::Size /* The view size to be repainted */) - // Notification that a move or resize renderer's containing window has // started. IPC_MESSAGE_ROUTED0(ViewMsg_MoveOrResizeStarted) @@ -502,7 +471,7 @@ IPC_MESSAGE_ROUTED1(ViewMsg_PpapiBrokerPermissionResult, // inside the popup, instruct the renderer to generate a synthetic tap at that // offset. IPC_MESSAGE_ROUTED3(ViewMsg_ResolveTapDisambiguation, - double /* timestamp_seconds */, + base::TimeTicks /* timestamp */, gfx::Point /* tap_viewport_offset */, bool /* is_long_press */) @@ -523,6 +492,9 @@ IPC_MESSAGE_ROUTED2(ViewMsg_SetViewportIntersection, // Sets the inert bit on an out-of-process iframe. IPC_MESSAGE_ROUTED1(ViewMsg_SetIsInert, bool /* inert */) +// Sets the inherited effective touch action on an out-of-process iframe. +IPC_MESSAGE_ROUTED1(ViewMsg_SetInheritedEffectiveTouchAction, cc::TouchAction) + // Toggles render throttling for an out-of-process iframe. IPC_MESSAGE_ROUTED2(ViewMsg_UpdateRenderThrottlingStatus, bool /* is_throttled */, @@ -579,12 +551,6 @@ IPC_MESSAGE_ROUTED1(ViewHostMsg_UpdateTargetURL, IPC_MESSAGE_ROUTED1(ViewHostMsg_DocumentAvailableInMainFrame, bool /* uses_temporary_zoom_level */) -// Sent as an acknowledgement to a previous resize request. This indicates that -// the compositor has received a frame from the renderer corresponding to the -// previous reszie request. -IPC_MESSAGE_ROUTED1(ViewHostMsg_ResizeOrRepaint_ACK, - ViewHostMsg_ResizeOrRepaint_ACK_Params) - IPC_MESSAGE_ROUTED0(ViewHostMsg_Focus) IPC_MESSAGE_ROUTED1(ViewHostMsg_SetCursor, content::WebCursor) diff --git a/chromium/content/common/visual_properties.cc b/chromium/content/common/visual_properties.cc new file mode 100644 index 00000000000..34c16c3fba5 --- /dev/null +++ b/chromium/content/common/visual_properties.cc @@ -0,0 +1,18 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/common/visual_properties.h" + +namespace content { + +VisualProperties::VisualProperties() = default; + +VisualProperties::VisualProperties(const VisualProperties& other) = default; + +VisualProperties::~VisualProperties() = default; + +VisualProperties& VisualProperties::operator=(const VisualProperties& other) = + default; + +} // namespace content diff --git a/chromium/content/common/visual_properties.h b/chromium/content/common/visual_properties.h new file mode 100644 index 00000000000..dd29af0cb31 --- /dev/null +++ b/chromium/content/common/visual_properties.h @@ -0,0 +1,81 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_COMMON_VISUAL_PROPERTIES_H_ +#define CONTENT_COMMON_VISUAL_PROPERTIES_H_ + +#include "base/optional.h" +#include "components/viz/common/surfaces/local_surface_id.h" +#include "content/common/content_export.h" +#include "content/public/common/screen_info.h" +#include "third_party/blink/public/common/manifest/web_display_mode.h" +#include "ui/gfx/geometry/size.h" + +namespace content { + +struct CONTENT_EXPORT VisualProperties { + VisualProperties(); + VisualProperties(const VisualProperties& other); + ~VisualProperties(); + + VisualProperties& operator=(const VisualProperties& other); + + // Information about the screen (dpi, depth, etc..). + ScreenInfo screen_info; + + // Whether or not blink should be in auto-resize mode. + bool auto_resize_enabled = false; + + // The minimum size for Blink if auto-resize is enabled. + gfx::Size min_size_for_auto_resize; + + // The maximum size for Blink if auto-resize is enabled. + gfx::Size max_size_for_auto_resize; + + // The size for the widget in DIPs. + gfx::Size new_size; + + // The size of compositor's viewport in pixels. Note that this may differ + // from a ScaleToCeiledSize of |new_size| due to Android's keyboard or due + // to rounding particulars. + gfx::Size compositor_viewport_pixel_size; + + // Whether or not Blink's viewport size should be shrunk by the height of the + // URL-bar (always false on platforms where URL-bar hiding isn't supported). + bool browser_controls_shrink_blink_size = false; + + // Whether or not the focused node should be scrolled into view after the + // resize. + bool scroll_focused_node_into_view = false; + + // The height of the top controls (always 0 on platforms where URL-bar hiding + // isn't supported). + float top_controls_height = 0.f; + + // The height of the bottom controls. + float bottom_controls_height = 0.f; + + // The local surface ID to use (if valid). + base::Optional local_surface_id; + + // 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; + + // Indicates whether tab-initiated fullscreen was granted. + bool is_fullscreen_granted = false; + + // The display mode. + blink::WebDisplayMode display_mode = blink::kWebDisplayModeUndefined; + + // This represents the latest capture sequence number requested. When this is + // incremented, that means the caller wants to synchronize surfaces which + // should cause a new LocalSurfaceId to be generated. + uint32_t capture_sequence_number = 0u; +}; + +} // namespace content + +#endif // CONTENT_COMMON_VISUAL_PROPERTIES_H_ diff --git a/chromium/content/common/wrapper_shared_url_loader_factory.cc b/chromium/content/common/wrapper_shared_url_loader_factory.cc deleted file mode 100644 index 370d3ab507e..00000000000 --- a/chromium/content/common/wrapper_shared_url_loader_factory.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/wrapper_shared_url_loader_factory.h" - - -namespace content { - -WrapperSharedURLLoaderFactoryInfo::WrapperSharedURLLoaderFactoryInfo() = - default; - -WrapperSharedURLLoaderFactoryInfo::WrapperSharedURLLoaderFactoryInfo( - network::mojom::URLLoaderFactoryPtrInfo factory_ptr_info) - : factory_ptr_info_(std::move(factory_ptr_info)) {} - -WrapperSharedURLLoaderFactoryInfo::~WrapperSharedURLLoaderFactoryInfo() = - default; - -scoped_refptr -WrapperSharedURLLoaderFactoryInfo::CreateFactory() { - return base::MakeRefCounted( - std::move(factory_ptr_info_)); -} - -} // namespace content diff --git a/chromium/content/common/wrapper_shared_url_loader_factory.h b/chromium/content/common/wrapper_shared_url_loader_factory.h deleted file mode 100644 index fd1bc0871b8..00000000000 --- a/chromium/content/common/wrapper_shared_url_loader_factory.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_COMMON_WRAPPER_SHARED_URL_LOADER_FACTORY_H_ -#define CONTENT_COMMON_WRAPPER_SHARED_URL_LOADER_FACTORY_H_ - -#include "content/common/content_export.h" -#include "content/common/possibly_associated_interface_ptr.h" -#include "mojo/public/cpp/bindings/interface_request.h" -#include "services/network/public/cpp/shared_url_loader_factory.h" -#include "services/network/public/mojom/url_loader_factory.mojom.h" - -namespace content { - -// A SharedURLLoaderFactoryInfo implementation that wraps a -// network::mojom::URLLoaderFactoryPtrInfo. -class CONTENT_EXPORT WrapperSharedURLLoaderFactoryInfo - : public network::SharedURLLoaderFactoryInfo { - public: - WrapperSharedURLLoaderFactoryInfo(); - explicit WrapperSharedURLLoaderFactoryInfo( - network::mojom::URLLoaderFactoryPtrInfo factory_ptr_info); - - ~WrapperSharedURLLoaderFactoryInfo() override; - - private: - // SharedURLLoaderFactoryInfo implementation. - scoped_refptr CreateFactory() override; - - network::mojom::URLLoaderFactoryPtrInfo factory_ptr_info_; -}; - -// A SharedURLLoaderFactory implementation that wraps a -// PtrTemplateType. -template