diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform')
898 files changed, 31159 insertions, 17717 deletions
diff --git a/chromium/third_party/blink/renderer/platform/BUILD.gn b/chromium/third_party/blink/renderer/platform/BUILD.gn index 2cdb42f959b..6658b51a7d0 100644 --- a/chromium/third_party/blink/renderer/platform/BUILD.gn +++ b/chromium/third_party/blink/renderer/platform/BUILD.gn @@ -4,6 +4,7 @@ import("//build/buildflag_header.gni") import("//build/compiled_action.gni") +import("//build/config/compiler/compiler.gni") import("//build/config/features.gni") import("//build/config/jumbo.gni") import("//build/config/ui.gni") @@ -66,14 +67,10 @@ blink_python_runner("color_data") { script = "../build/scripts/gperf.py" color_data_gperf = "color_data.gperf" - inputs = [ - color_data_gperf, - ] + inputs = [ color_data_gperf ] output_file = "$blink_platform_output_dir/color_data.cc" - outputs = [ - output_file, - ] + outputs = [ output_file ] args = [ gperf_exe, @@ -88,9 +85,7 @@ blink_python_runner("color_data") { compiled_action("character_data") { tool = ":character_data_generator" - outputs = [ - "$blink_platform_output_dir/character_property_data.cc", - ] + outputs = [ "$blink_platform_output_dir/character_property_data.cc" ] args = rebase_path(outputs, root_build_dir) } @@ -150,6 +145,7 @@ group("blink_platform_public_deps") { "//gpu/command_buffer/client:client", "//gpu/command_buffer/client:gles2_interface", "//gpu/command_buffer/common:common", + "//gpu/command_buffer/common:gles2_utils", "//net", "//services/device/public/mojom:generic_sensor_blink", "//services/device/public/mojom:mojom_blink", @@ -185,6 +181,9 @@ group("blink_platform_public_deps") { declare_args() { runtime_call_stats_count_everything = false + + # Enable TRACE_EVENT instrumentation for Blink bindings. Disabled by default as it increases binary size. + enable_blink_bindings_tracing = false } buildflag_header("bindings_buildflags") { @@ -192,8 +191,9 @@ buildflag_header("bindings_buildflags") { header_dir = "third_party/blink/renderer/platform/bindings" flags = [ - "RAW_HEAP_SNAPSHOTS=$v8_enable_raw_heap_snapshots", + "RAW_HEAP_SNAPSHOTS=$enable_additional_blink_object_names", "RCS_COUNT_EVERYTHING=$runtime_call_stats_count_everything", + "BLINK_BINDINGS_TRACE_ENABLED=$enable_blink_bindings_tracing", ] } @@ -225,9 +225,7 @@ config("blink_platform_pch") { } source_set("platform_export") { - sources = [ - "platform_export.h", - ] + sources = [ "platform_export.h" ] visibility = [] # Allow re-assignment of list. visibility = [ @@ -362,8 +360,6 @@ jumbo_component("platform") { "audio/mac/vector_math_mac.h", "audio/media_multi_channel_resampler.cc", "audio/media_multi_channel_resampler.h", - "audio/multi_channel_resampler.cc", - "audio/multi_channel_resampler.h", "audio/panner.cc", "audio/panner.h", "audio/pffft/fft_frame_pffft.cc", @@ -394,6 +390,8 @@ jumbo_component("platform") { "bindings/active_script_wrappable_base.h", "bindings/binding_security_for_platform.cc", "bindings/binding_security_for_platform.h", + "bindings/blink_isolate/blink_isolate.cc", + "bindings/blink_isolate/blink_isolate.h", "bindings/callback_function_base.cc", "bindings/callback_function_base.h", "bindings/callback_interface_base.cc", @@ -406,6 +404,7 @@ jumbo_component("platform") { "bindings/dom_data_store.h", "bindings/dom_wrapper_world.cc", "bindings/dom_wrapper_world.h", + "bindings/enumeration_base.h", "bindings/exception_code.h", "bindings/exception_messages.cc", "bindings/exception_messages.h", @@ -430,7 +429,6 @@ jumbo_component("platform") { "bindings/script_state.h", "bindings/script_wrappable.cc", "bindings/script_wrappable.h", - "bindings/shared_persistent.h", "bindings/string_resource.cc", "bindings/string_resource.h", "bindings/to_v8.h", @@ -438,17 +436,21 @@ jumbo_component("platform") { "bindings/trace_wrapper_v8_reference.h", "bindings/trace_wrapper_v8_string.cc", "bindings/trace_wrapper_v8_string.h", + "bindings/union_base.h", "bindings/v0_custom_element_binding.cc", "bindings/v0_custom_element_binding.h", "bindings/v8_binding.cc", "bindings/v8_binding.h", "bindings/v8_binding_macros.h", "bindings/v8_cross_origin_callback_info.h", + "bindings/v8_cross_origin_property_support.cc", + "bindings/v8_cross_origin_property_support.h", "bindings/v8_dom_activity_logger.cc", "bindings/v8_dom_activity_logger.h", "bindings/v8_dom_wrapper.cc", "bindings/v8_dom_wrapper.h", "bindings/v8_global_value_map.h", + "bindings/v8_interface_bridge.h", "bindings/v8_object_constructor.cc", "bindings/v8_object_constructor.h", "bindings/v8_per_context_data.cc", @@ -457,6 +459,8 @@ jumbo_component("platform") { "bindings/v8_per_isolate_data.h", "bindings/v8_private_property.cc", "bindings/v8_private_property.h", + "bindings/v8_set_return_value.cc", + "bindings/v8_set_return_value.h", "bindings/v8_throw_exception.cc", "bindings/v8_throw_exception.h", "bindings/v8_value_cache.cc", @@ -466,6 +470,9 @@ jumbo_component("platform") { "bindings/wrapper_type_info.cc", "bindings/wrapper_type_info.h", "content_decryption_module_result.h", + "context_lifecycle_notifier.h", + "context_lifecycle_observer.cc", + "context_lifecycle_observer.h", "cookie/canonical_cookie.cc", "cookie/canonical_cookie.h", "cookie/canonical_cookie_mojom_traits.cc", @@ -474,13 +481,14 @@ jumbo_component("platform") { "crypto.cc", "crypto.h", "crypto_result.h", - "cursor.cc", - "cursor.h", + "cursors.cc", + "cursors.h", "data_resource_helper.cc", "data_resource_helper.h", + "disk_data_allocator.cc", + "disk_data_allocator.h", "exported/file_path_conversion.cc", "exported/interface_registry.cc", - "exported/mediastream/media_stream_audio_track.cc", "exported/mediastream/web_media_element_source_utils.cc", "exported/mediastream/web_platform_media_stream_source.cc", "exported/mediastream/web_platform_media_stream_track.cc", @@ -500,21 +508,19 @@ jumbo_component("platform") { "exported/web_crypto_key.cc", "exported/web_crypto_key_algorithm.cc", "exported/web_crypto_result.cc", - "exported/web_cursor_info.cc", "exported/web_data.cc", "exported/web_drag_data.cc", "exported/web_encrypted_media_client.cc", "exported/web_encrypted_media_key_information.cc", "exported/web_encrypted_media_request.cc", + "exported/web_failing_url_loader_factory.cc", "exported/web_font.cc", "exported/web_font_description.cc", - "exported/web_gesture_event.cc", "exported/web_http_body.cc", "exported/web_http_load_info.cc", "exported/web_icon_sizes_parser.cc", "exported/web_image_generator.cc", - "exported/web_input_event.cc", - "exported/web_media_constraints.cc", + "exported/web_isolate.cc", "exported/web_media_player_client.cc", "exported/web_media_player_encrypted_media_client.cc", "exported/web_media_player_source.cc", @@ -524,15 +530,7 @@ jumbo_component("platform") { "exported/web_media_stream_track.cc", "exported/web_memory_pressure_listener.cc", "exported/web_mixed_content.cc", - "exported/web_mouse_event.cc", - "exported/web_mouse_wheel_event.cc", "exported/web_network_state_notifier.cc", - "exported/web_pointer_event.cc", - "exported/web_prerender.cc", - "exported/web_prerendering_support.cc", - "exported/web_resource_timing_info.cc", - "exported/web_rtc_peer_connection_handler_client.cc", - "exported/web_rtc_stats.cc", "exported/web_runtime_features.cc", "exported/web_security_origin.cc", "exported/web_string.cc", @@ -541,10 +539,8 @@ jumbo_component("platform") { "exported/web_text_run.cc", "exported/web_thread_safe_data.cc", "exported/web_time_range.cc", - "exported/web_touch_event.cc", "exported/web_url.cc", "exported/web_url_error.cc", - "exported/web_url_load_timing.cc", "exported/web_url_loader_client.cc", "exported/web_url_loader_test_delegate.cc", "exported/web_url_request.cc", @@ -643,6 +639,9 @@ jumbo_component("platform") { "fonts/opentype/open_type_caps_support.cc", "fonts/opentype/open_type_caps_support.h", "fonts/opentype/open_type_caps_support_mpl.cc", + "fonts/opentype/open_type_math_stretch_data.h", + "fonts/opentype/open_type_math_support.cc", + "fonts/opentype/open_type_math_support.h", "fonts/opentype/open_type_types.h", "fonts/opentype/open_type_vertical_data.cc", "fonts/opentype/open_type_vertical_data.h", @@ -684,6 +683,8 @@ jumbo_component("platform") { "fonts/shaping/shape_result_view.h", "fonts/shaping/shaping_line_breaker.cc", "fonts/shaping/shaping_line_breaker.h", + "fonts/shaping/stretchy_operator_shaper.cc", + "fonts/shaping/stretchy_operator_shaper.h", "fonts/simple_font_data.cc", "fonts/simple_font_data.h", "fonts/skia/font_cache_skia.cc", @@ -719,8 +720,6 @@ jumbo_component("platform") { "fonts/win/dwrite_font_format_support.h", "fonts/win/fallback_family_style_cache_win.cc", "fonts/win/fallback_family_style_cache_win.h", - "fonts/win/fallback_lru_cache_win.cc", - "fonts/win/fallback_lru_cache_win.h", "fonts/win/font_cache_skia_win.cc", "fonts/win/font_fallback_win.cc", "fonts/win/font_fallback_win.h", @@ -951,6 +950,8 @@ jumbo_component("platform") { "graphics/gpu/shared_gpu_context.h", "graphics/gpu/webgl_image_conversion.cc", "graphics/gpu/webgl_image_conversion.h", + "graphics/gpu/webgpu_image_bitmap_handler.cc", + "graphics/gpu/webgpu_image_bitmap_handler.h", "graphics/gpu/webgpu_swap_buffer_provider.cc", "graphics/gpu/webgpu_swap_buffer_provider.h", "graphics/gpu/xr_frame_transport.cc", @@ -974,8 +975,6 @@ jumbo_component("platform") { "graphics/graphics_types.cc", "graphics/graphics_types.h", "graphics/graphics_types_3d.h", - "graphics/hit_test_rect.cc", - "graphics/hit_test_rect.h", "graphics/image.cc", "graphics/image.h", "graphics/image_animation_policy.h", @@ -1000,10 +999,12 @@ jumbo_component("platform") { "graphics/lab_color_space.h", "graphics/logging_canvas.cc", "graphics/logging_canvas.h", - "graphics/mailbox_texture_holder.cc", - "graphics/mailbox_texture_holder.h", "graphics/main_thread_mutator_client.cc", "graphics/main_thread_mutator_client.h", + "graphics/memory_managed_paint_canvas.cc", + "graphics/memory_managed_paint_canvas.h", + "graphics/memory_managed_paint_recorder.cc", + "graphics/memory_managed_paint_recorder.h", "graphics/mutator_client.h", "graphics/offscreen_canvas_placeholder.cc", "graphics/offscreen_canvas_placeholder.h", @@ -1035,10 +1036,10 @@ jumbo_component("platform") { "graphics/paint/geometry_mapper_clip_cache.h", "graphics/paint/geometry_mapper_transform_cache.cc", "graphics/paint/geometry_mapper_transform_cache.h", + "graphics/paint/graphics_layer_display_item.cc", + "graphics/paint/graphics_layer_display_item.h", "graphics/paint/hit_test_data.cc", "graphics/paint/hit_test_data.h", - "graphics/paint/hit_test_display_item.cc", - "graphics/paint/hit_test_display_item.h", "graphics/paint/paint_artifact.cc", "graphics/paint/paint_artifact.h", "graphics/paint/paint_canvas.h", @@ -1067,9 +1068,8 @@ jumbo_component("platform") { "graphics/paint/raster_invalidator.h", "graphics/paint/ref_counted_property_tree_state.h", "graphics/paint/scoped_display_item_fragment.h", + "graphics/paint/scoped_paint_chunk_hint.h", "graphics/paint/scoped_paint_chunk_properties.h", - "graphics/paint/scroll_hit_test_display_item.cc", - "graphics/paint/scroll_hit_test_display_item.h", "graphics/paint/scroll_paint_property_node.cc", "graphics/paint/scroll_paint_property_node.h", "graphics/paint/scrollbar_display_item.cc", @@ -1111,8 +1111,6 @@ jumbo_component("platform") { "graphics/skia/sk_size_hash.h", "graphics/skia/skia_utils.cc", "graphics/skia/skia_utils.h", - "graphics/skia_texture_holder.cc", - "graphics/skia_texture_holder.h", "graphics/squashing_disallowed_reasons.cc", "graphics/squashing_disallowed_reasons.h", "graphics/static_bitmap_image.cc", @@ -1123,8 +1121,9 @@ jumbo_component("platform") { "graphics/subtree_paint_property_update_reason.h", "graphics/surface_layer_bridge.cc", "graphics/surface_layer_bridge.h", - "graphics/texture_holder.h", "graphics/touch_action.h", + "graphics/touch_action_rect.cc", + "graphics/touch_action_rect.h", "graphics/unaccelerated_static_bitmap_image.cc", "graphics/unaccelerated_static_bitmap_image.h", "graphics/video_frame_resource_provider.cc", @@ -1133,6 +1132,7 @@ jumbo_component("platform") { "graphics/video_frame_submitter.h", "graphics/web_graphics_context_3d_provider_wrapper.cc", "graphics/web_graphics_context_3d_provider_wrapper.h", + "heap_observer_list.h", "image-decoders/bmp/bmp_image_decoder.cc", "image-decoders/bmp/bmp_image_decoder.h", "image-decoders/bmp/bmp_image_reader.cc", @@ -1171,8 +1171,6 @@ jumbo_component("platform") { "keyboard_codes.h", "language.cc", "language.h", - "lifecycle_notifier.h", - "lifecycle_observer.h", "link_hash.cc", "link_hash.h", "mac/block_exceptions.h", @@ -1185,6 +1183,7 @@ jumbo_component("platform") { "mac/local_current_graphics_context.mm", "mac/web_core_ns_cell_extras.h", "mac/web_core_ns_cell_extras.mm", + "media/web_audio_source_provider_client.h", "media/webaudiosourceprovider_impl.cc", "media_capabilities/web_audio_configuration.h", "media_capabilities/web_media_capabilities_info.h", @@ -1194,12 +1193,17 @@ jumbo_component("platform") { "mediastream/aec_dump_agent_impl.h", "mediastream/audio_service_audio_processor_proxy.cc", "mediastream/audio_service_audio_processor_proxy.h", + "mediastream/media_constraints.cc", + "mediastream/media_constraints.h", + "mediastream/media_stream_audio_deliverer.h", "mediastream/media_stream_audio_level_calculator.cc", "mediastream/media_stream_audio_level_calculator.h", "mediastream/media_stream_audio_processor_options.cc", "mediastream/media_stream_audio_processor_options.h", "mediastream/media_stream_audio_source.cc", "mediastream/media_stream_audio_source.h", + "mediastream/media_stream_audio_track.cc", + "mediastream/media_stream_audio_track.h", "mediastream/media_stream_component.cc", "mediastream/media_stream_component.h", "mediastream/media_stream_descriptor.cc", @@ -1208,6 +1212,7 @@ jumbo_component("platform") { "mediastream/media_stream_source.h", "mediastream/media_stream_web_audio_source.cc", "mediastream/media_stream_web_audio_source.h", + "mediastream/webaudio_destination_consumer.h", "mediastream/webaudio_media_stream_source.cc", "mediastream/webaudio_media_stream_source.h", "mediastream/webrtc_uma_histograms.cc", @@ -1225,6 +1230,11 @@ jumbo_component("platform") { "mojo/bluetooth_mojom_traits.cc", "mojo/bluetooth_mojom_traits.h", "mojo/fetch_api_request_headers_mojom_traits.h", + "mojo/heap_mojo_receiver.h", + "mojo/heap_mojo_receiver_set.h", + "mojo/heap_mojo_remote.h", + "mojo/heap_mojo_unique_receiver_set.h", + "mojo/heap_mojo_wrapper_mode.h", "mojo/kurl_mojom_traits.h", "mojo/mojo_helper.h", "mojo/security_origin_mojom_traits.h", @@ -1260,6 +1270,10 @@ jumbo_component("platform") { "peerconnection/rtc_api_name.h", "peerconnection/rtc_dtmf_sender_handler.cc", "peerconnection/rtc_dtmf_sender_handler.h", + "peerconnection/rtc_encoded_audio_stream_transformer.cc", + "peerconnection/rtc_encoded_audio_stream_transformer.h", + "peerconnection/rtc_encoded_video_stream_transformer.cc", + "peerconnection/rtc_encoded_video_stream_transformer.h", "peerconnection/rtc_event_log_output_sink.h", "peerconnection/rtc_event_log_output_sink_proxy.cc", "peerconnection/rtc_event_log_output_sink_proxy.h", @@ -1267,6 +1281,9 @@ jumbo_component("platform") { "peerconnection/rtc_ice_candidate_platform.h", "peerconnection/rtc_legacy_stats.h", "peerconnection/rtc_offer_options_platform.h", + "peerconnection/rtc_peer_connection_handler_client.cc", + "peerconnection/rtc_peer_connection_handler_client.h", + "peerconnection/rtc_peer_connection_handler_platform.h", "peerconnection/rtc_rtp_receiver_platform.cc", "peerconnection/rtc_rtp_receiver_platform.h", "peerconnection/rtc_rtp_sender_platform.cc", @@ -1275,6 +1292,7 @@ jumbo_component("platform") { "peerconnection/rtc_rtp_source.h", "peerconnection/rtc_rtp_transceiver_platform.cc", "peerconnection/rtc_rtp_transceiver_platform.h", + "peerconnection/rtc_scoped_refptr_cross_thread_copier.h", "peerconnection/rtc_session_description_platform.cc", "peerconnection/rtc_session_description_platform.h", "peerconnection/rtc_session_description_request.h", @@ -1303,9 +1321,6 @@ jumbo_component("platform") { "peerconnection/webrtc_util.h", "peerconnection/webrtc_video_track_source.cc", "peerconnection/webrtc_video_track_source.h", - "prerender.cc", - "prerender.h", - "prerender_client.h", "resolution_units.h", "supplementable.cc", "supplementable.h", @@ -1416,6 +1431,7 @@ jumbo_component("platform") { "weborigin/origin_access_entry.cc", "weborigin/origin_access_entry.h", "weborigin/referrer.h", + "weborigin/reporting_disposition.h", "weborigin/scheme_registry.cc", "weborigin/scheme_registry.h", "weborigin/security_origin.cc", @@ -1423,7 +1439,6 @@ jumbo_component("platform") { "weborigin/security_origin_hash.h", "weborigin/security_policy.cc", "weborigin/security_policy.h", - "weborigin/security_violation_reporting_policy.h", "webrtc/peer_connection_remote_audio_source.cc", "webrtc/peer_connection_remote_audio_source.h", "webrtc/track_observer.cc", @@ -1434,6 +1449,11 @@ jumbo_component("platform") { "webrtc/webrtc_video_frame_adapter.h", "webrtc/webrtc_video_utils.cc", "webrtc/webrtc_video_utils.h", + "widget/frame_widget.cc", + "widget/frame_widget.h", + "widget/widget_base.cc", + "widget/widget_base.h", + "widget/widget_base_client.h", "windows_keyboard_codes.h", ] @@ -1482,6 +1502,7 @@ jumbo_component("platform") { public_deps = [ ":blink_platform_public_deps", + ":platform_export", "//third_party/blink/renderer/platform/blob", "//third_party/blink/renderer/platform/heap", "//third_party/blink/renderer/platform/instrumentation", @@ -1491,7 +1512,6 @@ jumbo_component("platform") { "//ui/gfx", ] deps = [ - ":platform_export", "//base/allocator:buildflags", "//cc/ipc", "//components/paint_preview/common", @@ -1502,12 +1522,14 @@ jumbo_component("platform") { "//gin", "//jingle:webrtc_glue", "//media", + "//media/capture:capture_switches", "//media/capture/mojom:video_capture", "//mojo/public/cpp/base", "//mojo/public/cpp/bindings", "//mojo/public/cpp/bindings:wtf_support", "//services/service_manager/public/cpp", "//services/viz/public/cpp/gpu", + "//skia", "//skia:skcms", "//third_party:freetype_harfbuzz", "//third_party/abseil-cpp/absl/types:optional", @@ -1515,13 +1537,17 @@ jumbo_component("platform") { "//third_party/blink/public/common", "//third_party/blink/public/mojom:embedded_frame_sink_mojo_bindings_blink", "//third_party/blink/public/strings", + "//third_party/blink/renderer/platform/wtf", "//third_party/ced", "//third_party/emoji-segmenter", + "//third_party/harfbuzz-ng:hb_scoped_util", "//third_party/icu", "//third_party/libyuv", "//third_party/webrtc_overrides:webrtc_component", "//third_party/zlib/google:compression_utils", - "//ui/base:base", + "//ui/base/cursor", + "//ui/base/mojom:cursor_type_blink", + "//ui/events/ipc", "//ui/gfx/geometry", "//ui/gfx/mojom", ] @@ -1597,24 +1623,15 @@ jumbo_component("platform") { deps += [ "//third_party/pffft" ] } + if (!is_debug && !optimize_for_size) { + configs -= [ "//build/config/compiler:default_optimization" ] + configs += [ "//build/config/compiler:optimize_max" ] + } + configs -= [ "//build/config/compiler:default_symbols" ] configs += blink_symbols_config } -source_set("geometry_mojom_traits") { - visibility += [ "//ui/gfx/geometry/mojom:mojom_blink" ] - - sources = [ - "mojo/geometry_mojom_traits.cc", - "mojo/geometry_mojom_traits.h", - ] - - public_deps = [ - "//third_party/blink/public:blink_headers", - "//ui/gfx/geometry/mojom:mojom_blink_headers", - ] -} - jumbo_static_library("test_support") { visibility += [ "//third_party/blink/*" ] testonly = true @@ -1706,6 +1723,7 @@ jumbo_static_library("test_support") { "//mojo/public/cpp/bindings", "//services/service_manager/public/cpp", "//skia", + "//third_party/blink/public/common:common", "//third_party/blink/renderer/platform/blob:test_support", "//third_party/blink/renderer/platform/heap:test_support", "//third_party/blink/renderer/platform/loader:test_support", @@ -1719,9 +1737,7 @@ jumbo_static_library("test_support") { } test("blink_platform_unittests") { - deps = [ - ":blink_platform_unittests_sources", - ] + deps = [ ":blink_platform_unittests_sources" ] } jumbo_source_set("blink_platform_unittests_sources") { @@ -1744,6 +1760,8 @@ jumbo_source_set("blink_platform_unittests_sources") { "bindings/parkable_string_test.cc", "bindings/runtime_call_stats_test.cc", "cookie/canonical_cookie_test.cc", + "disk_data_allocator_test.cc", + "disk_data_allocator_test_utils.h", "exported/file_path_conversion_test.cc", "exported/mediastream/media_stream_audio_test.cc", "exported/page_zoom_test.cc", @@ -1765,6 +1783,7 @@ jumbo_source_set("blink_platform_unittests_sources") { "fonts/generic_font_family_settings_test.cc", "fonts/mac/font_matcher_mac_test.mm", "fonts/opentype/font_settings_test.cc", + "fonts/opentype/open_type_math_support_test.cc", "fonts/opentype/open_type_vertical_data_test.cc", "fonts/orientation_iterator_test.cc", "fonts/script_run_iterator_test.cc", @@ -1776,6 +1795,7 @@ jumbo_source_set("blink_platform_unittests_sources") { "fonts/shaping/shape_result_test.cc", "fonts/shaping/shape_result_view_test.cc", "fonts/shaping/shaping_line_breaker_test.cc", + "fonts/shaping/stretchy_operator_shaper_test.cc", "fonts/small_caps_iterator_test.cc", "fonts/symbols_iterator_test.cc", "fonts/typesetting_features_test.cc", @@ -1817,6 +1837,7 @@ jumbo_source_set("blink_platform_unittests_sources") { "graphics/gpu/drawing_buffer_test.cc", "graphics/gpu/shared_gpu_context_test.cc", "graphics/gpu/webgl_image_conversion_test.cc", + "graphics/gpu/webgpu_image_bitmap_handler_test.cc", "graphics/gpu/webgpu_swap_buffer_provider_test.cc", "graphics/graphics_context_test.cc", "graphics/lab_color_space_test.cc", @@ -1836,11 +1857,13 @@ jumbo_source_set("blink_platform_unittests_sources") { "graphics/paint/paint_property_node_test.cc", "graphics/paint/paint_record_builder_test.cc", "graphics/paint/raster_invalidator_test.cc", + "graphics/paint/scrollbar_display_item_test.cc", "graphics/paint_invalidation_reason_test.cc", "graphics/path_test.cc", "graphics/placeholder_image_test.cc", "graphics/static_bitmap_image_test.cc", "graphics/video_frame_submitter_test.cc", + "heap_observer_list_test.cc", "image-decoders/bmp/bmp_image_decoder_test.cc", "image-decoders/fast_shared_buffer_reader_test.cc", "image-decoders/gif/gif_image_decoder_test.cc", @@ -1857,7 +1880,6 @@ jumbo_source_set("blink_platform_unittests_sources") { "image-decoders/webp/webp_image_decoder_test.cc", "json/json_parser_test.cc", "json/json_values_test.cc", - "lifecycle_context_test.cc", "loader/allowed_by_nosniff_test.cc", "loader/cors/cors_test.cc", "mac/graphics_context_canvas_test.mm", @@ -1866,11 +1888,16 @@ jumbo_source_set("blink_platform_unittests_sources") { "mediastream/webrtc_uma_histograms_test.cc", "mhtml/mhtml_parser_test.cc", "mojo/big_string_mojom_traits_test.cc", - "mojo/geometry_mojom_traits_test.cc", + "mojo/heap_mojo_receiver_set_test.cc", + "mojo/heap_mojo_receiver_test.cc", + "mojo/heap_mojo_remote_test.cc", + "mojo/heap_mojo_unique_receiver_set_test.cc", "mojo/kurl_security_origin_test.cc", "mojo/string16_mojom_traits_test.cc", "p2p/filtering_network_manager_test.cc", "p2p/ipc_network_manager_test.cc", + "peerconnection/rtc_encoded_audio_stream_transformer_test.cc", + "peerconnection/rtc_encoded_video_stream_transformer_test.cc", "peerconnection/rtc_stats_test.cc", "peerconnection/rtc_video_decoder_adapter_test.cc", "peerconnection/rtc_video_encoder_test.cc", @@ -1878,7 +1905,9 @@ jumbo_source_set("blink_platform_unittests_sources") { "peerconnection/task_queue_factory_test.cc", "peerconnection/transmission_encoding_info_handler_test.cc", "peerconnection/two_keys_adapter_map_unittest.cc", + "peerconnection/webrtc_audio_sink_test.cc", "peerconnection/webrtc_video_track_source_test.cc", + "runtime_enabled_features_test.cc", "text/bidi_resolver_test.cc", "text/bidi_test_harness.h", "text/capitalize_test.cc", @@ -1913,8 +1942,10 @@ jumbo_source_set("blink_platform_unittests_sources") { ] if (is_win) { - sources += [ "text/locale_win_test.cc" ] - sources += [ "fonts/win/fallback_lru_cache_win_test.cc" ] + sources += [ + "fonts/win/fallback_lru_cache_win_test.cc", + "text/locale_win_test.cc", + ] } else if (is_mac) { sources += [ "fonts/opentype/open_type_caps_support_test.mm", @@ -1946,7 +1977,6 @@ jumbo_source_set("blink_platform_unittests_sources") { "//mojo/public/cpp/test_support:test_utils", "//mojo/public/interfaces/bindings/tests:test_interfaces_blink", "//services/viz/public/mojom", - "//services/viz/public/mojom:mojom_blink", "//skia", "//skia:skcms", "//skia:test_fonts", @@ -1965,13 +1995,12 @@ jumbo_source_set("blink_platform_unittests_sources") { "//ui/gfx", "//ui/gfx/geometry", "//ui/gfx/geometry/mojom:test_interfaces_blink", + "//ui/gfx/mojom:test_interfaces_blink", "//url", "//url/mojom:test_url_mojom_gurl_blink", ] - data_deps = [ - ":blink_platform_unittests_data", - ] + data_deps = [ ":blink_platform_unittests_data" ] defines = [ "INSIDE_BLINK" ] } @@ -1980,9 +2009,7 @@ executable("image_decode_bench") { visibility = [] # Allow re-assignment of list. visibility = [ "*" ] - sources = [ - "testing/image_decode_bench.cc", - ] + sources = [ "testing/image_decode_bench.cc" ] deps = [ ":platform", @@ -2038,6 +2065,9 @@ group("blink_platform_unittests_data") { # Required by some image decoder tests. "image-decoders/testing/", "//third_party/blink/web_tests/images/resources/", + + # Required by some font tests. + "//third_party/blink/web_tests/external/wpt/fonts/", ] } @@ -2049,9 +2079,7 @@ if (current_cpu == "x86" || current_cpu == "x64") { ":blink_platform_implementation", "//third_party/blink/renderer:non_test_config", ] - public_deps = [ - ":blink_platform_public_deps", - ] + public_deps = [ ":blink_platform_public_deps" ] if (is_win) { cflags = [ "/arch:AVX" ] } else { @@ -2067,9 +2095,7 @@ test("blink_fuzzer_unittests") { "//third_party/blink/public:test_support", ] - sources = [ - "testing/run_all_tests.cc", - ] + sources = [ "testing/run_all_tests.cc" ] if (is_linux) { deps += [ @@ -2097,9 +2123,7 @@ jumbo_source_set("blink_fuzzer_test_support") { # Fuzzer for blink::MHTMLParser. fuzzer_test("mhtml_parser_fuzzer") { - sources = [ - "mhtml/mhtml_fuzzer.cc", - ] + sources = [ "mhtml/mhtml_fuzzer.cc" ] deps = [ ":blink_fuzzer_test_support", ":platform", @@ -2114,9 +2138,7 @@ fuzzer_test("mhtml_parser_fuzzer") { # Fuzzer for blink::WebIconSizesParser. fuzzer_test("web_icon_sizes_fuzzer") { - sources = [ - "exported/web_icon_sizes_fuzzer.cc", - ] + sources = [ "exported/web_icon_sizes_fuzzer.cc" ] deps = [ ":blink_fuzzer_test_support", ":platform", @@ -2125,9 +2147,7 @@ fuzzer_test("web_icon_sizes_fuzzer") { } fuzzer_test("blink_png_decoder_fuzzer") { - sources = [ - "png_fuzzer.cc", - ] + sources = [ "png_fuzzer.cc" ] deps = [ ":blink_fuzzer_test_support", ":platform", @@ -2143,9 +2163,7 @@ fuzzer_test("blink_png_decoder_fuzzer") { # Fuzzer for blink::DateTimeFormat. fuzzer_test("blink_date_time_format_fuzzer") { - sources = [ - "text/date_time_format_fuzzer.cc", - ] + sources = [ "text/date_time_format_fuzzer.cc" ] deps = [ ":blink_fuzzer_test_support", ":platform", @@ -2156,9 +2174,7 @@ fuzzer_test("blink_date_time_format_fuzzer") { # Fuzzer for blink::JSONParser. fuzzer_test("blink_json_parser_fuzzer") { - sources = [ - "json/json_parser_fuzzer.cc", - ] + sources = [ "json/json_parser_fuzzer.cc" ] deps = [ ":blink_fuzzer_test_support", ":platform", @@ -2167,9 +2183,7 @@ fuzzer_test("blink_json_parser_fuzzer") { } fuzzer_test("blink_harfbuzz_shaper_fuzzer") { - sources = [ - "fonts/shaping/harfbuzz_shaper_fuzzer.cc", - ] + sources = [ "fonts/shaping/harfbuzz_shaper_fuzzer.cc" ] deps = [ ":blink_fuzzer_test_support", ":platform", @@ -2179,9 +2193,7 @@ fuzzer_test("blink_harfbuzz_shaper_fuzzer") { } fuzzer_test("blink_http_parsers_fuzzer") { - sources = [ - "network/http_parsers_fuzzer.cc", - ] + sources = [ "network/http_parsers_fuzzer.cc" ] deps = [ ":blink_fuzzer_test_support", ":platform", @@ -2197,9 +2209,7 @@ template("blink_text_codec_fuzzer") { } name = target_name fuzzer_test("blink_text_codec_" + name + "_fuzzer") { - sources = [ - "text_codec_fuzzer.cc", - ] + sources = [ "text_codec_fuzzer.cc" ] deps = [ ":blink_fuzzer_test_support", ":platform", @@ -2249,7 +2259,6 @@ jumbo_source_set("unit_tests") { "graphics/test/stub_image.h", # Tests migrated from the web/tests directory. - "exported/web_resource_timing_info_test.cc", "exported/web_url_request_test.cc", "exported/web_url_response_test.cc", "timer_perf_test.cc", @@ -2263,7 +2272,6 @@ jumbo_source_set("unit_tests") { deps = [ ":test_support", - "//services/viz/public/mojom:mojom_blink", "//testing/gmock", "//testing/gtest", ] diff --git a/chromium/third_party/blink/renderer/platform/DEPS b/chromium/third_party/blink/renderer/platform/DEPS index 4d639942df5..99201dec859 100644 --- a/chromium/third_party/blink/renderer/platform/DEPS +++ b/chromium/third_party/blink/renderer/platform/DEPS @@ -62,6 +62,8 @@ include_rules = [ "+services/metrics/public/cpp/ukm_entry_builder.h", "+services/metrics/public/cpp/ukm_recorder.h", "+services/metrics/public/cpp/ukm_source_id.h", + "+services/network/public/cpp", + "+services/network/public/mojom", "+services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h", "+skia/ext", #TODO(nverne): remove this @@ -69,6 +71,7 @@ include_rules = [ "+third_party/ced/src/compact_enc_det/compact_enc_det.h", "+third_party/khronos", "+third_party/skia", + "+ui/base/cursor/cursor.h", "+ui/base/resource/scale_factor.h", "+ui/gfx", "+url", diff --git a/chromium/third_party/blink/renderer/platform/PRESUBMIT.py b/chromium/third_party/blink/renderer/platform/PRESUBMIT.py index e053782b759..fe39215b5b2 100644 --- a/chromium/third_party/blink/renderer/platform/PRESUBMIT.py +++ b/chromium/third_party/blink/renderer/platform/PRESUBMIT.py @@ -1,7 +1,6 @@ # Copyright 2017 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - """Presubmit script for changes affecting Source/platform. See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts @@ -42,9 +41,12 @@ def _CheckRuntimeEnabledFeaturesSorted(input_api, output_api): # Diff the sorted/unsorted versions. differ = difflib.Differ() diff = differ.compare(features, features_sorted) - return [output_api.PresubmitError( - 'runtime_enabled_features.json5 features must be sorted alphabetically. ' - 'Diff of feature order follows:', long_text='\n'.join(diff))] + return [ + output_api.PresubmitError( + 'runtime_enabled_features.json5 features must be sorted alphabetically. ' + 'Diff of feature order follows:', + long_text='\n'.join(diff)) + ] def _CommonChecks(input_api, output_api): diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc index 4ea0dacd875..f02f038486a 100644 --- a/chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc +++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation.cc @@ -14,8 +14,7 @@ namespace blink { std::unique_ptr<CompositorAnimation> CompositorAnimation::Create() { return std::make_unique<CompositorAnimation>( - cc::SingleKeyframeEffectAnimation::Create( - cc::AnimationIdProvider::NextAnimationId())); + cc::Animation::Create(cc::AnimationIdProvider::NextAnimationId())); } std::unique_ptr<CompositorAnimation> @@ -23,17 +22,14 @@ CompositorAnimation::CreateWorkletAnimation( cc::WorkletAnimationId worklet_animation_id, const String& name, double playback_rate, - std::unique_ptr<CompositorScrollTimeline> scroll_timeline, std::unique_ptr<cc::AnimationOptions> options, std::unique_ptr<cc::AnimationEffectTimings> effect_timings) { return std::make_unique<CompositorAnimation>(cc::WorkletAnimation::Create( - worklet_animation_id, name.Utf8(), playback_rate, - std::move(scroll_timeline), std::move(options), + worklet_animation_id, name.Utf8(), playback_rate, std::move(options), std::move(effect_timings))); } -CompositorAnimation::CompositorAnimation( - scoped_refptr<cc::SingleKeyframeEffectAnimation> animation) +CompositorAnimation::CompositorAnimation(scoped_refptr<cc::Animation> animation) : animation_(animation), delegate_() {} CompositorAnimation::~CompositorAnimation() { @@ -44,7 +40,7 @@ CompositorAnimation::~CompositorAnimation() { animation_->animation_timeline()->DetachAnimation(animation_); } -cc::SingleKeyframeEffectAnimation* CompositorAnimation::CcAnimation() const { +cc::Animation* CompositorAnimation::CcAnimation() const { return animation_.get(); } @@ -76,7 +72,7 @@ void CompositorAnimation::RemoveKeyframeModel(int keyframe_model_id) { } void CompositorAnimation::PauseKeyframeModel(int keyframe_model_id, - double time_offset) { + base::TimeDelta time_offset) { animation_->PauseKeyframeModel(keyframe_model_id, time_offset); } @@ -88,9 +84,8 @@ void CompositorAnimation::UpdateScrollTimeline( base::Optional<cc::ElementId> element_id, base::Optional<double> start_scroll_offset, base::Optional<double> end_scroll_offset) { - cc::ToWorkletAnimation(animation_.get()) - ->UpdateScrollTimeline(element_id, start_scroll_offset, - end_scroll_offset); + animation_->UpdateScrollTimeline(element_id, start_scroll_offset, + end_scroll_offset); } void CompositorAnimation::UpdatePlaybackRate(double playback_rate) { diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation.h b/chromium/third_party/blink/renderer/platform/animation/compositor_animation.h index e900e473315..348a4480437 100644 --- a/chromium/third_party/blink/renderer/platform/animation/compositor_animation.h +++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation.h @@ -10,9 +10,8 @@ #include "base/macros.h" #include "base/memory/scoped_refptr.h" #include "base/optional.h" +#include "cc/animation/animation.h" #include "cc/animation/animation_delegate.h" -#include "cc/animation/scroll_timeline.h" -#include "cc/animation/single_keyframe_effect_animation.h" #include "cc/animation/worklet_animation.h" #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h" #include "third_party/blink/renderer/platform/platform_export.h" @@ -24,8 +23,6 @@ class AnimationCurve; namespace blink { -using CompositorScrollTimeline = cc::ScrollTimeline; - class CompositorAnimationDelegate; class CompositorKeyframeModel; @@ -37,15 +34,13 @@ class PLATFORM_EXPORT CompositorAnimation : public cc::AnimationDelegate { cc::WorkletAnimationId, const String& name, double playback_rate, - std::unique_ptr<CompositorScrollTimeline>, std::unique_ptr<cc::AnimationOptions>, std::unique_ptr<cc::AnimationEffectTimings> effect_timings); - explicit CompositorAnimation( - scoped_refptr<cc::SingleKeyframeEffectAnimation>); + explicit CompositorAnimation(scoped_refptr<cc::Animation>); ~CompositorAnimation() override; - cc::SingleKeyframeEffectAnimation* CcAnimation() const; + cc::Animation* CcAnimation() const; // An animation delegate is notified when animations are started and stopped. // The CompositorAnimation does not take ownership of the delegate, and @@ -59,7 +54,7 @@ class PLATFORM_EXPORT CompositorAnimation : public cc::AnimationDelegate { void AddKeyframeModel(std::unique_ptr<CompositorKeyframeModel>); void RemoveKeyframeModel(int keyframe_model_id); - void PauseKeyframeModel(int keyframe_model_id, double time_offset); + void PauseKeyframeModel(int keyframe_model_id, base::TimeDelta time_offset); void AbortKeyframeModel(int keyframe_model_id); void UpdateScrollTimeline(base::Optional<cc::ElementId>, @@ -85,7 +80,7 @@ class PLATFORM_EXPORT CompositorAnimation : public cc::AnimationDelegate { void NotifyLocalTimeUpdated( base::Optional<base::TimeDelta> local_time) override; - scoped_refptr<cc::SingleKeyframeEffectAnimation> animation_; + scoped_refptr<cc::Animation> animation_; CompositorAnimationDelegate* delegate_; DISALLOW_COPY_AND_ASSIGN(CompositorAnimation); diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_test.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_test.cc index c40fb3822d0..25a9e0b34ae 100644 --- a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_test.cc +++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_test.cc @@ -60,7 +60,7 @@ TEST_F(CompositorAnimationTest, NullDelegate) { std::unique_ptr<CompositorAnimationTestClient> client( new CompositorAnimationTestClient); CompositorAnimation* animation = client->GetCompositorAnimation(); - cc::SingleKeyframeEffectAnimation* cc_animation = animation->CcAnimation(); + cc::Animation* cc_animation = animation->CcAnimation(); timeline->AnimationAttached(*client); int timeline_id = cc_animation->animation_timeline()->id(); @@ -93,8 +93,7 @@ TEST_F(CompositorAnimationTest, NotifyFromCCAfterCompositorAnimationDeletion) { std::unique_ptr<CompositorAnimationTestClient> client( new CompositorAnimationTestClient); CompositorAnimation* animation = client->GetCompositorAnimation(); - scoped_refptr<cc::SingleKeyframeEffectAnimation> cc_animation = - animation->CcAnimation(); + scoped_refptr<cc::Animation> cc_animation = animation->CcAnimation(); timeline->AnimationAttached(*client); int timeline_id = cc_animation->animation_timeline()->id(); @@ -129,8 +128,7 @@ TEST_F(CompositorAnimationTest, scoped_refptr<cc::AnimationTimeline> cc_timeline = timeline->GetAnimationTimeline(); - scoped_refptr<cc::SingleKeyframeEffectAnimation> cc_animation = - client->animation_->CcAnimation(); + scoped_refptr<cc::Animation> cc_animation = client->animation_->CcAnimation(); EXPECT_FALSE(cc_animation->animation_timeline()); timeline->AnimationAttached(*client); diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc index eca78aece63..3da0644ab74 100644 --- a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc +++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.cc @@ -15,6 +15,10 @@ CompositorAnimationTimeline::CompositorAnimationTimeline() : animation_timeline_(cc::AnimationTimeline::Create( cc::AnimationIdProvider::NextTimelineId())) {} +CompositorAnimationTimeline::CompositorAnimationTimeline( + scoped_refptr<cc::AnimationTimeline> timeline) + : animation_timeline_(timeline) {} + CompositorAnimationTimeline::~CompositorAnimationTimeline() { // Detach timeline from host, otherwise it stays there (leaks) until // compositor shutdown. diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h index b4e6b8427d4..8307cd8f257 100644 --- a/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h +++ b/chromium/third_party/blink/renderer/platform/animation/compositor_animation_timeline.h @@ -24,6 +24,7 @@ class PLATFORM_EXPORT CompositorAnimationTimeline { public: CompositorAnimationTimeline(); + explicit CompositorAnimationTimeline(scoped_refptr<cc::AnimationTimeline>); ~CompositorAnimationTimeline(); cc::AnimationTimeline* GetAnimationTimeline() const; diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc index 656f2f22d6d..8aeb308e331 100644 --- a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc +++ b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.cc @@ -90,9 +90,8 @@ double CompositorKeyframeModel::TimeOffset() const { return keyframe_model_->time_offset().InSecondsF(); } -void CompositorKeyframeModel::SetTimeOffset(double monotonic_time) { - keyframe_model_->set_time_offset( - base::TimeDelta::FromSecondsD(monotonic_time)); +void CompositorKeyframeModel::SetTimeOffset(base::TimeDelta monotonic_time) { + keyframe_model_->set_time_offset(monotonic_time); } blink::CompositorKeyframeModel::Direction diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h index 3002115a428..7144083160b 100644 --- a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h +++ b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model.h @@ -63,7 +63,7 @@ class PLATFORM_EXPORT CompositorKeyframeModel { void SetStartTime(base::TimeTicks); double TimeOffset() const; - void SetTimeOffset(double monotonic_time); + void SetTimeOffset(base::TimeDelta monotonic_time); Direction GetDirection() const; void SetDirection(Direction); diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model_test.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model_test.cc index d6e98d70c29..98c3567a652 100644 --- a/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model_test.cc +++ b/chromium/third_party/blink/renderer/platform/animation/compositor_keyframe_model_test.cc @@ -28,7 +28,7 @@ TEST(WebCompositorAnimationTest, ModifiedSettings) { *curve, compositor_target_property::OPACITY, 0, 1); keyframe_model->SetIterations(2); keyframe_model->SetStartTime(2); - keyframe_model->SetTimeOffset(2); + keyframe_model->SetTimeOffset(base::TimeDelta::FromSeconds(2)); keyframe_model->SetDirection(CompositorKeyframeModel::Direction::REVERSE); EXPECT_EQ(2, keyframe_model->Iterations()); diff --git a/chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.cc b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.cc index 001388046da..9ee7cf65d70 100644 --- a/chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.cc +++ b/chromium/third_party/blink/renderer/platform/animation/compositor_transform_operations.cc @@ -28,29 +28,29 @@ void CompositorTransformOperations::AppendTranslate(double x, double y, double z) { transform_operations_.AppendTranslate( - SkDoubleToMScalar(x), SkDoubleToMScalar(y), SkDoubleToMScalar(z)); + SkDoubleToScalar(x), SkDoubleToScalar(y), SkDoubleToScalar(z)); } void CompositorTransformOperations::AppendRotate(double x, double y, double z, double degrees) { - transform_operations_.AppendRotate(SkDoubleToMScalar(x), SkDoubleToMScalar(y), - SkDoubleToMScalar(z), - SkDoubleToMScalar(degrees)); + transform_operations_.AppendRotate(SkDoubleToScalar(x), SkDoubleToScalar(y), + SkDoubleToScalar(z), + SkDoubleToScalar(degrees)); } void CompositorTransformOperations::AppendScale(double x, double y, double z) { - transform_operations_.AppendScale(SkDoubleToMScalar(x), SkDoubleToMScalar(y), - SkDoubleToMScalar(z)); + transform_operations_.AppendScale(SkDoubleToScalar(x), SkDoubleToScalar(y), + SkDoubleToScalar(z)); } void CompositorTransformOperations::AppendSkew(double x, double y) { - transform_operations_.AppendSkew(SkDoubleToMScalar(x), SkDoubleToMScalar(y)); + transform_operations_.AppendSkew(SkDoubleToScalar(x), SkDoubleToScalar(y)); } void CompositorTransformOperations::AppendPerspective(double depth) { - transform_operations_.AppendPerspective(SkDoubleToMScalar(depth)); + transform_operations_.AppendPerspective(SkDoubleToScalar(depth)); } void CompositorTransformOperations::AppendMatrix(const SkMatrix44& matrix) { diff --git a/chromium/third_party/blink/renderer/platform/animation/timing_function.cc b/chromium/third_party/blink/renderer/platform/animation/timing_function.cc index 2ea4d36719a..5d8e9c8806a 100644 --- a/chromium/third_party/blink/renderer/platform/animation/timing_function.cc +++ b/chromium/third_party/blink/renderer/platform/animation/timing_function.cc @@ -200,7 +200,7 @@ bool operator==(const CubicBezierTimingFunction& lhs, if (rhs.GetType() != TimingFunction::Type::CUBIC_BEZIER) return false; - const CubicBezierTimingFunction& ctf = ToCubicBezierTimingFunction(rhs); + const auto& ctf = To<CubicBezierTimingFunction>(rhs); if ((lhs.GetEaseType() == CubicBezierTimingFunction::EaseType::CUSTOM) && (ctf.GetEaseType() == CubicBezierTimingFunction::EaseType::CUSTOM)) return (lhs.X1() == ctf.X1()) && (lhs.Y1() == ctf.Y1()) && @@ -213,7 +213,7 @@ bool operator==(const StepsTimingFunction& lhs, const TimingFunction& rhs) { if (rhs.GetType() != TimingFunction::Type::STEPS) return false; - const StepsTimingFunction& stf = ToStepsTimingFunction(rhs); + const auto& stf = To<StepsTimingFunction>(rhs); return (lhs.NumberOfSteps() == stf.NumberOfSteps()) && (lhs.GetStepPosition() == stf.GetStepPosition()); } @@ -223,15 +223,15 @@ bool operator==(const StepsTimingFunction& lhs, const TimingFunction& rhs) { bool operator==(const TimingFunction& lhs, const TimingFunction& rhs) { switch (lhs.GetType()) { case TimingFunction::Type::LINEAR: { - const LinearTimingFunction& linear = ToLinearTimingFunction(lhs); + const auto& linear = To<LinearTimingFunction>(lhs); return (linear == rhs); } case TimingFunction::Type::CUBIC_BEZIER: { - const CubicBezierTimingFunction& cubic = ToCubicBezierTimingFunction(lhs); + const auto& cubic = To<CubicBezierTimingFunction>(lhs); return (cubic == rhs); } case TimingFunction::Type::STEPS: { - const StepsTimingFunction& step = ToStepsTimingFunction(lhs); + const auto& step = To<StepsTimingFunction>(lhs); return (step == rhs); } default: diff --git a/chromium/third_party/blink/renderer/platform/animation/timing_function.h b/chromium/third_party/blink/renderer/platform/animation/timing_function.h index f7a9eaef5b1..8cf6ef43f37 100644 --- a/chromium/third_party/blink/renderer/platform/animation/timing_function.h +++ b/chromium/third_party/blink/renderer/platform/animation/timing_function.h @@ -30,6 +30,7 @@ #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h" @@ -217,14 +218,24 @@ PLATFORM_EXPORT bool operator==(const StepsTimingFunction&, PLATFORM_EXPORT bool operator==(const TimingFunction&, const TimingFunction&); PLATFORM_EXPORT bool operator!=(const TimingFunction&, const TimingFunction&); -#define DEFINE_TIMING_FUNCTION_TYPE_CASTS(typeName, enumName) \ - DEFINE_TYPE_CASTS(typeName##TimingFunction, TimingFunction, value, \ - value->GetType() == TimingFunction::Type::enumName, \ - value.GetType() == TimingFunction::Type::enumName) - -DEFINE_TIMING_FUNCTION_TYPE_CASTS(Linear, LINEAR); -DEFINE_TIMING_FUNCTION_TYPE_CASTS(CubicBezier, CUBIC_BEZIER); -DEFINE_TIMING_FUNCTION_TYPE_CASTS(Steps, STEPS); +template <> +struct DowncastTraits<LinearTimingFunction> { + static bool AllowFrom(const TimingFunction& value) { + return value.GetType() == TimingFunction::Type::LINEAR; + } +}; +template <> +struct DowncastTraits<CubicBezierTimingFunction> { + static bool AllowFrom(const TimingFunction& value) { + return value.GetType() == TimingFunction::Type::CUBIC_BEZIER; + } +}; +template <> +struct DowncastTraits<StepsTimingFunction> { + static bool AllowFrom(const TimingFunction& value) { + return value.GetType() == TimingFunction::Type::STEPS; + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/audio/DEPS b/chromium/third_party/blink/renderer/platform/audio/DEPS index 5a7148091dd..970052c08fd 100644 --- a/chromium/third_party/blink/renderer/platform/audio/DEPS +++ b/chromium/third_party/blink/renderer/platform/audio/DEPS @@ -12,7 +12,6 @@ include_rules = [ "+third_party/blink/renderer/platform/wtf/cross_thread_functional.h", "+third_party/blink/renderer/platform/geometry/float_point_3d.h", "+third_party/blink/renderer/platform/heap", - "+third_party/blink/renderer/platform/instrumentation/histogram.h", "+third_party/blink/renderer/platform/instrumentation", "+third_party/blink/renderer/platform/platform_export.h", "+third_party/blink/renderer/platform/scheduler/public", diff --git a/chromium/third_party/blink/renderer/platform/audio/OWNERS b/chromium/third_party/blink/renderer/platform/audio/OWNERS index caaf05cf1d8..bcc0e71a548 100644 --- a/chromium/third_party/blink/renderer/platform/audio/OWNERS +++ b/chromium/third_party/blink/renderer/platform/audio/OWNERS @@ -1,5 +1,4 @@ hongchan@chromium.org -kbr@chromium.org rtoy@chromium.org # COMPONENT: Blink>WebAudio diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc b/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc index 3e2f1a5a3fa..819ca91d754 100644 --- a/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc +++ b/chromium/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc @@ -61,7 +61,8 @@ size_t AudioDelayDSPKernel::BufferLengthForDelay(double max_delay_time, // Compute the length of the buffer needed to handle a max delay of // |maxDelayTime|. One is added to handle the case where the actual delay // equals the maximum delay. - return 1 + audio_utilities::TimeToSampleFrame(max_delay_time, sample_rate); + return 1 + audio_utilities::TimeToSampleFrame(max_delay_time, sample_rate, + audio_utilities::kRoundUp); } bool AudioDelayDSPKernel::HasSampleAccurateValues() { @@ -79,14 +80,14 @@ double AudioDelayDSPKernel::DelayTime(float sample_rate) { void AudioDelayDSPKernel::Process(const float* source, float* destination, uint32_t frames_to_process) { - size_t buffer_length = buffer_.size(); + int buffer_length = buffer_.size(); float* buffer = buffer_.Data(); DCHECK(buffer_length); DCHECK(source); DCHECK(destination); DCHECK_GE(write_index_, 0); - DCHECK_LT(static_cast<size_t>(write_index_), buffer_length); + DCHECK_LT(write_index_, buffer_length); float sample_rate = this->SampleRate(); double max_time = MaxDelayTime(); @@ -112,28 +113,25 @@ void AudioDelayDSPKernel::Process(const float* source, // Linearly interpolate in-between delay times. int read_index1 = static_cast<int>(read_position); DCHECK_GE(read_index1, 0); - DCHECK_LT(static_cast<size_t>(read_index1), buffer_length); + DCHECK_LT(read_index1, buffer_length); int read_index2 = read_index1 + 1; - if (read_index2 >= static_cast<int>(buffer_length)) + if (read_index2 >= buffer_length) read_index2 -= buffer_length; DCHECK_GE(read_index2, 0); - DCHECK_LT(static_cast<size_t>(read_index2), buffer_length); - - double interpolation_factor = read_position - read_index1; + DCHECK_LT(read_index2, buffer_length); buffer[w_index] = *source++; - ++w_index; - if (w_index >= static_cast<int>(buffer_length)) - w_index -= buffer_length; + float interpolation_factor = read_position - read_index1; float sample1 = buffer[read_index1]; float sample2 = buffer[read_index2]; - double output = - (1 - interpolation_factor) * sample1 + interpolation_factor * sample2; + ++w_index; + if (w_index >= buffer_length) + w_index -= buffer_length; - *destination++ = static_cast<float>(output); + *destination++ = sample1 + interpolation_factor * (sample2 - sample1); } write_index_ = w_index; @@ -152,7 +150,8 @@ void AudioDelayDSPKernel::Process(const float* source, // Make sure the delay time is in a valid range. delay_time = clampTo(delay_time, 0.0, max_time); double desired_delay_frames = delay_time * sample_rate; - double read_position = write_index_ + buffer_length - desired_delay_frames; + int w_index = write_index_; + double read_position = w_index + buffer_length - desired_delay_frames; if (read_position >= buffer_length) read_position -= buffer_length; @@ -161,34 +160,34 @@ void AudioDelayDSPKernel::Process(const float* source, // interpolation. int read_index1 = static_cast<int>(read_position); int read_index2 = (read_index1 + 1) % buffer_length; - double interp_factor = read_position - read_index1; + float interp_factor = read_position - read_index1; - int w_index = write_index_; + float* w = &buffer[w_index]; + float* r1 = &buffer[read_index1]; + float* r2 = &buffer[read_index2]; + float* buffer_end = &buffer[buffer_length]; for (unsigned i = 0; i < frames_to_process; ++i) { // Copy the latest sample into the buffer. Needed because // w_index could be the same as read_index1 or read_index2. - buffer[w_index] = *source++; - float sample1 = buffer[read_index1]; - float sample2 = buffer[read_index2]; + *w++ = *source++; + float sample1 = *r1++; + float sample2 = *r2++; // Update the indices and wrap them to the beginning of the buffer if // needed. - ++w_index; - ++read_index1; - ++read_index2; - if (w_index >= static_cast<int>(buffer_length)) - w_index -= buffer_length; - if (read_index1 >= static_cast<int>(buffer_length)) - read_index1 -= buffer_length; - if (read_index2 >= static_cast<int>(buffer_length)) - read_index2 -= buffer_length; + if (w >= buffer_end) + w = buffer; + if (r1 >= buffer_end) + r1 = buffer; + if (r2 >= buffer_end) + r2 = buffer; // Linearly interpolate between samples. - *destination++ = (1 - interp_factor) * sample1 + interp_factor * sample2; + *destination++ = sample1 + interp_factor * (sample2 - sample1); } - write_index_ = w_index; + write_index_ = w - buffer; } } diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc b/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc index ad2ac3c5a15..c66a178d20a 100644 --- a/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc +++ b/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc @@ -32,13 +32,13 @@ #include <memory> #include <utility> +#include "base/metrics/histogram_functions.h" #include "media/base/audio_bus.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_audio_latency_hint.h" #include "third_party/blink/renderer/platform/audio/audio_utilities.h" #include "third_party/blink/renderer/platform/audio/push_pull_fifo.h" #include "third_party/blink/renderer/platform/audio/vector_math.h" -#include "third_party/blink/renderer/platform/instrumentation/histogram.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" @@ -67,7 +67,6 @@ AudioDestination::AudioDestination(AudioIOCallback& callback, const WebAudioLatencyHint& latency_hint, base::Optional<float> context_sample_rate) : number_of_output_channels_(number_of_output_channels), - play_state_(PlayState::kStopped), fifo_( std::make_unique<PushPullFIFO>(number_of_output_channels, kFIFOSize)), output_bus_(AudioBus::Create(number_of_output_channels, @@ -76,7 +75,8 @@ AudioDestination::AudioDestination(AudioIOCallback& callback, render_bus_(AudioBus::Create(number_of_output_channels, audio_utilities::kRenderQuantumFrames)), callback_(callback), - frames_elapsed_(0) { + frames_elapsed_(0), + device_state_(DeviceState::kStopped) { // Create WebAudioDevice. blink::WebAudioDevice is designed to support the // local input (e.g. loopback from OS audio system), but Chromium's media // renderer does not support it currently. Thus, we use zero for the number @@ -126,29 +126,23 @@ AudioDestination::AudioDestination(AudioIOCallback& callback, context_sample_rate_ = web_audio_device_->SampleRate(); } - DEFINE_STATIC_LOCAL(SparseHistogram, sample_rate_histogram, - ("WebAudio.AudioContext.HardwareSampleRate")); - - sample_rate_histogram.Sample(web_audio_device_->SampleRate()); - - // The actual supplied |sampleRate| is probably a small set including 44100, - // 48000, 22050, and 2400 Hz. Other valid values range from 3000 to 384000 - // Hz, but are not expected to be used much. - DEFINE_STATIC_LOCAL(SparseHistogram, selected_sample_rate_histogram, - ("WebAudio.AudioContextOptions.sampleRate")); - - // From the expected values above and the common HW sample rates, we expect - // the most common ratios to be the set 0.5, 44100/48000, and 48000/44100. - // Other values are possible but seem unlikely. - DEFINE_STATIC_LOCAL(SparseHistogram, sample_rate_ratio_histogram, - ("WebAudio.AudioContextOptions.sampleRateRatio")); + base::UmaHistogramSparse("WebAudio.AudioContext.HardwareSampleRate", + web_audio_device_->SampleRate()); // Record the selected sample rate and ratio if the sampleRate was given. The // ratio is recorded as a percentage, rounded to the nearest percent. if (context_sample_rate.has_value()) { - selected_sample_rate_histogram.Sample(context_sample_rate.value()); - sample_rate_ratio_histogram.Sample( - static_cast<int32_t>(100 * scale_factor + 0.5)); + // The actual supplied |sampleRate| is probably a small set including 44100, + // 48000, 22050, and 2400 Hz. Other valid values range from 3000 to 384000 + // Hz, but are not expected to be used much. + base::UmaHistogramSparse("WebAudio.AudioContextOptions.sampleRate", + context_sample_rate.value()); + // From the expected values above and the common HW sample rates, we expect + // the most common ratios to be the set 0.5, 44100/48000, and 48000/44100. + // Other values are possible but seem unlikely. + base::UmaHistogramSparse("WebAudio.AudioContextOptions.sampleRateRatio", + + static_cast<int32_t>(100 * scale_factor + 0.5)); } } @@ -214,6 +208,16 @@ void AudioDestination::RequestRender(size_t frames_requested, "frames_to_render", frames_to_render, "timestamp (s)", delay_timestamp); + MutexTryLocker locker(state_change_lock_); + + // The state might be changing by ::Stop() call. If the state is locked, do + // not touch the below. + if (!locker.Locked()) + return; + + if (device_state_ != DeviceState::kRunning) + return; + metric_reporter_.BeginTrace(); frames_elapsed_ -= std::min(frames_elapsed_, prior_frames_skipped); @@ -240,8 +244,8 @@ void AudioDestination::RequestRender(size_t frames_requested, output_position_.position = 0.0; if (resampler_) { - resampler_->Resample(audio_utilities::kRenderQuantumFrames, - resampler_bus_.get()); + resampler_->ResampleInternal(audio_utilities::kRenderQuantumFrames, + resampler_bus_.get()); } else { // Process WebAudio graph and push the rendered output to FIFO. callback_.Render(render_bus_.get(), audio_utilities::kRenderQuantumFrames, @@ -258,64 +262,72 @@ void AudioDestination::RequestRender(size_t frames_requested, void AudioDestination::Start() { DCHECK(IsMainThread()); + TRACE_EVENT0("webaudio", "AudioDestination::Start"); - // Start the "audio device" after the rendering thread is ready. - if (web_audio_device_ && play_state_ == PlayState::kStopped) { - TRACE_EVENT0("webaudio", "AudioDestination::Start"); - web_audio_device_->Start(); - play_state_ = PlayState::kPlaying; - } + if (device_state_ != DeviceState::kStopped) + return; + web_audio_device_->Start(); + SetDeviceState(DeviceState::kRunning); } void AudioDestination::StartWithWorkletTaskRunner( scoped_refptr<base::SingleThreadTaskRunner> worklet_task_runner) { DCHECK(IsMainThread()); + DCHECK_EQ(worklet_task_runner_, nullptr); + TRACE_EVENT0("webaudio", "AudioDestination::StartWithWorkletTaskRunner"); - if (web_audio_device_ && play_state_ == PlayState::kStopped) { - TRACE_EVENT0("webaudio", "AudioDestination::Start"); - worklet_task_runner_ = std::move(worklet_task_runner); - web_audio_device_->Start(); - play_state_ = PlayState::kPlaying; - } + if (device_state_ != DeviceState::kStopped) + return; + worklet_task_runner_ = std::move(worklet_task_runner); + web_audio_device_->Start(); + SetDeviceState(DeviceState::kRunning); } void AudioDestination::Stop() { DCHECK(IsMainThread()); + TRACE_EVENT0("webaudio", "AudioDestination::Stop"); - // This assumes stopping the "audio device" is synchronous and dumping the - // rendering thread is safe after that. - if (web_audio_device_ && play_state_ != PlayState::kStopped) { - TRACE_EVENT0("webaudio", "AudioDestination::Stop"); - web_audio_device_->Stop(); - worklet_task_runner_ = nullptr; - play_state_ = PlayState::kStopped; - } + if (device_state_ == DeviceState::kStopped) + return; + web_audio_device_->Stop(); + + // Resetting |worklet_task_runner_| here is safe because + // AudioDestination::Render() won't be called after WebAudioDevice::Stop() + // call above. + worklet_task_runner_ = nullptr; + + SetDeviceState(DeviceState::kStopped); } void AudioDestination::Pause() { DCHECK(IsMainThread()); - if (web_audio_device_ && play_state_ == PlayState::kPlaying) { - web_audio_device_->Pause(); - play_state_ = PlayState::kPaused; - } + TRACE_EVENT0("webaudio", "AudioDestination::Pause"); + + if (device_state_ != DeviceState::kRunning) + return; + web_audio_device_->Pause(); + SetDeviceState(DeviceState::kPaused); } void AudioDestination::Resume() { DCHECK(IsMainThread()); - if (web_audio_device_ && play_state_ == PlayState::kPaused) { - web_audio_device_->Resume(); - play_state_ = PlayState::kPlaying; - } + TRACE_EVENT0("webaudio", "AudioDestination::Resume"); + + if (device_state_ != DeviceState::kPaused) + return; + web_audio_device_->Resume(); + SetDeviceState(DeviceState::kRunning); } -uint32_t AudioDestination::CallbackBufferSize() const { +bool AudioDestination::IsPlaying() { DCHECK(IsMainThread()); - return callback_buffer_size_; + MutexLocker locker(state_change_lock_); + return device_state_ == DeviceState::kRunning; } -bool AudioDestination::IsPlaying() { +uint32_t AudioDestination::CallbackBufferSize() const { DCHECK(IsMainThread()); - return play_state_ == PlayState::kPlaying; + return callback_buffer_size_; } int AudioDestination::FramesPerBuffer() const { @@ -336,19 +348,16 @@ uint32_t AudioDestination::MaxChannelCount() { } bool AudioDestination::CheckBufferSize() { + // Record the sizes if we successfully created an output device. // Histogram for audioHardwareBufferSize - DEFINE_STATIC_LOCAL(SparseHistogram, hardware_buffer_size_histogram, - ("WebAudio.AudioDestination.HardwareBufferSize")); + base::UmaHistogramSparse("WebAudio.AudioDestination.HardwareBufferSize", + HardwareBufferSize()); // Histogram for the actual callback size used. Typically, this is the same // as audioHardwareBufferSize, but can be adjusted depending on some // heuristics below. - DEFINE_STATIC_LOCAL(SparseHistogram, callback_buffer_size_histogram, - ("WebAudio.AudioDestination.CallbackBufferSize")); - - // Record the sizes if we successfully created an output device. - hardware_buffer_size_histogram.Sample(HardwareBufferSize()); - callback_buffer_size_histogram.Sample(callback_buffer_size_); + base::UmaHistogramSparse("WebAudio.AudioDestination.CallbackBufferSize", + callback_buffer_size_); // Check if the requested buffer size is too large. bool is_buffer_size_valid = @@ -364,4 +373,11 @@ void AudioDestination::ProvideResamplerInput(int resampler_frame_delay, callback_.Render(dest, audio_utilities::kRenderQuantumFrames, output_position_, metric_reporter_.GetMetric()); } + +void AudioDestination::SetDeviceState(DeviceState state) { + DCHECK(IsMainThread()); + MutexLocker locker(state_change_lock_); + device_state_ = state; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_destination.h b/chromium/third_party/blink/renderer/platform/audio/audio_destination.h index f3045cceaa2..dafb1d9da4c 100644 --- a/chromium/third_party/blink/renderer/platform/audio/audio_destination.h +++ b/chromium/third_party/blink/renderer/platform/audio/audio_destination.h @@ -39,6 +39,7 @@ #include "third_party/blink/renderer/platform/audio/audio_io_callback.h" #include "third_party/blink/renderer/platform/audio/media_multi_channel_resampler.h" #include "third_party/blink/renderer/platform/scheduler/public/thread.h" +#include "third_party/blink/renderer/platform/wtf/threading_primitives.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h" @@ -117,11 +118,19 @@ class PLATFORM_EXPORT AudioDestination static uint32_t MaxChannelCount(); private: + // Represents the current state of the underlying |WebAudioDevice| object + // (RendererWebAudioDeviceImpl). + enum DeviceState { + kRunning, + kPaused, + kStopped, + }; + + void SetDeviceState(DeviceState); + // Provide input to the resampler (if used). void ProvideResamplerInput(int resampler_frame_delay, AudioBus* dest); - enum class PlayState { kStopped, kPlaying, kPaused }; - // Check if the buffer size chosen by the WebAudioDevice is too large. bool CheckBufferSize(); @@ -131,7 +140,6 @@ class PLATFORM_EXPORT AudioDestination std::unique_ptr<WebAudioDevice> web_audio_device_; const unsigned number_of_output_channels_; uint32_t callback_buffer_size_; - PlayState play_state_; // The task runner for AudioWorklet operation. This is only valid when // the AudioWorklet is activated. @@ -168,6 +176,13 @@ class PLATFORM_EXPORT AudioDestination AudioCallbackMetricReporter metric_reporter_; + // This protects |device_state_| below. + mutable Mutex state_change_lock_; + + // Modified only on the main thread, so it can be read without holding a lock + // there. + DeviceState device_state_; + DISALLOW_COPY_AND_ASSIGN(AudioDestination); }; diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_destination_test.cc b/chromium/third_party/blink/renderer/platform/audio/audio_destination_test.cc index 072ca234d0a..62ea2d72525 100644 --- a/chromium/third_party/blink/renderer/platform/audio/audio_destination_test.cc +++ b/chromium/third_party/blink/renderer/platform/audio/audio_destination_test.cc @@ -73,6 +73,7 @@ void CountWASamplesProcessedForRate(base::Optional<float> sample_rate) { scoped_refptr<AudioDestination> destination = AudioDestination::Create( callback, channel_count, latency_hint, sample_rate); + destination->Start(); Vector<float> channels[channel_count]; WebVector<float*> dest_data(static_cast<size_t>(channel_count)); diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_source_provider_client.h b/chromium/third_party/blink/renderer/platform/audio/audio_source_provider_client.h index 2d77c44a5cd..4372a94a853 100644 --- a/chromium/third_party/blink/renderer/platform/audio/audio_source_provider_client.h +++ b/chromium/third_party/blink/renderer/platform/audio/audio_source_provider_client.h @@ -42,7 +42,7 @@ class AudioSourceProviderClient : public GarbageCollectedMixin { // changed. virtual void OnCurrentSrcChanged(const KURL& current_src) {} - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} protected: virtual ~AudioSourceProviderClient() = default; diff --git a/chromium/third_party/blink/renderer/platform/audio/equal_power_panner.cc b/chromium/third_party/blink/renderer/platform/audio/equal_power_panner.cc index dbf97962bef..22fb5b2af72 100644 --- a/chromium/third_party/blink/renderer/platform/audio/equal_power_panner.cc +++ b/chromium/third_party/blink/renderer/platform/audio/equal_power_panner.cc @@ -34,7 +34,7 @@ namespace blink { EqualPowerPanner::EqualPowerPanner(float sample_rate) - : Panner(kPanningModelEqualPower) {} + : Panner(PanningModel::kEqualPower) {} void EqualPowerPanner::Pan(double azimuth, double /*elevation*/, diff --git a/chromium/third_party/blink/renderer/platform/audio/ffmpeg/fft_frame_ffmpeg.cc b/chromium/third_party/blink/renderer/platform/audio/ffmpeg/fft_frame_ffmpeg.cc index c2b692bc562..69b31dffe9d 100644 --- a/chromium/third_party/blink/renderer/platform/audio/ffmpeg/fft_frame_ffmpeg.cc +++ b/chromium/third_party/blink/renderer/platform/audio/ffmpeg/fft_frame_ffmpeg.cc @@ -84,8 +84,8 @@ FFTFrame::FFTFrame(const FFTFrame& frame) // Copy/setup frame data. unsigned nbytes = sizeof(float) * (fft_size_ / 2); - memcpy(RealData(), frame.RealData(), nbytes); - memcpy(ImagData(), frame.ImagData(), nbytes); + memcpy(RealData().Data(), frame.RealData().Data(), nbytes); + memcpy(ImagData().Data(), frame.ImagData().Data(), nbytes); } int FFTFrame::MinFFTSize() { diff --git a/chromium/third_party/blink/renderer/platform/audio/fft_frame.cc b/chromium/third_party/blink/renderer/platform/audio/fft_frame.cc index b5b02afabb7..b10f84b06c2 100644 --- a/chromium/third_party/blink/renderer/platform/audio/fft_frame.cc +++ b/chromium/third_party/blink/renderer/platform/audio/fft_frame.cc @@ -82,13 +82,13 @@ void FFTFrame::InterpolateFrequencyComponents(const FFTFrame& frame1, double interp) { // FIXME : with some work, this method could be optimized - float* real_p = RealData(); - float* imag_p = ImagData(); + AudioFloatArray& real = RealData(); + AudioFloatArray& imag = ImagData(); - const float* real_p1 = frame1.RealData(); - const float* imag_p1 = frame1.ImagData(); - const float* real_p2 = frame2.RealData(); - const float* imag_p2 = frame2.ImagData(); + const AudioFloatArray& real1 = frame1.RealData(); + const AudioFloatArray& imag1 = frame1.ImagData(); + const AudioFloatArray& real2 = frame2.RealData(); + const AudioFloatArray& imag2 = frame2.ImagData(); fft_size_ = frame1.FftSize(); log2fft_size_ = frame1.Log2FFTSize(); @@ -100,14 +100,26 @@ void FFTFrame::InterpolateFrequencyComponents(const FFTFrame& frame1, double last_phase1 = 0.0; double last_phase2 = 0.0; - real_p[0] = static_cast<float>(s1base * real_p1[0] + s2base * real_p2[0]); - imag_p[0] = static_cast<float>(s1base * imag_p1[0] + s2base * imag_p2[0]); + const float* real_p1_data = real1.Data(); + const float* real_p2_data = real2.Data(); + const float* imag_p1_data = imag1.Data(); + const float* imag_p2_data = imag2.Data(); + + real[0] = static_cast<float>(s1base * real_p1_data[0] + + s2base * real_p2_data[0]); + imag[0] = static_cast<float>(s1base * imag_p1_data[0] + + s2base * imag_p2_data[0]); int n = fft_size_ / 2; + DCHECK_GE(real1.size(), static_cast<uint32_t>(n)); + DCHECK_GE(imag1.size(), static_cast<uint32_t>(n)); + DCHECK_GE(real2.size(), static_cast<uint32_t>(n)); + DCHECK_GE(imag2.size(), static_cast<uint32_t>(n)); + for (int i = 1; i < n; ++i) { - std::complex<double> c1(real_p1[i], imag_p1[i]); - std::complex<double> c2(real_p2[i], imag_p2[i]); + std::complex<double> c1(real_p1_data[i], imag_p1_data[i]); + std::complex<double> c2(real_p2_data[i], imag_p2_data[i]); double mag1 = abs(c1); double mag2 = abs(c2); @@ -178,14 +190,14 @@ void FFTFrame::InterpolateFrequencyComponents(const FFTFrame& frame1, std::complex<double> c = std::polar(mag, phase_accum); - real_p[i] = static_cast<float>(c.real()); - imag_p[i] = static_cast<float>(c.imag()); + real[i] = static_cast<float>(c.real()); + imag[i] = static_cast<float>(c.imag()); } } double FFTFrame::ExtractAverageGroupDelay() { - float* real_p = RealData(); - float* imag_p = ImagData(); + AudioFloatArray& real = RealData(); + AudioFloatArray& imag = ImagData(); double ave_sum = 0.0; double weight_sum = 0.0; @@ -198,7 +210,7 @@ double FFTFrame::ExtractAverageGroupDelay() { // Calculate weighted average group delay for (int i = 0; i < half_size; i++) { - std::complex<double> c(real_p[i], imag_p[i]); + std::complex<double> c(real[i], imag[i]); double mag = abs(c); double phase = arg(c); @@ -228,7 +240,7 @@ double FFTFrame::ExtractAverageGroupDelay() { AddConstantGroupDelay(-ave_sample_delay); // Remove DC offset - real_p[0] = 0.0f; + real[0] = 0.0f; return ave_sample_delay; } @@ -236,8 +248,8 @@ double FFTFrame::ExtractAverageGroupDelay() { void FFTFrame::AddConstantGroupDelay(double sample_frame_delay) { int half_size = FftSize() / 2; - float* real_p = RealData(); - float* imag_p = ImagData(); + AudioFloatArray& real = RealData(); + AudioFloatArray& imag = ImagData(); const double sample_phase_delay = kTwoPiDouble / static_cast<double>(FftSize()); @@ -246,7 +258,7 @@ void FFTFrame::AddConstantGroupDelay(double sample_frame_delay) { // Add constant group delay for (int i = 1; i < half_size; i++) { - std::complex<double> c(real_p[i], imag_p[i]); + std::complex<double> c(real[i], imag[i]); double mag = abs(c); double phase = arg(c); @@ -254,8 +266,8 @@ void FFTFrame::AddConstantGroupDelay(double sample_frame_delay) { std::complex<double> c2 = std::polar(mag, phase); - real_p[i] = static_cast<float>(c2.real()); - imag_p[i] = static_cast<float>(c2.imag()); + real[i] = static_cast<float>(c2.real()); + imag[i] = static_cast<float>(c2.imag()); } } @@ -263,21 +275,27 @@ void FFTFrame::Multiply(const FFTFrame& frame) { FFTFrame& frame1 = *this; const FFTFrame& frame2 = frame; - float* real_p1 = frame1.RealData(); - float* imag_p1 = frame1.ImagData(); - const float* real_p2 = frame2.RealData(); - const float* imag_p2 = frame2.ImagData(); + AudioFloatArray& real1 = frame1.RealData(); + AudioFloatArray& imag1 = frame1.ImagData(); + const AudioFloatArray& real2 = frame2.RealData(); + const AudioFloatArray& imag2 = frame2.ImagData(); unsigned half_size = FftSize() / 2; - float real0 = real_p1[0]; - float imag0 = imag_p1[0]; + float real0 = real1[0]; + float imag0 = imag1[0]; + + DCHECK_GE(real1.size(), half_size); + DCHECK_GE(imag1.size(), half_size); + DCHECK_GE(real2.size(), half_size); + DCHECK_GE(imag2.size(), half_size); - vector_math::Zvmul(real_p1, imag_p1, real_p2, imag_p2, real_p1, imag_p1, + vector_math::Zvmul(real1.Data(), imag1.Data(), real2.Data(), + imag2.Data(), real1.Data(), imag1.Data(), half_size); // Multiply the packed DC/nyquist component - real_p1[0] = real0 * real_p2[0]; - imag_p1[0] = imag0 * imag_p2[0]; + real1[0] = real0 * real2.Data()[0]; + imag1[0] = imag0 * imag2.Data()[0]; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/audio/fft_frame.h b/chromium/third_party/blink/renderer/platform/audio/fft_frame.h index a6022b9d852..bfd2abd1548 100644 --- a/chromium/third_party/blink/renderer/platform/audio/fft_frame.h +++ b/chromium/third_party/blink/renderer/platform/audio/fft_frame.h @@ -38,13 +38,13 @@ #include "third_party/blink/renderer/platform/wtf/forward.h" #include "third_party/blink/renderer/platform/wtf/threading.h" -#if defined(OS_MACOSX) -#include <Accelerate/Accelerate.h> -#elif defined(WTF_USE_WEBAUDIO_FFMPEG) +#if defined(WTF_USE_WEBAUDIO_FFMPEG) struct RDFTContext; #elif defined(WTF_USE_WEBAUDIO_PFFFT) #include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/pffft/src/pffft.h" +#elif defined(OS_MACOSX) +#include <Accelerate/Accelerate.h> #endif namespace blink { @@ -83,10 +83,10 @@ class PLATFORM_EXPORT FFTFrame { // least |fft_size_| elements. void DoInverseFFT(float* data); - float* RealData() { return real_data_.Data(); } - const float* RealData() const { return real_data_.Data(); } - float* ImagData() { return imag_data_.Data(); } - const float* ImagData() const { return imag_data_.Data(); } + AudioFloatArray& RealData() { return real_data_; } + const AudioFloatArray& RealData() const { return real_data_; } + AudioFloatArray& ImagData() { return imag_data_; } + const AudioFloatArray& ImagData() const { return imag_data_; } unsigned FftSize() const { return fft_size_; } unsigned Log2FFTSize() const { return log2fft_size_; } @@ -144,7 +144,7 @@ class PLATFORM_EXPORT FFTFrame { AudioFloatArray real_data_; AudioFloatArray imag_data_; -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) && !defined(WTF_USE_WEBAUDIO_PFFFT) // Thin wrapper around FFTSetup so we can call the appropriate routines to // construct or release the FFTSetup objects. class FFTSetupDatum { diff --git a/chromium/third_party/blink/renderer/platform/audio/hrtf_panner.cc b/chromium/third_party/blink/renderer/platform/audio/hrtf_panner.cc index a4c440b1b87..cc12303b7f6 100644 --- a/chromium/third_party/blink/renderer/platform/audio/hrtf_panner.cc +++ b/chromium/third_party/blink/renderer/platform/audio/hrtf_panner.cc @@ -42,7 +42,7 @@ const double kMaxDelayTimeSeconds = 0.002; const int kUninitializedAzimuth = -1; HRTFPanner::HRTFPanner(float sample_rate, HRTFDatabaseLoader* database_loader) - : Panner(kPanningModelHRTF), + : Panner(PanningModel::kHRTF), database_loader_(database_loader), sample_rate_(sample_rate), crossfade_selection_(kCrossfadeSelection1), diff --git a/chromium/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc b/chromium/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc index 8b77f6dae3d..3315687447f 100644 --- a/chromium/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc +++ b/chromium/third_party/blink/renderer/platform/audio/mac/fft_frame_mac.cc @@ -30,7 +30,7 @@ #include "build/build_config.h" -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) && !defined(WTF_USE_WEBAUDIO_PFFFT) #include "third_party/blink/renderer/platform/audio/fft_frame.h" #include "third_party/blink/renderer/platform/audio/hrtf_panner.h" @@ -131,8 +131,8 @@ FFTFrame::FFTFrame(const FFTFrame& frame) // Copy/setup frame data unsigned nbytes = sizeof(float) * fft_size_; - memcpy(RealData(), frame.frame_.realp, nbytes); - memcpy(ImagData(), frame.frame_.imagp, nbytes); + memcpy(RealData().Data(), frame.frame_.realp, nbytes); + memcpy(ImagData().Data(), frame.frame_.imagp, nbytes); } FFTFrame::~FFTFrame() {} @@ -201,4 +201,4 @@ void FFTFrame::Cleanup() { } // namespace blink -#endif // #if defined(OS_MACOSX) +#endif // #if defined(OS_MACOSX) && !defined(WTF_USE_WEBAUDIO_PFFFT) diff --git a/chromium/third_party/blink/renderer/platform/audio/media_multi_channel_resampler.cc b/chromium/third_party/blink/renderer/platform/audio/media_multi_channel_resampler.cc index 97778014ac3..9f01d98e939 100644 --- a/chromium/third_party/blink/renderer/platform/audio/media_multi_channel_resampler.cc +++ b/chromium/third_party/blink/renderer/platform/audio/media_multi_channel_resampler.cc @@ -24,8 +24,21 @@ MediaMultiChannelResampler::MediaMultiChannelResampler( } void MediaMultiChannelResampler::Resample(int frames, - media::AudioBus* audio_bus) { - resampler_->Resample(audio_bus->frames(), audio_bus); + blink::AudioBus* audio_bus) { + // Create a media::AudioBus wrapper around the memory provided by the + // blink::AudioBus. + std::unique_ptr<media::AudioBus> resampler_bus_ = + media::AudioBus::CreateWrapper(audio_bus->NumberOfChannels()); + for (unsigned int i = 0; i < audio_bus->NumberOfChannels(); ++i) { + resampler_bus_->SetChannelData(i, audio_bus->Channel(i)->MutableData()); + } + resampler_bus_->set_frames(audio_bus->length()); + ResampleInternal(frames, resampler_bus_.get()); +} + +void MediaMultiChannelResampler::ResampleInternal(int frames, + media::AudioBus* audio_bus) { + resampler_->Resample(frames, audio_bus); } void MediaMultiChannelResampler::ProvideResamplerInput( diff --git a/chromium/third_party/blink/renderer/platform/audio/media_multi_channel_resampler.h b/chromium/third_party/blink/renderer/platform/audio/media_multi_channel_resampler.h index 73b8b34af94..18992a98c2c 100644 --- a/chromium/third_party/blink/renderer/platform/audio/media_multi_channel_resampler.h +++ b/chromium/third_party/blink/renderer/platform/audio/media_multi_channel_resampler.h @@ -31,7 +31,7 @@ class PLATFORM_EXPORT MediaMultiChannelResampler { // frames are available to satisfy the request. |frame_delay| is the number // of output frames already processed and can be used to estimate delay. typedef WTF::CrossThreadRepeatingFunction<void(int frame_delay, - AudioBus* audio_bus)> + blink::AudioBus* audio_bus)> ReadCB; public: @@ -44,8 +44,16 @@ class PLATFORM_EXPORT MediaMultiChannelResampler { size_t request_frames, ReadCB read_cb); - // Resamples |frames| of data from |read_cb_| into AudioBus. - void Resample(int frames, media::AudioBus* audio_bus); + // Resamples |frames| of data from |read_cb_| into a blink::AudioBus, this + // requires creating a wrapper for the media::AudioBus on each call and so + // resampling directly into a media::AudioBus using ResampleInternal() is + // preferred if possible. + void Resample(int frames, blink::AudioBus* audio_bus); + + // Resamples |frames| of data from |read_cb_| into a media::AudioBus by + // directly calling Resample() on the underlying + // media::MultiChannelResampler. + void ResampleInternal(int frames, media::AudioBus* audio_bus); private: // Wrapper method used to provide input to the media::MultiChannelResampler diff --git a/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.cc b/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.cc deleted file mode 100644 index ee5c7dac00d..00000000000 --- a/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.cc +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/renderer/platform/audio/multi_channel_resampler.h" - -#include <memory> -#include "third_party/blink/renderer/platform/audio/audio_bus.h" - -namespace blink { - -namespace { - -// ChannelProvider provides a single channel of audio data (one channel at a -// time) for each channel of data provided to us in a multi-channel provider. - -class ChannelProvider final : public AudioSourceProvider { - public: - ChannelProvider(AudioSourceProvider* multi_channel_provider, - unsigned number_of_channels) - : multi_channel_provider_(multi_channel_provider), - number_of_channels_(number_of_channels), - current_channel_(0), - frames_to_process_(0) {} - - // provideInput() will be called once for each channel, starting with the - // first channel. Each time it's called, it will provide the next channel of - // data. - void ProvideInput(AudioBus* bus, uint32_t frames_to_process) override { - DCHECK(bus); - DCHECK_EQ(bus->NumberOfChannels(), 1u); - - // Get the data from the multi-channel provider when the first channel asks - // for it. For subsequent channels, we can just dish out the channel data - // from that (stored in m_multiChannelBus). - if (!current_channel_) { - frames_to_process_ = frames_to_process; - multi_channel_bus_ = - AudioBus::Create(number_of_channels_, frames_to_process); - multi_channel_provider_->ProvideInput(multi_channel_bus_.get(), - frames_to_process); - } - - DCHECK(multi_channel_bus_.get()); - DCHECK_EQ(frames_to_process, frames_to_process_); - - // Copy the channel data from what we received from m_multiChannelProvider. - DCHECK_LT(current_channel_, number_of_channels_); - memcpy(bus->Channel(0)->MutableData(), - multi_channel_bus_->Channel(current_channel_)->Data(), - sizeof(float) * frames_to_process); - ++current_channel_; - } - - private: - AudioSourceProvider* multi_channel_provider_; - scoped_refptr<AudioBus> multi_channel_bus_; - unsigned number_of_channels_; - unsigned current_channel_; - // Used to verify that all channels ask for the same amount. - uint32_t frames_to_process_; -}; - -} // namespace - -MultiChannelResampler::MultiChannelResampler(double scale_factor, - unsigned number_of_channels) - : number_of_channels_(number_of_channels) { - // Create each channel's resampler. - for (unsigned channel_index = 0; channel_index < number_of_channels; - ++channel_index) - kernels_.push_back(std::make_unique<SincResampler>(scale_factor)); -} - -void MultiChannelResampler::Process(AudioSourceProvider* provider, - AudioBus* destination, - uint32_t frames_to_process) { - // The provider can provide us with multi-channel audio data. But each of our - // single-channel resamplers (kernels) below requires a provider which - // provides a single unique channel of data. channelProvider wraps the - // original multi-channel provider and dishes out one channel at a time. - ChannelProvider channel_provider(provider, number_of_channels_); - - for (unsigned channel_index = 0; channel_index < number_of_channels_; - ++channel_index) { - // Depending on the sample-rate scale factor, and the internal buffering - // used in a SincResampler kernel, this call to process() will only - // sometimes call provideInput() on the channelProvider. However, if it - // calls provideInput() for the first channel, then it will call it for the - // remaining channels, since they all buffer in the same way and are - // processing the same number of frames. - kernels_[channel_index]->Process( - &channel_provider, destination->Channel(channel_index)->MutableData(), - frames_to_process); - } -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.h b/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.h deleted file mode 100644 index 84eaa98e616..00000000000 --- a/chromium/third_party/blink/renderer/platform/audio/multi_channel_resampler.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_MULTI_CHANNEL_RESAMPLER_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_MULTI_CHANNEL_RESAMPLER_H_ - -#include <memory> - -#include "base/macros.h" -#include "third_party/blink/renderer/platform/audio/sinc_resampler.h" -#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" -#include "third_party/blink/renderer/platform/wtf/vector.h" - -namespace blink { - -class AudioBus; - -class PLATFORM_EXPORT MultiChannelResampler { - USING_FAST_MALLOC(MultiChannelResampler); - - public: - MultiChannelResampler(double scale_factor, unsigned number_of_channels); - - // Process given AudioSourceProvider for streaming applications. - void Process(AudioSourceProvider*, - AudioBus* destination, - uint32_t frames_to_process); - - private: - // FIXME: the mac port can have a more highly optimized implementation based - // on CoreAudio instead of SincResampler. For now the default implementation - // will be used on all ports. - // https://bugs.webkit.org/show_bug.cgi?id=75118 - - // Each channel will be resampled using a high-quality SincResampler. - Vector<std::unique_ptr<SincResampler>> kernels_; - - unsigned number_of_channels_; - - DISALLOW_COPY_AND_ASSIGN(MultiChannelResampler); -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_MULTI_CHANNEL_RESAMPLER_H_ diff --git a/chromium/third_party/blink/renderer/platform/audio/panner.cc b/chromium/third_party/blink/renderer/platform/audio/panner.cc index dfb9d7cf7c4..5721c6b7ccc 100644 --- a/chromium/third_party/blink/renderer/platform/audio/panner.cc +++ b/chromium/third_party/blink/renderer/platform/audio/panner.cc @@ -38,16 +38,14 @@ std::unique_ptr<Panner> Panner::Create(PanningModel model, float sample_rate, HRTFDatabaseLoader* database_loader) { switch (model) { - case kPanningModelEqualPower: + case PanningModel::kEqualPower: return std::make_unique<EqualPowerPanner>(sample_rate); - case kPanningModelHRTF: + case PanningModel::kHRTF: return std::make_unique<HRTFPanner>(sample_rate, database_loader); - - default: - NOTREACHED(); - return nullptr; } + NOTREACHED(); + return nullptr; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/audio/panner.h b/chromium/third_party/blink/renderer/platform/audio/panner.h index 1b5e3357fba..b2bed74ccb7 100644 --- a/chromium/third_party/blink/renderer/platform/audio/panner.h +++ b/chromium/third_party/blink/renderer/platform/audio/panner.h @@ -46,10 +46,13 @@ class PLATFORM_EXPORT Panner { USING_FAST_MALLOC(Panner); public: - // This values are used in histograms and should not be renumbered or deleted. - enum { kPanningModelEqualPower = 0, kPanningModelHRTF = 1 }; - - typedef unsigned PanningModel; + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class PanningModel { + kEqualPower = 0, + kHRTF = 1, + kMaxValue = kHRTF, + }; static std::unique_ptr<Panner> Create(PanningModel, float sample_rate, @@ -77,7 +80,7 @@ class PLATFORM_EXPORT Panner { virtual bool RequiresTailProcessing() const = 0; protected: - Panner(PanningModel model) : panning_model_(model) {} + explicit Panner(PanningModel model) : panning_model_(model) {} PanningModel panning_model_; diff --git a/chromium/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc b/chromium/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc index 7432ad58e2e..620188fff08 100644 --- a/chromium/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc +++ b/chromium/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc @@ -111,8 +111,8 @@ FFTFrame::FFTFrame(const FFTFrame& frame) // Copy/setup frame data. unsigned nbytes = sizeof(float) * (fft_size_ / 2); - memcpy(RealData(), frame.RealData(), nbytes); - memcpy(ImagData(), frame.ImagData(), nbytes); + memcpy(RealData().Data(), frame.RealData().Data(), nbytes); + memcpy(ImagData().Data(), frame.ImagData().Data(), nbytes); } int FFTFrame::MinFFTSize() { diff --git a/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.cc b/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.cc index 81e8266794c..cc56b77e982 100644 --- a/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.cc +++ b/chromium/third_party/blink/renderer/platform/audio/push_pull_fifo.cc @@ -7,9 +7,9 @@ #include <algorithm> #include <memory> +#include "base/metrics/histogram_functions.h" #include "build/build_config.h" #include "third_party/blink/renderer/platform/audio/audio_utilities.h" -#include "third_party/blink/renderer/platform/instrumentation/histogram.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" namespace blink { @@ -40,20 +40,16 @@ PushPullFIFO::~PushPullFIFO() { // Capture the percentage of underflow happened based on the total pull count. // (100 buckets of size 1) This is equivalent of // "Media.AudioRendererMissedDeadline" metric for WebAudio. - DEFINE_STATIC_LOCAL( - LinearHistogram, - fifo_underflow_percentage_histogram, - ("WebAudio.PushPullFIFO.UnderflowPercentage", 1, 100, 101)); - fifo_underflow_percentage_histogram.Count( + base::UmaHistogramPercentage( + "WebAudio.PushPullFIFO.UnderflowPercentage", static_cast<int32_t>(100.0 * underflow_count_ / pull_count_)); // We only collect the underflow count because no overflow can happen in the // current implementation. This is similar to // "Media.AudioRendererAudioGlitches" metric for WebAudio, which is a simple // flag indicates any instance of glitches during FIFO's lifetime. - DEFINE_STATIC_LOCAL(BooleanHistogram, fifo_underflow_glitches_histogram, - ("WebAudio.PushPullFIFO.UnderflowGlitches")); - fifo_underflow_glitches_histogram.Count(underflow_count_ > 0); + base::UmaHistogramBoolean("WebAudio.PushPullFIFO.UnderflowGlitches", + underflow_count_ > 0); } // Push the data from |input_bus| to FIFO. The size of push is determined by diff --git a/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.cc b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.cc index e56d98347f9..6a451b02b7f 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.cc +++ b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.cc @@ -45,7 +45,8 @@ void ActiveScriptWrappableBase::TraceActiveScriptWrappables( if (!active_wrappable->DispatchHasPendingActivity()) continue; - ScriptWrappable* script_wrappable = active_wrappable->ToScriptWrappable(); + const ScriptWrappable* script_wrappable = + active_wrappable->ToScriptWrappable(); visitor->Trace(script_wrappable); } } diff --git a/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h index f3e3f040e61..cf158f453c5 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h +++ b/chromium/third_party/blink/renderer/platform/bindings/active_script_wrappable_base.h @@ -31,7 +31,7 @@ class PLATFORM_EXPORT ActiveScriptWrappableBase : public GarbageCollectedMixin { virtual bool IsContextDestroyed() const = 0; virtual bool DispatchHasPendingActivity() const = 0; - virtual ScriptWrappable* ToScriptWrappable() = 0; + virtual const ScriptWrappable* ToScriptWrappable() const = 0; private: DISALLOW_COPY_AND_ASSIGN(ActiveScriptWrappableBase); diff --git a/chromium/third_party/blink/renderer/platform/bindings/blink_isolate/blink_isolate.cc b/chromium/third_party/blink/renderer/platform/bindings/blink_isolate/blink_isolate.cc new file mode 100644 index 00000000000..68332fece42 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/bindings/blink_isolate/blink_isolate.cc @@ -0,0 +1,22 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/bindings/blink_isolate/blink_isolate.h" + +namespace blink { + +// TODO(crbug.com/1051395) Actually instantiate multiple blink isolates. +static BlinkIsolate* g_single_isolate = nullptr; + +BlinkIsolate* BlinkIsolate::GetCurrent() { + return g_single_isolate; +} + +BlinkIsolate::BlinkIsolate() { + g_single_isolate = this; +} + +BlinkIsolate::~BlinkIsolate() = default; + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/bindings/blink_isolate/blink_isolate.h b/chromium/third_party/blink/renderer/platform/bindings/blink_isolate/blink_isolate.h new file mode 100644 index 00000000000..e48db29c9ac --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/bindings/blink_isolate/blink_isolate.h @@ -0,0 +1,39 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_BLINK_ISOLATE_BLINK_ISOLATE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_BLINK_ISOLATE_BLINK_ISOLATE_H_ + +#include "third_party/blink/public/platform/web_isolate.h" +#include "third_party/blink/renderer/platform/platform_export.h" + +namespace blink { + +// An BlinkIsolate is a memory and scheduling isolation boundary in Blink, +// which is currently being introduced as a part of Multiple Blink Isolates +// project. +// TODO(crbug.com/1051790): is the tracking bug for the project. +// +// An Isolate will hold a distinct memory region (V8 and Oilpan heaps), +// and a dedicated scheduler. +class PLATFORM_EXPORT BlinkIsolate : public WebIsolate { + public: + // Get the currently active BlinkIsolate. GetCurrent() may return nullptr if + // there is no active BlinkIsolate. + // TODO(crbug.com/1051395): GetCurrent() returns the singleton BlinkIsolate + // instance for now, but will change once we start instantiating multiple + // BlinkIsolates. + static BlinkIsolate* GetCurrent(); + + BlinkIsolate(); + ~BlinkIsolate() override; + + private: + BlinkIsolate(const BlinkIsolate&) = delete; + BlinkIsolate& operator=(const BlinkIsolate&) = delete; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_BLINK_ISOLATE_BLINK_ISOLATE_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.h b/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.h index 65fe7e63b89..942043b0907 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.h +++ b/chromium/third_party/blink/renderer/platform/bindings/callback_function_base.h @@ -28,7 +28,7 @@ class PLATFORM_EXPORT CallbackFunctionBase public: virtual ~CallbackFunctionBase() = default; - virtual void Trace(blink::Visitor* visitor); + virtual void Trace(Visitor* visitor); v8::Local<v8::Object> CallbackObject() { return callback_function_.NewLocal(GetIsolate()); diff --git a/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.h b/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.h index 6176390dcec..6c303236d50 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.h +++ b/chromium/third_party/blink/renderer/platform/bindings/callback_interface_base.h @@ -34,7 +34,7 @@ class PLATFORM_EXPORT CallbackInterfaceBase virtual ~CallbackInterfaceBase() = default; - virtual void Trace(blink::Visitor*); + virtual void Trace(Visitor*); // Check the identity of |callback_object_|. There can be multiple // CallbackInterfaceBase objects that have the same |callback_object_| but diff --git a/chromium/third_party/blink/renderer/platform/bindings/callback_method_retriever.h b/chromium/third_party/blink/renderer/platform/bindings/callback_method_retriever.h index 5187367c974..652ff138557 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/callback_method_retriever.h +++ b/chromium/third_party/blink/renderer/platform/bindings/callback_method_retriever.h @@ -75,7 +75,7 @@ class PLATFORM_EXPORT CallbackMethodRetriever { const StringView& property, ExceptionState&); - Member<CallbackFunctionBase> constructor_; + CallbackFunctionBase* constructor_; v8::Isolate* isolate_; v8::Local<v8::Context> current_context_; v8::Local<v8::Object> prototype_object_; diff --git a/chromium/third_party/blink/renderer/platform/bindings/dictionary_base.h b/chromium/third_party/blink/renderer/platform/bindings/dictionary_base.h index 6ee38d858ba..0d81ab9e815 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/dictionary_base.h +++ b/chromium/third_party/blink/renderer/platform/bindings/dictionary_base.h @@ -10,6 +10,9 @@ #include "v8/include/v8.h" namespace blink { + +class Visitor; + namespace bindings { // This class is the base class for all IDL dictionary implementations. This is @@ -33,6 +36,8 @@ class PLATFORM_EXPORT DictionaryBase : public GarbageCollected<DictionaryBase> { return v8_object; } + virtual void Trace(Visitor*) {} + protected: DictionaryBase() = default; @@ -41,7 +46,7 @@ class PLATFORM_EXPORT DictionaryBase : public GarbageCollected<DictionaryBase> { DictionaryBase& operator=(const DictionaryBase&) = delete; DictionaryBase& operator=(const DictionaryBase&&) = delete; - virtual void FillWithMembers(v8::Isolate* isolate, + virtual bool FillWithMembers(v8::Isolate* isolate, v8::Local<v8::Object> creation_context, v8::Local<v8::Object> v8_object) const = 0; }; diff --git a/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.cc b/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.cc index 5e10e7024b2..e0b991138b5 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.cc +++ b/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.cc @@ -10,20 +10,16 @@ DOMDataStore::DOMDataStore(v8::Isolate* isolate, bool is_main_world) : is_main_world_(is_main_world) {} void DOMDataStore::Dispose() { - for (const auto& it : wrapper_map_) { + for (auto& it : wrapper_map_) { // Explicitly reset references so that a following V8 GC will not find them // and treat them as roots. There's optimizations (see // EmbedderHeapTracer::IsRootForNonTracingGC) that would not treat them as // roots and then Blink would not be able to find and remove them from a DOM // world. Explicitly resetting on disposal avoids that problem - it.value->ref.Clear(); + it.value.Clear(); } } -void DOMDataStore::WrappedReference::Trace(Visitor* visitor) { - visitor->Trace(ref); -} - void DOMDataStore::Trace(Visitor* visitor) { visitor->Trace(wrapper_map_); } diff --git a/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.h b/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.h index 975c6b567c9..0d8cfbc5ce7 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.h +++ b/chromium/third_party/blink/renderer/platform/bindings/dom_data_store.h @@ -35,6 +35,7 @@ #include "base/optional.h" #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h" #include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h" #include "third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -118,7 +119,7 @@ class DOMDataStore final : public GarbageCollected<DOMDataStore> { return object->MainWorldWrapper(isolate); auto it = wrapper_map_.find(object); if (it != wrapper_map_.end()) - return it->value->ref.NewLocal(isolate); + return it->value.NewLocal(isolate); return v8::Local<v8::Object>(); } @@ -132,13 +133,12 @@ class DOMDataStore final : public GarbageCollected<DOMDataStore> { return object->SetWrapper(isolate, wrapper_type_info, wrapper); auto result = wrapper_map_.insert( - object, MakeGarbageCollected<WrappedReference>(isolate, wrapper)); + object, TraceWrapperV8Reference<v8::Object>(isolate, wrapper)); if (LIKELY(result.is_new_entry)) { - wrapper_type_info->ConfigureWrapper( - &result.stored_value->value->ref.Get()); + wrapper_type_info->ConfigureWrapper(&result.stored_value->value.Get()); } else { - DCHECK(!result.stored_value->value->ref.IsEmpty()); - wrapper = result.stored_value->value->ref.NewLocal(isolate); + DCHECK(!result.stored_value->value.IsEmpty()); + wrapper = result.stored_value->value.NewLocal(isolate); } return result.is_new_entry; } @@ -149,8 +149,8 @@ class DOMDataStore final : public GarbageCollected<DOMDataStore> { DCHECK(!is_main_world_); const auto& it = wrapper_map_.find(object); if (it != wrapper_map_.end()) { - if (it->value->ref.Get() == handle) { - it->value->ref.Clear(); + if (it->value.Get() == handle) { + it->value.Clear(); wrapper_map_.erase(it); return true; } @@ -164,7 +164,7 @@ class DOMDataStore final : public GarbageCollected<DOMDataStore> { return object->SetReturnValue(return_value); auto it = wrapper_map_.find(object); if (it != wrapper_map_.end()) { - return_value.Set(it->value->ref.Get()); + return_value.Set(it->value.Get()); return true; } return false; @@ -198,24 +198,10 @@ class DOMDataStore final : public GarbageCollected<DOMDataStore> { return wrappable->IsEqualTo(holder); } - // Wrapper around TraceWrapperV8Reference to allow use in ephemeron hash map - // below. - class PLATFORM_EXPORT WrappedReference final - : public GarbageCollected<WrappedReference> { - public: - WrappedReference() = default; - WrappedReference(v8::Isolate* isolate, v8::Local<v8::Object> handle) - : ref(isolate, handle) {} - - virtual void Trace(Visitor*); - - TraceWrapperV8Reference<v8::Object> ref; - }; - bool is_main_world_; - // Ephemeron map: WrappedReference will be kept alive as long as - // ScriptWrappable is alive. - HeapHashMap<WeakMember<const ScriptWrappable>, Member<WrappedReference>> + // Ephemeron map: V8 wrapper will be kept alive as long as ScriptWrappable is. + HeapHashMap<WeakMember<const ScriptWrappable>, + TraceWrapperV8Reference<v8::Object>> wrapper_map_; DISALLOW_COPY_AND_ASSIGN(DOMDataStore); diff --git a/chromium/third_party/blink/renderer/platform/bindings/enumeration_base.h b/chromium/third_party/blink/renderer/platform/bindings/enumeration_base.h new file mode 100644 index 00000000000..f5e0fcf185a --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/bindings/enumeration_base.h @@ -0,0 +1,92 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_ENUMERATION_BASE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_ENUMERATION_BASE_H_ + +#include <type_traits> + +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" +#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "v8/include/v8.h" + +namespace blink { + +namespace bindings { + +class PLATFORM_EXPORT EnumerationBase { + DISALLOW_NEW(); + + public: + ~EnumerationBase() = default; + + const char* AsCStr() const { return string_literal_; } + String AsString() const { return string_literal_; } + + // Migration adapter + operator AtomicString() const { return string_literal_; } + operator String() const { return string_literal_; } + + // Returns true if the value is invalid. The instance in this state must be + // created only when an exception is thrown. + bool IsEmpty() const { return !string_literal_; } + + protected: + // Integer type that is convertible from/to enum values defined in subclasses. + using enum_int_t = size_t; + + constexpr EnumerationBase() = default; + explicit constexpr EnumerationBase(enum_int_t enum_value, + const char* string_literal) + : enum_value_(enum_value), string_literal_(string_literal) {} + constexpr EnumerationBase(const EnumerationBase&) = default; + constexpr EnumerationBase(EnumerationBase&&) = default; + + EnumerationBase& operator=(const EnumerationBase&) = default; + EnumerationBase& operator=(EnumerationBase&&) = default; + + enum_int_t GetEnumValue() const { return enum_value_; } + + private: + // Subclasses are supposed to define a C++ enum class and a table of strings + // that represent IDL enumeration values. |enum_value_| is an enum value of + // the C++ enum class (must be convertible from/to enum_int_t) and + // |string_literal_| is a pointer to a string in the table. + enum_int_t enum_value_ = 0; + const char* string_literal_ = nullptr; + + template <typename T> + friend typename std:: + enable_if_t<std::is_base_of<bindings::EnumerationBase, T>::value, bool> + operator==(const T& lhs, const T& rhs); + + template <typename T> + friend typename std:: + enable_if_t<std::is_base_of<bindings::EnumerationBase, T>::value, bool> + operator!=(const T& lhs, const T& rhs); +}; + +} // namespace bindings + +template <typename T> +typename std::enable_if_t<std::is_base_of<bindings::EnumerationBase, T>::value, + bool> +operator==(const T& lhs, const T& rhs) { + DCHECK(!lhs.IsEmpty() && !rhs.IsEmpty()); + return lhs.string_literal_ == rhs.string_literal_; +} + +template <typename T> +typename std::enable_if_t<std::is_base_of<bindings::EnumerationBase, T>::value, + bool> +operator!=(const T& lhs, const T& rhs) { + DCHECK(!lhs.IsEmpty() && !rhs.IsEmpty()); + return lhs.string_literal_ != rhs.string_literal_; +} + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_ENUMERATION_BASE_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/parkable_string.cc b/chromium/third_party/blink/renderer/platform/bindings/parkable_string.cc index c9f4ac49bd9..e2f8e5672d6 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/parkable_string.cc +++ b/chromium/third_party/blink/renderer/platform/bindings/parkable_string.cc @@ -186,6 +186,18 @@ enum class ParkableStringImpl::Status : uint8_t { kLocked }; +ParkableStringImpl::ParkableMetadata::ParkableMetadata( + String string, + std::unique_ptr<SecureDigest> digest) + : mutex_(), + lock_depth_(0), + state_(State::kUnparked), + compressed_(nullptr), + digest_(*digest), + is_young_(true), + is_8bit_(string.Is8Bit()), + length_(string.length()) {} + // static std::unique_ptr<ParkableStringImpl::SecureDigest> ParkableStringImpl::HashString(StringImpl* string) { @@ -224,15 +236,10 @@ scoped_refptr<ParkableStringImpl> ParkableStringImpl::MakeParkable( ParkableStringImpl::ParkableStringImpl(scoped_refptr<StringImpl>&& impl, std::unique_ptr<SecureDigest> digest) - : mutex_(), - lock_depth_(0), - state_(State::kUnparked), - string_(std::move(impl)), - compressed_(nullptr), - digest_(std::move(digest)), - is_young_(true), - is_8bit_(string_.Is8Bit()), - length_(string_.length()) + : string_(std::move(impl)), + metadata_(digest ? std::make_unique<ParkableMetadata>(string_, + std::move(digest)) + : nullptr) #if DCHECK_IS_ON() , owning_thread_(CurrentThread()) @@ -248,7 +255,8 @@ ParkableStringImpl::~ParkableStringImpl() { DCHECK_EQ(0, lock_depth_for_testing()); AsanUnpoisonString(string_); - DCHECK(state_ == State::kParked || state_ == State::kUnparked); + DCHECK(metadata_->state_ == State::kParked || + metadata_->state_ == State::kUnparked); ParkableStringManager::Instance().Remove(this); } @@ -257,8 +265,8 @@ void ParkableStringImpl::Lock() { if (!may_be_parked()) return; - MutexLocker locker(mutex_); - lock_depth_ += 1; + MutexLocker locker(metadata_->mutex_); + metadata_->lock_depth_ += 1; // Make young as this is a strong (but not certain) indication that the string // will be accessed soon. MakeYoung(); @@ -268,8 +276,8 @@ void ParkableStringImpl::Lock() { void ParkableStringImpl::LockWithoutMakingYoung() { DCHECK(may_be_parked()); - MutexLocker locker(mutex_); - lock_depth_ += 1; + MutexLocker locker(metadata_->mutex_); + metadata_->lock_depth_ += 1; } #endif // defined(ADDRESS_SANITIZER) @@ -278,9 +286,9 @@ void ParkableStringImpl::Unlock() { if (!may_be_parked()) return; - MutexLocker locker(mutex_); - DCHECK_GT(lock_depth_, 0); - lock_depth_ -= 1; + MutexLocker locker(metadata_->mutex_); + DCHECK_GT(metadata_->lock_depth_, 0); + metadata_->lock_depth_ -= 1; #if defined(ADDRESS_SANITIZER) && DCHECK_IS_ON() // There are no external references to the data, nobody should touch the data. @@ -302,13 +310,8 @@ void ParkableStringImpl::Unlock() { void ParkableStringImpl::PurgeMemory() { AssertOnValidThread(); - if (state_ == State::kUnparked) - compressed_ = nullptr; -} - -void ParkableStringImpl::MakeYoung() { - mutex_.AssertAcquired(); - is_young_ = true; + if (metadata_->state_ == State::kUnparked) + metadata_->compressed_ = nullptr; } const String& ParkableStringImpl::ToString() { @@ -316,7 +319,7 @@ const String& ParkableStringImpl::ToString() { if (!may_be_parked()) return string_; - MutexLocker locker(mutex_); + MutexLocker locker(metadata_->mutex_); MakeYoung(); AsanUnpoisonString(string_); Unpark(); @@ -325,21 +328,42 @@ const String& ParkableStringImpl::ToString() { unsigned ParkableStringImpl::CharactersSizeInBytes() const { AssertOnValidThread(); - return length_ * (is_8bit() ? sizeof(LChar) : sizeof(UChar)); + if (!may_be_parked()) + return string_.CharactersSizeInBytes(); + + return metadata_->length_ * (is_8bit() ? sizeof(LChar) : sizeof(UChar)); +} + +size_t ParkableStringImpl::MemoryFootprintForDump() const { + AssertOnValidThread(); + size_t size = sizeof(ParkableStringImpl); + + if (!may_be_parked()) + return size + string_.CharactersSizeInBytes(); + + size += sizeof(ParkableMetadata); + + if (!is_parked()) + size += string_.CharactersSizeInBytes(); + + if (metadata_->compressed_) + size += metadata_->compressed_->size(); + + return size; } ParkableStringImpl::AgeOrParkResult ParkableStringImpl::MaybeAgeOrParkString() { - MutexLocker locker(mutex_); + MutexLocker locker(metadata_->mutex_); AssertOnValidThread(); DCHECK(may_be_parked()); DCHECK(!is_parked()); Status status = CurrentStatus(); - if (is_young_) { + if (metadata_->is_young_) { if (status == Status::kUnreferencedExternally) - is_young_ = false; + metadata_->is_young_ = false; } else { - if (state_ == State::kParkingInProgress) + if (metadata_->state_ == State::kParkingInProgress) return AgeOrParkResult::kSuccessOrTransientFailure; if (CanParkNow()) { @@ -356,16 +380,17 @@ ParkableStringImpl::AgeOrParkResult ParkableStringImpl::MaybeAgeOrParkString() { } bool ParkableStringImpl::Park(ParkingMode mode) { - MutexLocker locker(mutex_); + MutexLocker locker(metadata_->mutex_); AssertOnValidThread(); DCHECK(may_be_parked()); - if (state_ == State::kParkingInProgress || state_ == State::kParked) + if (metadata_->state_ == State::kParkingInProgress || + metadata_->state_ == State::kParked) return true; // Making the string old to cancel parking if it is accessed/locked before // parking is complete. - is_young_ = false; + metadata_->is_young_ = false; if (!CanParkNow()) return false; @@ -373,15 +398,42 @@ bool ParkableStringImpl::Park(ParkingMode mode) { return true; } +bool ParkableStringImpl::is_parked() const { + DCHECK(may_be_parked()); + return metadata_->state_ == State::kParked; +} + +void ParkableStringImpl::MakeYoung() { + metadata_->is_young_ = true; +} + +ParkableStringImpl::Status ParkableStringImpl::CurrentStatus() const { + AssertOnValidThread(); + DCHECK(may_be_parked()); + // Can park iff: + // - |this| is not locked. + // - There are no external reference to |string_|. Since |this| holds a + // reference to |string_|, it must the only one. + if (metadata_->lock_depth_ != 0) + return Status::kLocked; + if (!string_.Impl()->HasOneRef()) + return Status::kTooManyReferences; + return Status::kUnreferencedExternally; +} + +bool ParkableStringImpl::CanParkNow() const { + return CurrentStatus() == Status::kUnreferencedExternally && + !metadata_->is_young_; +} + void ParkableStringImpl::ParkInternal(ParkingMode mode) { - mutex_.AssertAcquired(); - DCHECK_EQ(State::kUnparked, state_); - DCHECK(!is_young_); + DCHECK_EQ(State::kUnparked, metadata_->state_); + DCHECK(!metadata_->is_young_); DCHECK(CanParkNow()); // Parking can proceed synchronously. if (has_compressed_data()) { - state_ = State::kParked; + metadata_->state_ = State::kParked; ParkableStringManager::Instance().OnParked(this); // Must unpoison the memory before releasing it. @@ -398,45 +450,21 @@ void ParkableStringImpl::ParkInternal(ParkingMode mode) { FROM_HERE, CrossThreadBindOnce(&ParkableStringImpl::CompressInBackground, WTF::Passed(std::move(params)))); - state_ = State::kParkingInProgress; + metadata_->state_ = State::kParkingInProgress; } } -bool ParkableStringImpl::is_parked() const { - return state_ == State::kParked; -} - -ParkableStringImpl::Status ParkableStringImpl::CurrentStatus() const { - AssertOnValidThread(); - mutex_.AssertAcquired(); - DCHECK(may_be_parked()); - // Can park iff: - // - |this| is not locked. - // - There are no external reference to |string_|. Since |this| holds a - // reference to |string_|, it must the only one. - if (lock_depth_ != 0) - return Status::kLocked; - if (!string_.Impl()->HasOneRef()) - return Status::kTooManyReferences; - return Status::kUnreferencedExternally; -} - -bool ParkableStringImpl::CanParkNow() const { - return CurrentStatus() == Status::kUnreferencedExternally && !is_young_; -} - void ParkableStringImpl::Unpark() { AssertOnValidThread(); DCHECK(may_be_parked()); - mutex_.AssertAcquired(); - if (state_ != State::kParked) + if (metadata_->state_ != State::kParked) return; TRACE_EVENT1("blink", "ParkableStringImpl::Unpark", "size", CharactersSizeInBytes()); - DCHECK(compressed_); + DCHECK(metadata_->compressed_); string_ = UnparkInternal(); - state_ = State::kUnparked; + metadata_->state_ = State::kUnparked; ParkableStringManager::Instance().OnUnparked(this); } @@ -448,8 +476,8 @@ String ParkableStringImpl::UnparkInternal() const { base::ElapsedTimer timer; base::StringPiece compressed_string_piece( - reinterpret_cast<const char*>(compressed_->data()), - compressed_->size() * sizeof(uint8_t)); + reinterpret_cast<const char*>(metadata_->compressed_->data()), + metadata_->compressed_->size() * sizeof(uint8_t)); String uncompressed; base::StringPiece uncompressed_string_piece; size_t size = CharactersSizeInBytes(); @@ -491,15 +519,15 @@ void ParkableStringImpl::OnParkingCompleteOnMainThread( std::unique_ptr<CompressionTaskParams> params, std::unique_ptr<Vector<uint8_t>> compressed, base::TimeDelta parking_thread_time) { - MutexLocker locker(mutex_); - DCHECK_EQ(State::kParkingInProgress, state_); + MutexLocker locker(metadata_->mutex_); + DCHECK_EQ(State::kParkingInProgress, metadata_->state_); // Always keep the compressed data. Compression is expensive, so even if the // uncompressed representation cannot be discarded now, avoid compressing // multiple times. This will allow synchronous parking next time. - DCHECK(!compressed_); + DCHECK(!metadata_->compressed_); if (compressed) - compressed_ = std::move(compressed); + metadata_->compressed_ = std::move(compressed); // Between |Park()| and now, things may have happened: // 1. |ToString()| or @@ -507,15 +535,15 @@ void ParkableStringImpl::OnParkingCompleteOnMainThread( // // Both of these will make the string young again, and if so we don't // discard the compressed representation yet. - if (CanParkNow() && compressed_) { - state_ = State::kParked; + if (CanParkNow() && metadata_->compressed_) { + metadata_->state_ = State::kParked; ParkableStringManager::Instance().OnParked(this); // Must unpoison the memory before releasing it. AsanUnpoisonString(string_); string_ = String(); } else { - state_ = State::kUnparked; + metadata_->state_ = State::kUnparked; } // Record the time no matter whether the string was parked or not, as the // parking cost was paid. @@ -606,6 +634,7 @@ void ParkableStringImpl::CompressInBackground( RecordStatistics(size, timer.Elapsed(), ParkingAction::kParked); } + ParkableString::ParkableString(scoped_refptr<StringImpl>&& impl) { if (!impl) { impl_ = nullptr; @@ -634,14 +663,16 @@ void ParkableString::Unlock() const { void ParkableString::OnMemoryDump(WebProcessMemoryDump* pmd, const String& name) const { - // Parkable strings are reported by ParkableStringManager. - if (!impl_ || may_be_parked()) + if (!impl_) return; auto* dump = pmd->CreateMemoryAllocatorDump(name); - dump->AddScalar("size", "bytes", CharactersSizeInBytes()); - pmd->AddSuballocation(dump->Guid(), - String(WTF::Partitions::kAllocatedObjectPoolName)); + dump->AddScalar("size", "bytes", impl_->MemoryFootprintForDump()); + + const char* parent_allocation = + may_be_parked() ? ParkableStringManager::kAllocatorDumpName + : WTF::Partitions::kAllocatedObjectPoolName; + pmd->AddSuballocation(dump->Guid(), parent_allocation); } bool ParkableString::Is8Bit() const { diff --git a/chromium/third_party/blink/renderer/platform/bindings/parkable_string.h b/chromium/third_party/blink/renderer/platform/bindings/parkable_string.h index cd1b5ee2be8..b9d2bd8ef62 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/parkable_string.h +++ b/chromium/third_party/blink/renderer/platform/bindings/parkable_string.h @@ -72,9 +72,29 @@ class PLATFORM_EXPORT ParkableStringImpl final const String& ToString(); // See the matching String methods. - bool is_8bit() const { return is_8bit_; } - unsigned length() const { return length_; } + bool is_8bit() const { + if (!may_be_parked()) + return string_.Is8Bit(); + + return metadata_->is_8bit_; + } + unsigned length() const { + if (!may_be_parked()) + return string_.length(); + + return metadata_->length_; + } unsigned CharactersSizeInBytes() const; + size_t MemoryFootprintForDump() const; + + // Returns true iff the string can be parked. This does not mean that the + // string can be parked now, merely that it is eligible to be parked at some + // point. + bool may_be_parked() const { return !!metadata_; } + + // Note: Public member functions below must only be called on strings for + // which |may_be_parked()| returns true. Otherwise, these will either trigger + // a DCHECK() or crash. // Tries to either age or park a string: // @@ -98,32 +118,30 @@ class PLATFORM_EXPORT ParkableStringImpl final // // Returns true if the string is being parked or has been parked. bool Park(ParkingMode mode); - // Returns true iff the string can be parked. This does not mean that the - // string can be parked now, merely that it is eligible to be parked at some - // point. - bool may_be_parked() const { return !!digest_; } + // Returns true if the string is parked. bool is_parked() const; + // Returns whether synchronous parking is possible, that is the string was // parked in the past. - bool has_compressed_data() const { return !!compressed_; } + bool has_compressed_data() const { return !!metadata_->compressed_; } + // Returns the compressed size, must not be called unless the string has a // compressed representation. size_t compressed_size() const { DCHECK(has_compressed_data()); - return compressed_->size(); + return metadata_->compressed_->size(); } bool is_young_for_testing() { - MutexLocker locker(mutex_); - return is_young_; + MutexLocker locker(metadata_->mutex_); + return metadata_->is_young_; } - // Must only be called on parkable ParkableStringImpls. - SecureDigest* digest() const { + const SecureDigest* digest() const { AssertOnValidThread(); - DCHECK(digest_); - return digest_.get(); + DCHECK(metadata_); + return &metadata_->digest_; } private: @@ -136,20 +154,25 @@ class PLATFORM_EXPORT ParkableStringImpl final ParkableStringImpl(scoped_refptr<StringImpl>&& impl, std::unique_ptr<SecureDigest> digest); + // Note: Private member functions below must only be called on strings for + // which |may_be_parked()| returns true. Otherwise, these will either trigger + // a DCHECK() or crash. + #if defined(ADDRESS_SANITIZER) // See |CompressInBackground()|. Doesn't make the string young. // May be called from any thread. void LockWithoutMakingYoung(); #endif // defined(ADDRESS_SANITIZER) // May be called from any thread. - void MakeYoung() EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void MakeYoung() EXCLUSIVE_LOCKS_REQUIRED(metadata_->mutex_); // Whether the string is referenced or locked. The return value is valid as // long as |mutex_| is held. - Status CurrentStatus() const EXCLUSIVE_LOCKS_REQUIRED(mutex_); - bool CanParkNow() const EXCLUSIVE_LOCKS_REQUIRED(mutex_); - void ParkInternal(ParkingMode mode); - void Unpark(); - String UnparkInternal() const; + Status CurrentStatus() const EXCLUSIVE_LOCKS_REQUIRED(metadata_->mutex_); + bool CanParkNow() const EXCLUSIVE_LOCKS_REQUIRED(metadata_->mutex_); + void ParkInternal(ParkingMode mode) + EXCLUSIVE_LOCKS_REQUIRED(metadata_->mutex_); + void Unpark() EXCLUSIVE_LOCKS_REQUIRED(metadata_->mutex_); + String UnparkInternal() const EXCLUSIVE_LOCKS_REQUIRED(metadata_->mutex_); // Called on the main thread after compression is done. // |params| is the same as the one passed to |CompressInBackground()|, // |compressed| is the compressed data, nullptr if compression failed. @@ -164,31 +187,40 @@ class PLATFORM_EXPORT ParkableStringImpl final static void CompressInBackground(std::unique_ptr<CompressionTaskParams>); int lock_depth_for_testing() { - MutexLocker locker_(mutex_); - return lock_depth_; + MutexLocker locker_(metadata_->mutex_); + return metadata_->lock_depth_; } - Mutex mutex_; - int lock_depth_ GUARDED_BY(mutex_); + // Metadata only used for parkable ParkableStrings. + struct ParkableMetadata { + ParkableMetadata(String string, std::unique_ptr<SecureDigest> digest); + + Mutex mutex_; + int lock_depth_ GUARDED_BY(mutex_); + + // Main thread only. + State state_; + std::unique_ptr<Vector<uint8_t>> compressed_; + const SecureDigest digest_; + + // A string can either be "young" or "old". It starts young, and transitions + // are: + // Young -> Old: By calling |MaybeAgeOrParkString()|. + // Old -> Young: When the string is accessed, either by |Lock()|-ing it or + // calling |ToString()|. + // + // Thread safety: it is typically not safe to guard only one part of a + // bitfield with a mutex, but this is correct here, as the other members are + // const (and never change). + bool is_young_ : 1 GUARDED_BY(mutex_); + const bool is_8bit_ : 1; + const unsigned length_; + + DISALLOW_COPY_AND_ASSIGN(ParkableMetadata); + }; - // Main thread only. - State state_; String string_; - std::unique_ptr<Vector<uint8_t>> compressed_; - const std::unique_ptr<SecureDigest> digest_; - - // A string can either be "young" or "old". It starts young, and transitions - // are: - // Young -> Old: By calling |MaybeAgeOrParkString()|. - // Old -> Young: When the string is accessed, either by |Lock()|-ing it or - // calling |ToString()|. - // - // Thread safety: it is typically not safe to guard only one part of a - // bitfield with a mutex, but this is correct here, as the other members are - // const (and never change). - bool is_young_ : 1 GUARDED_BY(mutex_); - const bool is_8bit_ : 1; - const unsigned length_; + const std::unique_ptr<ParkableMetadata> metadata_; #if DCHECK_IS_ON() const base::PlatformThreadId owning_thread_; @@ -200,13 +232,26 @@ class PLATFORM_EXPORT ParkableStringImpl final #endif } - FRIEND_TEST_ALL_PREFIXES(ParkableStringTest, LockUnlock); - FRIEND_TEST_ALL_PREFIXES(ParkableStringTest, LockParkedString); + public: FRIEND_TEST_ALL_PREFIXES(ParkableStringTest, Equality); FRIEND_TEST_ALL_PREFIXES(ParkableStringTest, EqualityNoUnparking); + FRIEND_TEST_ALL_PREFIXES(ParkableStringTest, LockUnlock); + FRIEND_TEST_ALL_PREFIXES(ParkableStringTest, LockParkedString); + FRIEND_TEST_ALL_PREFIXES(ParkableStringTest, ReportMemoryDump); + FRIEND_TEST_ALL_PREFIXES(ParkableStringTest, MemoryFootprintForDump); + DISALLOW_COPY_AND_ASSIGN(ParkableStringImpl); }; +#if !DCHECK_IS_ON() +// 3 pointers: +// - vtable (from RefCounted) +// - string_.Impl() +// - metadata_ +static_assert(sizeof(ParkableStringImpl) == 3 * sizeof(void*), + "ParkableStringImpl should not be too large"); +#endif + class PLATFORM_EXPORT ParkableString final { DISALLOW_NEW(); @@ -249,6 +294,9 @@ class PLATFORM_EXPORT ParkableString final { scoped_refptr<ParkableStringImpl> impl_; }; +static_assert(sizeof(ParkableString) == sizeof(void*), + "ParkableString should be small"); + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_PARKABLE_STRING_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc b/chromium/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc index 45da38a8126..dd987fac607 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc +++ b/chromium/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc @@ -62,17 +62,20 @@ class OnPurgeMemoryListener : public GarbageCollected<OnPurgeMemoryListener>, } // namespace +const char* ParkableStringManager::kAllocatorDumpName = "parkable_strings"; + // Compares not the pointers, but the arrays. Uses pointers to save space. struct ParkableStringManager::SecureDigestHash { STATIC_ONLY(SecureDigestHash); - static unsigned GetHash(ParkableStringImpl::SecureDigest* const digest) { + static unsigned GetHash( + const ParkableStringImpl::SecureDigest* const digest) { // The first bytes of the hash are as good as anything else. return *reinterpret_cast<const unsigned*>(digest->data()); } - static inline bool Equal(ParkableStringImpl::SecureDigest* const a, - ParkableStringImpl::SecureDigest* const b) { + static inline bool Equal(const ParkableStringImpl::SecureDigest* const a, + const ParkableStringImpl::SecureDigest* const b) { return a == b || std::equal(a->data(), a->data() + ParkableStringImpl::kDigestSize, b->data()); @@ -121,7 +124,7 @@ bool ParkableStringManager::OnMemoryDump( base::trace_event::ProcessMemoryDump* pmd) { DCHECK(IsMainThread()); base::trace_event::MemoryAllocatorDump* dump = - pmd->CreateAllocatorDump("parkable_strings"); + pmd->CreateAllocatorDump(kAllocatorDumpName); Statistics stats = ComputeStatistics(); @@ -375,7 +378,7 @@ ParkableStringManager::Statistics ParkableStringManager::ComputeStatistics() // The digest has an inline capacity set to the digest size, hence sizeof() is // accurate. constexpr size_t kParkableStringImplActualSize = - sizeof(ParkableStringImpl) + sizeof(ParkableStringImpl::SecureDigest); + sizeof(ParkableStringImpl) + sizeof(ParkableStringImpl::ParkableMetadata); for (const auto& kv : unparked_strings_) { ParkableStringImpl* str = kv.value; @@ -386,6 +389,15 @@ ParkableStringManager::Statistics ParkableStringManager::ComputeStatistics() if (str->has_compressed_data()) stats.overhead_size += str->compressed_size(); + + // Since ParkableStringManager wants to have a finer breakdown of memory + // footprint, this doesn't directly use + // |ParkableStringImpl::MemoryFootprintForDump()|. However we want the two + // computations to be consistent, hence the DCHECK(). + size_t memory_footprint = + (str->has_compressed_data() ? str->compressed_size() : 0) + size + + kParkableStringImplActualSize; + DCHECK_EQ(memory_footprint, str->MemoryFootprintForDump()); } for (const auto& kv : parked_strings_) { @@ -395,6 +407,11 @@ ParkableStringManager::Statistics ParkableStringManager::ComputeStatistics() stats.original_size += size; stats.compressed_size += str->compressed_size(); stats.metadata_size += kParkableStringImplActualSize; + + // See comment above. + size_t memory_footprint = + str->compressed_size() + kParkableStringImplActualSize; + DCHECK_EQ(memory_footprint, str->MemoryFootprintForDump()); } stats.total_size = stats.uncompressed_size + stats.compressed_size + diff --git a/chromium/third_party/blink/renderer/platform/bindings/parkable_string_manager.h b/chromium/third_party/blink/renderer/platform/bindings/parkable_string_manager.h index ecc958b620f..e7fd7298c4d 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/parkable_string_manager.h +++ b/chromium/third_party/blink/renderer/platform/bindings/parkable_string_manager.h @@ -67,6 +67,8 @@ class PLATFORM_EXPORT ParkableStringManager { // Public for testing. constexpr static int kAgingIntervalInSeconds = 2; + static const char* kAllocatorDumpName; + private: friend class ParkableString; friend class ParkableStringImpl; @@ -102,11 +104,11 @@ class PLATFORM_EXPORT ParkableStringManager { // Relies on secure hash equality for deduplication. If one day SHA256 becomes // insecure, then this would need to be updated to a more robust hash. - WTF::HashMap<ParkableStringImpl::SecureDigest*, + WTF::HashMap<const ParkableStringImpl::SecureDigest*, ParkableStringImpl*, SecureDigestHash> unparked_strings_; - WTF::HashMap<ParkableStringImpl::SecureDigest*, + WTF::HashMap<const ParkableStringImpl::SecureDigest*, ParkableStringImpl*, SecureDigestHash> parked_strings_; diff --git a/chromium/third_party/blink/renderer/platform/bindings/parkable_string_test.cc b/chromium/third_party/blink/renderer/platform/bindings/parkable_string_test.cc index a6b796743d6..a1e7a6a9e6c 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/parkable_string_test.cc +++ b/chromium/third_party/blink/renderer/platform/bindings/parkable_string_test.cc @@ -611,6 +611,9 @@ TEST_F(ParkableStringTest, ReportMemoryDump) { using testing::Contains; using testing::Eq; + constexpr size_t kActualSize = + sizeof(ParkableStringImpl) + sizeof(ParkableStringImpl::ParkableMetadata); + auto& manager = ParkableStringManager::Instance(); ParkableString parkable1(MakeLargeString('a').ReleaseImpl()); ParkableString parkable2(MakeLargeString('b').ReleaseImpl()); @@ -647,29 +650,53 @@ TEST_F(ParkableStringTest, ReportMemoryDump) { kCompressedSize); EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(overhead)))); - constexpr size_t kParkableStringImplActualSize = - sizeof(ParkableStringImpl) + sizeof(ParkableStringImpl::SecureDigest); MemoryAllocatorDump::Entry metadata("metadata_size", "bytes", - 2 * kParkableStringImplActualSize); + 2 * kActualSize); EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(metadata)))); MemoryAllocatorDump::Entry savings( "savings_size", "bytes", - 2 * kStringSize - (kStringSize + 2 * kCompressedSize + - 2 * kParkableStringImplActualSize)); + 2 * kStringSize - (kStringSize + 2 * kCompressedSize + 2 * kActualSize)); EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(savings)))); } +TEST_F(ParkableStringTest, MemoryFootprintForDump) { + constexpr size_t kActualSize = + sizeof(ParkableStringImpl) + sizeof(ParkableStringImpl::ParkableMetadata); + + size_t memory_footprint; + ParkableString parkable1(MakeLargeString('a').ReleaseImpl()); + ParkableString parkable2(MakeLargeString('b').ReleaseImpl()); + ParkableString parkable3(String("short string, not parkable").ReleaseImpl()); + + WaitForDelayedParking(); + parkable1.ToString(); + + // Compressed and uncompressed data. + memory_footprint = kActualSize + parkable1.Impl()->compressed_size() + + parkable1.Impl()->CharactersSizeInBytes(); + EXPECT_EQ(memory_footprint, parkable1.Impl()->MemoryFootprintForDump()); + + // Compressed uncompressed data only. + memory_footprint = kActualSize + parkable2.Impl()->compressed_size(); + EXPECT_EQ(memory_footprint, parkable2.Impl()->MemoryFootprintForDump()); + + // Short string, no metadata. + memory_footprint = + sizeof(ParkableStringImpl) + parkable3.Impl()->CharactersSizeInBytes(); + EXPECT_EQ(memory_footprint, parkable3.Impl()->MemoryFootprintForDump()); +} + TEST_F(ParkableStringTest, CompressionDisabled) { base::test::ScopedFeatureList features; features.InitAndDisableFeature(kCompressParkableStrings); ParkableString parkable(MakeLargeString().ReleaseImpl()); WaitForDelayedParking(); - EXPECT_FALSE(parkable.Impl()->is_parked()); + EXPECT_FALSE(parkable.Impl()->may_be_parked()); MemoryPressureListenerRegistry::Instance().OnPurgeMemory(); - EXPECT_FALSE(parkable.Impl()->is_parked()); + EXPECT_FALSE(parkable.Impl()->may_be_parked()); } TEST_F(ParkableStringTest, Aging) { diff --git a/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h b/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h index d898c3c5280..99a79bb2eb2 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h +++ b/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h @@ -23,6 +23,10 @@ #include "third_party/blink/renderer/platform/wtf/vector.h" #endif +#if BUILDFLAG(BLINK_BINDINGS_TRACE_ENABLED) +#include "base/trace_event/trace_event.h" +#endif + namespace base { class TickClock; } @@ -167,6 +171,15 @@ class PLATFORM_EXPORT RuntimeCallTimer { } while (false) #endif +#if BUILDFLAG(BLINK_BINDINGS_TRACE_ENABLED) +#define BLINK_BINDINGS_TRACE_EVENT(trace_event_name) \ + TRACE_EVENT0("blink.bindings", trace_event_name) +#else +#define BLINK_BINDINGS_TRACE_EVENT(trace_event_name) \ + do { \ + } while (false) +#endif + // Maintains a stack of timers and provides functions to manage recording scopes // by pausing and resuming timers in the chain when entering and leaving a // scope. @@ -236,6 +249,7 @@ class PLATFORM_EXPORT RuntimeCallStats { #define CALLBACK_COUNTERS(V) \ BINDINGS_METHOD(V, ElementGetBoundingClientRect) \ + BINDINGS_METHOD(V, ElementGetInnerHTML) \ BINDINGS_METHOD(V, EventTargetDispatchEvent) \ BINDINGS_METHOD(V, HTMLElementClick) \ BINDINGS_METHOD(V, NodeAppendChild) \ diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_promise_properties.h b/chromium/third_party/blink/renderer/platform/bindings/script_promise_properties.h deleted file mode 100644 index e6224751292..00000000000 --- a/chromium/third_party/blink/renderer/platform/bindings/script_promise_properties.h +++ /dev/null @@ -1,20 +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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_PROMISE_PROPERTIES_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_PROMISE_PROPERTIES_H_ - -// See ScriptPromiseProperty.h -#define SCRIPT_PROMISE_PROPERTIES(P, ...) \ - P(ScriptPromise, kReady##__VA_ARGS__) \ - P(ScriptPromise, kClosed##__VA_ARGS__) \ - P(ScriptPromise, kFinished##__VA_ARGS__) \ - P(ScriptPromise, kLoaded##__VA_ARGS__) \ - P(ScriptPromise, kLost##__VA_ARGS__) \ - P(ScriptPromise, kReleased##__VA_ARGS__) \ - P(ScriptPromise, kResponseReady##__VA_ARGS__) \ - P(ScriptPromise, kUserChoice##__VA_ARGS__) \ - P(ScriptPromise, kPreloadResponse##__VA_ARGS__) - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_PROMISE_PROPERTIES_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_state.h b/chromium/third_party/blink/renderer/platform/bindings/script_state.h index f4ba8ef7edc..22baa5189bb 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/script_state.h +++ b/chromium/third_party/blink/renderer/platform/bindings/script_state.h @@ -75,7 +75,7 @@ class V8PerContextData; // all V8 proxy objects that have references to the ScriptState are destructed. class PLATFORM_EXPORT ScriptState final : public GarbageCollected<ScriptState> { public: - class Scope { + class Scope final { STACK_ALLOCATED(); public: @@ -95,10 +95,36 @@ class PLATFORM_EXPORT ScriptState final : public GarbageCollected<ScriptState> { v8::Local<v8::Context> context_; }; + // Use EscapableScope if you have to return a v8::Local to an outer scope. + // See v8::EscapableHandleScope. + class EscapableScope final { + STACK_ALLOCATED(); + + public: + // You need to make sure that scriptState->context() is not empty before + // creating a Scope. + explicit EscapableScope(ScriptState* script_state) + : handle_scope_(script_state->GetIsolate()), + context_(script_state->GetContext()) { + DCHECK(script_state->ContextIsValid()); + context_->Enter(); + } + + ~EscapableScope() { context_->Exit(); } + + v8::Local<v8::Value> Escape(v8::Local<v8::Value> value) { + return handle_scope_.Escape(value); + } + + private: + v8::EscapableHandleScope handle_scope_; + v8::Local<v8::Context> context_; + }; + ScriptState(v8::Local<v8::Context>, scoped_refptr<DOMWrapperWorld>); ~ScriptState(); - void Trace(blink::Visitor*) {} + void Trace(Visitor*) {} static ScriptState* Current(v8::Isolate* isolate) { // DEPRECATED return From(isolate->GetCurrentContext()); @@ -193,9 +219,9 @@ class PLATFORM_EXPORT ScriptState final : public GarbageCollected<ScriptState> { // exactly. SelfKeepAlive<ScriptState> reference_from_v8_context_; - static constexpr int kV8ContextPerContextDataIndex = static_cast<int>( - gin::kPerContextDataStartIndex + // NOLINT(readability/enum_casing) - gin::kEmbedderBlink); // NOLINT(readability/enum_casing) + static constexpr int kV8ContextPerContextDataIndex = + static_cast<int>(gin::kPerContextDataStartIndex) + + static_cast<int>(gin::kEmbedderBlink); DISALLOW_COPY_AND_ASSIGN(ScriptState); }; @@ -215,7 +241,7 @@ class ScriptStateProtectingContext final } } - void Trace(blink::Visitor* visitor) { visitor->Trace(script_state_); } + void Trace(Visitor* visitor) { visitor->Trace(script_state_); } ScriptState* Get() const { return script_state_; } void Reset() { diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc index 3f344d0ccf0..aaea2cdebb7 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc +++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.cc @@ -17,7 +17,7 @@ struct SameSizeAsScriptWrappable { static_assert(sizeof(ScriptWrappable) <= sizeof(SameSizeAsScriptWrappable), "ScriptWrappable should stay small"); -v8::Local<v8::Object> ScriptWrappable::Wrap( +v8::Local<v8::Value> ScriptWrappable::Wrap( v8::Isolate* isolate, v8::Local<v8::Object> creation_context) { const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo(); diff --git a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h index 25475561588..2a0f41e3f32 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h +++ b/chromium/third_party/blink/renderer/platform/bindings/script_wrappable.h @@ -48,7 +48,7 @@ namespace blink { // a v8::Object and toScriptWrappable() converts a v8::Object back to // a ScriptWrappable. v8::Object as platform object is called "wrapper object". // The wrapper object for the main world is stored in ScriptWrappable. Wrapper -// objects for other worlds are stored in DOMWrapperMap. +// objects for other worlds are stored in DOMDataStore. class PLATFORM_EXPORT ScriptWrappable : public GarbageCollected<ScriptWrappable>, public NameClient { @@ -68,7 +68,7 @@ class PLATFORM_EXPORT ScriptWrappable const char* NameInHeapSnapshot() const override; - virtual void Trace(blink::Visitor*); + virtual void Trace(Visitor*); template <typename T> T* ToImpl() { @@ -78,16 +78,14 @@ class PLATFORM_EXPORT ScriptWrappable sizeof(T) && WTF::IsGarbageCollectedType<T>::value, "Classes implementing ScriptWrappable must be garbage collected."); -// Check if T* is castable to ScriptWrappable*, which means T doesn't -// have two or more ScriptWrappable as superclasses. If T has two -// ScriptWrappable as superclasses, conversions from T* to -// ScriptWrappable* are ambiguous. -#if !defined(COMPILER_MSVC) - // MSVC 2013 doesn't support static_assert + constexpr well. + // Check if T* is castable to ScriptWrappable*, which means T doesn't + // have two or more ScriptWrappable as superclasses. If T has two + // ScriptWrappable as superclasses, conversions from T* to + // ScriptWrappable* are ambiguous. static_assert(!static_cast<ScriptWrappable*>(static_cast<T*>(nullptr)), "Class T must not have two or more ScriptWrappable as its " "superclasses."); -#endif + return static_cast<T*>(this); } @@ -97,8 +95,8 @@ class PLATFORM_EXPORT ScriptWrappable virtual const WrapperTypeInfo* GetWrapperTypeInfo() const = 0; // Creates and returns a new wrapper object. - virtual v8::Local<v8::Object> Wrap(v8::Isolate*, - v8::Local<v8::Object> creation_context); + virtual v8::Local<v8::Value> Wrap(v8::Isolate*, + v8::Local<v8::Object> creation_context); // Associates the instance with the given |wrapper| if this instance is not // yet associated with any wrapper. Returns the wrapper already associated @@ -190,6 +188,9 @@ inline bool ScriptWrappable::UnsetMainWorldWrapperIfSet( const WrapperTypeInfo* GetWrapperTypeInfo() const override { \ return &wrapper_type_info_; \ } \ + static const WrapperTypeInfo* GetStaticWrapperTypeInfo() { \ + return &wrapper_type_info_; \ + } \ \ private: \ static const WrapperTypeInfo& wrapper_type_info_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/shared_persistent.h b/chromium/third_party/blink/renderer/platform/bindings/shared_persistent.h deleted file mode 100644 index 03a87e9e10c..00000000000 --- a/chromium/third_party/blink/renderer/platform/bindings/shared_persistent.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SHARED_PERSISTENT_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SHARED_PERSISTENT_H_ - -#include "base/memory/scoped_refptr.h" -#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" -#include "third_party/blink/renderer/platform/bindings/scoped_persistent.h" -#include "third_party/blink/renderer/platform/wtf/ref_counted.h" -#include "v8/include/v8.h" - -namespace blink { - -// A ref counted version of ScopedPersistent. This class is intended for use by -// ScriptValue and not for normal use. Consider using ScopedPersistent directly -// instead. -// TODO(crbug.com/1029738): Remove once bug is fixed. -template <typename T> -class SharedPersistent : public RefCounted<SharedPersistent<T>> { - USING_FAST_MALLOC(SharedPersistent); - - public: - static scoped_refptr<SharedPersistent<T>> Create(v8::Isolate* isolate, - v8::Local<T> value) { - return base::AdoptRef(new SharedPersistent<T>(isolate, value)); - } - - // Returns the V8 reference. Crashes if |world_| is set and it is - // different from |target_script_state|'s world. - v8::Local<T> Get(ScriptState* target_script_state) const { - DCHECK(!value_.IsEmpty()); - if (world_) { - CHECK_EQ(world_.get(), &target_script_state->World()); - } - return value_.NewLocal(target_script_state->GetIsolate()); - } - - // Returns a V8 reference that is safe to access in |target_script_state|. - // The return value may be a cloned object. - v8::Local<T> GetAcrossWorld(ScriptState* target_script_state) const { - CHECK(world_); - DCHECK(!value_.IsEmpty()); - - v8::Isolate* isolate = target_script_state->GetIsolate(); - - if (world_ == &target_script_state->World()) - return value_.NewLocal(isolate); - - // If |v8_reference| is a v8::Object, clones |v8_reference| in the context - // of |target_script_state| and returns it. Otherwise returns - // |v8_reference| itself that is already safe to access in - // |target_script_state|. - - v8::Local<v8::Value> value = value_.NewLocal(isolate); - DCHECK(value->IsObject()); - return value.template As<T>(); - } - - bool IsEmpty() { return value_.IsEmpty(); } - - bool operator==(const SharedPersistent<T>& other) { - return value_ == other.value_; - } - - private: - explicit SharedPersistent(v8::Isolate* isolate, v8::Local<T> value) - : value_(isolate, value) { - DCHECK(!value.IsEmpty()); - // Basically, |world_| is a world when this V8 reference is created. - // However, when this V8 reference isn't created in context and value is - // object, we set |world_| to a value's creation cotext's world. - if (isolate->InContext()) { - world_ = &DOMWrapperWorld::Current(isolate); - if (value->IsObject()) { - v8::Local<v8::Context> context = - value.template As<v8::Object>()->CreationContext(); - // Creation context is null if the value is a remote object. - if (!context.IsEmpty()) { - ScriptState* script_state = ScriptState::From(context); - CHECK_EQ(world_.get(), &script_state->World()); - } - } - } else if (value->IsObject()) { - ScriptState* script_state = - ScriptState::From(value.template As<v8::Object>()->CreationContext()); - world_ = &script_state->World(); - } - } - - ScopedPersistent<T> value_; - // The world of the current context at the time when |value_| was set. - // It's guaranteed that, if |value_| is a v8::Object, the world of the - // creation context of |value_| is the same as |world_|. - scoped_refptr<const DOMWrapperWorld> world_; - - DISALLOW_COPY_AND_ASSIGN(SharedPersistent); -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SHARED_PERSISTENT_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/to_v8.h b/chromium/third_party/blink/renderer/platform/bindings/to_v8.h index 246140e5dcd..50ae965e76e 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/to_v8.h +++ b/chromium/third_party/blink/renderer/platform/bindings/to_v8.h @@ -19,6 +19,7 @@ #include "third_party/blink/renderer/platform/bindings/dom_data_store.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/bindings/union_base.h" #include "third_party/blink/renderer/platform/bindings/v8_binding.h" #include "third_party/blink/renderer/platform/wtf/forward.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" @@ -85,6 +86,17 @@ inline v8::Local<v8::Value> ToV8(CallbackInterfaceBase* callback, : v8::Null(isolate).As<v8::Value>(); } +// Union type + +inline v8::Local<v8::Value> ToV8(const bindings::UnionBase& value, + v8::Local<v8::Object> creation_context, + v8::Isolate* isolate) { + v8::Local<v8::Value> v8_value = + value.CreateV8Object(isolate, creation_context); + DCHECK(!v8_value.IsEmpty()); + return v8_value; +} + // Primitives inline v8::Local<v8::Value> ToV8(const String& value, @@ -208,27 +220,27 @@ inline v8::Local<v8::Value> ToV8(const base::Optional<InnerType>& value, // Declare the function here but define it later so it can call the ToV8() // overloads below. template <typename Sequence> -inline v8::Local<v8::Value> ToV8SequenceInternal( +inline v8::Local<v8::Array> ToV8SequenceInternal( const Sequence&, v8::Local<v8::Object> creation_context, v8::Isolate*); template <typename T, size_t Extent> -inline v8::Local<v8::Value> ToV8(base::span<T, Extent> value, +inline v8::Local<v8::Array> ToV8(base::span<T, Extent> value, v8::Local<v8::Object> creation_context, v8::Isolate* isolate) { return ToV8SequenceInternal(value, creation_context, isolate); } template <typename T, wtf_size_t inlineCapacity> -inline v8::Local<v8::Value> ToV8(const Vector<T, inlineCapacity>& value, +inline v8::Local<v8::Array> ToV8(const Vector<T, inlineCapacity>& value, v8::Local<v8::Object> creation_context, v8::Isolate* isolate) { return ToV8SequenceInternal(value, creation_context, isolate); } template <typename T, wtf_size_t inlineCapacity> -inline v8::Local<v8::Value> ToV8(const HeapVector<T, inlineCapacity>& value, +inline v8::Local<v8::Array> ToV8(const HeapVector<T, inlineCapacity>& value, v8::Local<v8::Object> creation_context, v8::Isolate* isolate) { return ToV8SequenceInternal(value, creation_context, isolate); @@ -289,7 +301,7 @@ inline v8::Local<v8::Value> ToV8(const HeapVector<std::pair<String, T>>& value, } template <typename Sequence> -inline v8::Local<v8::Value> ToV8SequenceInternal( +inline v8::Local<v8::Array> ToV8SequenceInternal( const Sequence& sequence, v8::Local<v8::Object> creation_context, v8::Isolate* isolate) { @@ -312,7 +324,7 @@ inline v8::Local<v8::Value> ToV8SequenceInternal( if (!array->CreateDataProperty(context, index++, value) .To(&created_property) || !created_property) { - return v8::Local<v8::Value>(); + return v8::Local<v8::Array>(); } } return array; diff --git a/chromium/third_party/blink/renderer/platform/bindings/union_base.h b/chromium/third_party/blink/renderer/platform/bindings/union_base.h new file mode 100644 index 00000000000..35ce7f8151b --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/bindings/union_base.h @@ -0,0 +1,45 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_UNION_BASE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_UNION_BASE_H_ + +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "v8/include/v8.h" + +namespace blink { + +class Visitor; + +namespace bindings { + +// This class is the base class for all IDL dictionary implementations. This +// is designed to collaborate with NativeValueTraits and ToV8 with supporting +// type dispatching (SFINAE, etc.). +class PLATFORM_EXPORT UnionBase { + DISALLOW_NEW(); + + public: + virtual ~UnionBase() = default; + + virtual v8::Local<v8::Value> CreateV8Object( + v8::Isolate* isolate, + v8::Local<v8::Object> creation_context) const = 0; + + void Trace(Visitor*) {} + + protected: + UnionBase() = default; + UnionBase(const UnionBase&) = default; + UnionBase(UnionBase&&) = default; + UnionBase& operator=(const UnionBase&) = default; + UnionBase& operator=(UnionBase&&) = default; +}; + +} // namespace bindings + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_UNION_BASE_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_binding.h b/chromium/third_party/blink/renderer/platform/bindings/v8_binding.h index ba7445d3342..ed84d117363 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/v8_binding.h +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_binding.h @@ -46,6 +46,10 @@ namespace blink { +namespace bindings { +class DictionaryBase; +} + // This file contains bindings helper functions that do not have dependencies // to core/ or bindings/core. For core-specific helper functions, see // bindings/core/v8/V8BindingForCore.h. @@ -99,6 +103,11 @@ inline void V8SetReturnValue(const CallbackInfo& info, uint32_t value) { } template <typename CallbackInfo> +inline void V8SetReturnValue(const CallbackInfo& info, uint64_t value) { + info.GetReturnValue().Set(static_cast<double>(value)); +} + +template <typename CallbackInfo> inline void V8SetReturnValueBool(const CallbackInfo& info, bool v) { info.GetReturnValue().Set(v); } @@ -162,7 +171,7 @@ inline void V8SetReturnValue(const CallbackInfo& callback_info, } if (DOMDataStore::SetReturnValue(callback_info.GetReturnValue(), impl)) return; - v8::Local<v8::Object> wrapper = + v8::Local<v8::Value> wrapper = impl->Wrap(callback_info.GetIsolate(), creation_context); V8SetReturnValue(callback_info, wrapper); } @@ -184,7 +193,7 @@ inline void V8SetReturnValueForMainWorld(const CallbackInfo& callback_info, if (DOMDataStore::SetReturnValueForMainWorld(callback_info.GetReturnValue(), impl)) return; - v8::Local<v8::Object> wrapper = + v8::Local<v8::Value> wrapper = impl->Wrap(callback_info.GetIsolate(), callback_info.Holder()); V8SetReturnValue(callback_info, wrapper); } @@ -200,7 +209,7 @@ inline void V8SetReturnValueFast(const CallbackInfo& callback_info, if (DOMDataStore::SetReturnValueFast(callback_info.GetReturnValue(), impl, callback_info.Holder(), wrappable)) return; - v8::Local<v8::Object> wrapper = + v8::Local<v8::Value> wrapper = impl->Wrap(callback_info.GetIsolate(), callback_info.Holder()); V8SetReturnValue(callback_info, wrapper); } @@ -212,6 +221,20 @@ inline void V8SetReturnValueFast(const CallbackInfo& callback_info, V8SetReturnValue(callback_info, handle); } +// Dictionary +template <class CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + bindings::DictionaryBase* value, + v8::Local<v8::Object> creation_context) { + V8SetReturnValue(info, ToV8(value, creation_context, info.GetIsolate())); +} + +template <class CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + bindings::DictionaryBase* value) { + V8SetReturnValue(info, ToV8(value, info.Holder(), info.GetIsolate())); +} + // Convert v8::String to a WTF::String. If the V8 string is not already // an external string then it is transformed into an external string at this // point to avoid repeated conversions. @@ -359,6 +382,24 @@ static void IndexedPropertyEnumerator( PLATFORM_EXPORT v8::Local<v8::Value> FreezeV8Object(v8::Local<v8::Value>, v8::Isolate*); +// Return values of indexed properties and named properties + +enum class IndexedPropertySetterResult { + kDidNotIntercept, // Fallback to the default set operation. + kIntercepted, // Intercepted regardless of whether it succeeded or not. +}; + +enum class NamedPropertySetterResult { + kDidNotIntercept, // Fallback to the default set operation. + kIntercepted, // Intercepted regardless of whether it succeeded or not. +}; + +enum class NamedPropertyDeleterResult { + kDidNotIntercept, // Fallback to the default delete operation. + kDeleted, // Successfully deleted. + kDidNotDelete, // Intercepted but failed to delete. +}; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_BINDING_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_binding_macros.h b/chromium/third_party/blink/renderer/platform/bindings/v8_binding_macros.h index 2ec174ff7aa..1c5a0f9dacf 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/v8_binding_macros.h +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_binding_macros.h @@ -59,16 +59,6 @@ namespace blink { if (UNLIKELY(!var.Prepare())) \ return retVal; -// Checks for a given v8::Value (value) whether it is an ArrayBufferView and -// below a certain size limit. If below the limit, memory is allocated on the -// stack to hold the actual payload. Keep the limit in sync with V8's -// typed_array_max_size. -#define allocateFlexibleArrayBufferViewStorage(value) \ - (value->IsArrayBufferView() && \ - (value.As<v8::ArrayBufferView>()->ByteLength() <= 64) \ - ? alloca(value.As<v8::ArrayBufferView>()->ByteLength()) \ - : nullptr) - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_BINDING_MACROS_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_cross_origin_property_support.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_cross_origin_property_support.cc new file mode 100644 index 00000000000..a434695b8e2 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_cross_origin_property_support.cc @@ -0,0 +1,96 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/bindings/v8_cross_origin_property_support.h" + +#include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/bindings/v8_binding.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" + +namespace blink { + +namespace bindings { + +v8::MaybeLocal<v8::Function> GetCrossOriginFunction( + v8::Isolate* isolate, + v8::FunctionCallback callback, + int func_length, + const WrapperTypeInfo* wrapper_type_info) { + v8::Local<v8::Context> current_context = isolate->GetCurrentContext(); + ScriptState* script_state = ScriptState::From(current_context); + V8PerIsolateData* per_isolate_data = V8PerIsolateData::From(isolate); + const void* callback_key = reinterpret_cast<const void*>(callback); + + // ES functions accessible across origins are not interface objects, but we + // reuse the cache of interface objects, which just works because both are + // V8 function template. + v8::Local<v8::FunctionTemplate> function_template = + per_isolate_data->FindInterfaceTemplate(script_state->World(), + callback_key); + if (function_template.IsEmpty()) { + v8::Local<v8::FunctionTemplate> interface_template = + per_isolate_data->FindInterfaceTemplate(script_state->World(), + wrapper_type_info); + v8::Local<v8::Signature> signature = + v8::Signature::New(isolate, interface_template); + function_template = v8::FunctionTemplate::New( + isolate, callback, v8::Local<v8::Value>(), signature, func_length, + v8::ConstructorBehavior::kThrow, v8::SideEffectType::kHasSideEffect); + per_isolate_data->SetInterfaceTemplate(script_state->World(), callback_key, + function_template); + } + return function_template->GetFunction(current_context); +} + +v8::MaybeLocal<v8::Value> GetCrossOriginFunctionOrUndefined( + v8::Isolate* isolate, + v8::FunctionCallback callback, + int func_length, + const WrapperTypeInfo* wrapper_type_info) { + if (!callback) { + return v8::Undefined(isolate); + } + v8::Local<v8::Function> function; + if (GetCrossOriginFunction(isolate, callback, func_length, wrapper_type_info) + .ToLocal(&function)) { + return function; + } + return v8::MaybeLocal<v8::Value>(); +} + +bool IsSupportedInCrossOriginPropertyFallback( + v8::Isolate* isolate, + v8::Local<v8::Name> property_name) { + return (property_name == V8AtomicString(isolate, "then") || + property_name == v8::Symbol::GetToStringTag(isolate) || + property_name == v8::Symbol::GetHasInstance(isolate) || + property_name == v8::Symbol::GetIsConcatSpreadable(isolate)); +} + +v8::Local<v8::Array> EnumerateCrossOriginProperties( + v8::Isolate* isolate, + base::span<const CrossOriginAttributeTableEntry> attributes, + base::span<const CrossOriginOperationTableEntry> operations) { + v8::Local<v8::Value> default_supported[] = { + V8AtomicString(isolate, "then"), + v8::Symbol::GetToStringTag(isolate), + v8::Symbol::GetHasInstance(isolate), + v8::Symbol::GetIsConcatSpreadable(isolate), + }; + const uint32_t length = + attributes.size() + operations.size() + base::size(default_supported); + Vector<v8::Local<v8::Value>> elements; + elements.ReserveCapacity(length); + for (const auto& attribute : attributes) + elements.UncheckedAppend(V8AtomicString(isolate, attribute.name)); + for (const auto& operation : operations) + elements.UncheckedAppend(V8AtomicString(isolate, operation.name)); + for (const auto& name : default_supported) + elements.UncheckedAppend(name); + return v8::Array::New(isolate, elements.data(), elements.size()); +} + +} // namespace bindings + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_cross_origin_property_support.h b/chromium/third_party/blink/renderer/platform/bindings/v8_cross_origin_property_support.h new file mode 100644 index 00000000000..d441696a42a --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_cross_origin_property_support.h @@ -0,0 +1,65 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_CROSS_ORIGIN_PROPERTY_SUPPORT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_CROSS_ORIGIN_PROPERTY_SUPPORT_H_ + +#include "base/containers/span.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "v8/include/v8.h" + +// This file provides utilities to support implementation of cross origin +// properties in generated bindings code. Should be used only in generated +// bindings code. + +namespace blink { + +struct WrapperTypeInfo; + +namespace bindings { + +struct CrossOriginAttributeTableEntry final { + const char* name; + v8::FunctionCallback get_callback; + v8::FunctionCallback set_callback; + v8::GenericNamedPropertyGetterCallback get_value; + v8::GenericNamedPropertySetterCallback set_value; +}; + +struct CrossOriginOperationTableEntry final { + const char* name; + v8::FunctionCallback callback; + int func_length; +}; + +PLATFORM_EXPORT v8::MaybeLocal<v8::Function> GetCrossOriginFunction( + v8::Isolate* isolate, + v8::FunctionCallback callback, + int func_length, + const WrapperTypeInfo* wrapper_type_info); + +PLATFORM_EXPORT v8::MaybeLocal<v8::Value> GetCrossOriginFunctionOrUndefined( + v8::Isolate* isolate, + v8::FunctionCallback callback, + int func_length, + const WrapperTypeInfo* wrapper_type_info); + +// HTML 7.2.3.2 CrossOriginPropertyFallback ( P ) +// https://html.spec.whatwg.org/C/#crossoriginpropertyfallback-(-p-) +PLATFORM_EXPORT bool IsSupportedInCrossOriginPropertyFallback( + v8::Isolate* isolate, + v8::Local<v8::Name> property_name); + +// HTML 7.2.3.7 CrossOriginOwnPropertyKeys ( O ) +// https://html.spec.whatwg.org/C/#crossoriginownpropertykeys-(-o-) +PLATFORM_EXPORT v8::Local<v8::Array> EnumerateCrossOriginProperties( + v8::Isolate* isolate, + base::span<const CrossOriginAttributeTableEntry> attributes, + base::span<const CrossOriginOperationTableEntry> operations); + +} // namespace bindings + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_CROSS_ORIGIN_PROPERTY_SUPPORT_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_interface_bridge.h b/chromium/third_party/blink/renderer/platform/bindings/v8_interface_bridge.h new file mode 100644 index 00000000000..402b97cc68a --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_interface_bridge.h @@ -0,0 +1,99 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_INTERFACE_BRIDGE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_INTERFACE_BRIDGE_H_ + +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" +#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" +#include "v8/include/v8.h" + +namespace blink { + +class DOMWrapperWorld; + +namespace bindings { + +// The common base class of code-generated V8-Blink bridge class of IDL +// interfaces and namespaces. +class PLATFORM_EXPORT V8InterfaceBridgeBase { + STATIC_ONLY(V8InterfaceBridgeBase); + + protected: + using InstallInterfaceTemplateFuncType = + void (*)(v8::Isolate* isolate, + const DOMWrapperWorld& world, + v8::Local<v8::FunctionTemplate> interface_template); + using InstallUnconditionalPropertiesFuncType = + void (*)(v8::Isolate* isolate, + const DOMWrapperWorld& world, + v8::Local<v8::ObjectTemplate> instance_template, + v8::Local<v8::ObjectTemplate> prototype_template, + v8::Local<v8::FunctionTemplate> interface_template); + using InstallContextIndependentPropertiesFuncType = + void (*)(v8::Isolate* isolate, + const DOMWrapperWorld& world, + v8::Local<v8::ObjectTemplate> instance_template, + v8::Local<v8::ObjectTemplate> prototype_template, + v8::Local<v8::FunctionTemplate> interface_template); + using InstallContextDependentPropertiesFuncType = + void (*)(v8::Local<v8::Context> context, + const DOMWrapperWorld& world, + v8::Local<v8::Object> instance_object, + v8::Local<v8::Object> prototype_object, + v8::Local<v8::Function> interface_object); +}; + +template <class V8T, class T> +class V8InterfaceBridge : public V8InterfaceBridgeBase { + public: + static T* ToWrappable(v8::Isolate* isolate, v8::Local<v8::Value> value) { + return HasInstance(isolate, value) + ? ToWrappableUnsafe(value.As<v8::Object>()) + : nullptr; + } + + static T* ToWrappableUnsafe(v8::Local<v8::Object> value) { + return ToScriptWrappable(value)->ToImpl<T>(); + } + + static bool HasInstance(v8::Isolate* isolate, v8::Local<v8::Value> value) { + return V8PerIsolateData::From(isolate)->HasInstance( + V8T::GetWrapperTypeInfo(), value); + } + + // Migration adapter + static bool HasInstance(v8::Local<v8::Value> value, v8::Isolate* isolate) { + return HasInstance(isolate, value); + } + + static T* ToImpl(v8::Local<v8::Object> value) { + return ToWrappableUnsafe(value); + } + + static T* ToImplWithTypeCheck(v8::Isolate* isolate, + v8::Local<v8::Value> value) { + return ToWrappable(isolate, value); + } + + static void InstallContextDependentAdapter( + v8::Local<v8::Context> context, + const DOMWrapperWorld& world, + v8::Local<v8::Object> instance_object, + v8::Local<v8::Object> prototype_object, + v8::Local<v8::Function> interface_object, + v8::Local<v8::FunctionTemplate> interface_template) { + V8T::InstallContextDependentProperties(context, world, instance_object, + prototype_object, interface_object); + } +}; + +} // namespace bindings + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_INTERFACE_BRIDGE_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc index 6c25c30dcb3..032aca70e3f 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.cc @@ -172,8 +172,9 @@ void V8PerIsolateData::WillBeDestroyed(v8::Isolate* isolate) { // callbacks are missing and state gets out of sync. ThreadState* const thread_state = ThreadState::Current(); thread_state->FinishIncrementalMarkingIfRunning( - BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking, - BlinkGC::kEagerSweeping, BlinkGC::GCReason::kThreadTerminationGC); + BlinkGC::CollectionType::kMajor, BlinkGC::kHeapPointersOnStack, + BlinkGC::kAtomicMarking, BlinkGC::kEagerSweeping, + BlinkGC::GCReason::kThreadTerminationGC); thread_state->DetachFromIsolate(); } @@ -267,26 +268,28 @@ void V8PerIsolateData::ClearPersistentsForV8ContextSnapshot() { private_property_.reset(); } -const v8::Eternal<v8::Name>* V8PerIsolateData::FindOrCreateEternalNameCache( +const base::span<const v8::Eternal<v8::Name>> +V8PerIsolateData::FindOrCreateEternalNameCache( const void* lookup_key, - const char* const names[], - size_t count) { + const base::span<const char* const>& names) { auto it = eternal_name_cache_.find(lookup_key); const Vector<v8::Eternal<v8::Name>>* vector = nullptr; if (UNLIKELY(it == eternal_name_cache_.end())) { v8::Isolate* isolate = this->GetIsolate(); - Vector<v8::Eternal<v8::Name>> new_vector(count); - std::transform( - names, names + count, new_vector.begin(), [isolate](const char* name) { - return v8::Eternal<v8::Name>(isolate, V8AtomicString(isolate, name)); - }); + Vector<v8::Eternal<v8::Name>> new_vector(names.size()); + std::transform(names.begin(), names.end(), new_vector.begin(), + [isolate](const char* name) { + return v8::Eternal<v8::Name>( + isolate, V8AtomicString(isolate, name)); + }); vector = &eternal_name_cache_.Set(lookup_key, std::move(new_vector)) .stored_value->value; } else { vector = &it->value; } - DCHECK_EQ(vector->size(), count); - return vector->data(); + DCHECK_EQ(vector->size(), names.size()); + return base::span<const v8::Eternal<v8::Name>>(vector->data(), + vector->size()); } v8::Local<v8::Context> V8PerIsolateData::EnsureScriptRegexpContext() { diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h b/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h index 6b5c64a59f3..f64cd5c5029 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h @@ -28,6 +28,7 @@ #include <memory> +#include "base/containers/span.h" #include "base/macros.h" #include "gin/public/gin_embedders.h" #include "gin/public/isolate_holder.h" @@ -106,7 +107,7 @@ class PLATFORM_EXPORT V8PerIsolateData { public: virtual ~GarbageCollectedData() = default; virtual void WillBeDestroyed() {} - virtual void Trace(blink::Visitor*) {} + virtual void Trace(Visitor*) {} }; static v8::Isolate* Initialize(scoped_refptr<base::SingleThreadTaskRunner>, @@ -182,10 +183,9 @@ class PLATFORM_EXPORT V8PerIsolateData { // yet exist, it is created from the given array of strings. Once created, // these live for as long as the isolate, so this is appropriate only for a // compile-time list of related names, such as IDL dictionary keys. - const v8::Eternal<v8::Name>* FindOrCreateEternalNameCache( + const base::span<const v8::Eternal<v8::Name>> FindOrCreateEternalNameCache( const void* lookup_key, - const char* const names[], - size_t count); + const base::span<const char* const>& names); bool HasInstance(const WrapperTypeInfo* untrusted, v8::Local<v8::Value>); v8::Local<v8::Object> FindInstanceInPrototypeChain(const WrapperTypeInfo*, diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.h b/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.h index 864454f18c2..bf19f476ecc 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.h +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.h @@ -8,7 +8,6 @@ #include <memory> #include "base/memory/ptr_util.h" -#include "third_party/blink/renderer/platform/bindings/script_promise_properties.h" #include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h" #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" #include "third_party/blink/renderer/platform/platform_export.h" diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_set_return_value.cc b/chromium/third_party/blink/renderer/platform/bindings/v8_set_return_value.cc new file mode 100644 index 00000000000..a80ede46d7d --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_set_return_value.cc @@ -0,0 +1,61 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/bindings/v8_set_return_value.h" + +#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h" + +namespace blink { + +namespace bindings { + +v8::Local<v8::Object> CreatePropertyDescriptorObject( + v8::Isolate* isolate, + const v8::PropertyDescriptor& desc) { + // https://tc39.es/ecma262/#sec-frompropertydescriptor + v8::Local<v8::Context> current_context = isolate->GetCurrentContext(); + v8::Local<v8::Object> object = v8::Object::New(isolate); + + auto add_property = [&](const char* name, v8::Local<v8::Value> value) { + return object->CreateDataProperty(current_context, V8String(isolate, name), + value); + }; + auto add_property_bool = [&](const char* name, bool value) { + return add_property(name, value ? v8::True(isolate) : v8::False(isolate)); + }; + + bool result; + if (desc.has_value()) { + if (!(add_property("value", desc.value()).To(&result) && + add_property_bool("writable", desc.writable()).To(&result))) + return v8::Local<v8::Object>(); + } else { + if (!(add_property("get", desc.get()).To(&result) && + add_property("set", desc.set()).To(&result))) + return v8::Local<v8::Object>(); + } + if (!(add_property_bool("enumerable", desc.enumerable()).To(&result) && + add_property_bool("configurable", desc.configurable()).To(&result))) + return v8::Local<v8::Object>(); + + return object; +} + +v8::Local<v8::Value> GetInterfaceObjectExposedOnGlobal( + v8::Isolate* isolate, + v8::Local<v8::Object> creation_context, + const WrapperTypeInfo* wrapper_type_info) { + RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT( + isolate, "Blink_GetInterfaceObjectExposedOnGlobal"); + V8PerContextData* per_context_data = + V8PerContextData::From(creation_context->CreationContext()); + if (!per_context_data) + return v8::Local<v8::Value>(); + return per_context_data->ConstructorForType(wrapper_type_info); +} + +} // namespace bindings + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_set_return_value.h b/chromium/third_party/blink/renderer/platform/bindings/v8_set_return_value.h new file mode 100644 index 00000000000..6c7ffa11316 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_set_return_value.h @@ -0,0 +1,336 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_SET_RETURN_VALUE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_SET_RETURN_VALUE_H_ + +#include "third_party/blink/public/platform/web_string.h" +#include "third_party/blink/renderer/platform/bindings/dom_data_store.h" +#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/bindings/v8_binding.h" +#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" +#include "third_party/blink/renderer/platform/bindings/v8_value_cache.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "v8/include/v8.h" + +namespace blink { + +namespace bindings { + +// V8SetReturnValue sets a return value in a V8 callback function. The first +// two arguments are fixed as v8::{Function,Property}CallbackInfo and the +// return value. V8SetReturnValue may take more arguments as optimization hints +// depending on the return value type. + +struct V8ReturnValue { + // Support compile-time overload resolution by making each value have its own + // type. + + // Nullable or not + enum NonNullable { kNonNullable }; + enum Nullable { kNullable }; + + // Main world or not + enum MainWorld { kMainWorld }; + + // Returns the interface object of the given type. + enum InterfaceObject { kInterfaceObject }; +}; + +// V8 handle types +template <typename CallbackInfo, typename S> +void V8SetReturnValue(const CallbackInfo& info, const v8::Global<S> value) { + info.GetReturnValue().Set(value); +} + +template <typename CallbackInfo, typename S> +void V8SetReturnValue(const CallbackInfo& info, const v8::Local<S> value) { + info.GetReturnValue().Set(value); +} + +// Property descriptor +PLATFORM_EXPORT v8::Local<v8::Object> CreatePropertyDescriptorObject( + v8::Isolate* isolate, + const v8::PropertyDescriptor& desc); + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const v8::PropertyDescriptor& value) { + info.GetReturnValue().Set( + CreatePropertyDescriptorObject(info.GetIsolate(), value)); +} + +// Indexed properties and named properties +PLATFORM_EXPORT inline void V8SetReturnValue( + const v8::FunctionCallbackInfo<v8::Value>& info, + IndexedPropertySetterResult value) { + // If an operation implementing indexed property setter is invoked as a + // regular operation, and the return type is not type void (V8SetReturnValue + // won't be called in case of type void), then return the given value as is. + info.GetReturnValue().Set(info[1]); +} + +PLATFORM_EXPORT inline void V8SetReturnValue( + const v8::PropertyCallbackInfo<v8::Value>& info, + IndexedPropertySetterResult value) { + if (value == IndexedPropertySetterResult::kDidNotIntercept) { + // Do not set the return value to indicate that the request was not + // intercepted. + return; + } + info.GetReturnValue().SetNull(); +} + +PLATFORM_EXPORT inline void V8SetReturnValue( + const v8::FunctionCallbackInfo<v8::Value>& info, + NamedPropertySetterResult value) { + // If an operation implementing named property setter is invoked as a + // regular operation, and the return type is not type void (V8SetReturnValue + // won't be called in case of type void), then return the given value as is. + info.GetReturnValue().Set(info[1]); +} + +PLATFORM_EXPORT inline void V8SetReturnValue( + const v8::PropertyCallbackInfo<v8::Value>& info, + NamedPropertySetterResult value) { + if (value == NamedPropertySetterResult::kDidNotIntercept) { + // Do not set the return value to indicate that the request was not + // intercepted. + return; + } + info.GetReturnValue().SetNull(); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + NamedPropertyDeleterResult value) { + if (value == NamedPropertyDeleterResult::kDidNotIntercept) { + // Do not set the return value to indicate that the request was not + // intercepted. + return; + } + info.GetReturnValue().Set(value == NamedPropertyDeleterResult::kDeleted); +} + +// nullptr +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, std::nullptr_t) { + info.GetReturnValue().SetNull(); +} + +// Primitive types +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, bool value) { + info.GetReturnValue().Set(value); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, int32_t value) { + info.GetReturnValue().Set(value); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, uint32_t value) { + info.GetReturnValue().Set(value); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, int64_t value) { + // ECMAScript doesn't support 64-bit integer in Number type. + info.GetReturnValue().Set(static_cast<double>(value)); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, uint64_t value) { + // ECMAScript doesn't support 64-bit integer in Number type. + info.GetReturnValue().Set(static_cast<double>(value)); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, double value) { + info.GetReturnValue().Set(value); +} + +// String types +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const AtomicString& string, + v8::Isolate* isolate, + V8ReturnValue::NonNullable) { + if (string.IsNull()) + return info.GetReturnValue().SetEmptyString(); + V8PerIsolateData::From(isolate)->GetStringCache()->SetReturnValueFromString( + info.GetReturnValue(), string.Impl()); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const String& string, + v8::Isolate* isolate, + V8ReturnValue::NonNullable) { + if (string.IsNull()) + return info.GetReturnValue().SetEmptyString(); + V8PerIsolateData::From(isolate)->GetStringCache()->SetReturnValueFromString( + info.GetReturnValue(), string.Impl()); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const WebString& string, + v8::Isolate* isolate, + V8ReturnValue::NonNullable) { + if (string.IsNull()) + return info.GetReturnValue().SetEmptyString(); + V8PerIsolateData::From(isolate)->GetStringCache()->SetReturnValueFromString( + info.GetReturnValue(), static_cast<String>(string).Impl()); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const AtomicString& string, + v8::Isolate* isolate, + V8ReturnValue::Nullable) { + if (string.IsNull()) + return info.GetReturnValue().SetNull(); + V8PerIsolateData::From(isolate)->GetStringCache()->SetReturnValueFromString( + info.GetReturnValue(), string.Impl()); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const String& string, + v8::Isolate* isolate, + V8ReturnValue::Nullable) { + if (string.IsNull()) + return info.GetReturnValue().SetNull(); + V8PerIsolateData::From(isolate)->GetStringCache()->SetReturnValueFromString( + info.GetReturnValue(), string.Impl()); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const WebString& string, + v8::Isolate* isolate, + V8ReturnValue::Nullable) { + if (string.IsNull()) + return info.GetReturnValue().SetNull(); + V8PerIsolateData::From(isolate)->GetStringCache()->SetReturnValueFromString( + info.GetReturnValue(), static_cast<String>(string).Impl()); +} + +// ScriptWrappable +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const ScriptWrappable* value, + V8ReturnValue::MainWorld) { + DCHECK(DOMWrapperWorld::Current(info.GetIsolate()).IsMainWorld()); + if (UNLIKELY(!value)) + return info.GetReturnValue().SetNull(); + + ScriptWrappable* wrappable = const_cast<ScriptWrappable*>(value); + if (DOMDataStore::SetReturnValueForMainWorld(info.GetReturnValue(), + wrappable)) + return; + + info.GetReturnValue().Set(wrappable->Wrap(info.GetIsolate(), info.This())); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const ScriptWrappable& value, + V8ReturnValue::MainWorld) { + DCHECK(DOMWrapperWorld::Current(info.GetIsolate()).IsMainWorld()); + ScriptWrappable* wrappable = const_cast<ScriptWrappable*>(&value); + if (DOMDataStore::SetReturnValueForMainWorld(info.GetReturnValue(), + wrappable)) + return; + + info.GetReturnValue().Set(wrappable->Wrap(info.GetIsolate(), info.This())); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const ScriptWrappable* value, + const ScriptWrappable* receiver) { + if (UNLIKELY(!value)) + return info.GetReturnValue().SetNull(); + + ScriptWrappable* wrappable = const_cast<ScriptWrappable*>(value); + if (DOMDataStore::SetReturnValueFast(info.GetReturnValue(), wrappable, + info.This(), receiver)) { + return; + } + + info.GetReturnValue().Set(wrappable->Wrap(info.GetIsolate(), info.This())); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const ScriptWrappable& value, + const ScriptWrappable* receiver) { + ScriptWrappable* wrappable = const_cast<ScriptWrappable*>(&value); + if (DOMDataStore::SetReturnValueFast(info.GetReturnValue(), wrappable, + info.This(), receiver)) { + return; + } + + info.GetReturnValue().Set(wrappable->Wrap(info.GetIsolate(), info.This())); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const ScriptWrappable* value, + v8::Local<v8::Context> creation_context) { + if (UNLIKELY(!value)) + return info.GetReturnValue().SetNull(); + + ScriptWrappable* wrappable = const_cast<ScriptWrappable*>(value); + if (DOMDataStore::SetReturnValue(info.GetReturnValue(), wrappable)) + return; + + info.GetReturnValue().Set( + wrappable->Wrap(info.GetIsolate(), creation_context->Global())); +} + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + ScriptWrappable& value, + v8::Local<v8::Context> creation_context) { + ScriptWrappable* wrappable = const_cast<ScriptWrappable*>(&value); + if (DOMDataStore::SetReturnValue(info.GetReturnValue(), wrappable)) + return; + + info.GetReturnValue().Set( + wrappable->Wrap(info.GetIsolate(), creation_context->Global())); +} + +// Interface object +PLATFORM_EXPORT v8::Local<v8::Value> GetInterfaceObjectExposedOnGlobal( + v8::Isolate* isolate, + v8::Local<v8::Object> creation_context, + const WrapperTypeInfo* wrapper_type_info); + +template <typename CallbackInfo> +void V8SetReturnValue(const CallbackInfo& info, + const WrapperTypeInfo* wrapper_type_info, + V8ReturnValue::InterfaceObject) { + info.GetReturnValue().Set(GetInterfaceObjectExposedOnGlobal( + info.GetIsolate(), info.This(), wrapper_type_info)); +} + +// Nullable types +template <typename CallbackInfo, typename T> +void V8SetReturnValue(const CallbackInfo& info, base::Optional<T> value) { + if (value.has_value()) + V8SetReturnValue(info, value.value()); + else + info.GetReturnValue().SetNull(); +} + +} // namespace bindings + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_SET_RETURN_VALUE_H_ diff --git a/chromium/third_party/blink/renderer/platform/bindings/v8_value_or_script_wrappable_adapter.h b/chromium/third_party/blink/renderer/platform/bindings/v8_value_or_script_wrappable_adapter.h index b2d53160dfb..b8aa4bd15b7 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/v8_value_or_script_wrappable_adapter.h +++ b/chromium/third_party/blink/renderer/platform/bindings/v8_value_or_script_wrappable_adapter.h @@ -51,7 +51,7 @@ class PLATFORM_EXPORT V8ValueOrScriptWrappableAdapter { private: v8::Local<v8::Value> v8_value_; - Member<ScriptWrappable> script_wrappable_; + ScriptWrappable* script_wrappable_ = nullptr; }; } // namespace bindings diff --git a/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc b/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc index a9ce328a3b6..d621fa0345c 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc +++ b/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc @@ -27,14 +27,14 @@ void WrapperTypeInfo::WrapperDestroyed() { stats_collector->IncreaseCollectedWrapperCount(1); } -void WrapperTypeInfo::Trace(Visitor* visitor, void* impl) const { +void WrapperTypeInfo::Trace(Visitor* visitor, const void* impl) const { switch (wrapper_class_id) { case WrapperTypeInfo::kNodeClassId: case WrapperTypeInfo::kObjectClassId: - visitor->Trace(reinterpret_cast<ScriptWrappable*>(impl)); + visitor->Trace(reinterpret_cast<const ScriptWrappable*>(impl)); break; case WrapperTypeInfo::kCustomWrappableId: - visitor->Trace(reinterpret_cast<CustomWrappable*>(impl)); + visitor->Trace(reinterpret_cast<const CustomWrappable*>(impl)); break; default: NOTREACHED(); diff --git a/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.h b/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.h index 4782a9e4557..f0b2232622b 100644 --- a/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.h +++ b/chromium/third_party/blink/renderer/platform/bindings/wrapper_type_info.h @@ -153,7 +153,7 @@ struct WrapperTypeInfo { // Garbage collection support for when the type depends the WrapperTypeInfo // object. - PLATFORM_EXPORT void Trace(Visitor*, void*) const; + PLATFORM_EXPORT void Trace(Visitor*, const void*) const; // This field must be the first member of the struct WrapperTypeInfo. // See also static_assert() in .cpp file. diff --git a/chromium/third_party/blink/renderer/platform/blob/BUILD.gn b/chromium/third_party/blink/renderer/platform/blob/BUILD.gn index 635665ec38c..0f744c876b8 100644 --- a/chromium/third_party/blink/renderer/platform/blob/BUILD.gn +++ b/chromium/third_party/blink/renderer/platform/blob/BUILD.gn @@ -33,9 +33,7 @@ blink_platform_sources("blob") { "serialized_blob_mojom_traits.h", ] - deps = [ - "//services/network/public/mojom:mojom_blink", - ] + deps = [ "//services/network/public/mojom:mojom_blink" ] } jumbo_source_set("unit_tests") { diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.cc b/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.cc index 22cac3dac0e..833971da422 100644 --- a/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.cc +++ b/chromium/third_party/blink/renderer/platform/blob/blob_bytes_provider.cc @@ -4,8 +4,10 @@ #include "third_party/blink/renderer/platform/blob/blob_bytes_provider.h" +#include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" #include "base/task/post_task.h" +#include "base/task/thread_pool.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/instrumentation/histogram.h" @@ -117,8 +119,8 @@ constexpr size_t BlobBytesProvider::kMaxConsolidatedItemSizeInBytes; // static BlobBytesProvider* BlobBytesProvider::CreateAndBind( mojo::PendingReceiver<mojom::blink::BytesProvider> receiver) { - auto task_runner = base::CreateSequencedTaskRunner( - {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE}); + auto task_runner = base::ThreadPool::CreateSequencedTaskRunner( + {base::MayBlock(), base::TaskPriority::USER_VISIBLE}); auto provider = base::WrapUnique(new BlobBytesProvider(task_runner)); auto* result = provider.get(); // TODO(mek): Consider binding BytesProvider on the IPC thread instead, only diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_data.cc b/chromium/third_party/blink/renderer/platform/blob/blob_data.cc index 1b5e7a11cc6..a381dbc3140 100644 --- a/chromium/third_party/blink/renderer/platform/blob/blob_data.cc +++ b/chromium/third_party/blink/renderer/platform/blob/blob_data.cc @@ -38,11 +38,11 @@ #include "base/single_thread_task_runner.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/network/public/mojom/data_pipe_getter.mojom-blink.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/blob/blob.mojom-blink.h" #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h" #include "third_party/blink/public/mojom/blob/data_element.mojom-blink.h" #include "third_party/blink/public/platform/file_path_conversion.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/blob/blob_bytes_provider.h" #include "third_party/blink/renderer/platform/instrumentation/histogram.h" @@ -85,10 +85,10 @@ mojom::blink::BlobRegistry* GetThreadSpecificRegistry() { DEFINE_THREAD_SAFE_STATIC_LOCAL( ThreadSpecific<mojo::Remote<mojom::blink::BlobRegistry>>, registry, ()); if (UNLIKELY(!registry.IsSet())) { - // TODO(mek): Going through InterfaceProvider to get a + // TODO(mek): Going through BrowserInterfaceBroker to get a // mojom::blink::BlobRegistry ends up going through the main thread. Ideally // workers wouldn't need to do that. - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( (*registry).BindNewPipeAndPassReceiver()); } return registry->get(); @@ -103,7 +103,7 @@ RawData::RawData() = default; BlobData::BlobData(FileCompositionStatus composition) : file_composition_(composition) {} -BlobData::~BlobData() {} +BlobData::~BlobData() = default; Vector<mojom::blink::DataElementPtr> BlobData::ReleaseElements() { return std::move(elements_); @@ -173,6 +173,7 @@ void BlobData::AppendFile( "create a blob with a single file with unknown size, use " "BlobData::createForFileWithUnknownSize. Otherwise please provide the " "file size."; + DCHECK_GE(length, 0); // Skip zero-byte items, as they don't matter for the contents of the blob. if (length == 0) return; @@ -201,6 +202,7 @@ void BlobData::AppendFileSystemURL( const base::Optional<base::Time>& expected_modification_time) { DCHECK_EQ(file_composition_, FileCompositionStatus::NO_UNKNOWN_SIZE_FILES) << "Blobs with a unknown-size file cannot have other items."; + DCHECK_GE(length, 0); // Skip zero-byte items, as they don't matter for the contents of the blob. if (length == 0) return; @@ -359,8 +361,7 @@ BlobDataHandle::BlobDataHandle( DCHECK(blob_remote_.is_valid()); } -BlobDataHandle::~BlobDataHandle() { -} +BlobDataHandle::~BlobDataHandle() = default; mojo::PendingRemote<mojom::blink::Blob> BlobDataHandle::CloneBlobRemote() { MutexLocker locker(blob_remote_mutex_); @@ -415,6 +416,16 @@ void BlobDataHandle::ReadRange( blob_remote_ = blob.Unbind(); } +bool BlobDataHandle::CaptureSnapshot( + uint64_t* snapshot_size, + base::Optional<base::Time>* snapshot_modification_time) { + // This method operates on a cloned blob remote; this lets us avoid holding + // the |blob_remote_mutex_| locked during the duration of the (synchronous) + // CaptureSnapshot call. + mojo::Remote<mojom::blink::Blob> remote(CloneBlobRemote()); + return remote->CaptureSnapshot(snapshot_size, snapshot_modification_time); +} + // static mojom::blink::BlobRegistry* BlobDataHandle::GetBlobRegistry() { return GetThreadSpecificRegistry(); diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_data.h b/chromium/third_party/blink/renderer/platform/blob/blob_data.h index 9965fe9c64b..33c842a9510 100644 --- a/chromium/third_party/blink/renderer/platform/blob/blob_data.h +++ b/chromium/third_party/blink/renderer/platform/blob/blob_data.h @@ -219,6 +219,16 @@ class PLATFORM_EXPORT BlobDataHandle mojo::ScopedDataPipeProducerHandle, mojo::PendingRemote<mojom::blink::BlobReaderClient>); + // This does synchronous IPC, and possibly synchronous file operations. Think + // twice before calling this function. + bool CaptureSnapshot(uint64_t* snapshot_size, + base::Optional<base::Time>* snapshot_modification_time); + + void SetBlobRemoteForTesting(mojo::PendingRemote<mojom::blink::Blob> remote) { + MutexLocker locker(blob_remote_mutex_); + blob_remote_ = std::move(remote); + } + static mojom::blink::BlobRegistry* GetBlobRegistry(); static void SetBlobRegistryForTesting(mojom::blink::BlobRegistry*); diff --git a/chromium/third_party/blink/renderer/platform/blob/blob_data_test.cc b/chromium/third_party/blink/renderer/platform/blob/blob_data_test.cc index aa375aa798d..b515e0b11ad 100644 --- a/chromium/third_party/blink/renderer/platform/blob/blob_data_test.cc +++ b/chromium/third_party/blink/renderer/platform/blob/blob_data_test.cc @@ -15,7 +15,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h" #include "third_party/blink/public/platform/file_path_conversion.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/renderer/platform/blob/blob_bytes_provider.h" #include "third_party/blink/renderer/platform/blob/testing/fake_blob_registry.h" #include "third_party/blink/renderer/platform/testing/testing_platform_support.h" diff --git a/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.cc b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.cc index 733798e9c77..5dd4ae6b61e 100644 --- a/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.cc +++ b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.cc @@ -80,6 +80,10 @@ void FakeBlob::ReadSideData(ReadSideDataCallback callback) { NOTREACHED(); } +void FakeBlob::CaptureSnapshot(CaptureSnapshotCallback callback) { + std::move(callback).Run(body_.length(), base::nullopt); +} + void FakeBlob::GetInternalUUID(GetInternalUUIDCallback callback) { std::move(callback).Run(uuid_); } diff --git a/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.h b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.h index 8ec5c7654c1..845330f9ee4 100644 --- a/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.h +++ b/chromium/third_party/blink/renderer/platform/blob/testing/fake_blob.h @@ -32,8 +32,10 @@ class FakeBlob : public mojom::blink::Blob { void ReadAll(mojo::ScopedDataPipeProducerHandle, mojo::PendingRemote<mojom::blink::BlobReaderClient>) override; void ReadSideData(ReadSideDataCallback) override; + void CaptureSnapshot(CaptureSnapshotCallback) override; void GetInternalUUID(GetInternalUUIDCallback) override; - private: + + protected: String uuid_; String body_; State* state_; diff --git a/chromium/third_party/blink/renderer/platform/content_decryption_module_result.h b/chromium/third_party/blink/renderer/platform/content_decryption_module_result.h index d9d00569546..5bcfa0a11ea 100644 --- a/chromium/third_party/blink/renderer/platform/content_decryption_module_result.h +++ b/chromium/third_party/blink/renderer/platform/content_decryption_module_result.h @@ -36,7 +36,7 @@ class ContentDecryptionModuleResult return WebContentDecryptionModuleResult(this); } - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/context_lifecycle_notifier.h b/chromium/third_party/blink/renderer/platform/context_lifecycle_notifier.h new file mode 100644 index 00000000000..6b16cfe76bb --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/context_lifecycle_notifier.h @@ -0,0 +1,23 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_LIFECYCLE_NOTIFIER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_LIFECYCLE_NOTIFIER_H_ + +#include "third_party/blink/renderer/platform/heap/handle.h" + +namespace blink { + +class ContextLifecycleObserver; + +// Notifier interface for ContextLifecycleObserver. +class PLATFORM_EXPORT ContextLifecycleNotifier : public GarbageCollectedMixin { + public: + virtual void AddContextLifecycleObserver(ContextLifecycleObserver*) = 0; + virtual void RemoveContextLifecycleObserver(ContextLifecycleObserver*) = 0; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_LIFECYCLE_NOTIFIER_H_ diff --git a/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.cc b/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.cc new file mode 100644 index 00000000000..9387ae06507 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.cc @@ -0,0 +1,33 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/context_lifecycle_observer.h" + +#include "third_party/blink/renderer/platform/context_lifecycle_notifier.h" + +namespace blink { + +void ContextLifecycleObserver::ObserverListWillBeCleared() { + notifier_ = nullptr; +} + +void ContextLifecycleObserver::SetContextLifecycleNotifier( + ContextLifecycleNotifier* notifier) { + if (notifier == notifier_) + return; + + if (notifier_) + notifier_->RemoveContextLifecycleObserver(this); + + notifier_ = notifier; + + if (notifier_) + notifier_->AddContextLifecycleObserver(this); +} + +void ContextLifecycleObserver::Trace(Visitor* visitor) { + visitor->Trace(notifier_); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.h b/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.h new file mode 100644 index 00000000000..cc5ef14ff92 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/context_lifecycle_observer.h @@ -0,0 +1,41 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_LIFECYCLE_OBSERVER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_LIFECYCLE_OBSERVER_H_ + +#include "third_party/blink/renderer/platform/heap/handle.h" + +namespace blink { + +class ContextLifecycleNotifier; + +// Observer that gets notified when the context is destroyed. Used to observe +// ExecutionContext from platform/. +class PLATFORM_EXPORT ContextLifecycleObserver : public GarbageCollectedMixin { + public: + virtual void ContextDestroyed() = 0; + + // Call before clearing an observer list. + void ObserverListWillBeCleared(); + + ContextLifecycleNotifier* GetContextLifecycleNotifier() const { + return notifier_; + } + void SetContextLifecycleNotifier(ContextLifecycleNotifier*); + + virtual bool IsExecutionContextLifecycleObserver() const { return false; } + + void Trace(Visitor*) override; + + protected: + ContextLifecycleObserver() = default; + + private: + WeakMember<ContextLifecycleNotifier> notifier_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CONTEXT_LIFECYCLE_OBSERVER_H_ diff --git a/chromium/third_party/blink/renderer/platform/crypto_result.h b/chromium/third_party/blink/renderer/platform/crypto_result.h index 2fc6b5aa1ee..9b4e3d54825 100644 --- a/chromium/third_party/blink/renderer/platform/crypto_result.h +++ b/chromium/third_party/blink/renderer/platform/crypto_result.h @@ -61,7 +61,7 @@ class PLATFORM_EXPORT CryptoResult : public GarbageCollected<CryptoResult> { virtual void CompleteWithKeyPair(const WebCryptoKey& public_key, const WebCryptoKey& private_key) = 0; - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/cursor.cc b/chromium/third_party/blink/renderer/platform/cursor.cc deleted file mode 100644 index e8eb0874b09..00000000000 --- a/chromium/third_party/blink/renderer/platform/cursor.cc +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (C) 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/renderer/platform/cursor.h" -#include "third_party/blink/renderer/platform/wtf/assertions.h" - -namespace blink { - -IntPoint DetermineHotSpot(Image* image, - bool hot_spot_specified, - const IntPoint& specified_hot_spot) { - if (image->IsNull()) - return IntPoint(); - - IntRect image_rect = image->Rect(); - - // Hot spot must be inside cursor rectangle. - if (hot_spot_specified) { - if (image_rect.Contains(specified_hot_spot)) { - return specified_hot_spot; - } - - return IntPoint(clampTo<int>(specified_hot_spot.X(), image_rect.X(), - image_rect.MaxX() - 1), - clampTo<int>(specified_hot_spot.Y(), image_rect.Y(), - image_rect.MaxY() - 1)); - } - - // If hot spot is not specified externally, it can be extracted from some - // image formats (e.g. .cur). - IntPoint intrinsic_hot_spot; - bool image_has_intrinsic_hot_spot = image->GetHotSpot(intrinsic_hot_spot); - if (image_has_intrinsic_hot_spot && image_rect.Contains(intrinsic_hot_spot)) - return intrinsic_hot_spot; - - // If neither is provided, use a default value of (0, 0). - return IntPoint(); -} - -Cursor::Cursor(Image* image, bool hot_spot_specified, const IntPoint& hot_spot) - : type_(ui::CursorType::kCustom), - image_(image), - hot_spot_(DetermineHotSpot(image, hot_spot_specified, hot_spot)), - image_scale_factor_(1) {} - -Cursor::Cursor(Image* image, - bool hot_spot_specified, - const IntPoint& hot_spot, - float scale) - : type_(ui::CursorType::kCustom), - image_(image), - hot_spot_(DetermineHotSpot(image, hot_spot_specified, hot_spot)), - image_scale_factor_(scale) {} - -Cursor::Cursor(ui::CursorType type) : type_(type), image_scale_factor_(1) {} - -Cursor::Cursor(const Cursor& other) = default; - -Cursor& Cursor::operator=(const Cursor& other) { - type_ = other.type_; - image_ = other.image_; - hot_spot_ = other.hot_spot_; - image_scale_factor_ = other.image_scale_factor_; - return *this; -} - -Cursor::~Cursor() = default; - -const Cursor& PointerCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kPointer)); - return c; -} - -const Cursor& CrossCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kCross)); - return c; -} - -const Cursor& HandCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kHand)); - return c; -} - -const Cursor& MoveCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kMove)); - return c; -} - -const Cursor& VerticalTextCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kVerticalText)); - return c; -} - -const Cursor& CellCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kCell)); - return c; -} - -const Cursor& ContextMenuCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kContextMenu)); - return c; -} - -const Cursor& AliasCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kAlias)); - return c; -} - -const Cursor& ZoomInCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kZoomIn)); - return c; -} - -const Cursor& ZoomOutCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kZoomOut)); - return c; -} - -const Cursor& CopyCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kCopy)); - return c; -} - -const Cursor& NoneCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNone)); - return c; -} - -const Cursor& ProgressCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kProgress)); - return c; -} - -const Cursor& NoDropCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNoDrop)); - return c; -} - -const Cursor& NotAllowedCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNotAllowed)); - return c; -} - -const Cursor& IBeamCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kIBeam)); - return c; -} - -const Cursor& WaitCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kWait)); - return c; -} - -const Cursor& HelpCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kHelp)); - return c; -} - -const Cursor& EastResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kEastResize)); - return c; -} - -const Cursor& NorthResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNorthResize)); - return c; -} - -const Cursor& NorthEastResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNorthEastResize)); - return c; -} - -const Cursor& NorthWestResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNorthWestResize)); - return c; -} - -const Cursor& SouthResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kSouthResize)); - return c; -} - -const Cursor& SouthEastResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kSouthEastResize)); - return c; -} - -const Cursor& SouthWestResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kSouthWestResize)); - return c; -} - -const Cursor& WestResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kWestResize)); - return c; -} - -const Cursor& NorthSouthResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNorthSouthResize)); - return c; -} - -const Cursor& EastWestResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kEastWestResize)); - return c; -} - -const Cursor& NorthEastSouthWestResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNorthEastSouthWestResize)); - return c; -} - -const Cursor& NorthWestSouthEastResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNorthWestSouthEastResize)); - return c; -} - -const Cursor& ColumnResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kColumnResize)); - return c; -} - -const Cursor& RowResizeCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kRowResize)); - return c; -} - -const Cursor& MiddlePanningCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kMiddlePanning)); - return c; -} - -const Cursor& MiddlePanningVerticalCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kMiddlePanningVertical)); - return c; -} - -const Cursor& MiddlePanningHorizontalCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kMiddlePanningHorizontal)); - return c; -} - -const Cursor& EastPanningCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kEastPanning)); - return c; -} - -const Cursor& NorthPanningCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNorthPanning)); - return c; -} - -const Cursor& NorthEastPanningCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNorthEastPanning)); - return c; -} - -const Cursor& NorthWestPanningCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kNorthWestPanning)); - return c; -} - -const Cursor& SouthPanningCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kSouthPanning)); - return c; -} - -const Cursor& SouthEastPanningCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kSouthEastPanning)); - return c; -} - -const Cursor& SouthWestPanningCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kSouthWestPanning)); - return c; -} - -const Cursor& WestPanningCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kWestPanning)); - return c; -} - -const Cursor& GrabCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kGrab)); - return c; -} - -const Cursor& GrabbingCursor() { - DEFINE_STATIC_LOCAL(Cursor, c, (ui::CursorType::kGrabbing)); - return c; -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/cursor.h b/chromium/third_party/blink/renderer/platform/cursor.h deleted file mode 100644 index 4317df0a668..00000000000 --- a/chromium/third_party/blink/renderer/platform/cursor.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_CURSOR_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CURSOR_H_ - -#include "base/memory/scoped_refptr.h" -#include "third_party/blink/public/platform/web_cursor_info.h" -#include "third_party/blink/renderer/platform/geometry/int_point.h" -#include "third_party/blink/renderer/platform/graphics/image.h" -#include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/wtf/assertions.h" - -// To avoid conflicts with the CreateWindow macro from the Windows SDK... -#undef CopyCursor - -namespace blink { - -class PLATFORM_EXPORT Cursor { - USING_FAST_MALLOC(Cursor); - - public: - Cursor() - // This is an invalid Cursor and should never actually get used. - : type_(ui::CursorType::kNull) {} - - Cursor(Image*, bool hot_spot_specified, const IntPoint& hot_spot); - - // Hot spot is in image pixels. - Cursor(Image*, - bool hot_spot_specified, - const IntPoint& hot_spot, - float image_scale_factor); - - Cursor(const Cursor&); - ~Cursor(); - Cursor& operator=(const Cursor&); - - explicit Cursor(ui::CursorType); - ui::CursorType GetType() const { - DCHECK_GE(type_, static_cast<ui::CursorType>(0)); - DCHECK_LE(type_, ui::CursorType::kCustom); - return type_; - } - Image* GetImage() const { return image_.get(); } - const IntPoint& HotSpot() const { return hot_spot_; } - // Image scale in image pixels per logical (UI) pixel. - float ImageScaleFactor() const { return image_scale_factor_; } - - private: - ui::CursorType type_; - scoped_refptr<Image> image_; - IntPoint hot_spot_; - float image_scale_factor_; -}; - -PLATFORM_EXPORT IntPoint DetermineHotSpot(Image*, - bool hot_spot_specified, - const IntPoint& specified_hot_spot); - -PLATFORM_EXPORT const Cursor& PointerCursor(); -PLATFORM_EXPORT const Cursor& CrossCursor(); -PLATFORM_EXPORT const Cursor& HandCursor(); -PLATFORM_EXPORT const Cursor& MoveCursor(); -PLATFORM_EXPORT const Cursor& IBeamCursor(); -PLATFORM_EXPORT const Cursor& WaitCursor(); -PLATFORM_EXPORT const Cursor& HelpCursor(); -PLATFORM_EXPORT const Cursor& EastResizeCursor(); -PLATFORM_EXPORT const Cursor& NorthResizeCursor(); -PLATFORM_EXPORT const Cursor& NorthEastResizeCursor(); -PLATFORM_EXPORT const Cursor& NorthWestResizeCursor(); -PLATFORM_EXPORT const Cursor& SouthResizeCursor(); -PLATFORM_EXPORT const Cursor& SouthEastResizeCursor(); -PLATFORM_EXPORT const Cursor& SouthWestResizeCursor(); -PLATFORM_EXPORT const Cursor& WestResizeCursor(); -PLATFORM_EXPORT const Cursor& NorthSouthResizeCursor(); -PLATFORM_EXPORT const Cursor& EastWestResizeCursor(); -PLATFORM_EXPORT const Cursor& NorthEastSouthWestResizeCursor(); -PLATFORM_EXPORT const Cursor& NorthWestSouthEastResizeCursor(); -PLATFORM_EXPORT const Cursor& ColumnResizeCursor(); -PLATFORM_EXPORT const Cursor& RowResizeCursor(); -PLATFORM_EXPORT const Cursor& MiddlePanningCursor(); -PLATFORM_EXPORT const Cursor& MiddlePanningVerticalCursor(); -PLATFORM_EXPORT const Cursor& MiddlePanningHorizontalCursor(); -PLATFORM_EXPORT const Cursor& EastPanningCursor(); -PLATFORM_EXPORT const Cursor& NorthPanningCursor(); -PLATFORM_EXPORT const Cursor& NorthEastPanningCursor(); -PLATFORM_EXPORT const Cursor& NorthWestPanningCursor(); -PLATFORM_EXPORT const Cursor& SouthPanningCursor(); -PLATFORM_EXPORT const Cursor& SouthEastPanningCursor(); -PLATFORM_EXPORT const Cursor& SouthWestPanningCursor(); -PLATFORM_EXPORT const Cursor& WestPanningCursor(); -PLATFORM_EXPORT const Cursor& VerticalTextCursor(); -PLATFORM_EXPORT const Cursor& CellCursor(); -PLATFORM_EXPORT const Cursor& ContextMenuCursor(); -PLATFORM_EXPORT const Cursor& NoDropCursor(); -PLATFORM_EXPORT const Cursor& NotAllowedCursor(); -PLATFORM_EXPORT const Cursor& ProgressCursor(); -PLATFORM_EXPORT const Cursor& AliasCursor(); -PLATFORM_EXPORT const Cursor& ZoomInCursor(); -PLATFORM_EXPORT const Cursor& ZoomOutCursor(); -PLATFORM_EXPORT const Cursor& CopyCursor(); -PLATFORM_EXPORT const Cursor& NoneCursor(); -PLATFORM_EXPORT const Cursor& GrabCursor(); -PLATFORM_EXPORT const Cursor& GrabbingCursor(); - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CURSOR_H_ diff --git a/chromium/third_party/blink/renderer/platform/cursors.cc b/chromium/third_party/blink/renderer/platform/cursors.cc new file mode 100644 index 00000000000..a3a9c9f7fe9 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/cursors.cc @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "third_party/blink/renderer/platform/cursors.h" + +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" +#include "ui/base/cursor/cursor.h" +#include "ui/base/mojom/cursor_type.mojom-blink.h" + +// To avoid conflicts with the CreateWindow macro from the Windows SDK... +#undef CopyCursor + +namespace blink { + +const ui::Cursor& PointerCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kPointer)); + return c; +} + +const ui::Cursor& CrossCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kCross)); + return c; +} + +const ui::Cursor& HandCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kHand)); + return c; +} + +const ui::Cursor& MoveCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kMove)); + return c; +} + +const ui::Cursor& VerticalTextCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kVerticalText)); + return c; +} + +const ui::Cursor& CellCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kCell)); + return c; +} + +const ui::Cursor& ContextMenuCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kContextMenu)); + return c; +} + +const ui::Cursor& AliasCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kAlias)); + return c; +} + +const ui::Cursor& ZoomInCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kZoomIn)); + return c; +} + +const ui::Cursor& ZoomOutCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kZoomOut)); + return c; +} + +const ui::Cursor& CopyCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kCopy)); + return c; +} + +const ui::Cursor& NoneCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kNone)); + return c; +} + +const ui::Cursor& ProgressCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kProgress)); + return c; +} + +const ui::Cursor& NoDropCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kNoDrop)); + return c; +} + +const ui::Cursor& NotAllowedCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kNotAllowed)); + return c; +} + +const ui::Cursor& IBeamCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kIBeam)); + return c; +} + +const ui::Cursor& WaitCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kWait)); + return c; +} + +const ui::Cursor& HelpCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kHelp)); + return c; +} + +const ui::Cursor& EastResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kEastResize)); + return c; +} + +const ui::Cursor& NorthResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kNorthResize)); + return c; +} + +const ui::Cursor& NorthEastResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kNorthEastResize)); + return c; +} + +const ui::Cursor& NorthWestResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kNorthWestResize)); + return c; +} + +const ui::Cursor& SouthResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kSouthResize)); + return c; +} + +const ui::Cursor& SouthEastResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kSouthEastResize)); + return c; +} + +const ui::Cursor& SouthWestResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kSouthWestResize)); + return c; +} + +const ui::Cursor& WestResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kWestResize)); + return c; +} + +const ui::Cursor& NorthSouthResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kNorthSouthResize)); + return c; +} + +const ui::Cursor& EastWestResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kEastWestResize)); + return c; +} + +const ui::Cursor& NorthEastSouthWestResizeCursor() { + DEFINE_STATIC_LOCAL( + ui::Cursor, c, (ui::mojom::blink::CursorType::kNorthEastSouthWestResize)); + return c; +} + +const ui::Cursor& NorthWestSouthEastResizeCursor() { + DEFINE_STATIC_LOCAL( + ui::Cursor, c, (ui::mojom::blink::CursorType::kNorthWestSouthEastResize)); + return c; +} + +const ui::Cursor& ColumnResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kColumnResize)); + return c; +} + +const ui::Cursor& RowResizeCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kRowResize)); + return c; +} + +const ui::Cursor& MiddlePanningCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kMiddlePanning)); + return c; +} + +const ui::Cursor& MiddlePanningVerticalCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kMiddlePanningVertical)); + return c; +} + +const ui::Cursor& MiddlePanningHorizontalCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kMiddlePanningHorizontal)); + return c; +} + +const ui::Cursor& EastPanningCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kEastPanning)); + return c; +} + +const ui::Cursor& NorthPanningCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kNorthPanning)); + return c; +} + +const ui::Cursor& NorthEastPanningCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kNorthEastPanning)); + return c; +} + +const ui::Cursor& NorthWestPanningCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kNorthWestPanning)); + return c; +} + +const ui::Cursor& SouthPanningCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kSouthPanning)); + return c; +} + +const ui::Cursor& SouthEastPanningCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kSouthEastPanning)); + return c; +} + +const ui::Cursor& SouthWestPanningCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kSouthWestPanning)); + return c; +} + +const ui::Cursor& WestPanningCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, + (ui::mojom::blink::CursorType::kWestPanning)); + return c; +} + +const ui::Cursor& GrabCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kGrab)); + return c; +} + +const ui::Cursor& GrabbingCursor() { + DEFINE_STATIC_LOCAL(ui::Cursor, c, (ui::mojom::blink::CursorType::kGrabbing)); + return c; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/cursors.h b/chromium/third_party/blink/renderer/platform/cursors.h new file mode 100644 index 00000000000..8cfba611ec7 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/cursors.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_CURSORS_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CURSORS_H_ + +#include "third_party/blink/renderer/platform/platform_export.h" + +// To avoid conflicts with the CreateWindow macro from the Windows SDK... +#undef CopyCursor + +namespace ui { +class Cursor; +} + +namespace blink { + +PLATFORM_EXPORT const ui::Cursor& PointerCursor(); +PLATFORM_EXPORT const ui::Cursor& CrossCursor(); +PLATFORM_EXPORT const ui::Cursor& HandCursor(); +PLATFORM_EXPORT const ui::Cursor& MoveCursor(); +PLATFORM_EXPORT const ui::Cursor& IBeamCursor(); +PLATFORM_EXPORT const ui::Cursor& WaitCursor(); +PLATFORM_EXPORT const ui::Cursor& HelpCursor(); +PLATFORM_EXPORT const ui::Cursor& EastResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& NorthResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& NorthEastResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& NorthWestResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& SouthResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& SouthEastResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& SouthWestResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& WestResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& NorthSouthResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& EastWestResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& NorthEastSouthWestResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& NorthWestSouthEastResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& ColumnResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& RowResizeCursor(); +PLATFORM_EXPORT const ui::Cursor& MiddlePanningCursor(); +PLATFORM_EXPORT const ui::Cursor& MiddlePanningVerticalCursor(); +PLATFORM_EXPORT const ui::Cursor& MiddlePanningHorizontalCursor(); +PLATFORM_EXPORT const ui::Cursor& EastPanningCursor(); +PLATFORM_EXPORT const ui::Cursor& NorthPanningCursor(); +PLATFORM_EXPORT const ui::Cursor& NorthEastPanningCursor(); +PLATFORM_EXPORT const ui::Cursor& NorthWestPanningCursor(); +PLATFORM_EXPORT const ui::Cursor& SouthPanningCursor(); +PLATFORM_EXPORT const ui::Cursor& SouthEastPanningCursor(); +PLATFORM_EXPORT const ui::Cursor& SouthWestPanningCursor(); +PLATFORM_EXPORT const ui::Cursor& WestPanningCursor(); +PLATFORM_EXPORT const ui::Cursor& VerticalTextCursor(); +PLATFORM_EXPORT const ui::Cursor& CellCursor(); +PLATFORM_EXPORT const ui::Cursor& ContextMenuCursor(); +PLATFORM_EXPORT const ui::Cursor& NoDropCursor(); +PLATFORM_EXPORT const ui::Cursor& NotAllowedCursor(); +PLATFORM_EXPORT const ui::Cursor& ProgressCursor(); +PLATFORM_EXPORT const ui::Cursor& AliasCursor(); +PLATFORM_EXPORT const ui::Cursor& ZoomInCursor(); +PLATFORM_EXPORT const ui::Cursor& ZoomOutCursor(); +PLATFORM_EXPORT const ui::Cursor& CopyCursor(); +PLATFORM_EXPORT const ui::Cursor& NoneCursor(); +PLATFORM_EXPORT const ui::Cursor& GrabCursor(); +PLATFORM_EXPORT const ui::Cursor& GrabbingCursor(); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CURSORS_H_ diff --git a/chromium/third_party/blink/renderer/platform/disk_data_allocator.cc b/chromium/third_party/blink/renderer/platform/disk_data_allocator.cc new file mode 100644 index 00000000000..2ca595652a0 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/disk_data_allocator.cc @@ -0,0 +1,206 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/disk_data_allocator.h" + +#include <algorithm> +#include <utility> + +#include "base/logging.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" +#include "third_party/blink/renderer/platform/wtf/wtf.h" + +namespace blink { + +DiskDataAllocator::DiskDataAllocator() + : free_chunks_size_(0), file_tail_(0), may_write_(false) {} + +DiskDataAllocator::~DiskDataAllocator() = default; + +bool DiskDataAllocator::may_write() { + MutexLocker locker(mutex_); + return may_write_; +} + +void DiskDataAllocator::set_may_write_for_testing(bool may_write) { + MutexLocker locker(mutex_); + may_write_ = may_write; +} + +DiskDataAllocator::Metadata DiskDataAllocator::FindChunk(size_t size) { + // Try to reuse some space. Policy: + // 1. Exact fit + // 2. Worst fit + Metadata chosen_chunk{-1, 0}; + + size_t worst_fit_size = 0; + for (const auto& chunk : free_chunks_) { + size_t chunk_size = chunk.second; + if (size == chunk_size) { + chosen_chunk = {chunk.first, chunk.second}; + break; + } else if (chunk_size > size && chunk_size > worst_fit_size) { + chosen_chunk = {chunk.first, chunk.second}; + worst_fit_size = chunk.second; + } + } + + if (chosen_chunk.start_offset() != -1) { + free_chunks_size_ -= size; + free_chunks_.erase(chosen_chunk.start_offset()); + if (chosen_chunk.size() > size) { + std::pair<int64_t, size_t> remainder_chunk = { + chosen_chunk.start_offset() + size, chosen_chunk.size() - size}; + auto result = free_chunks_.insert(remainder_chunk); + DCHECK(result.second); + chosen_chunk.size_ = size; + } + } else { + chosen_chunk = {file_tail_, size}; + file_tail_ += size; + } + + return chosen_chunk; +} + +void DiskDataAllocator::ReleaseChunk(const Metadata& metadata) { + Metadata chunk = metadata; + DCHECK(free_chunks_.find(chunk.start_offset()) == free_chunks_.end()); + + auto lower_bound = free_chunks_.lower_bound(chunk.start_offset()); + DCHECK(free_chunks_.upper_bound(chunk.start_offset()) == + free_chunks_.lower_bound(chunk.start_offset())); + if (lower_bound != free_chunks_.begin()) { + // There is a chunk left. + auto left = --lower_bound; + // Can merge with the left chunk. + int64_t left_chunk_end = left->first + left->second; + DCHECK_LE(left_chunk_end, chunk.start_offset()); + if (left_chunk_end == chunk.start_offset()) { + chunk = {left->first, left->second + chunk.size()}; + free_chunks_size_ -= left->second; + free_chunks_.erase(left); + } + } + + auto right = free_chunks_.upper_bound(chunk.start_offset()); + if (right != free_chunks_.end()) { + DCHECK_NE(right->first, chunk.start_offset()); + int64_t chunk_end = chunk.start_offset() + chunk.size(); + DCHECK_LE(chunk_end, right->first); + if (right->first == chunk_end) { + chunk = {chunk.start_offset(), chunk.size() + right->second}; + free_chunks_size_ -= right->second; + free_chunks_.erase(right); + } + } + + auto result = free_chunks_.insert({chunk.start_offset(), chunk.size()}); + DCHECK(result.second); + free_chunks_size_ += chunk.size(); +} + +std::unique_ptr<DiskDataAllocator::Metadata> DiskDataAllocator::Write( + const void* data, + size_t size) { + Metadata chosen_chunk = {0, 0}; + + { + MutexLocker locker(mutex_); + if (!may_write_) + return nullptr; + + chosen_chunk = FindChunk(size); + } // Don't hold the lock during the actual Write(). + + int size_int = static_cast<int>(size); + const char* data_char = reinterpret_cast<const char*>(data); + int written = DoWrite(chosen_chunk.start_offset(), data_char, size_int); + + MutexLocker locker(mutex_); + if (size_int != written) { + // Assume that the error is not transient. This can happen if the disk is + // full for instance, in which case it is likely better not to try writing + // later. + may_write_ = false; + return nullptr; + } + +#if DCHECK_IS_ON() + allocated_chunks_.insert({chosen_chunk.start_offset(), chosen_chunk.size()}); +#endif + + return std::unique_ptr<Metadata>( + new Metadata(chosen_chunk.start_offset(), chosen_chunk.size())); +} + +bool DiskDataAllocator::Read(const Metadata& metadata, void* data) { + DCHECK(IsMainThread()); + + // Doesn't need locking as files support concurrent access, and we don't + // update metadata. + char* data_char = reinterpret_cast<char*>(data); + DoRead(metadata.start_offset(), data_char, metadata.size()); + +#if DCHECK_IS_ON() + { + MutexLocker locker(mutex_); + auto it = allocated_chunks_.find(metadata.start_offset()); + DCHECK(it != allocated_chunks_.end()); + DCHECK_EQ(metadata.size(), it->second); + } +#endif + + return true; +} + +void DiskDataAllocator::Discard(std::unique_ptr<Metadata> metadata) { + MutexLocker locker(mutex_); + DCHECK(may_write_ || file_.IsValid()); + +#if DCHECK_IS_ON() + auto it = allocated_chunks_.find(metadata->start_offset()); + DCHECK(it != allocated_chunks_.end()); + DCHECK_EQ(metadata->size(), it->second); + allocated_chunks_.erase(it); +#endif + + ReleaseChunk(*metadata); +} + +int DiskDataAllocator::DoWrite(int64_t offset, const char* data, int size) { + int rv = file_.Write(offset, data, size); + + // No PCHECK(), since a file writing error is recoverable. + if (rv != size) { + LOG(ERROR) << "DISK: Cannot write to disk. written = " << rv << " " + << base::File::ErrorToString(base::File::GetLastFileError()); + } + return rv; +} + +void DiskDataAllocator::DoRead(int64_t offset, char* data, int size) { + int rv = file_.Read(offset, data, size); + // Can only crash, since we cannot continue without the data. + PCHECK(rv == size) << "Likely file corruption."; +} + +void DiskDataAllocator::ProvideTemporaryFile(base::File file) { + MutexLocker locker(mutex_); + DCHECK(IsMainThread()); + DCHECK(!file_.IsValid()); + DCHECK(!may_write_); + + file_ = std::move(file); + if (file_.IsValid()) + may_write_ = true; +} + +// static +DiskDataAllocator& DiskDataAllocator::Instance() { + DEFINE_STATIC_LOCAL(DiskDataAllocator, instance, ()); + return instance; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/disk_data_allocator.h b/chromium/third_party/blink/renderer/platform/disk_data_allocator.h new file mode 100644 index 00000000000..5c53a04738e --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/disk_data_allocator.h @@ -0,0 +1,116 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_DISK_DATA_ALLOCATOR_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_DISK_DATA_ALLOCATOR_H_ + +#include <map> +#include <memory> + +#include "base/files/file.h" +#include "base/synchronization/lock.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/threading.h" +#include "third_party/blink/renderer/platform/wtf/threading_primitives.h" + +namespace blink { + +// Stores data onto a single file. +// +// The file is provided after construction. As a consequence, the allocator +// initially does not accept writes, that is |Write()| returns nullptr. It may +// also become not usable later, for instance if disk space is no longer +// available. +// +// Threading: +// - Reads must be done from the main thread +// - Writes can be done from any thread. +// - public methods are thread-safe, and unless otherwise noted, can be called +// from any thread. +class PLATFORM_EXPORT DiskDataAllocator { + public: + class Metadata { + public: + int64_t start_offset() const { return start_offset_; } + size_t size() const { return size_; } + Metadata(Metadata&& other) = delete; + + private: + Metadata(int64_t start_offset, size_t size) + : start_offset_(start_offset), size_(size) {} + Metadata(const Metadata& other) = default; + Metadata& operator=(const Metadata& other) = default; + + int64_t start_offset_; + size_t size_; + + friend class DiskDataAllocator; + }; + + // Must be called on the main thread. + void ProvideTemporaryFile(::base::File file); + + // Whether writes may succeed. This is not a guarantee. However, when this + // returns false, writes will fail. + bool may_write() LOCKS_EXCLUDED(mutex_); + + // Returns |nullptr| in case of error. + // Note that this performs a blocking disk write. + std::unique_ptr<Metadata> Write(const void* data, size_t size); + + // Returns |false| in case of error. + // Must be called from the main thread. + // Can be called at any time before |Discard()| destroys |metadata|. + // + // |data| must point to an area large enough to fit a |metadata.size|-ed + // array. Note that this performs a blocking disk read. + bool Read(const Metadata& metadata, void* data); + + // Discards existing data pointed at by |metadata|. + void Discard(std::unique_ptr<Metadata> metadata); + + virtual ~DiskDataAllocator(); + static DiskDataAllocator& Instance(); + + protected: + // Protected methods for testing. + DiskDataAllocator(); + void set_may_write_for_testing(bool may_write) LOCKS_EXCLUDED(mutex_); + + private: + Metadata FindChunk(size_t size) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + void ReleaseChunk(const Metadata& metadata) EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Virtual for testing. + virtual int DoWrite(int64_t offset, const char* data, int size) + LOCKS_EXCLUDED(mutex_); + // CHECK()s that the read is successful. + virtual void DoRead(int64_t offset, char* data, int size); + + base::File file_; // May be invalid. + + protected: // For testing. + Mutex mutex_; + // Using a std::map because we rely on |{lower,upper}_bound()|. + std::map<int64_t, size_t> free_chunks_ GUARDED_BY(mutex_); + size_t free_chunks_size_ GUARDED_BY(mutex_); + + private: + int64_t file_tail_ GUARDED_BY(mutex_); + // Whether writing is possible now. This can be true if: + // - |set_may_write_for_testing()| was called, or + // - |file_.IsValid()| and no write error occurred (which would set + // |may_write_| to false). + bool may_write_ GUARDED_BY(mutex_); +#if DCHECK_IS_ON() + std::map<int64_t, size_t> allocated_chunks_ GUARDED_BY(mutex_); +#endif + + FRIEND_TEST_ALL_PREFIXES(DiskDataAllocatorTest, ProvideInvalidFile); + FRIEND_TEST_ALL_PREFIXES(DiskDataAllocatorTest, ProvideValidFile); +}; + +} // namespace blink +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_DISK_DATA_ALLOCATOR_H_ diff --git a/chromium/third_party/blink/renderer/platform/disk_data_allocator_test.cc b/chromium/third_party/blink/renderer/platform/disk_data_allocator_test.cc new file mode 100644 index 00000000000..b1ac3b51e4e --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/disk_data_allocator_test.cc @@ -0,0 +1,266 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/disk_data_allocator.h" + +#include <cstring> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/files/file.h" +#include "base/files/file_util.h" +#include "base/rand_util.h" +#include "base/test/task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/disk_data_allocator_test_utils.h" + +using ThreadPoolExecutionMode = + base::test::TaskEnvironment::ThreadPoolExecutionMode; + +namespace blink { + +class DiskDataAllocatorTest : public ::testing::Test { + public: + explicit DiskDataAllocatorTest( + ThreadPoolExecutionMode thread_pool_execution_mode = + ThreadPoolExecutionMode::DEFAULT) + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME, + thread_pool_execution_mode) {} + + static std::vector<std::unique_ptr<DiskDataAllocator::Metadata>> + Allocate(InMemoryDataAllocator* allocator, size_t size, size_t count) { + std::string random_data = base::RandBytesAsString(size); + + std::vector<std::unique_ptr<DiskDataAllocator::Metadata>> all_metadata; + for (size_t i = 0; i < count; i++) { + auto metadata = allocator->Write(random_data.c_str(), random_data.size()); + EXPECT_EQ(metadata->start_offset(), static_cast<int64_t>(i * size)); + all_metadata.push_back(std::move(metadata)); + } + return all_metadata; + } + + protected: + base::test::TaskEnvironment task_environment_; +}; + +TEST_F(DiskDataAllocatorTest, ReadWrite) { + InMemoryDataAllocator allocator; + + constexpr size_t kSize = 1000; + std::string random_data = base::RandBytesAsString(kSize); + auto metadata = allocator.Write(random_data.c_str(), random_data.size()); + EXPECT_TRUE(metadata); + EXPECT_EQ(kSize, metadata->size()); + + auto read_data = std::vector<char>(kSize); + bool ok = allocator.Read(*metadata, &read_data[0]); + EXPECT_TRUE(ok); + + EXPECT_EQ(0, memcmp(&read_data[0], random_data.c_str(), kSize)); +} + +TEST_F(DiskDataAllocatorTest, ReadWriteDiscardMultiple) { + InMemoryDataAllocator allocator; + + std::vector< + std::pair<std::unique_ptr<DiskDataAllocator::Metadata>, std::string>> + data_written; + + for (int i = 0; i < 10; i++) { + int size = base::RandInt(100, 1000); + auto data = base::RandBytesAsString(size); + auto metadata = allocator.Write(&data[0], size); + ASSERT_TRUE(metadata); + data_written.emplace_back(std::move(metadata), data); + } + + base::RandomShuffle(data_written.begin(), data_written.end()); + + for (const auto& p : data_written) { + size_t size = p.first->size(); + auto read_data = std::vector<char>(size); + bool ok = allocator.Read(*p.first, &read_data[0]); + EXPECT_TRUE(ok); + + EXPECT_EQ(0, memcmp(&read_data[0], &p.second[0], size)); + } + + base::RandomShuffle(data_written.begin(), data_written.end()); + + for (auto& p : data_written) { + auto metadata = std::move(p.first); + allocator.Discard(std::move(metadata)); + } +} + +TEST_F(DiskDataAllocatorTest, WriteEventuallyFail) { + InMemoryDataAllocator allocator; + + constexpr size_t kSize = 1 << 18; + std::string random_data = base::RandBytesAsString(kSize); + + static_assert(4 * kSize == InMemoryDataAllocator::kMaxSize, ""); + for (int i = 0; i < 4; i++) { + auto metadata = allocator.Write(random_data.c_str(), random_data.size()); + EXPECT_TRUE(metadata); + } + auto metadata = allocator.Write(random_data.c_str(), random_data.size()); + EXPECT_FALSE(metadata); + EXPECT_FALSE(allocator.may_write()); +} + +TEST_F(DiskDataAllocatorTest, CanReuseFreedChunk) { + InMemoryDataAllocator allocator; + + constexpr size_t kSize = 1 << 10; + std::vector<std::unique_ptr<DiskDataAllocator::Metadata>> all_metadata; + + for (int i = 0; i < 10; i++) { + std::string random_data = base::RandBytesAsString(kSize); + auto metadata = allocator.Write(random_data.c_str(), random_data.size()); + ASSERT_TRUE(metadata); + all_metadata.push_back(std::move(metadata)); + } + + auto metadata = std::move(all_metadata[4]); + ASSERT_TRUE(metadata); + int64_t start_offset = metadata->start_offset(); + allocator.Discard(std::move(metadata)); + + std::string random_data = base::RandBytesAsString(kSize); + auto new_metadata = allocator.Write(random_data.c_str(), random_data.size()); + ASSERT_TRUE(new_metadata); + EXPECT_EQ(new_metadata->start_offset(), start_offset); +} + +TEST_F(DiskDataAllocatorTest, ExactThenWorstFit) { + InMemoryDataAllocator allocator; + + int count = 10; + size_t size_increment = 1000; + std::vector<std::unique_ptr<DiskDataAllocator::Metadata>> all_metadata; + + size_t size = 10000; + // Allocate a bunch of random-sized + for (int i = 0; i < count; i++) { + std::string random_data = base::RandBytesAsString(size); + auto metadata = allocator.Write(random_data.c_str(), random_data.size()); + ASSERT_TRUE(metadata); + all_metadata.push_back(std::move(metadata)); + size += size_increment; + } + + auto& hole_metadata = all_metadata[4]; + size_t hole_size = hole_metadata->size(); + int64_t hole_offset = hole_metadata->start_offset(); + allocator.Discard(std::move(hole_metadata)); + + auto& larger_hole_metadata = all_metadata[9]; + int64_t larger_hole_offset = larger_hole_metadata->start_offset(); + allocator.Discard(std::move(larger_hole_metadata)); + + std::string random_data = base::RandBytesAsString(hole_size); + auto metadata = allocator.Write(random_data.c_str(), random_data.size()); + // Exact fit. + EXPECT_EQ(metadata->start_offset(), hole_offset); + allocator.Discard(std::move(metadata)); + + // -1 to check that this is not best fit. + random_data = base::RandBytesAsString(hole_size - 1); + metadata = allocator.Write(random_data.c_str(), random_data.size()); + EXPECT_EQ(metadata->start_offset(), larger_hole_offset); +} + +TEST_F(DiskDataAllocatorTest, FreeChunksMerging) { + constexpr size_t kSize = 100; + + auto allocator = std::make_unique<InMemoryDataAllocator>(); + auto chunks = Allocate(allocator.get(), kSize, 4); + + // Layout is (indices in |chunks|): + // | 0 | 1 | 2 | 3 | + // Discarding a higher index after a lower one triggers merging on the left. + + // Merge left. + allocator->Discard(std::move(chunks[0])); + EXPECT_EQ(1u, allocator->FreeChunks().size()); + allocator->Discard(std::move(chunks[1])); + EXPECT_EQ(1u, allocator->FreeChunks().size()); + EXPECT_EQ(2 * kSize, allocator->FreeChunks().begin()->second); + allocator->Discard(std::move(chunks[2])); + EXPECT_EQ(1u, allocator->FreeChunks().size()); + EXPECT_EQ(3 * kSize, allocator->FreeChunks().begin()->second); + allocator->Discard(std::move(chunks[3])); + EXPECT_EQ(1u, allocator->FreeChunks().size()); + EXPECT_EQ(4 * kSize, allocator->FreeChunks().begin()->second); + + allocator = std::make_unique<InMemoryDataAllocator>(); + chunks = Allocate(allocator.get(), kSize, 4); + + // Merge right. + allocator->Discard(std::move(chunks[3])); + EXPECT_EQ(1u, allocator->FreeChunks().size()); + allocator->Discard(std::move(chunks[2])); + EXPECT_EQ(1u, allocator->FreeChunks().size()); + EXPECT_EQ(2 * kSize, allocator->FreeChunks().begin()->second); + allocator->Discard(std::move(chunks[0])); + EXPECT_EQ(2u, allocator->FreeChunks().size()); + // Multiple merges: left, then right. + allocator->Discard(std::move(chunks[1])); + EXPECT_EQ(1u, allocator->FreeChunks().size()); + + allocator = std::make_unique<InMemoryDataAllocator>(); + chunks = Allocate(allocator.get(), kSize, 4); + + // Left then right merging. + allocator->Discard(std::move(chunks[0])); + allocator->Discard(std::move(chunks[2])); + EXPECT_EQ(2u, allocator->FreeChunks().size()); + allocator->Discard(std::move(chunks[1])); + EXPECT_EQ(1u, allocator->FreeChunks().size()); +} + +TEST_F(DiskDataAllocatorTest, ProvideInvalidFile) { + DiskDataAllocator allocator; + EXPECT_FALSE(allocator.may_write()); + allocator.ProvideTemporaryFile(base::File()); + EXPECT_FALSE(allocator.may_write()); +} + +TEST_F(DiskDataAllocatorTest, ProvideValidFile) { + base::FilePath path; + if (!base::CreateTemporaryFile(&path)) + GTEST_SKIP() << "Cannot create temporary file."; + + int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ | + base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE; + auto file = base::File(base::FilePath(path), flags); + if (!file.IsValid()) + GTEST_SKIP() << "Cannot create temporary file."; + + DiskDataAllocator allocator; + EXPECT_FALSE(allocator.may_write()); + allocator.ProvideTemporaryFile(std::move(file)); + EXPECT_TRUE(allocator.may_write()); + + // Test read/write with a real file. + constexpr size_t kSize = 1000; + std::string random_data = base::RandBytesAsString(kSize); + auto metadata = allocator.Write(random_data.c_str(), random_data.size()); + if (!metadata) + GTEST_SKIP() << "Disk full?"; + + EXPECT_EQ(kSize, metadata->size()); + + auto read_data = std::vector<char>(kSize); + bool ok = allocator.Read(*metadata, &read_data[0]); + EXPECT_TRUE(ok); + + EXPECT_EQ(0, memcmp(&read_data[0], random_data.c_str(), kSize)); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/disk_data_allocator_test_utils.h b/chromium/third_party/blink/renderer/platform/disk_data_allocator_test_utils.h new file mode 100644 index 00000000000..4c7bb2f37da --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/disk_data_allocator_test_utils.h @@ -0,0 +1,66 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_DISK_DATA_ALLOCATOR_TEST_UTILS_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_DISK_DATA_ALLOCATOR_TEST_UTILS_H_ + +#include "third_party/blink/renderer/platform/disk_data_allocator.h" + +#include <algorithm> +#include <cstdint> +#include <cstring> +#include <map> +#include <vector> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace blink { + +class InMemoryDataAllocator : public DiskDataAllocator { + public: + constexpr static size_t kMaxSize = 1 << 20; + + InMemoryDataAllocator() : max_offset_(0), data_(kMaxSize) { + set_may_write_for_testing(true); + } + ~InMemoryDataAllocator() override = default; + + std::map<int64_t, size_t> FreeChunks() { + MutexLocker locker(mutex_); + + size_t free_size = 0; + for (const auto& p : free_chunks_) + free_size += p.second; + + EXPECT_EQ(free_size, free_chunks_size_); + + return free_chunks_; + } + + private: + int DoWrite(int64_t offset, const char* data, int size) override { + int64_t end_offset = offset + size; + if (static_cast<size_t>(end_offset) > kMaxSize) + return -1; + + memcpy(&data_[0] + offset, data, size); + max_offset_ = std::max(end_offset, max_offset_); + return size; + } + + void DoRead(int64_t offset, char* data, int size) override { + int64_t end_offset = offset + size; + ASSERT_LE(end_offset, max_offset_); + + memcpy(data, &data_[0] + offset, size); + } + + private: + int64_t max_offset_; + std::vector<char> data_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_DISK_DATA_ALLOCATOR_TEST_UTILS_H_ diff --git a/chromium/third_party/blink/renderer/platform/encrypted_media_request.h b/chromium/third_party/blink/renderer/platform/encrypted_media_request.h index 25770c1e020..be409773273 100644 --- a/chromium/third_party/blink/renderer/platform/encrypted_media_request.h +++ b/chromium/third_party/blink/renderer/platform/encrypted_media_request.h @@ -32,7 +32,7 @@ class EncryptedMediaRequest : public GarbageCollected<EncryptedMediaRequest> { std::unique_ptr<WebContentDecryptionModuleAccess>) = 0; virtual void RequestNotSupported(const WebString& error_message) = 0; - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/OWNERS b/chromium/third_party/blink/renderer/platform/exported/OWNERS index 9a717779420..f80e768428d 100644 --- a/chromium/third_party/blink/renderer/platform/exported/OWNERS +++ b/chromium/third_party/blink/renderer/platform/exported/OWNERS @@ -1,2 +1,7 @@ per-file notification_data_conversions*=peter@chromium.org per-file web_rtc_*=hbos@chromium.org + +# Any core owner can also approve changes to runtime-enabled features. +# Please make sure to get a review from someone with expertise on the feature +# you are changing. +per-file web_runtime_features*=file://third_party/blink/renderer/core/OWNERS diff --git a/chromium/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_test.cc b/chromium/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_test.cc index c5825eed5ef..e6c83416711 100644 --- a/chromium/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_test.cc +++ b/chromium/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_test.cc @@ -13,12 +13,12 @@ #include "media/base/audio_bus.h" #include "media/base/audio_parameters.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_track.h" #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_audio_sink.h" #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/web/web_heap.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h" +#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/platform/exported/platform.cc b/chromium/third_party/blink/renderer/platform/exported/platform.cc index 55f9e8146b7..d498e7d31b7 100644 --- a/chromium/third_party/blink/renderer/platform/exported/platform.cc +++ b/chromium/third_party/blink/renderer/platform/exported/platform.cc @@ -38,13 +38,11 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/memory_dump_manager.h" #include "build/build_config.h" -#include "services/service_manager/public/cpp/interface_provider.h" #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h" #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h" -#include "third_party/blink/public/platform/web_prerendering_support.h" #include "third_party/blink/public/platform/websocket_handshake_throttle.h" +#include "third_party/blink/renderer/platform/bindings/blink_isolate/blink_isolate.h" #include "third_party/blink/renderer/platform/bindings/parkable_string_manager.h" #include "third_party/blink/renderer/platform/font_family_names.h" #include "third_party/blink/renderer/platform/fonts/font_cache_memory_dump_provider.h" @@ -69,22 +67,6 @@ namespace blink { namespace { -class DefaultInterfaceProvider : public InterfaceProvider { - USING_FAST_MALLOC(DefaultInterfaceProvider); - - public: - DefaultInterfaceProvider() = default; - ~DefaultInterfaceProvider() = default; - - // InterfaceProvider implementation: - void GetInterface(const char* interface_name, - mojo::ScopedMessagePipeHandle interface_pipe) override { - Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( - mojo::GenericPendingReceiver(interface_name, - std::move(interface_pipe))); - } -}; - class DefaultBrowserInterfaceBrokerProxy : public ThreadSafeBrowserInterfaceBrokerProxy { USING_FAST_MALLOC(DefaultBrowserInterfaceBrokerProxy); @@ -140,13 +122,6 @@ static Platform* g_platform = nullptr; static GCTaskRunner* g_gc_task_runner = nullptr; -static void CallOnMainThreadFunction(WTF::MainThreadFunction function, - void* context) { - PostCrossThreadTask( - *Thread::MainThread()->GetTaskRunner(), FROM_HERE, - CrossThreadBindOnce(function, CrossThreadUnretained(context))); -} - Platform::Platform() { WTF::Partitions::Initialize(); } @@ -157,6 +132,8 @@ namespace { class SimpleMainThread : public Thread { public: + SimpleMainThread() : isolate_(WebIsolate::Create()) {} + // We rely on base::ThreadTaskRunnerHandle for tasks posted on the main // thread. The task runner handle may not be available on Blink's startup // (== on SimpleMainThread's construction), because some tests like @@ -190,6 +167,7 @@ class SimpleMainThread : public Thread { private: bool IsSimpleMainThread() const override { return true; } + std::unique_ptr<WebIsolate> isolate_; scheduler::SimpleThreadScheduler scheduler_; scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_for_testing_; @@ -215,7 +193,7 @@ void Platform::CreateMainThreadAndInitialize(Platform* platform) { void Platform::InitializeCommon(Platform* platform, std::unique_ptr<Thread> main_thread) { - WTF::Initialize(CallOnMainThreadFunction); + WTF::Initialize(); Thread::SetMainThread(std::move(main_thread)); @@ -287,11 +265,6 @@ Platform* Platform::Current() { return g_platform; } -InterfaceProvider* Platform::GetInterfaceProvider() { - DEFINE_STATIC_LOCAL(DefaultInterfaceProvider, provider, ()); - return &provider; -} - ThreadSafeBrowserInterfaceBrokerProxy* Platform::GetBrowserInterfaceBroker() { DEFINE_STATIC_LOCAL(DefaultBrowserInterfaceBrokerProxy, proxy, ()); return &proxy; diff --git a/chromium/third_party/blink/renderer/platform/exported/url_conversion.cc b/chromium/third_party/blink/renderer/platform/exported/url_conversion.cc index 5980f067a4a..4b149343d90 100644 --- a/chromium/third_party/blink/renderer/platform/exported/url_conversion.cc +++ b/chromium/third_party/blink/renderer/platform/exported/url_conversion.cc @@ -5,7 +5,6 @@ #include "third_party/blink/public/platform/url_conversion.h" #include "third_party/blink/public/platform/web_string.h" -#include "third_party/blink/renderer/platform/blob/blob_data.h" #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "url/gurl.h" @@ -27,15 +26,4 @@ GURL WebStringToGURL(const WebString& web_string) { return GURL(base::StringPiece16(str.Characters16(), str.length())); } -mojo::ScopedMessagePipeHandle DataURLToMessagePipeHandle( - const WebString& data_url) { - auto blob_data = std::make_unique<blink::BlobData>(); - blob_data->AppendBytes(data_url.Utf8().data(), data_url.length()); - scoped_refptr<blink::BlobDataHandle> blob_data_handle = - blink::BlobDataHandle::Create(std::move(blob_data), data_url.length()); - mojo::PendingRemote<mojom::blink::Blob> data_url_blob = - blob_data_handle->CloneBlobRemote(); - return data_url_blob.PassPipe(); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager_test.cc b/chromium/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager_test.cc index 3c104d77bf2..ecaddbf977b 100644 --- a/chromium/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager_test.cc +++ b/chromium/third_party/blink/renderer/platform/exported/video_capture/web_video_capture_impl_manager_test.cc @@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/run_loop.h" +#include "base/test/gmock_callback_support.h" #include "base/test/task_environment.h" #include "media/base/bind_to_current_loop.h" #include "media/capture/mojom/video_capture.mojom-blink.h" @@ -21,6 +22,7 @@ #include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +using base::test::RunOnceClosure; using media::BindToCurrentLoop; using ::testing::_; using ::testing::DoAll; @@ -29,10 +31,6 @@ using ::testing::SaveArg; namespace blink { -ACTION_P(RunClosure, closure) { - closure.Run(); -} - namespace { // Callback interface to be implemented by VideoCaptureImplManagerTest. @@ -166,7 +164,7 @@ class VideoCaptureImplManagerTest : public ::testing::Test, .RetiresOnSaturation(); } EXPECT_CALL(*this, OnStarted(_)) - .WillOnce(RunClosure(std::move(quit_closure))) + .WillOnce(RunOnceClosure(std::move(quit_closure))) .RetiresOnSaturation(); std::array<base::OnceClosure, kNumClients> stop_callbacks; media::VideoCaptureParams params; @@ -189,7 +187,7 @@ class VideoCaptureImplManagerTest : public ::testing::Test, .Times(kNumClients - 1) .RetiresOnSaturation(); EXPECT_CALL(*this, OnStopped(_)) - .WillOnce(RunClosure(std::move(quit_closure))) + .WillOnce(RunOnceClosure(std::move(quit_closure))) .RetiresOnSaturation(); for (auto& stop_callback : *stop_callbacks) std::move(stop_callback).Run(); @@ -281,7 +279,7 @@ TEST_F(VideoCaptureImplManagerTest, SuspendAndResumeSessions) { .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*this, OnPaused(session_ids_[2])) - .WillOnce(RunClosure(std::move(quit_closure))) + .WillOnce(RunOnceClosure(std::move(quit_closure))) .RetiresOnSaturation(); manager_->SuspendDevices(video_devices, true); run_loop.Run(); @@ -299,7 +297,7 @@ TEST_F(VideoCaptureImplManagerTest, SuspendAndResumeSessions) { .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*this, OnResumed(session_ids_[2])) - .WillOnce(RunClosure(std::move(quit_closure))) + .WillOnce(RunOnceClosure(std::move(quit_closure))) .RetiresOnSaturation(); manager_->SuspendDevices(video_devices, false); run_loop.Run(); @@ -312,7 +310,7 @@ TEST_F(VideoCaptureImplManagerTest, SuspendAndResumeSessions) { base::RepeatingClosure quit_closure = BindToCurrentLoop(run_loop.QuitClosure()); EXPECT_CALL(*this, OnPaused(session_ids_[0])) - .WillOnce(RunClosure(std::move(quit_closure))) + .WillOnce(RunOnceClosure(std::move(quit_closure))) .RetiresOnSaturation(); manager_->Suspend(session_ids_[0]); run_loop.Run(); @@ -328,7 +326,7 @@ TEST_F(VideoCaptureImplManagerTest, SuspendAndResumeSessions) { .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*this, OnPaused(session_ids_[2])) - .WillOnce(RunClosure(std::move(quit_closure))) + .WillOnce(RunOnceClosure(std::move(quit_closure))) .RetiresOnSaturation(); manager_->SuspendDevices(video_devices, true); run_loop.Run(); @@ -353,7 +351,7 @@ TEST_F(VideoCaptureImplManagerTest, SuspendAndResumeSessions) { .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*this, OnResumed(session_ids_[2])) - .WillOnce(RunClosure(std::move(quit_closure))) + .WillOnce(RunOnceClosure(std::move(quit_closure))) .RetiresOnSaturation(); manager_->SuspendDevices(video_devices, false); run_loop.Run(); diff --git a/chromium/third_party/blink/renderer/platform/exported/web_blob_info.cc b/chromium/third_party/blink/renderer/platform/exported/web_blob_info.cc index b6d911afba9..28d5befee6e 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_blob_info.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_blob_info.cc @@ -23,7 +23,6 @@ WebBlobInfo::WebBlobInfo(const WebString& uuid, mojom::blink::Blob::Version_))) {} WebBlobInfo::WebBlobInfo(const WebString& uuid, - const WebString& file_path, const WebString& file_name, const WebString& type, const base::Optional<base::Time>& last_modified, @@ -36,7 +35,6 @@ WebBlobInfo::WebBlobInfo(const WebString& uuid, mojo::PendingRemote<mojom::blink::Blob>( std::move(handle), mojom::blink::Blob::Version_)), - file_path, file_name, last_modified) {} @@ -49,10 +47,9 @@ WebBlobInfo WebBlobInfo::BlobForTesting(const WebString& uuid, // static WebBlobInfo WebBlobInfo::FileForTesting(const WebString& uuid, - const WebString& file_path, const WebString& file_name, const WebString& type) { - return WebBlobInfo(uuid, file_path, file_name, type, base::nullopt, + return WebBlobInfo(uuid, file_name, type, base::nullopt, std::numeric_limits<uint64_t>::max(), mojo::MessagePipe().handle0); } @@ -77,11 +74,9 @@ WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle) : WebBlobInfo(handle, handle->GetType(), handle->size()) {} WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle, - const WebString& file_path, const WebString& file_name, const base::Optional<base::Time>& last_modified) : WebBlobInfo(handle, - file_path, file_name, handle->GetType(), last_modified, @@ -97,7 +92,6 @@ WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle, blob_handle_(std::move(handle)) {} WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle, - const WebString& file_path, const WebString& file_name, const WebString& type, const base::Optional<base::Time>& last_modified, @@ -107,7 +101,6 @@ WebBlobInfo::WebBlobInfo(scoped_refptr<BlobDataHandle> handle, type_(type), size_(size), blob_handle_(std::move(handle)), - file_path_(file_path), file_name_(file_name), last_modified_(last_modified) {} diff --git a/chromium/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc b/chromium/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc index 807a31c6a34..21edc5fe307 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_coalesced_input_event.cc @@ -4,54 +4,14 @@ #include "third_party/blink/public/platform/web_coalesced_input_event.h" -#include "third_party/blink/public/platform/web_gesture_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_pointer_event.h" -#include "third_party/blink/public/platform/web_touch_event.h" +#include "third_party/blink/public/common/input/web_gesture_event.h" +#include "third_party/blink/public/common/input/web_keyboard_event.h" +#include "third_party/blink/public/common/input/web_mouse_wheel_event.h" +#include "third_party/blink/public/common/input/web_pointer_event.h" +#include "third_party/blink/public/common/input/web_touch_event.h" namespace blink { -namespace { - -struct WebInputEventDelete { - template <class EventType> - bool Execute(WebInputEvent* event) const { - if (!event) - return false; - DCHECK_EQ(sizeof(EventType), event->size()); - delete static_cast<EventType*>(event); - return true; - } -}; - -template <typename Operator, typename ArgIn> -bool Apply(Operator op, WebInputEvent::Type type, const ArgIn& arg_in) { - if (WebInputEvent::IsMouseEventType(type)) - return op.template Execute<WebMouseEvent>(arg_in); - if (type == WebInputEvent::kMouseWheel) - return op.template Execute<WebMouseWheelEvent>(arg_in); - if (WebInputEvent::IsKeyboardEventType(type)) - return op.template Execute<WebKeyboardEvent>(arg_in); - if (WebInputEvent::IsTouchEventType(type)) - return op.template Execute<WebTouchEvent>(arg_in); - if (WebInputEvent::IsGestureEventType(type)) - return op.template Execute<WebGestureEvent>(arg_in); - if (WebInputEvent::IsPointerEventType(type)) - return op.template Execute<WebPointerEvent>(arg_in); - - NOTREACHED() << "Unknown webkit event type " << type; - return false; -} -} - -void WebCoalescedInputEvent::WebInputEventDeleter::operator()( - WebInputEvent* event) const { - if (!event) - return; - Apply(WebInputEventDelete(), event->GetType(), event); -} - WebInputEvent* WebCoalescedInputEvent::EventPointer() { return event_.get(); } diff --git a/chromium/third_party/blink/renderer/platform/exported/web_crypto_algorithm.cc b/chromium/third_party/blink/renderer/platform/exported/web_crypto_algorithm.cc index 67b793f5ca1..1628116da5b 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_crypto_algorithm.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_crypto_algorithm.cc @@ -286,6 +286,40 @@ constexpr WebCryptoAlgorithmInfo kAlgorithmIdToInfo[] = { WebCryptoAlgorithmInfo::kUndefined, // WrapKey WebCryptoAlgorithmInfo::kUndefined // UnwrapKey }}, + {// Index 16 + // TODO(crbug.com/1032821): Ed25519 is experimental behind a flag. See + // https://chromestatus.com/feature/4913922408710144 for the status. + "ED25519", + { + WebCryptoAlgorithmInfo::kUndefined, // Encrypt + WebCryptoAlgorithmInfo::kUndefined, // Decrypt + kWebCryptoAlgorithmParamsTypeEd25519Params, // Sign + kWebCryptoAlgorithmParamsTypeEd25519Params, // Verify + WebCryptoAlgorithmInfo::kUndefined, // Digest + kWebCryptoAlgorithmParamsTypeNone, // GenerateKey + kWebCryptoAlgorithmParamsTypeNone, // ImportKey + WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength + WebCryptoAlgorithmInfo::kUndefined, // DeriveBits + WebCryptoAlgorithmInfo::kUndefined, // WrapKey + WebCryptoAlgorithmInfo::kUndefined // UnwrapKey + }}, + {// Index 17 + // TODO(crbug.com/1032821): X25519 is experimental behind a flag. See + // https://chromestatus.com/feature/4913922408710144 for the status. + "X25519", + { + WebCryptoAlgorithmInfo::kUndefined, // Encrypt + WebCryptoAlgorithmInfo::kUndefined, // Decrypt + WebCryptoAlgorithmInfo::kUndefined, // Sign + WebCryptoAlgorithmInfo::kUndefined, // Verify + WebCryptoAlgorithmInfo::kUndefined, // Digest + kWebCryptoAlgorithmParamsTypeNone, // GenerateKey + kWebCryptoAlgorithmParamsTypeNone, // ImportKey + WebCryptoAlgorithmInfo::kUndefined, // GetKeyLength + kWebCryptoAlgorithmParamsTypeX25519KeyDeriveParams, // DeriveBits + WebCryptoAlgorithmInfo::kUndefined, // WrapKey + WebCryptoAlgorithmInfo::kUndefined // UnwrapKey + }}, }; // Initializing the algorithmIdToInfo table above depends on knowing the enum @@ -308,7 +342,9 @@ static_assert(kWebCryptoAlgorithmIdEcdsa == 12, "ECDSA id must match"); static_assert(kWebCryptoAlgorithmIdEcdh == 13, "ECDH id must match"); static_assert(kWebCryptoAlgorithmIdHkdf == 14, "HKDF id must match"); static_assert(kWebCryptoAlgorithmIdPbkdf2 == 15, "Pbkdf2 id must match"); -static_assert(kWebCryptoAlgorithmIdLast == 15, "last id must match"); +static_assert(kWebCryptoAlgorithmIdEd25519 == 16, "X25519 id must match"); +static_assert(kWebCryptoAlgorithmIdX25519 == 17, "Ed25519 id must match"); +static_assert(kWebCryptoAlgorithmIdLast == 17, "last id must match"); static_assert(10 == kWebCryptoOperationLast, "the parameter mapping needs to be updated"); @@ -489,6 +525,21 @@ const WebCryptoPbkdf2Params* WebCryptoAlgorithm::Pbkdf2Params() const { return nullptr; } +const WebCryptoEd25519Params* WebCryptoAlgorithm::Ed25519Params() const { + DCHECK(!IsNull()); + if (ParamsType() == kWebCryptoAlgorithmParamsTypeEd25519Params) + return static_cast<WebCryptoEd25519Params*>(private_->params.get()); + return nullptr; +} + +const WebCryptoX25519KeyDeriveParams* +WebCryptoAlgorithm::X25519KeyDeriveParams() const { + DCHECK(!IsNull()); + if (ParamsType() == kWebCryptoAlgorithmParamsTypeX25519KeyDeriveParams) + return static_cast<WebCryptoX25519KeyDeriveParams*>(private_->params.get()); + return nullptr; +} + bool WebCryptoAlgorithm::IsHash(WebCryptoAlgorithmId id) { switch (id) { case kWebCryptoAlgorithmIdSha1: @@ -508,6 +559,8 @@ bool WebCryptoAlgorithm::IsHash(WebCryptoAlgorithmId id) { case kWebCryptoAlgorithmIdEcdh: case kWebCryptoAlgorithmIdHkdf: case kWebCryptoAlgorithmIdPbkdf2: + case kWebCryptoAlgorithmIdEd25519: + case kWebCryptoAlgorithmIdX25519: break; } return false; @@ -532,6 +585,8 @@ bool WebCryptoAlgorithm::IsKdf(WebCryptoAlgorithmId id) { case kWebCryptoAlgorithmIdRsaPss: case kWebCryptoAlgorithmIdEcdsa: case kWebCryptoAlgorithmIdEcdh: + case kWebCryptoAlgorithmIdEd25519: + case kWebCryptoAlgorithmIdX25519: break; } return false; diff --git a/chromium/third_party/blink/renderer/platform/exported/web_cursor_info.cc b/chromium/third_party/blink/renderer/platform/exported/web_cursor_info.cc deleted file mode 100644 index c26adc1f2f1..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_cursor_info.cc +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/public/platform/web_cursor_info.h" - -#include "third_party/blink/renderer/platform/cursor.h" - -namespace blink { - -static SkBitmap GetCursorBitmap(const Cursor& cursor) { - if (!cursor.GetImage()) - return {}; - return cursor.GetImage()->AsSkBitmapForCurrentFrame( - kDoNotRespectImageOrientation); -} - -WebCursorInfo::WebCursorInfo(const Cursor& cursor) - : type(static_cast<ui::CursorType>(cursor.GetType())), - hot_spot(cursor.HotSpot()), - image_scale_factor(cursor.ImageScaleFactor()), - custom_image(GetCursorBitmap(cursor)) {} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_failing_url_loader_factory.cc b/chromium/third_party/blink/renderer/platform/exported/web_failing_url_loader_factory.cc new file mode 100644 index 00000000000..e1633daf19b --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/exported/web_failing_url_loader_factory.cc @@ -0,0 +1,88 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/public/platform/web_failing_url_loader_factory.h" + +#include <memory> + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "services/network/public/cpp/resource_request.h" +#include "third_party/blink/public/platform/scheduler/web_resource_loading_task_runner_handle.h" +#include "third_party/blink/public/platform/web_url_error.h" +#include "third_party/blink/public/platform/web_url_loader.h" +#include "third_party/blink/public/platform/web_url_loader_client.h" +#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" + +namespace blink { + +namespace { + +// A WebURLLoader which always fails loading. +class FailingLoader final : public WebURLLoader { + public: + explicit FailingLoader( + std::unique_ptr<scheduler::WebResourceLoadingTaskRunnerHandle> + task_runner_handle) + : task_runner_handle_(std::move(task_runner_handle)) {} + ~FailingLoader() override = default; + + // WebURLLoader implementation: + void LoadSynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequest::ExtraData> request_extra_data, + int requestor_id, + bool download_to_network_cache_only, + bool pass_response_pipe_to_client, + bool no_mime_sniffing, + base::TimeDelta timeout_interval, + WebURLLoaderClient*, + WebURLResponse&, + base::Optional<WebURLError>& error, + WebData&, + int64_t& encoded_data_length, + int64_t& encoded_body_length, + WebBlobInfo& downloaded_blob) override { + error = ResourceError::Failure(KURL(request->url)); + } + void LoadAsynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequest::ExtraData> request_extra_data, + int requestor_id, + bool download_to_network_cache_only, + bool no_mime_sniffing, + WebURLLoaderClient* client) override { + GetTaskRunner()->PostTask( + FROM_HERE, WTF::Bind(&FailingLoader::Fail, weak_factory_.GetWeakPtr(), + KURL(request->url), WTF::Unretained(client))); + } + void SetDefersLoading(bool) override {} + void DidChangePriority(WebURLRequest::Priority, int) override {} + scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override { + return task_runner_handle_->GetTaskRunner(); + } + + private: + void Fail(const KURL& url, WebURLLoaderClient* client) { + client->DidFail(ResourceError::Failure(url), 0, 0, 0); + } + + const std::unique_ptr<scheduler::WebResourceLoadingTaskRunnerHandle> + task_runner_handle_; + + // This must be the last member. + base::WeakPtrFactory<FailingLoader> weak_factory_{this}; +}; + +} // namespace + +std::unique_ptr<WebURLLoader> WebFailingURLLoaderFactory::CreateURLLoader( + const WebURLRequest&, + std::unique_ptr<scheduler::WebResourceLoadingTaskRunnerHandle> handle) { + return std::make_unique<FailingLoader>(std::move(handle)); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_font.cc b/chromium/third_party/blink/renderer/platform/exported/web_font.cc index 96ad579b538..f8fee0c8b30 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_font.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_font.cc @@ -4,7 +4,6 @@ #include "third_party/blink/public/platform/web_font.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_font_description.h" #include "third_party/blink/public/platform/web_rect.h" @@ -30,7 +29,6 @@ class WebFont::Impl final { public: explicit Impl(const WebFontDescription& description) : font_(description) { - font_.Update(nullptr); } const Font& GetFont() const { return font_; } @@ -82,7 +80,7 @@ float WebFont::XHeight() const { void WebFont::DrawText(cc::PaintCanvas* canvas, const WebTextRun& run, - const WebFloatPoint& left_baseline, + const gfx::PointF& left_baseline, SkColor color) const { FontCachePurgePreventer font_cache_purge_preventer; TextRun text_run(run); @@ -95,7 +93,7 @@ void WebFont::DrawText(cc::PaintCanvas* canvas, DrawingRecorder recorder(context, builder, DisplayItem::kWebFont); context.Save(); context.SetFillColor(color); - context.DrawText(private_->GetFont(), run_info, left_baseline, + context.DrawText(private_->GetFont(), run_info, FloatPoint(left_baseline), kInvalidDOMNodeId); context.Restore(); } @@ -113,12 +111,12 @@ int WebFont::OffsetForPosition(const WebTextRun& run, float position) const { } WebFloatRect WebFont::SelectionRectForText(const WebTextRun& run, - const WebFloatPoint& left_baseline, + const gfx::PointF& left_baseline, int height, int from, int to) const { - return private_->GetFont().SelectionRectForText(run, left_baseline, height, - from, to); + return private_->GetFont().SelectionRectForText( + run, FloatPoint(left_baseline), height, from, to); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_gesture_event.cc b/chromium/third_party/blink/renderer/platform/exported/web_gesture_event.cc deleted file mode 100644 index 406e279d2c5..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_gesture_event.cc +++ /dev/null @@ -1,155 +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 "third_party/blink/public/platform/web_gesture_event.h" - -namespace blink { - -float WebGestureEvent::DeltaXInRootFrame() const { - if (type_ == WebInputEvent::kGestureScrollBegin) - return data.scroll_begin.delta_x_hint / frame_scale_; - DCHECK(type_ == WebInputEvent::kGestureScrollUpdate); - return data.scroll_update.delta_x / frame_scale_; -} - -float WebGestureEvent::DeltaYInRootFrame() const { - if (type_ == WebInputEvent::kGestureScrollBegin) - return data.scroll_begin.delta_y_hint / frame_scale_; - DCHECK(type_ == WebInputEvent::kGestureScrollUpdate); - return data.scroll_update.delta_y / frame_scale_; -} - -ui::input_types::ScrollGranularity WebGestureEvent::DeltaUnits() const { - if (type_ == WebInputEvent::kGestureScrollBegin) - return data.scroll_begin.delta_hint_units; - if (type_ == WebInputEvent::kGestureScrollUpdate) - return data.scroll_update.delta_units; - DCHECK(type_ == WebInputEvent::kGestureScrollEnd); - return data.scroll_end.delta_units; -} - -WebGestureEvent::InertialPhaseState WebGestureEvent::InertialPhase() const { - if (type_ == WebInputEvent::kGestureScrollBegin) - return data.scroll_begin.inertial_phase; - if (type_ == WebInputEvent::kGestureScrollUpdate) - return data.scroll_update.inertial_phase; - DCHECK(type_ == WebInputEvent::kGestureScrollEnd); - return data.scroll_end.inertial_phase; -} - -bool WebGestureEvent::Synthetic() const { - if (type_ == WebInputEvent::kGestureScrollBegin) - return data.scroll_begin.synthetic; - DCHECK(type_ == WebInputEvent::kGestureScrollEnd); - return data.scroll_end.synthetic; -} - -float WebGestureEvent::VelocityX() const { - if (type_ == WebInputEvent::kGestureScrollUpdate) - return data.scroll_update.velocity_x; - DCHECK(type_ == WebInputEvent::kGestureFlingStart); - return data.fling_start.velocity_x; -} - -float WebGestureEvent::VelocityY() const { - if (type_ == WebInputEvent::kGestureScrollUpdate) - return data.scroll_update.velocity_y; - DCHECK(type_ == WebInputEvent::kGestureFlingStart); - return data.fling_start.velocity_y; -} - -WebFloatSize WebGestureEvent::TapAreaInRootFrame() const { - if (type_ == WebInputEvent::kGestureTwoFingerTap) { - return WebFloatSize(data.two_finger_tap.first_finger_width / frame_scale_, - data.two_finger_tap.first_finger_height / frame_scale_); - } else if (type_ == WebInputEvent::kGestureLongPress || - type_ == WebInputEvent::kGestureLongTap) { - return WebFloatSize(data.long_press.width / frame_scale_, - data.long_press.height / frame_scale_); - } else if (type_ == WebInputEvent::kGestureTap || - type_ == WebInputEvent::kGestureTapUnconfirmed || - type_ == WebInputEvent::kGestureDoubleTap) { - return WebFloatSize(data.tap.width / frame_scale_, - data.tap.height / frame_scale_); - } else if (type_ == WebInputEvent::kGestureTapDown) { - return WebFloatSize(data.tap_down.width / frame_scale_, - data.tap_down.height / frame_scale_); - } else if (type_ == WebInputEvent::kGestureShowPress) { - return WebFloatSize(data.show_press.width / frame_scale_, - data.show_press.height / frame_scale_); - } - // This function is called for all gestures and determined if the tap - // area is empty or not; so return an empty rect here. - return WebFloatSize(); -} - -WebFloatPoint WebGestureEvent::PositionInRootFrame() const { - return WebFloatPoint( - (position_in_widget_.x / frame_scale_) + frame_translate_.x, - (position_in_widget_.y / frame_scale_) + frame_translate_.y); -} - -int WebGestureEvent::TapCount() const { - DCHECK(type_ == WebInputEvent::kGestureTap); - return data.tap.tap_count; -} - -void WebGestureEvent::ApplyTouchAdjustment(WebFloatPoint root_frame_coords) { - // Update the window-relative position of the event so that the node that - // was ultimately hit is under this point (i.e. elementFromPoint for the - // client co-ordinates in a 'click' event should yield the target). The - // global position is intentionally left unmodified because it's intended to - // reflect raw co-ordinates unrelated to any content. - frame_translate_.x = - root_frame_coords.x - (position_in_widget_.x / frame_scale_); - frame_translate_.y = - root_frame_coords.y - (position_in_widget_.y / frame_scale_); -} - -void WebGestureEvent::FlattenTransform() { - if (frame_scale_ != 1) { - switch (type_) { - case WebInputEvent::kGestureScrollBegin: - data.scroll_begin.delta_x_hint /= frame_scale_; - data.scroll_begin.delta_y_hint /= frame_scale_; - break; - case WebInputEvent::kGestureScrollUpdate: - data.scroll_update.delta_x /= frame_scale_; - data.scroll_update.delta_y /= frame_scale_; - break; - case WebInputEvent::kGestureTwoFingerTap: - data.two_finger_tap.first_finger_width /= frame_scale_; - data.two_finger_tap.first_finger_height /= frame_scale_; - break; - case WebInputEvent::kGestureLongPress: - case WebInputEvent::kGestureLongTap: - data.long_press.width /= frame_scale_; - data.long_press.height /= frame_scale_; - break; - case WebInputEvent::kGestureTap: - case WebInputEvent::kGestureTapUnconfirmed: - case WebInputEvent::kGestureDoubleTap: - data.tap.width /= frame_scale_; - data.tap.height /= frame_scale_; - break; - case WebInputEvent::kGestureTapDown: - data.tap_down.width /= frame_scale_; - data.tap_down.height /= frame_scale_; - break; - case WebInputEvent::kGestureShowPress: - data.show_press.width /= frame_scale_; - data.show_press.height /= frame_scale_; - break; - default: - break; - } - } - - SetPositionInWidget(PositionInRootFrame()); - frame_translate_.x = 0; - frame_translate_.y = 0; - frame_scale_ = 1; -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_http_body.cc b/chromium/third_party/blink/renderer/platform/exported/web_http_body.cc index f60fa03f4c9..83c1f68f7d3 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_http_body.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_http_body.cc @@ -121,11 +121,6 @@ void WebHTTPBody::AppendData(const WebData& data) { }); } -void WebHTTPBody::AppendFile(const WebString& file_path) { - EnsureMutable(); - private_->AppendFile(file_path); -} - void WebHTTPBody::AppendFileRange( const WebString& file_path, int64_t file_start, diff --git a/chromium/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc b/chromium/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc index 9d1960e18cf..ec882242c25 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc @@ -50,10 +50,10 @@ static inline int PartialStringToInt(const String& string, } // namespace -WebVector<WebSize> WebIconSizesParser::ParseIconSizes( +WebVector<gfx::Size> WebIconSizesParser::ParseIconSizes( const WebString& web_sizes_string) { String sizes_string = web_sizes_string; - Vector<WebSize> icon_sizes; + Vector<gfx::Size> icon_sizes; if (sizes_string.IsEmpty()) return icon_sizes; @@ -67,7 +67,7 @@ WebVector<WebSize> WebIconSizesParser::ParseIconSizes( // See if the current size is "any". if (sizes_string.Substring(i, 3).StartsWithIgnoringCase("any") && (i + 3 == length || IsWhitespace(sizes_string[i + 3]))) { - icon_sizes.push_back(WebSize(0, 0)); + icon_sizes.push_back(gfx::Size()); i = i + 3; continue; } @@ -100,8 +100,8 @@ WebVector<WebSize> WebIconSizesParser::ParseIconSizes( // Append the parsed size to iconSizes. icon_sizes.push_back( - WebSize(PartialStringToInt(sizes_string, width_start, width_end), - PartialStringToInt(sizes_string, height_start, height_end))); + gfx::Size(PartialStringToInt(sizes_string, width_start, width_end), + PartialStringToInt(sizes_string, height_start, height_end))); } return icon_sizes; } diff --git a/chromium/third_party/blink/renderer/platform/exported/web_icon_sizes_parser_test.cc b/chromium/third_party/blink/renderer/platform/exported/web_icon_sizes_parser_test.cc index 53959c6c10e..d35d2f3431b 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_icon_sizes_parser_test.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_icon_sizes_parser_test.cc @@ -5,9 +5,9 @@ #include "third_party/blink/public/platform/web_icon_sizes_parser.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/platform/web_size.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" +#include "ui/gfx/geometry/size.h" namespace blink { @@ -15,17 +15,17 @@ class WebIconSizesParserTest : public testing::Test {}; TEST(WebIconSizesParserTest, parseSizes) { WebString sizes_attribute = "32x33"; - WebVector<WebSize> sizes; + WebVector<gfx::Size> sizes; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(1U, sizes.size()); - EXPECT_EQ(32, sizes[0].width); - EXPECT_EQ(33, sizes[0].height); + EXPECT_EQ(32, sizes[0].width()); + EXPECT_EQ(33, sizes[0].height()); sizes_attribute = " 10x11 "; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(1u, sizes.size()); - EXPECT_EQ(10, sizes[0].width); - EXPECT_EQ(11, sizes[0].height); + EXPECT_EQ(10, sizes[0].width()); + EXPECT_EQ(11, sizes[0].height()); sizes_attribute = "0x33"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); @@ -35,18 +35,18 @@ TEST(WebIconSizesParserTest, parseSizes) { sizes_attribute = AtomicString(attribute); sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(1U, sizes.size()); - EXPECT_EQ(32, sizes[0].width); - EXPECT_EQ(33, sizes[0].height); + EXPECT_EQ(32, sizes[0].width()); + EXPECT_EQ(33, sizes[0].height()); sizes_attribute = " 32x33 16X17 128x129 "; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(3U, sizes.size()); - EXPECT_EQ(32, sizes[0].width); - EXPECT_EQ(33, sizes[0].height); - EXPECT_EQ(16, sizes[1].width); - EXPECT_EQ(17, sizes[1].height); - EXPECT_EQ(128, sizes[2].width); - EXPECT_EQ(129, sizes[2].height); + EXPECT_EQ(32, sizes[0].width()); + EXPECT_EQ(33, sizes[0].height()); + EXPECT_EQ(16, sizes[1].width()); + EXPECT_EQ(17, sizes[1].height()); + EXPECT_EQ(128, sizes[2].width()); + EXPECT_EQ(129, sizes[2].height()); sizes_attribute = " \n 32x33 \r 16X17 \t 128x129 \f "; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); @@ -55,8 +55,8 @@ TEST(WebIconSizesParserTest, parseSizes) { sizes_attribute = "any"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(1U, sizes.size()); - EXPECT_EQ(0, sizes[0].width); - EXPECT_EQ(0, sizes[0].height); + EXPECT_EQ(0, sizes[0].width()); + EXPECT_EQ(0, sizes[0].height()); sizes_attribute = "ANY"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); @@ -69,22 +69,22 @@ TEST(WebIconSizesParserTest, parseSizes) { sizes_attribute = " any"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(1U, sizes.size()); - EXPECT_EQ(0, sizes[0].width); - EXPECT_EQ(0, sizes[0].height); + EXPECT_EQ(0, sizes[0].width()); + EXPECT_EQ(0, sizes[0].height()); sizes_attribute = " any "; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(1U, sizes.size()); - EXPECT_EQ(0, sizes[0].width); - EXPECT_EQ(0, sizes[0].height); + EXPECT_EQ(0, sizes[0].width()); + EXPECT_EQ(0, sizes[0].height()); sizes_attribute = "any 10x10"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(2u, sizes.size()); - EXPECT_EQ(0, sizes[0].width); - EXPECT_EQ(0, sizes[0].height); - EXPECT_EQ(10, sizes[1].width); - EXPECT_EQ(10, sizes[1].height); + EXPECT_EQ(0, sizes[0].width()); + EXPECT_EQ(0, sizes[0].height()); + EXPECT_EQ(10, sizes[1].width()); + EXPECT_EQ(10, sizes[1].height()); sizes_attribute = "an"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); @@ -129,34 +129,34 @@ TEST(WebIconSizesParserTest, parseSizes) { sizes_attribute = "32x33 32"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(1U, sizes.size()); - EXPECT_EQ(32, sizes[0].width); - EXPECT_EQ(33, sizes[0].height); + EXPECT_EQ(32, sizes[0].width()); + EXPECT_EQ(33, sizes[0].height()); sizes_attribute = "32x33 32x"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(1U, sizes.size()); - EXPECT_EQ(32, sizes[0].width); - EXPECT_EQ(33, sizes[0].height); + EXPECT_EQ(32, sizes[0].width()); + EXPECT_EQ(33, sizes[0].height()); sizes_attribute = "32x33 x32"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(1U, sizes.size()); - EXPECT_EQ(32, sizes[0].width); - EXPECT_EQ(33, sizes[0].height); + EXPECT_EQ(32, sizes[0].width()); + EXPECT_EQ(33, sizes[0].height()); sizes_attribute = "32x33 any"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(2U, sizes.size()); - EXPECT_EQ(32, sizes[0].width); - EXPECT_EQ(33, sizes[0].height); - EXPECT_EQ(0, sizes[1].width); - EXPECT_EQ(0, sizes[1].height); + EXPECT_EQ(32, sizes[0].width()); + EXPECT_EQ(33, sizes[0].height()); + EXPECT_EQ(0, sizes[1].width()); + EXPECT_EQ(0, sizes[1].height()); sizes_attribute = "32x33, 64x64"; sizes = WebIconSizesParser::ParseIconSizes(sizes_attribute); ASSERT_EQ(1U, sizes.size()); - EXPECT_EQ(64, sizes[0].width); - EXPECT_EQ(64, sizes[0].height); + EXPECT_EQ(64, sizes[0].width()); + EXPECT_EQ(64, sizes[0].height()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_input_event.cc b/chromium/third_party/blink/renderer/platform/exported/web_input_event.cc deleted file mode 100644 index 85661bc1203..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_input_event.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/public/platform/web_input_event.h" - -#include <ctype.h> -#include "third_party/blink/public/platform/web_gesture_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" -#include "third_party/blink/renderer/platform/keyboard_codes.h" -#include "third_party/blink/renderer/platform/wtf/assertions.h" -#include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h" - -namespace blink { - -struct SameSizeAsWebInputEvent { - int input_data[8]; -}; - -struct SameSizeAsWebKeyboardEvent : public SameSizeAsWebInputEvent { - int keyboard_data[9]; -}; - -struct SameSizeAsWebMouseEvent : public SameSizeAsWebInputEvent { - int mouse_data[17]; -}; - -struct SameSizeAsWebMouseWheelEvent : public SameSizeAsWebMouseEvent { - int mousewheel_data[12]; -}; - -struct SameSizeAsWebGestureEvent : public SameSizeAsWebInputEvent { - int gesture_data[14]; -}; - -struct SameSizeAsWebTouchEvent : public SameSizeAsWebInputEvent { - WebTouchPoint touch_points[WebTouchEvent::kTouchesLengthCap]; - int touch_data[4]; -}; - -static_assert(sizeof(WebInputEvent) == sizeof(SameSizeAsWebInputEvent), - "WebInputEvent should not have gaps"); -static_assert(sizeof(WebKeyboardEvent) == sizeof(SameSizeAsWebKeyboardEvent), - "WebKeyboardEvent should not have gaps"); -static_assert(sizeof(WebMouseEvent) == sizeof(SameSizeAsWebMouseEvent), - "WebMouseEvent should not have gaps"); -static_assert(sizeof(WebMouseWheelEvent) == - sizeof(SameSizeAsWebMouseWheelEvent), - "WebMouseWheelEvent should not have gaps"); -static_assert(sizeof(WebGestureEvent) == sizeof(SameSizeAsWebGestureEvent), - "WebGestureEvent should not have gaps"); -static_assert(sizeof(WebTouchEvent) == sizeof(SameSizeAsWebTouchEvent), - "WebTouchEvent should not have gaps"); - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_isolate.cc b/chromium/third_party/blink/renderer/platform/exported/web_isolate.cc new file mode 100644 index 00000000000..a61fda7ca44 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/exported/web_isolate.cc @@ -0,0 +1,13 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/bindings/blink_isolate/blink_isolate.h" + +namespace blink { + +std::unique_ptr<WebIsolate> WebIsolate::Create() { + return std::unique_ptr<WebIsolate>(new BlinkIsolate()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_stream_audio_sink.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_stream_audio_sink.cc index 7276a8bd885..083bc2b1d8f 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_media_stream_audio_sink.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_media_stream_audio_sink.cc @@ -5,9 +5,9 @@ #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_audio_sink.h" #include "base/logging.h" -#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_track.h" #include "third_party/blink/public/platform/web_media_stream_source.h" #include "third_party/blink/public/platform/web_media_stream_track.h" +#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_stream_source.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_stream_source.cc index 75a6efcc795..27e4fda4194 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_media_stream_source.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_media_stream_source.cc @@ -34,12 +34,12 @@ #include <utility> #include "base/memory/ptr_util.h" -#include "third_party/blink/public/platform/web_audio_destination_consumer.h" -#include "third_party/blink/public/platform/web_media_constraints.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/renderer/platform/audio/audio_bus.h" #include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/mediastream/media_constraints.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h" +#include "third_party/blink/renderer/platform/mediastream/webaudio_destination_consumer.h" #include "third_party/blink/renderer/platform/wtf/vector.h" namespace blink { @@ -182,7 +182,7 @@ void ConsumerWrapper::ConsumeAudio(AudioBus* bus, size_t number_of_frames) { // Wrap AudioBus. size_t number_of_channels = bus->NumberOfChannels(); - WebVector<const float*> bus_vector(number_of_channels); + Vector<const float*> bus_vector(number_of_channels); for (size_t i = 0; i < number_of_channels; ++i) bus_vector[i] = bus->Channel(i)->Data(); diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_stream_track.cc b/chromium/third_party/blink/renderer/platform/exported/web_media_stream_track.cc index 4e51ff18abc..340b06a12a9 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_media_stream_track.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_media_stream_track.cc @@ -29,11 +29,11 @@ #include "base/memory/ptr_util.h" #include "third_party/blink/public/platform/web_audio_source_provider.h" -#include "third_party/blink/public/platform/web_media_constraints.h" #include "third_party/blink/public/platform/web_media_stream.h" #include "third_party/blink/public/platform/web_media_stream_source.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/mediastream/media_constraints.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_component.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h" @@ -84,13 +84,12 @@ WebMediaStreamTrack::ContentHintType WebMediaStreamTrack::ContentHint() const { return private_->ContentHint(); } -WebMediaConstraints WebMediaStreamTrack::Constraints() const { +MediaConstraints WebMediaStreamTrack::Constraints() const { DCHECK(!private_.IsNull()); return private_->Constraints(); } -void WebMediaStreamTrack::SetConstraints( - const WebMediaConstraints& constraints) { +void WebMediaStreamTrack::SetConstraints(const MediaConstraints& constraints) { DCHECK(!private_.IsNull()); return private_->SetConstraints(constraints); } diff --git a/chromium/third_party/blink/renderer/platform/exported/web_mouse_event.cc b/chromium/third_party/blink/renderer/platform/exported/web_mouse_event.cc deleted file mode 100644 index ff5338a2fd4..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_mouse_event.cc +++ /dev/null @@ -1,69 +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 "third_party/blink/public/platform/web_mouse_event.h" - -#include "third_party/blink/public/platform/web_gesture_event.h" - -namespace blink { - -WebMouseEvent::WebMouseEvent(WebInputEvent::Type type, - const WebGestureEvent& gesture_event, - Button button_param, - int click_count_param, - int modifiers, - base::TimeTicks time_stamp, - PointerId id_param) - : WebInputEvent(sizeof(WebMouseEvent), type, modifiers, time_stamp), - WebPointerProperties(id_param, - WebPointerProperties::PointerType::kMouse, - button_param), - click_count(click_count_param) { - DCHECK_GE(type, kMouseTypeFirst); - DCHECK_LE(type, kMouseTypeLast); - SetPositionInWidget(gesture_event.PositionInWidget()); - SetPositionInScreen(gesture_event.PositionInScreen()); - SetFrameScale(gesture_event.FrameScale()); - SetFrameTranslate(gesture_event.FrameTranslate()); - SetMenuSourceType(gesture_event.GetType()); -} - -WebFloatPoint WebMouseEvent::PositionInRootFrame() const { - return WebFloatPoint( - (position_in_widget_.x / frame_scale_) + frame_translate_.x, - (position_in_widget_.y / frame_scale_) + frame_translate_.y); -} - -WebMouseEvent WebMouseEvent::FlattenTransform() const { - WebMouseEvent result = *this; - result.FlattenTransformSelf(); - return result; -} - -void WebMouseEvent::FlattenTransformSelf() { - position_in_widget_ = PositionInRootFrame(); - frame_translate_.x = 0; - frame_translate_.y = 0; - frame_scale_ = 1; -} - -void WebMouseEvent::SetMenuSourceType(WebInputEvent::Type type) { - switch (type) { - case kGestureTapDown: - case kGestureTap: - case kGestureDoubleTap: - menu_source_type = kMenuSourceTouch; - break; - case kGestureLongPress: - menu_source_type = kMenuSourceLongPress; - break; - case kGestureLongTap: - menu_source_type = kMenuSourceLongTap; - break; - default: - menu_source_type = kMenuSourceNone; - } -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_mouse_wheel_event.cc b/chromium/third_party/blink/renderer/platform/exported/web_mouse_wheel_event.cc deleted file mode 100644 index cc02b1e4947..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_mouse_wheel_event.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/public/platform/web_mouse_wheel_event.h" - -namespace blink { - -float WebMouseWheelEvent::DeltaXInRootFrame() const { - return delta_x / frame_scale_; -} - -float WebMouseWheelEvent::DeltaYInRootFrame() const { - return delta_y / frame_scale_; -} - -WebMouseWheelEvent WebMouseWheelEvent::FlattenTransform() const { - WebMouseWheelEvent result = *this; - result.delta_x /= result.frame_scale_; - result.delta_y /= result.frame_scale_; - result.FlattenTransformSelf(); - return result; -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_pointer_event.cc b/chromium/third_party/blink/renderer/platform/exported/web_pointer_event.cc deleted file mode 100644 index 8c710c12480..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_pointer_event.cc +++ /dev/null @@ -1,101 +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 "third_party/blink/public/platform/web_pointer_event.h" - -#include "third_party/blink/public/platform/web_float_point.h" - -namespace blink { - -namespace { - -WebInputEvent::Type PointerEventTypeForTouchPointState( - WebTouchPoint::State state) { - switch (state) { - case WebTouchPoint::kStateReleased: - return WebInputEvent::Type::kPointerUp; - case WebTouchPoint::kStateCancelled: - return WebInputEvent::Type::kPointerCancel; - case WebTouchPoint::kStatePressed: - return WebInputEvent::Type::kPointerDown; - case WebTouchPoint::kStateMoved: - return WebInputEvent::Type::kPointerMove; - case WebTouchPoint::kStateStationary: - default: - NOTREACHED(); - return WebInputEvent::Type::kUndefined; - } -} - -} // namespace - -WebPointerEvent::WebPointerEvent(const WebTouchEvent& touch_event, - const WebTouchPoint& touch_point) - : WebInputEvent(sizeof(WebPointerEvent), - PointerEventTypeForTouchPointState(touch_point.state), - touch_event.GetModifiers(), - touch_event.TimeStamp()), - - WebPointerProperties(touch_point), - hovering(touch_event.hovering), - width(touch_point.radius_x * 2.f), - height(touch_point.radius_y * 2.f) { - // WebInutEvent attributes - SetFrameScale(touch_event.FrameScale()); - SetFrameTranslate(touch_event.FrameTranslate()); - // WebTouchEvent attributes - dispatch_type = touch_event.dispatch_type; - moved_beyond_slop_region = touch_event.moved_beyond_slop_region; - touch_start_or_first_touch_move = touch_event.touch_start_or_first_touch_move; - unique_touch_event_id = touch_event.unique_touch_event_id; - // WebTouchPoint attributes - rotation_angle = touch_point.rotation_angle; - // TODO(crbug.com/816504): Touch point button is not set at this point yet. - button = (GetType() == WebInputEvent::kPointerDown || - GetType() == WebInputEvent::kPointerUp) - ? WebPointerProperties::Button::kLeft - : WebPointerProperties::Button::kNoButton; -} - -WebPointerEvent::WebPointerEvent(WebInputEvent::Type type, - const WebMouseEvent& mouse_event) - : WebInputEvent(sizeof(WebPointerEvent), - type, - mouse_event.GetModifiers(), - mouse_event.TimeStamp()), - WebPointerProperties(mouse_event), - hovering(true), - width(std::numeric_limits<float>::quiet_NaN()), - height(std::numeric_limits<float>::quiet_NaN()) { - DCHECK_GE(type, WebInputEvent::kPointerTypeFirst); - DCHECK_LE(type, WebInputEvent::kPointerTypeLast); - SetFrameScale(mouse_event.FrameScale()); - SetFrameTranslate(mouse_event.FrameTranslate()); -} - -WebPointerEvent WebPointerEvent::CreatePointerCausesUaActionEvent( - WebPointerProperties::PointerType type, - base::TimeTicks time_stamp) { - WebPointerEvent event; - event.pointer_type = type; - event.SetTimeStamp(time_stamp); - event.SetType(WebInputEvent::Type::kPointerCausedUaAction); - return event; -} - -WebPointerEvent WebPointerEvent::WebPointerEventInRootFrame() const { - WebPointerEvent transformed_event = *this; - if (HasWidth()) - transformed_event.width /= frame_scale_; - if (HasHeight()) - transformed_event.height /= frame_scale_; - transformed_event.position_in_widget_ = - WebFloatPoint((transformed_event.PositionInWidget().x / frame_scale_) + - frame_translate_.x, - (transformed_event.PositionInWidget().y / frame_scale_) + - frame_translate_.y); - return transformed_event; -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_prerender.cc b/chromium/third_party/blink/renderer/platform/exported/web_prerender.cc deleted file mode 100644 index 36f7daee909..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_prerender.cc +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/public/platform/web_prerender.h" - -#include <memory> - -#include "base/memory/ptr_util.h" -#include "base/memory/scoped_refptr.h" -#include "third_party/blink/renderer/platform/prerender.h" -#include "third_party/blink/renderer/platform/weborigin/security_origin.h" - -namespace blink { - -namespace { - -class PrerenderExtraDataContainer : public Prerender::ExtraData { - public: - static scoped_refptr<PrerenderExtraDataContainer> Create( - WebPrerender::ExtraData* extra_data) { - return base::AdoptRef(new PrerenderExtraDataContainer(extra_data)); - } - - ~PrerenderExtraDataContainer() override = default; - - WebPrerender::ExtraData* GetExtraData() const { return extra_data_.get(); } - - private: - explicit PrerenderExtraDataContainer(WebPrerender::ExtraData* extra_data) - : extra_data_(base::WrapUnique(extra_data)) {} - - std::unique_ptr<WebPrerender::ExtraData> extra_data_; -}; - -} // namespace - -WebPrerender::WebPrerender(Prerender* prerender) : private_(prerender) {} - -const Prerender* WebPrerender::ToPrerender() const { - return private_.Get(); -} - -void WebPrerender::Reset() { - private_.Reset(); -} - -void WebPrerender::Assign(const WebPrerender& other) { - private_ = other.private_; -} - -bool WebPrerender::IsNull() const { - return private_.IsNull(); -} - -WebURL WebPrerender::Url() const { - return WebURL(private_->Url()); -} - -unsigned WebPrerender::RelTypes() const { - return private_->RelTypes(); -} - -WebString WebPrerender::GetReferrer() const { - return private_->GetReferrer(); -} - -url::Origin WebPrerender::SecurityOrigin() const { - auto* security_origin = private_->GetSecurityOrigin(); - return security_origin ? security_origin->ToUrlOrigin() : url::Origin(); -} - -network::mojom::ReferrerPolicy WebPrerender::GetReferrerPolicy() const { - return private_->GetReferrerPolicy(); -} - -void WebPrerender::SetExtraData(WebPrerender::ExtraData* extra_data) { - private_->SetExtraData(PrerenderExtraDataContainer::Create(extra_data)); -} - -const WebPrerender::ExtraData* WebPrerender::GetExtraData() const { - scoped_refptr<Prerender::ExtraData> webcore_extra_data = - private_->GetExtraData(); - if (!webcore_extra_data) - return nullptr; - return static_cast<PrerenderExtraDataContainer*>(webcore_extra_data.get()) - ->GetExtraData(); -} - -void WebPrerender::DidStartPrerender() { - private_->DidStartPrerender(); -} - -void WebPrerender::DidStopPrerender() { - private_->DidStopPrerender(); -} - -void WebPrerender::DidSendLoadForPrerender() { - private_->DidSendLoadForPrerender(); -} - -void WebPrerender::DidSendDOMContentLoadedForPrerender() { - private_->DidSendDOMContentLoadedForPrerender(); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_prerendering_support.cc b/chromium/third_party/blink/renderer/platform/exported/web_prerendering_support.cc deleted file mode 100644 index edc12ec2d3c..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_prerendering_support.cc +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/public/platform/web_prerendering_support.h" - -namespace blink { - -WebPrerenderingSupport* WebPrerenderingSupport::platform_ = nullptr; - -// static -void WebPrerenderingSupport::Initialize(WebPrerenderingSupport* platform) { - platform_ = platform; -} - -// static -void WebPrerenderingSupport::Shutdown() { - platform_ = nullptr; -} - -// static -WebPrerenderingSupport* WebPrerenderingSupport::Current() { - return platform_; -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_resource_timing_info.cc b/chromium/third_party/blink/renderer/platform/exported/web_resource_timing_info.cc deleted file mode 100644 index ac11337e73a..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_resource_timing_info.cc +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/public/platform/web_resource_timing_info.h" - -#include "third_party/blink/public/platform/web_url_load_timing.h" -#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" - -namespace blink { - -namespace { - -bool IsSameServerTimingInfo(const WebVector<WebServerTimingInfo>& lhs, - const WebVector<WebServerTimingInfo>& rhs) { - if (lhs.size() != rhs.size()) - return false; - for (size_t i = 0; i < lhs.size(); ++i) { - if (lhs[i] != rhs[i]) - return false; - } - return true; -} - -} // namespace - -bool WebServerTimingInfo::operator==(const WebServerTimingInfo& other) const { - return name == other.name && duration == other.duration && - description == other.description; -} - -bool WebServerTimingInfo::operator!=(const WebServerTimingInfo& other) const { - return !(*this == other); -} - -bool WebResourceTimingInfo::operator==( - const WebResourceTimingInfo& other) const { - return name == other.name && start_time == other.start_time && - alpn_negotiated_protocol == other.alpn_negotiated_protocol && - connection_info == other.connection_info && timing == other.timing && - last_redirect_end_time == other.last_redirect_end_time && - response_end == other.response_end && - transfer_size == other.transfer_size && - encoded_body_size == other.encoded_body_size && - decoded_body_size == other.decoded_body_size && - context_type == other.context_type && - did_reuse_connection == other.did_reuse_connection && - is_secure_context == other.is_secure_context && - allow_timing_details == other.allow_timing_details && - allow_redirect_details == other.allow_redirect_details && - allow_negative_values == other.allow_negative_values && - IsSameServerTimingInfo(server_timing, other.server_timing); -} - -} // namespace blink - -namespace WTF { -#if INSIDE_BLINK -CrossThreadCopier<blink::WebResourceTimingInfo>::Type -CrossThreadCopier<blink::WebResourceTimingInfo>::Copy( - const blink::WebResourceTimingInfo& info) { - blink::WebResourceTimingInfo copy; - - copy.name = String(info.name).IsolatedCopy(); - copy.start_time = info.start_time; - - copy.alpn_negotiated_protocol = - String(info.alpn_negotiated_protocol).IsolatedCopy(); - copy.connection_info = String(info.connection_info).IsolatedCopy(); - - if (!info.timing.IsNull()) - copy.timing = CrossThreadCopier<blink::WebURLLoadTiming>::Copy(info.timing); - - copy.last_redirect_end_time = info.last_redirect_end_time; - copy.response_end = info.response_end; - - copy.transfer_size = info.transfer_size; - copy.encoded_body_size = info.encoded_body_size; - copy.decoded_body_size = info.decoded_body_size; - copy.context_type = info.context_type; - - copy.did_reuse_connection = info.did_reuse_connection; - copy.is_secure_context = info.is_secure_context; - - copy.allow_timing_details = info.allow_timing_details; - copy.allow_redirect_details = info.allow_redirect_details; - - copy.allow_negative_values = info.allow_negative_values; - for (auto& entry : info.server_timing) { - blink::WebServerTimingInfo entry_copy( - String(entry.name).IsolatedCopy(), entry.duration, - String(entry.description).IsolatedCopy()); - copy.server_timing.emplace_back(std::move(entry_copy)); - } - - return copy; -} -#endif -} // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/exported/web_resource_timing_info_test.cc b/chromium/third_party/blink/renderer/platform/exported/web_resource_timing_info_test.cc deleted file mode 100644 index 1caab47f81c..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_resource_timing_info_test.cc +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/public/platform/web_resource_timing_info.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h" -#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread.h" -#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h" -#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" - -namespace blink { - -namespace { - -scoped_refptr<ResourceLoadTiming> CreateResourceLoadTiming( - const base::TimeTicks pseudo_time) { - auto timing = ResourceLoadTiming::Create(); - timing->SetRequestTime(pseudo_time); - timing->SetProxyStart(pseudo_time); - timing->SetProxyEnd(pseudo_time); - timing->SetDnsStart(pseudo_time); - timing->SetDnsEnd(pseudo_time); - timing->SetConnectStart(pseudo_time); - timing->SetConnectEnd(pseudo_time); - timing->SetWorkerStart(pseudo_time); - timing->SetWorkerReady(pseudo_time); - timing->SetSendStart(pseudo_time); - timing->SetSendEnd(pseudo_time); - timing->SetReceiveHeadersStart(pseudo_time); - timing->SetReceiveHeadersEnd(pseudo_time); - timing->SetSslStart(pseudo_time); - timing->SetSslStart(pseudo_time); - timing->SetSslEnd(pseudo_time); - timing->SetPushEnd(pseudo_time); - return timing; -} - -WebResourceTimingInfo CreateWebResourceTimingInfo( - const base::TimeTicks pseudo_time) { - WebVector<WebServerTimingInfo> server_timing; - server_timing.emplace_back(WebServerTimingInfo("server_timing_name", 1.0, - "server_timing_description")); - - WebResourceTimingInfo info = { - .name = "name", - .start_time = pseudo_time, - - .alpn_negotiated_protocol = "protocol", - .connection_info = "info", - - .timing = CreateResourceLoadTiming(pseudo_time), - .last_redirect_end_time = pseudo_time, - .response_end = pseudo_time, - - .transfer_size = 1, - .encoded_body_size = 2, - .decoded_body_size = 3, - - .did_reuse_connection = true, - .is_secure_context = true, - - .allow_timing_details = true, - .allow_redirect_details = true, - - .allow_negative_values = true, - - .server_timing = server_timing, - }; - return info; -} - -void CheckWebResourceTimingInfoOnThread(const WebResourceTimingInfo& info, - const base::TimeTicks pseudo_time) { - WebResourceTimingInfo expected = CreateWebResourceTimingInfo(pseudo_time); - EXPECT_EQ(expected, info); - - EXPECT_EQ("name", info.name); - EXPECT_EQ(pseudo_time, info.start_time); - - EXPECT_EQ("protocol", info.alpn_negotiated_protocol); - EXPECT_EQ("info", info.connection_info); - - WebURLLoadTiming expected_timing(CreateResourceLoadTiming(pseudo_time)); - EXPECT_EQ(expected_timing, info.timing); - EXPECT_EQ(pseudo_time, info.last_redirect_end_time); - EXPECT_EQ(pseudo_time, info.response_end); - - EXPECT_EQ(1u, info.transfer_size); - EXPECT_EQ(2u, info.encoded_body_size); - EXPECT_EQ(3u, info.decoded_body_size); - - EXPECT_TRUE(info.did_reuse_connection); - EXPECT_TRUE(info.is_secure_context); - - EXPECT_TRUE(info.allow_timing_details); - EXPECT_TRUE(info.allow_redirect_details); - - EXPECT_TRUE(info.allow_negative_values); - - EXPECT_EQ(1u, info.server_timing.size()); - - auto& entry = info.server_timing[0]; - EXPECT_EQ("server_timing_name", entry.name); - EXPECT_EQ(1.0, entry.duration); - EXPECT_EQ("server_timing_description", entry.description); -} - -} // namespace - -TEST(WebResourceTimingInfoTest, CrossThreadCopy) { - ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> - platform; - - base::TimeTicks pseudo_time = base::TimeTicks::Now(); - WebResourceTimingInfo info = CreateWebResourceTimingInfo(pseudo_time); - - std::unique_ptr<Thread> thread = Platform::Current()->CreateThread( - ThreadCreationParams(ThreadType::kTestThread) - .SetThreadNameForTest("TestThread")); - PostCrossThreadTask(*thread->GetTaskRunner(), FROM_HERE, - CrossThreadBindOnce(&CheckWebResourceTimingInfoOnThread, - info, pseudo_time)); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_peer_connection_handler_client.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_peer_connection_handler_client.cc deleted file mode 100644 index 9b3be0525c0..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_rtc_peer_connection_handler_client.cc +++ /dev/null @@ -1,14 +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 "third_party/blink/public/platform/web_rtc_peer_connection_handler_client.h" - -namespace blink { - -WebRTCPeerConnectionHandlerClient::~WebRTCPeerConnectionHandlerClient() = - default; - -void WebRTCPeerConnectionHandlerClient::ClosePeerConnection() {} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats.cc b/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats.cc deleted file mode 100644 index 52175634f23..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_rtc_stats.cc +++ /dev/null @@ -1,9 +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 "third_party/blink/public/platform/web_rtc_stats.h" - -namespace blink { - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/chromium/third_party/blink/renderer/platform/exported/web_runtime_features.cc index a9da7db2638..8eae6d43357 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_runtime_features.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_runtime_features.cc @@ -40,6 +40,16 @@ void WebRuntimeFeatures::EnableBlockingFocusWithoutUserActivation(bool enable) { RuntimeEnabledFeatures::SetBlockingFocusWithoutUserActivationEnabled(enable); } +void WebRuntimeFeatures::EnableBrowserVerifiedUserActivationKeyboard( + bool enable) { + RuntimeEnabledFeatures::SetBrowserVerifiedUserActivationKeyboardEnabled( + enable); +} + +void WebRuntimeFeatures::EnableBrowserVerifiedUserActivationMouse(bool enable) { + RuntimeEnabledFeatures::SetBrowserVerifiedUserActivationMouseEnabled(enable); +} + void WebRuntimeFeatures::EnableExperimentalFeatures(bool enable) { RuntimeEnabledFeatures::SetExperimentalFeaturesEnabled(enable); } @@ -105,6 +115,10 @@ void WebRuntimeFeatures::EnableAccessibilityExposeDisplayNone(bool enable) { RuntimeEnabledFeatures::SetAccessibilityExposeDisplayNoneEnabled(enable); } +void WebRuntimeFeatures::EnableAccessibilityExposeHTMLElement(bool enable) { + RuntimeEnabledFeatures::SetAccessibilityExposeHTMLElementEnabled(enable); +} + void WebRuntimeFeatures::EnableAccessibilityObjectModel(bool enable) { RuntimeEnabledFeatures::SetAccessibilityObjectModelEnabled(enable); } @@ -141,10 +155,6 @@ void WebRuntimeFeatures::EnableCookiesWithoutSameSiteMustBeSecure(bool enable) { RuntimeEnabledFeatures::SetCookiesWithoutSameSiteMustBeSecureEnabled(enable); } -void WebRuntimeFeatures::EnableWasmCodeCache(bool enable) { - RuntimeEnabledFeatures::SetWasmCodeCacheEnabled(enable); -} - void WebRuntimeFeatures::EnableCanvas2dImageChromium(bool enable) { RuntimeEnabledFeatures::SetCanvas2dImageChromiumEnabled(enable); } @@ -209,10 +219,6 @@ void WebRuntimeFeatures::EnableForceTallerSelectPopup(bool enable) { RuntimeEnabledFeatures::SetForceTallerSelectPopupEnabled(enable); } -void WebRuntimeFeatures::EnableFormControlsRefresh(bool enable) { - RuntimeEnabledFeatures::SetFormControlsRefreshEnabled(enable); -} - void WebRuntimeFeatures::EnableGenericSensorExtraClasses(bool enable) { RuntimeEnabledFeatures::SetSensorExtraClassesEnabled(enable); } @@ -229,18 +235,6 @@ void WebRuntimeFeatures::EnableInputMultipleFieldsUI(bool enable) { RuntimeEnabledFeatures::SetInputMultipleFieldsUIEnabled(enable); } -void WebRuntimeFeatures::EnableBuiltInModuleAll(bool enable) { - RuntimeEnabledFeatures::SetBuiltInModuleAllEnabled(enable); -} - -void WebRuntimeFeatures::EnableBuiltInModuleInfra(bool enable) { - RuntimeEnabledFeatures::SetBuiltInModuleInfraEnabled(enable); -} - -void WebRuntimeFeatures::EnableBuiltInModuleKvStorage(bool enable) { - RuntimeEnabledFeatures::SetBuiltInModuleKvStorageEnabled(enable); -} - void WebRuntimeFeatures::EnableLayoutNG(bool enable) { RuntimeEnabledFeatures::SetLayoutNGEnabled(enable); } @@ -329,15 +323,24 @@ void WebRuntimeFeatures::EnablePaymentApp(bool enable) { RuntimeEnabledFeatures::SetPaymentAppEnabled(enable); } +void WebRuntimeFeatures::EnablePaymentHandlerMinimalUI(bool enable) { + RuntimeEnabledFeatures::SetPaymentHandlerMinimalUIEnabled(enable); +} + void WebRuntimeFeatures::EnablePaymentRequest(bool enable) { RuntimeEnabledFeatures::SetPaymentRequestEnabled(enable); if (!enable) { // Disable features that depend on Payment Request API. - RuntimeEnabledFeatures::SetPaymentMethodChangeEventEnabled(false); RuntimeEnabledFeatures::SetPaymentAppEnabled(false); + RuntimeEnabledFeatures::SetPaymentHandlerMinimalUIEnabled(false); + RuntimeEnabledFeatures::SetPaymentMethodChangeEventEnabled(false); } } +void WebRuntimeFeatures::EnablePercentBasedScrolling(bool enable) { + RuntimeEnabledFeatures::SetPercentBasedScrollingEnabled(enable); +} + void WebRuntimeFeatures::EnablePerformanceManagerInstrumentation(bool enable) { RuntimeEnabledFeatures::SetPerformanceManagerInstrumentationEnabled(enable); } @@ -374,10 +377,6 @@ void WebRuntimeFeatures::EnableScriptedSpeechSynthesis(bool enable) { RuntimeEnabledFeatures::SetScriptedSpeechSynthesisEnabled(enable); } -void WebRuntimeFeatures::EnableUpdateHoverAtBeginFrame(bool enable) { - RuntimeEnabledFeatures::SetUpdateHoverAtBeginFrameEnabled(enable); -} - void WebRuntimeFeatures::EnableUserActivationPostMessageTransfer(bool enable) { RuntimeEnabledFeatures::SetUserActivationPostMessageTransferEnabled(enable); } @@ -430,10 +429,6 @@ void WebRuntimeFeatures::EnablePreciseMemoryInfo(bool enable) { RuntimeEnabledFeatures::SetPreciseMemoryInfoEnabled(enable); } -void WebRuntimeFeatures::EnablePrintBrowser(bool enable) { - RuntimeEnabledFeatures::SetPrintBrowserEnabled(enable); -} - void WebRuntimeFeatures::EnableV8IdleTasks(bool enable) { RuntimeEnabledFeatures::SetV8IdleTasksEnabled(enable); } @@ -466,24 +461,12 @@ void WebRuntimeFeatures::EnableWebXRARModule(bool enable) { RuntimeEnabledFeatures::SetWebXRARModuleEnabled(enable); } -void WebRuntimeFeatures::EnableWebXRARDOMOverlay(bool enable) { - RuntimeEnabledFeatures::SetWebXRARDOMOverlayEnabled(enable); -} - -void WebRuntimeFeatures::EnableWebXRAnchors(bool enable) { - RuntimeEnabledFeatures::SetWebXRAnchorsEnabled(enable); -} - -void WebRuntimeFeatures::EnableWebXrGamepadModule(bool enable) { - RuntimeEnabledFeatures::SetWebXrGamepadModuleEnabled(enable); -} - void WebRuntimeFeatures::EnableWebXRHitTest(bool enable) { RuntimeEnabledFeatures::SetWebXRHitTestEnabled(enable); } -void WebRuntimeFeatures::EnableWebXRPlaneDetection(bool enable) { - RuntimeEnabledFeatures::SetWebXRPlaneDetectionEnabled(enable); +void WebRuntimeFeatures::EnableWebXRIncubations(bool enable) { + RuntimeEnabledFeatures::SetWebXRIncubationsEnabled(enable); } void WebRuntimeFeatures::EnablePresentationAPI(bool enable) { @@ -514,14 +497,6 @@ void WebRuntimeFeatures::EnableExpensiveBackgroundTimerThrottling(bool enable) { RuntimeEnabledFeatures::SetExpensiveBackgroundTimerThrottlingEnabled(enable); } -void WebRuntimeFeatures::EnableFetchMetadata(bool enable) { - RuntimeEnabledFeatures::SetFetchMetadataEnabled(enable); -} - -void WebRuntimeFeatures::EnableFetchMetadataDestination(bool enable) { - RuntimeEnabledFeatures::SetFetchMetadataDestinationEnabled(enable); -} - void WebRuntimeFeatures::EnableTimerThrottlingForBackgroundTabs(bool enable) { RuntimeEnabledFeatures::SetTimerThrottlingForBackgroundTabsEnabled(enable); } @@ -552,10 +527,6 @@ void WebRuntimeFeatures::EnableVideoRotateToFullscreen(bool enable) { RuntimeEnabledFeatures::SetVideoRotateToFullscreenEnabled(enable); } -void WebRuntimeFeatures::EnableVideoFullscreenDetection(bool enable) { - RuntimeEnabledFeatures::SetVideoFullscreenDetectionEnabled(enable); -} - void WebRuntimeFeatures::EnableVideoPlaybackQuality(bool enable) { RuntimeEnabledFeatures::SetVideoPlaybackQualityEnabled(enable); } @@ -668,10 +639,6 @@ void WebRuntimeFeatures::EnableSmsReceiver(bool enable) { RuntimeEnabledFeatures::SetSmsReceiverEnabled(enable); } -void WebRuntimeFeatures::EnableDisplayLocking(bool enable) { - RuntimeEnabledFeatures::SetDisplayLockingEnabled(enable); -} - void WebRuntimeFeatures::EnableConsolidatedMovementXY(bool enable) { RuntimeEnabledFeatures::SetConsolidatedMovementXYEnabled(enable); } @@ -696,4 +663,12 @@ void WebRuntimeFeatures::EnableAcceleratedSmallCanvases(bool enable) { RuntimeEnabledFeatures::SetAcceleratedSmallCanvasesEnabled(enable); } +void WebRuntimeFeatures::EnableTrustTokens(bool enable) { + RuntimeEnabledFeatures::SetTrustTokensEnabled(enable); +} + +void WebRuntimeFeatures::EnableInstalledApp(bool enable) { + RuntimeEnabledFeatures::SetInstalledAppEnabled(enable); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_touch_event.cc b/chromium/third_party/blink/renderer/platform/exported/web_touch_event.cc deleted file mode 100644 index e358ccb9d5f..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_touch_event.cc +++ /dev/null @@ -1,37 +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 "third_party/blink/public/platform/web_touch_event.h" - -namespace blink { - -WebTouchEvent WebTouchEvent::FlattenTransform() const { - WebTouchEvent transformed_event = *this; - for (unsigned i = 0; i < touches_length; ++i) { - transformed_event.touches[i] = TouchPointInRootFrame(i); - } - transformed_event.frame_translate_.x = 0; - transformed_event.frame_translate_.y = 0; - transformed_event.frame_scale_ = 1; - - return transformed_event; -} - -WebTouchPoint WebTouchEvent::TouchPointInRootFrame(unsigned point) const { - DCHECK_LT(point, touches_length); - if (point >= touches_length) - return WebTouchPoint(); - - WebTouchPoint transformed_point = touches[point]; - transformed_point.radius_x /= frame_scale_; - transformed_point.radius_y /= frame_scale_; - transformed_point.SetPositionInWidget( - (transformed_point.PositionInWidget().x / frame_scale_) + - frame_translate_.x, - (transformed_point.PositionInWidget().y / frame_scale_) + - frame_translate_.y); - return transformed_point; -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_error.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_error.cc index 9bc40c70762..c06728263cb 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_url_error.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_url_error.cc @@ -15,11 +15,13 @@ WebURLError::WebURLError(int reason, const WebURL& url) WebURLError::WebURLError(int reason, int extended_reason, + net::ResolveErrorInfo resolve_error_info, HasCopyInCache has_copy_in_cache, IsWebSecurityViolation is_web_security_violation, const WebURL& url) : reason_(reason), extended_reason_(extended_reason), + resolve_error_info_(resolve_error_info), has_copy_in_cache_(has_copy_in_cache == HasCopyInCache::kTrue), is_web_security_violation_(is_web_security_violation == IsWebSecurityViolation::kTrue), @@ -27,6 +29,18 @@ WebURLError::WebURLError(int reason, DCHECK_NE(reason_, 0); } +WebURLError::WebURLError(network::BlockedByResponseReason blocked_reason, + net::ResolveErrorInfo resolve_error_info, + HasCopyInCache has_copy_in_cache, + const WebURL& url) + : reason_(net::ERR_BLOCKED_BY_RESPONSE), + extended_reason_(0), + resolve_error_info_(resolve_error_info), + has_copy_in_cache_(has_copy_in_cache == HasCopyInCache::kTrue), + is_web_security_violation_(false), + url_(url), + blocked_by_response_reason_(blocked_reason) {} + WebURLError::WebURLError(const network::CorsErrorStatus& cors_error_status, HasCopyInCache has_copy_in_cache, const WebURL& url) diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_load_timing.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_load_timing.cc deleted file mode 100644 index 861cbdc1f72..00000000000 --- a/chromium/third_party/blink/renderer/platform/exported/web_url_load_timing.cc +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/public/platform/web_url_load_timing.h" - -#include "third_party/blink/public/platform/web_string.h" -#include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h" - -namespace blink { - -void WebURLLoadTiming::Initialize() { - private_ = ResourceLoadTiming::Create(); -} - -void WebURLLoadTiming::Reset() { - private_.Reset(); -} - -void WebURLLoadTiming::Assign(const WebURLLoadTiming& other) { - private_ = other.private_; -} - -base::TimeTicks WebURLLoadTiming::RequestTime() const { - return private_->RequestTime(); -} - -void WebURLLoadTiming::SetRequestTime(base::TimeTicks time) { - private_->SetRequestTime(time); -} - -base::TimeTicks WebURLLoadTiming::ProxyStart() const { - return private_->ProxyStart(); -} - -void WebURLLoadTiming::SetProxyStart(base::TimeTicks start) { - private_->SetProxyStart(start); -} - -base::TimeTicks WebURLLoadTiming::ProxyEnd() const { - return private_->ProxyEnd(); -} - -void WebURLLoadTiming::SetProxyEnd(base::TimeTicks end) { - private_->SetProxyEnd(end); -} - -base::TimeTicks WebURLLoadTiming::DnsStart() const { - return private_->DnsStart(); -} - -void WebURLLoadTiming::SetDNSStart(base::TimeTicks start) { - private_->SetDnsStart(start); -} - -base::TimeTicks WebURLLoadTiming::DnsEnd() const { - return private_->DnsEnd(); -} - -void WebURLLoadTiming::SetDNSEnd(base::TimeTicks end) { - private_->SetDnsEnd(end); -} - -base::TimeTicks WebURLLoadTiming::ConnectStart() const { - return private_->ConnectStart(); -} - -void WebURLLoadTiming::SetConnectStart(base::TimeTicks start) { - private_->SetConnectStart(start); -} - -base::TimeTicks WebURLLoadTiming::ConnectEnd() const { - return private_->ConnectEnd(); -} - -void WebURLLoadTiming::SetConnectEnd(base::TimeTicks end) { - private_->SetConnectEnd(end); -} - -base::TimeTicks WebURLLoadTiming::WorkerStart() const { - return private_->WorkerStart(); -} - -void WebURLLoadTiming::SetWorkerStart(base::TimeTicks start) { - private_->SetWorkerStart(start); -} - -base::TimeTicks WebURLLoadTiming::WorkerReady() const { - return private_->WorkerReady(); -} - -void WebURLLoadTiming::SetWorkerReady(base::TimeTicks ready) { - private_->SetWorkerReady(ready); -} - -base::TimeTicks WebURLLoadTiming::SendStart() const { - return private_->SendStart(); -} - -void WebURLLoadTiming::SetSendStart(base::TimeTicks start) { - private_->SetSendStart(start); -} - -base::TimeTicks WebURLLoadTiming::SendEnd() const { - return private_->SendEnd(); -} - -void WebURLLoadTiming::SetSendEnd(base::TimeTicks end) { - private_->SetSendEnd(end); -} - -base::TimeTicks WebURLLoadTiming::ReceiveHeadersStart() const { - return private_->ReceiveHeadersStart(); -} - -void WebURLLoadTiming::SetReceiveHeadersStart(base::TimeTicks start) { - private_->SetReceiveHeadersStart(start); -} - -base::TimeTicks WebURLLoadTiming::ReceiveHeadersEnd() const { - return private_->ReceiveHeadersEnd(); -} - -void WebURLLoadTiming::SetReceiveHeadersEnd(base::TimeTicks end) { - private_->SetReceiveHeadersEnd(end); -} - -base::TimeTicks WebURLLoadTiming::SslStart() const { - return private_->SslStart(); -} - -void WebURLLoadTiming::SetSSLStart(base::TimeTicks start) { - private_->SetSslStart(start); -} - -base::TimeTicks WebURLLoadTiming::SslEnd() const { - return private_->SslEnd(); -} - -void WebURLLoadTiming::SetSSLEnd(base::TimeTicks end) { - private_->SetSslEnd(end); -} - -base::TimeTicks WebURLLoadTiming::PushStart() const { - return private_->PushStart(); -} - -void WebURLLoadTiming::SetPushStart(base::TimeTicks start) { - private_->SetPushStart(start); -} - -base::TimeTicks WebURLLoadTiming::PushEnd() const { - return private_->PushEnd(); -} - -void WebURLLoadTiming::SetPushEnd(base::TimeTicks end) { - private_->SetPushEnd(end); -} - -WebURLLoadTiming::WebURLLoadTiming(scoped_refptr<ResourceLoadTiming> value) - : private_(std::move(value)) {} - -WebURLLoadTiming& WebURLLoadTiming::operator=( - scoped_refptr<ResourceLoadTiming> value) { - private_ = std::move(value); - return *this; -} - -WebURLLoadTiming::operator scoped_refptr<ResourceLoadTiming>() const { - return private_.Get(); -} - -WebURLLoadTiming WebURLLoadTiming::DeepCopy() const { - return private_->DeepCopy(); -} - -bool WebURLLoadTiming::operator==(const WebURLLoadTiming& other) const { - return *private_ == *other.private_; -} - -} // namespace blink - -namespace WTF { - -CrossThreadCopier<blink::WebURLLoadTiming>::Type CrossThreadCopier< - blink::WebURLLoadTiming>::Copy(const blink::WebURLLoadTiming& timing) { - return timing.DeepCopy(); -} - -} // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_request.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_request.cc index 50ebdbbad7c..651e79a8859 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_url_request.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_url_request.cc @@ -35,12 +35,15 @@ #include "base/time/time.h" #include "net/base/load_flags.h" #include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/optional_trust_token_params.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" +#include "third_party/blink/public/platform/url_conversion.h" #include "third_party/blink/public/platform/web_http_body.h" #include "third_party/blink/public/platform/web_http_header_visitor.h" #include "third_party/blink/public/platform/web_security_origin.h" #include "third_party/blink/public/platform/web_url.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h" +#include "third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h" #include "third_party/blink/renderer/platform/network/encoded_form_data.h" #include "third_party/blink/renderer/platform/weborigin/referrer.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" @@ -50,43 +53,74 @@ using blink::mojom::FetchCacheMode; namespace blink { -WebURLRequest::ExtraData::ExtraData() : render_frame_id_(MSG_ROUTING_NONE) {} +// This is complementary to ConvertNetPriorityToWebKitPriority, defined in +// service_worker_context_client.cc. +net::RequestPriority ConvertWebKitPriorityToNetPriority( + WebURLRequest::Priority priority) { + switch (priority) { + case WebURLRequest::Priority::kVeryHigh: + return net::HIGHEST; + + case WebURLRequest::Priority::kHigh: + return net::MEDIUM; + + case WebURLRequest::Priority::kMedium: + return net::LOW; -// The purpose of this struct is to permit allocating a ResourceRequest on the -// heap, which is otherwise disallowed by DISALLOW_NEW annotation on -// ResourceRequest. -// TODO(keishi): Replace with GCWrapper<ResourceRequest> -struct WebURLRequest::ResourceRequestContainer { - ResourceRequestContainer() = default; - explicit ResourceRequestContainer(const ResourceRequest& r) - : resource_request(r) {} + case WebURLRequest::Priority::kLow: + return net::LOWEST; - ResourceRequest resource_request; -}; + case WebURLRequest::Priority::kVeryLow: + return net::IDLE; + + case WebURLRequest::Priority::kUnresolved: + default: + NOTREACHED(); + return net::LOW; + } +} + +WebURLRequest::ExtraData::ExtraData() : render_frame_id_(MSG_ROUTING_NONE) {} WebURLRequest::~WebURLRequest() = default; WebURLRequest::WebURLRequest() - : owned_resource_request_(new ResourceRequestContainer()), - resource_request_(&owned_resource_request_->resource_request) {} + : owned_resource_request_(std::make_unique<ResourceRequest>()), + resource_request_(owned_resource_request_.get()) {} + +WebURLRequest::WebURLRequest(WebURLRequest&& src) { + *this = std::move(src); +} -WebURLRequest::WebURLRequest(const WebURLRequest& r) - : owned_resource_request_( - new ResourceRequestContainer(*r.resource_request_)), - resource_request_(&owned_resource_request_->resource_request) {} +WebURLRequest& WebURLRequest::operator=(WebURLRequest&& src) { + if (this == &src) { + return *this; + } + if (src.owned_resource_request_) { + owned_resource_request_ = std::move(src.owned_resource_request_); + resource_request_ = owned_resource_request_.get(); + } else { + owned_resource_request_ = std::make_unique<ResourceRequest>(); + resource_request_ = owned_resource_request_.get(); + CopyFrom(src); + } + src.resource_request_ = nullptr; + return *this; +} WebURLRequest::WebURLRequest(const WebURL& url) : WebURLRequest() { SetUrl(url); } -WebURLRequest& WebURLRequest::operator=(const WebURLRequest& r) { +void WebURLRequest::CopyFrom(const WebURLRequest& r) { // Copying subclasses that have different m_resourceRequest ownership // semantics via this operator is just not supported. DCHECK(owned_resource_request_); - DCHECK(resource_request_); - if (&r != this) - *resource_request_ = *r.resource_request_; - return *this; + DCHECK_EQ(owned_resource_request_.get(), resource_request_); + DCHECK(owned_resource_request_->IsNull()); + DCHECK(this != &r); + resource_request_->CopyHeadFrom(*r.resource_request_); + resource_request_->SetHttpBody(r.resource_request_->HttpBody()); } bool WebURLRequest::IsNull() const { @@ -101,11 +135,12 @@ void WebURLRequest::SetUrl(const WebURL& url) { resource_request_->SetUrl(url); } -WebURL WebURLRequest::SiteForCookies() const { +const net::SiteForCookies& WebURLRequest::SiteForCookies() const { return resource_request_->SiteForCookies(); } -void WebURLRequest::SetSiteForCookies(const WebURL& site_for_cookies) { +void WebURLRequest::SetSiteForCookies( + const net::SiteForCookies& site_for_cookies) { resource_request_->SetSiteForCookies(site_for_cookies); } @@ -166,24 +201,10 @@ WebString WebURLRequest::HttpHeaderField(const WebString& name) const { void WebURLRequest::SetHttpHeaderField(const WebString& name, const WebString& value) { - CHECK(!DeprecatedEqualIgnoringCase(name, "referer")); + CHECK(!EqualIgnoringASCIICase(name, "referer")); resource_request_->SetHttpHeaderField(name, value); } -void WebURLRequest::SetHttpReferrer( - const WebString& web_referrer, - network::mojom::ReferrerPolicy referrer_policy) { - // WebString doesn't have the distinction between empty and null. We use - // the null WTFString for referrer. - DCHECK_EQ(Referrer::NoReferrer(), String()); - String referrer = - web_referrer.IsEmpty() ? Referrer::NoReferrer() : String(web_referrer); - // TODO(domfarolino): Stop storing ResourceRequest's generated referrer as a - // header and instead use a separate member. See https://crbug.com/850813. - resource_request_->SetHttpReferrer(Referrer(referrer, referrer_policy)); - resource_request_->SetReferrerString(referrer); -} - void WebURLRequest::AddHttpHeaderField(const WebString& name, const WebString& value) { resource_request_->AddHttpHeaderField(name, value); @@ -227,6 +248,24 @@ mojom::RequestContextType WebURLRequest::GetRequestContext() const { return resource_request_->GetRequestContext(); } +network::mojom::RequestDestination WebURLRequest::GetRequestDestination() + const { + return resource_request_->GetRequestDestination(); +} + +void WebURLRequest::SetReferrerString(const WebString& referrer) { + resource_request_->SetReferrerString(referrer); +} + +void WebURLRequest::SetReferrerPolicy( + network::mojom::ReferrerPolicy referrer_policy) { + resource_request_->SetReferrerPolicy(referrer_policy); +} + +WebString WebURLRequest::ReferrerString() const { + return resource_request_->ReferrerString(); +} + network::mojom::ReferrerPolicy WebURLRequest::GetReferrerPolicy() const { return resource_request_->GetReferrerPolicy(); } @@ -248,6 +287,11 @@ void WebURLRequest::SetRequestContext( resource_request_->SetRequestContext(request_context); } +void WebURLRequest::SetRequestDestination( + network::mojom::RequestDestination destination) { + resource_request_->SetRequestDestination(destination); +} + int WebURLRequest::RequestorID() const { return resource_request_->RequestorID(); } @@ -333,11 +377,12 @@ void WebURLRequest::SetPreviewsState( return resource_request_->SetPreviewsState(previews_state); } -WebURLRequest::ExtraData* WebURLRequest::GetExtraData() const { +const scoped_refptr<WebURLRequest::ExtraData>& WebURLRequest::GetExtraData() + const { return resource_request_->GetExtraData(); } -void WebURLRequest::SetExtraData(std::unique_ptr<ExtraData> extra_data) { +void WebURLRequest::SetExtraData(scoped_refptr<ExtraData> extra_data) { resource_request_->SetExtraData(std::move(extra_data)); } @@ -504,6 +549,10 @@ base::Optional<base::UnguessableToken> WebURLRequest::RecursivePrefetchToken() return resource_request_->RecursivePrefetchToken(); } +network::OptionalTrustTokenParams WebURLRequest::TrustTokenParams() const { + return ConvertTrustTokenParams(resource_request_->TrustTokenParams()); +} + WebURLRequest::WebURLRequest(ResourceRequest& r) : resource_request_(&r) {} } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_request_test.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_request_test.cc index bce21fa0f57..c03d48e319c 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_url_request_test.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_url_request_test.cc @@ -40,9 +40,9 @@ class RequestTestExtraData : public WebURLRequest::ExtraData { public: explicit RequestTestExtraData(bool* alive) : alive_(alive) { *alive = true; } + private: ~RequestTestExtraData() override { *alive_ = false; } - private: bool* alive_; }; @@ -52,14 +52,15 @@ TEST(WebURLRequestTest, ExtraData) { bool alive = false; { WebURLRequest url_request; - auto extra_data = std::make_unique<RequestTestExtraData>(&alive); + auto extra_data = base::MakeRefCounted<RequestTestExtraData>(&alive); EXPECT_TRUE(alive); auto* raw_extra_data_pointer = extra_data.get(); url_request.SetExtraData(std::move(extra_data)); EXPECT_EQ(raw_extra_data_pointer, url_request.GetExtraData()); { - WebURLRequest other_url_request = url_request; + WebURLRequest other_url_request; + other_url_request.CopyFrom(url_request); EXPECT_TRUE(alive); EXPECT_EQ(raw_extra_data_pointer, other_url_request.GetExtraData()); EXPECT_EQ(raw_extra_data_pointer, url_request.GetExtraData()); diff --git a/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc b/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc index e8eca6afa73..6723c059edb 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_url_response.cc @@ -36,11 +36,11 @@ #include "base/memory/ptr_util.h" #include "base/memory/scoped_refptr.h" +#include "services/network/public/mojom/load_timing_info.mojom.h" #include "third_party/blink/public/platform/web_http_header_visitor.h" #include "third_party/blink/public/platform/web_http_load_info.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/platform/web_url.h" -#include "third_party/blink/public/platform/web_url_load_timing.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_load_info.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" @@ -99,10 +99,27 @@ void WebURLResponse::SetConnectionReused(bool connection_reused) { resource_response_->SetConnectionReused(connection_reused); } -void WebURLResponse::SetLoadTiming(const WebURLLoadTiming& timing) { - scoped_refptr<ResourceLoadTiming> load_timing = - scoped_refptr<ResourceLoadTiming>(timing); - resource_response_->SetResourceLoadTiming(std::move(load_timing)); +void WebURLResponse::SetLoadTiming( + const network::mojom::LoadTimingInfo& mojo_timing) { + auto timing = ResourceLoadTiming::Create(); + timing->SetRequestTime(mojo_timing.request_start); + timing->SetProxyStart(mojo_timing.proxy_resolve_start); + timing->SetProxyEnd(mojo_timing.proxy_resolve_end); + timing->SetDnsStart(mojo_timing.connect_timing.dns_start); + timing->SetDnsEnd(mojo_timing.connect_timing.dns_end); + timing->SetConnectStart(mojo_timing.connect_timing.connect_start); + timing->SetConnectEnd(mojo_timing.connect_timing.connect_end); + timing->SetWorkerStart(mojo_timing.service_worker_start_time); + timing->SetWorkerReady(mojo_timing.service_worker_ready_time); + timing->SetSendStart(mojo_timing.send_start); + timing->SetSendEnd(mojo_timing.send_end); + timing->SetReceiveHeadersStart(mojo_timing.receive_headers_start); + timing->SetReceiveHeadersEnd(mojo_timing.receive_headers_end); + timing->SetSslStart(mojo_timing.connect_timing.ssl_start); + timing->SetSslEnd(mojo_timing.connect_timing.ssl_end); + timing->SetPushStart(mojo_timing.push_start); + timing->SetPushEnd(mojo_timing.push_end); + resource_response_->SetResourceLoadTiming(std::move(timing)); } void WebURLResponse::SetHTTPLoadInfo(const WebHTTPLoadInfo& value) { @@ -244,6 +261,10 @@ void WebURLResponse::SetIsLegacyTLSVersion(bool value) { resource_response_->SetIsLegacyTLSVersion(value); } +void WebURLResponse::SetTimingAllowPassed(bool value) { + resource_response_->SetTimingAllowPassed(value); +} + void WebURLResponse::SetSecurityStyle(SecurityStyle security_style) { resource_response_->SetSecurityStyle(security_style); } diff --git a/chromium/third_party/blink/renderer/platform/exported/web_video_frame_submitter.cc b/chromium/third_party/blink/renderer/platform/exported/web_video_frame_submitter.cc index 93a3d2217ae..df11208b5df 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_video_frame_submitter.cc +++ b/chromium/third_party/blink/renderer/platform/exported/web_video_frame_submitter.cc @@ -26,10 +26,12 @@ namespace blink { std::unique_ptr<WebVideoFrameSubmitter> WebVideoFrameSubmitter::Create( WebContextProviderCallback context_provider_callback, + cc::PlaybackRoughnessReportingCallback roughness_reporting_callback, const cc::LayerTreeSettings& settings, bool use_sync_primitives) { return std::make_unique<VideoFrameSubmitter>( std::move(context_provider_callback), + std::move(roughness_reporting_callback), std::make_unique<VideoFrameResourceProvider>(settings, use_sync_primitives)); } diff --git a/chromium/third_party/blink/renderer/platform/file_metadata.cc b/chromium/third_party/blink/renderer/platform/file_metadata.cc index c78e471f403..51d0190b0b9 100644 --- a/chromium/third_party/blink/renderer/platform/file_metadata.cc +++ b/chromium/third_party/blink/renderer/platform/file_metadata.cc @@ -36,9 +36,9 @@ #include "base/optional.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/base/filename_util.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/file/file_utilities.mojom-blink.h" #include "third_party/blink/public/platform/file_path_conversion.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" #include "third_party/blink/renderer/platform/wtf/thread_specific.h" @@ -92,14 +92,14 @@ void RebindFileUtilitiesForTesting() { if (host) { host.Unbind().reset(); } - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( host.BindNewPipeAndPassReceiver()); } bool GetFileMetadata(const String& path, FileMetadata& metadata) { auto& host = GetFileUtilitiesHost(); if (!host) { - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( host.BindNewPipeAndPassReceiver()); } diff --git a/chromium/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc b/chromium/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc index c9a48d57f2f..48295090b57 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc @@ -4,7 +4,7 @@ #include "third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.h" #include "mojo/public/mojom/base/shared_memory.mojom-blink.h" -#include "third_party/blink/public/platform/interface_provider.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -86,7 +86,7 @@ void FontUniqueNameLookupAndroid::EnsureServiceConnected() { if (service_) return; - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( service_.BindNewPipeAndPassReceiver()); } diff --git a/chromium/third_party/blink/renderer/platform/fonts/font.cc b/chromium/third_party/blink/renderer/platform/fonts/font.cc index 2b110b2e0e0..7a9414845a0 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font.cc @@ -48,27 +48,20 @@ namespace blink { -Font::Font() : can_shape_word_by_word_(0), shape_word_by_word_computed_(0) {} - -Font::Font(const FontDescription& fd) - : font_description_(fd), - can_shape_word_by_word_(0), - shape_word_by_word_computed_(0) {} - -Font::Font(const Font& other) - : font_description_(other.font_description_), - font_fallback_list_(other.font_fallback_list_), - // TODO(yosin): We should have a comment the reason why don't we copy - // |m_canShapeWordByWord| and |m_shapeWordByWordComputed| from |other|, - // since |operator=()| copies them from |other|. - can_shape_word_by_word_(0), - shape_word_by_word_computed_(0) {} +Font::Font() = default; + +Font::Font(const FontDescription& fd) : font_description_(fd) {} + +Font::Font(const FontDescription& font_description, FontSelector* font_selector) + : font_description_(font_description), + font_fallback_list_( + font_selector ? FontFallbackList::Create(font_selector) : nullptr) {} + +Font::Font(const Font& other) = default; Font& Font::operator=(const Font& other) { font_description_ = other.font_description_; font_fallback_list_ = other.font_fallback_list_; - can_shape_word_by_word_ = other.can_shape_word_by_word_; - shape_word_by_word_computed_ = other.shape_word_by_word_computed_; return *this; } @@ -91,18 +84,6 @@ bool Font::operator==(const Font& other) const { : 0); } -void Font::Update(FontSelector* font_selector) const { - // FIXME: It is pretty crazy that we are willing to just poke into a RefPtr, - // but it ends up being reasonably safe (because inherited fonts in the render - // tree pick up the new style anyway. Other copies are transient, e.g., the - // state in the GraphicsContext, and won't stick around long enough to get you - // in trouble). Still, this is pretty disgusting, and could eventually be - // rectified by using RefPtrs for Fonts themselves. - if (!font_fallback_list_) - font_fallback_list_ = FontFallbackList::Create(); - font_fallback_list_->Invalidate(font_selector); -} - namespace { void DrawBlobs(cc::PaintCanvas* canvas, @@ -217,7 +198,10 @@ bool Font::DrawBidiText(cc::PaintCanvas* canvas, TextRunPaintInfo subrun_info(subrun); - ShapeResultBloberizer bloberizer(*this, device_scale_factor); + // Fix regression with -ftrivial-auto-var-init=pattern. See + // crbug.com/1055652. + STACK_UNINITIALIZED ShapeResultBloberizer bloberizer(*this, + device_scale_factor); ShapeResultBuffer buffer; word_shaper.FillResultBuffer(subrun_info, &buffer); float run_width = bloberizer.FillGlyphs(subrun_info, buffer); @@ -422,31 +406,15 @@ int Font::OffsetForPosition(const TextRun& run, } ShapeCache* Font::GetShapeCache() const { - return font_fallback_list_->GetShapeCache(font_description_); + return EnsureFontFallbackList()->GetShapeCache(font_description_); } bool Font::CanShapeWordByWord() const { - if (!shape_word_by_word_computed_) { - can_shape_word_by_word_ = ComputeCanShapeWordByWord(); - shape_word_by_word_computed_ = true; - } - return can_shape_word_by_word_; -} - -bool Font::ComputeCanShapeWordByWord() const { - if (!GetFontDescription().GetTypesettingFeatures()) - return true; - - if (!PrimaryFont()) - return false; - - const FontPlatformData& platform_data = PrimaryFont()->PlatformData(); - TypesettingFeatures features = GetFontDescription().GetTypesettingFeatures(); - return !platform_data.HasSpaceInLigaturesOrKerning(features); + return EnsureFontFallbackList()->CanShapeWordByWord(GetFontDescription()); } void Font::ReportNotDefGlyph() const { - FontSelector* fontSelector = font_fallback_list_->GetFontSelector(); + FontSelector* fontSelector = EnsureFontFallbackList()->GetFontSelector(); // We have a few non-DOM usages of Font code, for example in DragImage::Create // and in EmbeddedObjectPainter::paintReplaced. In those cases, we can't // retrieve a font selector as our connection to a Document object to report diff --git a/chromium/third_party/blink/renderer/platform/fonts/font.h b/chromium/third_party/blink/renderer/platform/fonts/font.h index ca24f5f5e49..2aeb6057caa 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font.h @@ -54,7 +54,6 @@ namespace blink { struct CharacterRange; class FloatPoint; class FloatRect; -class FontData; class FontSelector; class ShapeCache; class TextRun; @@ -66,7 +65,8 @@ class PLATFORM_EXPORT Font { public: Font(); - Font(const FontDescription&); + explicit Font(const FontDescription&); + Font(const FontDescription&, FontSelector*); ~Font(); Font(const Font&); @@ -79,8 +79,6 @@ class PLATFORM_EXPORT Font { return font_description_; } - void Update(FontSelector*) const; - enum CustomFontNotReadyAction { kDoNotPaintIfFontNotReady, kUseFallbackIfFontNotReady @@ -203,7 +201,6 @@ class PLATFORM_EXPORT Font { // loaded. This *should* not happen but in reality it does ever now and then // when, for whatever reason, the last resort font cannot be loaded. const SimpleFontData* PrimaryFont() const; - const FontData* FontDataAt(unsigned) const; // Access the shape cache associated with this particular font object. // Should *not* be retained across layout calls as it may become invalid. @@ -215,8 +212,7 @@ class PLATFORM_EXPORT Font { bool CanShapeWordByWord() const; void SetCanShapeWordByWordForTesting(bool b) { - can_shape_word_by_word_ = b; - shape_word_by_word_computed_ = true; + EnsureFontFallbackList()->SetCanShapeWordByWordForTesting(b); } void ReportNotDefGlyph() const; @@ -226,12 +222,11 @@ class PLATFORM_EXPORT Font { GlyphData GetEmphasisMarkGlyphData(const AtomicString&) const; - bool ComputeCanShapeWordByWord() const; - public: FontSelector* GetFontSelector() const; FontFallbackIterator CreateFontFallbackIterator( FontFallbackPriority fallback_priority) const { + EnsureFontFallbackList(); return FontFallbackIterator(font_description_, font_fallback_list_, fallback_priority); } @@ -246,25 +241,20 @@ class PLATFORM_EXPORT Font { } private: + FontFallbackList* EnsureFontFallbackList() const { + if (!font_fallback_list_) + font_fallback_list_ = FontFallbackList::Create(nullptr); + return font_fallback_list_.get(); + } + FontDescription font_description_; mutable scoped_refptr<FontFallbackList> font_fallback_list_; - mutable unsigned can_shape_word_by_word_ : 1; - mutable unsigned shape_word_by_word_computed_ : 1; - - // For m_fontDescription & m_fontFallbackList access. - friend class CachingWordShaper; }; inline Font::~Font() = default; inline const SimpleFontData* Font::PrimaryFont() const { - DCHECK(font_fallback_list_); - return font_fallback_list_->PrimarySimpleFontData(font_description_); -} - -inline const FontData* Font::FontDataAt(unsigned index) const { - DCHECK(font_fallback_list_); - return font_fallback_list_->FontDataAt(font_description_, index); + return EnsureFontFallbackList()->PrimarySimpleFontData(font_description_); } inline FontSelector* Font::GetFontSelector() const { diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache.cc b/chromium/third_party/blink/renderer/platform/fonts/font_cache.cc index b4a7e9509cf..396a27aa4fe 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_cache.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache.cc @@ -386,6 +386,11 @@ void FontCache::InvalidateShapeCache() { PurgeFallbackListShaperCache(); } +void FontCache::InvalidateEnumerationCache() { + TRACE_EVENT0("fonts,ui", "FontCache::InvalidateEnumerationCache"); + font_enumeration_cache_.clear(); +} + void FontCache::Purge(PurgeSeverity purge_severity) { // Ideally we should never be forcing the purge while the // FontCachePurgePreventer is in scope, but we call purge() at any timing @@ -398,6 +403,7 @@ void FontCache::Purge(PurgeSeverity purge_severity) { PurgePlatformFontDataCache(); PurgeFallbackListShaperCache(); + InvalidateEnumerationCache(); } void FontCache::AddClient(FontCacheClient* client) { @@ -418,6 +424,10 @@ uint16_t FontCache::Generation() { void FontCache::Invalidate() { TRACE_EVENT0("fonts,ui", "FontCache::Invalidate"); font_platform_data_cache_.clear(); + // TODO(https://crbug.com/1061630): Determine optimal cache invalidation + // strategy for enumeration. As implemented, the enumeration cache might not + // get invalidated when the system fonts change. + InvalidateEnumerationCache(); generation_++; if (font_cache_clients_) { @@ -524,4 +534,15 @@ FontCache::Bcp47Vector FontCache::GetBcp47LocaleForRequest( return result; } +const std::vector<FontEnumerationEntry>& FontCache::EnumerateAvailableFonts() { + if (font_enumeration_cache_.size() == 0) { + base::TimeTicks enum_start = base::TimeTicks::Now(); + font_enumeration_cache_ = EnumeratePlatformAvailableFonts(); + base::TimeDelta time_taken = base::TimeTicks::Now() - enum_start; + UMA_HISTOGRAM_TIMES("Blink.Fonts.Enumeration.Duration", time_taken); + } + + return font_enumeration_cache_; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache.h b/chromium/third_party/blink/renderer/platform/fonts/font_cache.h index 08c723bc646..41c63400fbb 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_cache.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache.h @@ -90,21 +90,24 @@ enum class AlternateFontName { kLastResort }; +struct FontEnumerationEntry { + String postscript_name; + String full_name; + String family; +}; + typedef HashMap<unsigned, std::unique_ptr<FontPlatformData>, WTF::IntHash<unsigned>, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> SizedFontPlatformDataSet; -typedef HashMap<FontCacheKey, - SizedFontPlatformDataSet, - FontCacheKeyHash, - FontCacheKeyTraits> - FontPlatformDataCache; +typedef HashMap<FontCacheKey, SizedFontPlatformDataSet> FontPlatformDataCache; typedef HashMap<FallbackListCompositeKey, std::unique_ptr<ShapeCache>, FallbackListCompositeKeyHash, FallbackListCompositeKeyTraits> FallbackListShaperCache; +typedef std::vector<FontEnumerationEntry> FontEnumerationCache; class PLATFORM_EXPORT FontCache { friend class FontCachePurgePreventer; @@ -252,7 +255,13 @@ class PLATFORM_EXPORT FontCache { ShouldRetain = kRetain, bool subpixel_ascent_descent = false); + const std::vector<FontEnumerationEntry>& EnumerateAvailableFonts(); + size_t EnumerationCacheSizeForTesting() { + return font_enumeration_cache_.size(); + } + void InvalidateShapeCache(); + void InvalidateEnumerationCache(); static void CrashWithFontInfo(const FontDescription*); @@ -312,6 +321,7 @@ class PLATFORM_EXPORT FontCache { const FontDescription&, const FontFaceCreationParams&, float font_size); + std::vector<FontEnumerationEntry> EnumeratePlatformAvailableFonts(); sk_sp<SkTypeface> CreateTypeface(const FontDescription&, const FontFaceCreationParams&, @@ -366,6 +376,9 @@ class PLATFORM_EXPORT FontCache { FontPlatformDataCache font_platform_data_cache_; FallbackListShaperCache fallback_list_shaper_cache_; FontDataCache font_data_cache_; + // TODO(https://crbug.com/1061625): Move to the browser process for better + // resource utilization. + FontEnumerationCache font_enumeration_cache_; void PurgePlatformFontDataCache(); void PurgeFallbackListShaperCache(); diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache_client.h b/chromium/third_party/blink/renderer/platform/fonts/font_cache_client.h index 2160fcb2c3b..6b7c90a227d 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_cache_client.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache_client.h @@ -42,7 +42,7 @@ class PLATFORM_EXPORT FontCacheClient virtual ~FontCacheClient() = default; virtual void FontCacheInvalidated() = 0; - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache_key.h b/chromium/third_party/blink/renderer/platform/fonts/font_cache_key.h index 90063cb2eac..a860a0ad051 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_cache_key.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache_key.h @@ -93,13 +93,19 @@ struct FontCacheKey { } bool operator==(const FontCacheKey& other) const { + bool variation_settings_equal = + (!variation_settings_ && !other.variation_settings_) || + (variation_settings_ && other.variation_settings_ && + *variation_settings_ == *other.variation_settings_); return creation_params_ == other.creation_params_ && font_size_ == other.font_size_ && options_ == other.options_ && device_scale_factor_ == other.device_scale_factor_ && - variation_settings_ == other.variation_settings_ && + variation_settings_equal && is_unique_match_ == other.is_unique_match_; } + bool operator!=(const FontCacheKey& other) const { return !(*this == other); } + static constexpr unsigned PrecisionMultiplier() { return kFontSizePrecisionMultiplier; } @@ -141,4 +147,23 @@ struct FontCacheKeyTraits : WTF::SimpleClassHashTraits<FontCacheKey> { } // namespace blink +namespace WTF { +template <> +struct DefaultHash<blink::FontCacheKey> { + STATIC_ONLY(DefaultHash); + typedef blink::FontCacheKeyHash Hash; +}; + +template <> +struct HashTraits<blink::FontCacheKey> + : WTF::SimpleClassHashTraits<blink::FontCacheKey> { + STATIC_ONLY(HashTraits); + + // std::string's empty state need not be zero in all implementations, + // and it is held within FontFaceCreationParams. + static const bool kEmptyValueIsZero = false; +}; + +} // namespace WTF + #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_CACHE_KEY_H_ diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_cache_test.cc b/chromium/third_party/blink/renderer/platform/fonts/font_cache_test.cc index a9233996c15..0f2c270892c 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_cache_test.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font_cache_test.cc @@ -89,4 +89,79 @@ TEST(FontCache, systemFont) { } #endif +class EnumerationConsumer { + public: + explicit EnumerationConsumer( + const std::vector<FontEnumerationEntry>& expectations) { + for (const auto& f : expectations) { + ps_name_set_.insert(f.postscript_name.Utf8()); + full_name_set_.insert(f.full_name.Utf8()); + family_set_.insert(f.family.Utf8()); + } + } + + void Consume(const std::vector<FontEnumerationEntry>& entries) { + for (auto f : entries) { + ps_name_set_.erase(f.postscript_name.Utf8()); + full_name_set_.erase(f.full_name.Utf8()); + family_set_.erase(f.family.Utf8()); + } + } + + bool AllExpectationsMet() { + return ps_name_set_.empty() && full_name_set_.empty() && + family_set_.empty(); + } + + private: + std::set<std::string> ps_name_set_; + std::set<std::string> full_name_set_; + std::set<std::string> family_set_; +}; + +TEST(FontCache, EnumerateAvailableFonts) { + FontCache* font_cache = FontCache::GetFontCache(); + ASSERT_TRUE(font_cache); + + std::vector<FontEnumerationEntry> expectations; + +#if defined(OS_MACOSX) + expectations.push_back(FontEnumerationEntry{"Monaco", "Monaco", "Monaco"}); + expectations.push_back( + FontEnumerationEntry{"Menlo-Regular", "Menlo Regular", "Menlo"}); + expectations.push_back( + FontEnumerationEntry{"Menlo-Bold", "Menlo Bold", "Menlo"}); + expectations.push_back( + FontEnumerationEntry{"Menlo-BoldItalic", "Menlo Bold Italic", "Menlo"}); +#endif + + auto entries = font_cache->EnumerateAvailableFonts(); + auto consumer = EnumerationConsumer(expectations); + + consumer.Consume(entries); + ASSERT_TRUE(consumer.AllExpectationsMet()); +} + +TEST(FontCache, EnumerateAvailableFontsInvalidation) { + FontCache* font_cache = FontCache::GetFontCache(); + ASSERT_TRUE(font_cache); + + // Make sure we start at zero. + font_cache->Invalidate(); + size_t zero = 0; + ASSERT_EQ(zero, font_cache->EnumerationCacheSizeForTesting()); + + // The cache gets populated. + size_t enum_size_1 = font_cache->EnumerateAvailableFonts().size(); + ASSERT_EQ(enum_size_1, font_cache->EnumerationCacheSizeForTesting()); + + // Invalidation clears the cache. + font_cache->Invalidate(); + ASSERT_EQ(zero, font_cache->EnumerationCacheSizeForTesting()); + + // The cache gets re-populated. + size_t enum_size_2 = font_cache->EnumerateAvailableFonts().size(); + ASSERT_EQ(enum_size_1, enum_size_2); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc b/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc index ecc8454ff02..b00c9e9b10d 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc @@ -45,16 +45,6 @@ namespace blink { -namespace { -sk_sp<SkFontMgr> FontManagerForSubType( - FontFormatCheck::VariableFontSubType font_sub_type) { - CHECK_NE(font_sub_type, FontFormatCheck::VariableFontSubType::kNotVariable); - if (font_sub_type == FontFormatCheck::VariableFontSubType::kVariableCFF2) - return WebFontTypefaceFactory::FreeTypeFontManager(); - return WebFontTypefaceFactory::FontManagerForVariations(); -} -} // namespace - FontCustomPlatformData::FontCustomPlatformData(sk_sp<SkTypeface> typeface, size_t data_size) : base_typeface_(std::move(typeface)), data_size_(data_size) {} @@ -97,9 +87,14 @@ FontPlatformData FontCustomPlatformData::GetFontPlatformData( SkSetFourByteTag('w', 'd', 't', 'h'), SkFloatToScalar(selection_capabilities.width.clampToRange( selection_request.width))}; + // CSS and OpenType have opposite definitions of direction of slant + // angle. In OpenType positive values turn counter-clockwise, negative + // values clockwise - in CSS positive values are clockwise rotations / + // skew. See note in https://drafts.csswg.org/css-fonts/#font-style-prop - + // map value from CSS to OpenType here. SkFontArguments::Axis slant_axis = { SkSetFourByteTag('s', 'l', 'n', 't'), - SkFloatToScalar(selection_capabilities.slope.clampToRange( + SkFloatToScalar(-selection_capabilities.slope.clampToRange( selection_request.slope))}; axes.push_back(weight_axis); @@ -124,12 +119,8 @@ FontPlatformData FontCustomPlatformData::GetFontPlatformData( axes.push_back(opsz_axis); } - int index; - std::unique_ptr<SkStreamAsset> stream(base_typeface_->openStream(&index)); - sk_sp<SkTypeface> sk_variation_font(FontManagerForSubType(font_sub_type) - ->makeFromStream(std::move(stream), - SkFontArguments().setCollectionIndex(index) - .setAxes(axes.data(), axes.size()))); + sk_sp<SkTypeface> sk_variation_font(base_typeface_->makeClone( + SkFontArguments().setAxes(axes.data(), axes.size()))); if (sk_variation_font) { return_typeface = sk_variation_font; diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_data.h b/chromium/third_party/blink/renderer/platform/fonts/font_data.h index 3535115d1e4..d79e7fd5a70 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_data.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font_data.h @@ -57,15 +57,6 @@ class PLATFORM_EXPORT FontData : public RefCounted<FontData> { DISALLOW_COPY_AND_ASSIGN(FontData); }; -#define DEFINE_FONT_DATA_TYPE_CASTS(thisType, predicate) \ - template <typename T> \ - inline thisType* To##thisType(const scoped_refptr<T>& fontData) { \ - return To##thisType(fontData.get()); \ - } \ - DEFINE_TYPE_CASTS(thisType, FontData, fontData, \ - fontData->IsSegmented() == predicate, \ - fontData.IsSegmented() == predicate) - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_DATA_H_ diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_data_cache.cc b/chromium/third_party/blink/renderer/platform/fonts/font_data_cache.cc index 060207ea5e5..680da1679bd 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_data_cache.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font_data_cache.cc @@ -132,7 +132,7 @@ bool FontDataCache::PurgeLeastRecentlyUsed(int count) { auto end = inactive_font_data_.end(); auto it = inactive_font_data_.begin(); for (int i = 0; i < count && it != end; ++it, ++i) { - scoped_refptr<SimpleFontData>& font_data = *it.Get(); + const scoped_refptr<SimpleFontData>& font_data = *it; cache_.erase(&(font_data->PlatformData())); // We should not delete SimpleFontData here because deletion can modify // m_inactiveFontData. See http://trac.webkit.org/changeset/44011 diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_description.cc b/chromium/third_party/blink/renderer/platform/fonts/font_description.cc index 6f20525f341..9e3ffe63834 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_description.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font_description.cc @@ -436,8 +436,6 @@ String FontDescription::ToString(GenericFamilyType familyType) { return "Cursive"; case GenericFamilyType::kFantasyFamily: return "Fantasy"; - case GenericFamilyType::kPictographFamily: - return "Pictograph"; } return "Unknown"; } diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_description.h b/chromium/third_party/blink/renderer/platform/fonts/font_description.h index 8d19bd6873c..782b843707b 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_description.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font_description.h @@ -63,8 +63,7 @@ class PLATFORM_EXPORT FontDescription { kSansSerifFamily, kMonospaceFamily, kCursiveFamily, - kFantasyFamily, - kPictographFamily + kFantasyFamily }; static String ToString(GenericFamilyType); diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_description_test.cc b/chromium/third_party/blink/renderer/platform/fonts/font_description_test.cc index 737580ef6bf..1bb413937f9 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_description_test.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font_description_test.cc @@ -63,6 +63,78 @@ TEST(FontDescriptionTest, TestHashCollision) { } } +TEST(FontDescriptionTest, VariationSettingsIdentical) { + FontDescription a; + FontDescription b(a); + + scoped_refptr<FontVariationSettings> settings_a = + FontVariationSettings::Create(); + settings_a->Append(FontVariationAxis("test", 1)); + + scoped_refptr<FontVariationSettings> settings_b = + FontVariationSettings::Create(); + settings_b->Append(FontVariationAxis("test", 1)); + + ASSERT_EQ(*settings_a, *settings_b); + + a.SetVariationSettings(settings_a); + b.SetVariationSettings(settings_b); + + ASSERT_EQ(a, b); + + FontFaceCreationParams test_creation_params; + FontCacheKey cache_key_a = a.CacheKey(test_creation_params, false); + FontCacheKey cache_key_b = b.CacheKey(test_creation_params, false); + + ASSERT_EQ(cache_key_a, cache_key_b); +} + +TEST(FontDescriptionTest, VariationSettingsDifferent) { + FontDescription a; + FontDescription b(a); + + scoped_refptr<FontVariationSettings> settings_a = + FontVariationSettings::Create(); + settings_a->Append(FontVariationAxis("test", 1)); + + scoped_refptr<FontVariationSettings> settings_b = + FontVariationSettings::Create(); + settings_b->Append(FontVariationAxis("0000", 1)); + + ASSERT_NE(*settings_a, *settings_b); + + a.SetVariationSettings(settings_a); + b.SetVariationSettings(settings_b); + + ASSERT_NE(a, b); + + FontFaceCreationParams test_creation_params; + + FontCacheKey cache_key_a = a.CacheKey(test_creation_params, false); + FontCacheKey cache_key_b = b.CacheKey(test_creation_params, false); + + ASSERT_NE(cache_key_a, cache_key_b); + + scoped_refptr<FontVariationSettings> second_settings_a = + FontVariationSettings::Create(); + second_settings_a->Append(FontVariationAxis("test", 1)); + + scoped_refptr<FontVariationSettings> second_settings_b = + FontVariationSettings::Create(); + + ASSERT_NE(*second_settings_a, *second_settings_b); + + a.SetVariationSettings(second_settings_a); + b.SetVariationSettings(second_settings_b); + + ASSERT_NE(a, b); + + FontCacheKey second_cache_key_a = a.CacheKey(test_creation_params, false); + FontCacheKey second_cache_key_b = b.CacheKey(test_creation_params, false); + + ASSERT_NE(second_cache_key_a, second_cache_key_b); +} + TEST(FontDescriptionTest, ToString) { FontDescription description; diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc index 1362d480c4b..eb076725400 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc @@ -153,7 +153,7 @@ scoped_refptr<FontDataForRangeSet> FontFallbackIterator::Next( current_font_data_index_++; if (!font_data->IsLoading()) { scoped_refptr<SimpleFontData> non_segmented = - const_cast<SimpleFontData*>(ToSimpleFontData(font_data)); + const_cast<SimpleFontData*>(To<SimpleFontData>(font_data)); // The fontData object that we have here is tracked in m_fontList of // FontFallbackList and gets released in the font cache when the // FontFallbackList is destroyed. @@ -165,7 +165,7 @@ scoped_refptr<FontDataForRangeSet> FontFallbackIterator::Next( // Iterate over ranges of a segmented font below. - const SegmentedFontData* segmented = ToSegmentedFontData(font_data); + const auto* segmented = To<SegmentedFontData>(font_data); if (fallback_stage_ != kSegmentedFace) { segmented_face_index_ = 0; fallback_stage_ = kSegmentedFace; diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.cc b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.cc index fa1a455e591..d00d18310bb 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.cc @@ -39,22 +39,24 @@ namespace blink { -FontFallbackList::FontFallbackList() +FontFallbackList::FontFallbackList(FontSelector* font_selector) : cached_primary_simple_font_data_(nullptr), - font_selector_(nullptr), - font_selector_version_(0), + font_selector_(font_selector), + font_selector_version_(font_selector ? font_selector->Version() : 0), family_index_(0), generation_(FontCache::GetFontCache()->Generation()), - has_loading_fallback_(false) {} + has_loading_fallback_(false), + can_shape_word_by_word_(false), + can_shape_word_by_word_computed_(false) {} -void FontFallbackList::Invalidate(FontSelector* font_selector) { +void FontFallbackList::Invalidate() { ReleaseFontData(); font_list_.clear(); cached_primary_simple_font_data_ = nullptr; family_index_ = 0; has_loading_fallback_ = false; - if (font_selector_ != font_selector) - font_selector_ = font_selector; + can_shape_word_by_word_ = false; + can_shape_word_by_word_computed_ = false; font_selector_version_ = font_selector_ ? font_selector_->Version() : 0; generation_ = FontCache::GetFontCache()->Generation(); } @@ -65,13 +67,18 @@ void FontFallbackList::ReleaseFontData() { if (!font_list_[i]->IsCustomFont()) { DCHECK(!font_list_[i]->IsSegmented()); FontCache::GetFontCache()->ReleaseFontData( - ToSimpleFontData(font_list_[i])); + To<SimpleFontData>(font_list_[i].get())); } } shape_cache_.reset(); // Clear the weak pointer to the cache instance. } bool FontFallbackList::LoadingCustomFonts() const { + // This function is only used for style and layout invalidation purposes. We + // don't need it for invalidation when the feature below is enabled. + if (RuntimeEnabledFeatures::CSSReducedFontLoadingInvalidationsEnabled()) + return false; + if (!has_loading_fallback_) return false; @@ -84,6 +91,8 @@ bool FontFallbackList::LoadingCustomFonts() const { } bool FontFallbackList::ShouldSkipDrawing() const { + DCHECK(IsValid()); + if (!has_loading_fallback_) return false; @@ -96,7 +105,7 @@ bool FontFallbackList::ShouldSkipDrawing() const { } const SimpleFontData* FontFallbackList::DeterminePrimarySimpleFontData( - const FontDescription& font_description) const { + const FontDescription& font_description) { bool should_load_custom_font = true; for (unsigned font_index = 0;; ++font_index) { @@ -114,8 +123,8 @@ const SimpleFontData* FontFallbackList::DeterminePrimarySimpleFontData( return last_resort_fallback; } - if (font_data->IsSegmented() && - !ToSegmentedFontData(font_data)->ContainsCharacter(kSpaceCharacter)) + const auto* segmented = DynamicTo<SegmentedFontData>(font_data); + if (segmented && !segmented->ContainsCharacter(kSpaceCharacter)) continue; const SimpleFontData* font_data_for_space = @@ -128,8 +137,7 @@ const SimpleFontData* FontFallbackList::DeterminePrimarySimpleFontData( if (!font_data_for_space->IsLoadingFallback()) return font_data_for_space; - if (font_data->IsSegmented()) { - const SegmentedFontData* segmented = ToSegmentedFontData(font_data); + if (segmented) { for (unsigned i = 0; i < segmented->NumFaces(); i++) { const SimpleFontData* range_font_data = segmented->FaceAt(i)->FontData(); @@ -212,8 +220,9 @@ FallbackListCompositeKey FontFallbackList::CompositeKey( if (result) { bool is_unique_match = false; key.Add(font_description.CacheKey(params, is_unique_match)); - if (!result->IsSegmented() && !result->IsCustomFont()) - FontCache::GetFontCache()->ReleaseFontData(ToSimpleFontData(result)); + auto* font_data = DynamicTo<SimpleFontData>(result.get()); + if (!font_data && !result->IsCustomFont()) + FontCache::GetFontCache()->ReleaseFontData(font_data); } } current_family = current_family->Next(); @@ -224,7 +233,12 @@ FallbackListCompositeKey FontFallbackList::CompositeKey( const FontData* FontFallbackList::FontDataAt( const FontDescription& font_description, - unsigned realized_font_index) const { + unsigned realized_font_index) { + if (RuntimeEnabledFeatures::CSSReducedFontLoadingInvalidationsEnabled()) { + if (!IsValid()) + Invalidate(); + } + // This fallback font is already in our list. if (realized_font_index < font_list_.size()) return font_list_[realized_font_index].get(); @@ -250,6 +264,34 @@ const FontData* FontFallbackList::FontDataAt( return result.get(); } +bool FontFallbackList::ComputeCanShapeWordByWord( + const FontDescription& font_description) { + if (!font_description.GetTypesettingFeatures()) + return true; + + const SimpleFontData* primary_font = PrimarySimpleFontData(font_description); + if (!primary_font) + return false; + + const FontPlatformData& platform_data = primary_font->PlatformData(); + TypesettingFeatures features = font_description.GetTypesettingFeatures(); + return !platform_data.HasSpaceInLigaturesOrKerning(features); +} + +bool FontFallbackList::CanShapeWordByWord( + const FontDescription& font_description) { + if (RuntimeEnabledFeatures::CSSReducedFontLoadingInvalidationsEnabled()) { + if (!IsValid()) + Invalidate(); + } + + if (!can_shape_word_by_word_computed_) { + can_shape_word_by_word_ = ComputeCanShapeWordByWord(font_description); + can_shape_word_by_word_computed_ = true; + } + return can_shape_word_by_word_; +} + bool FontFallbackList::IsValid() const { if (!font_selector_) return font_selector_version_ == 0; diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.h b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.h index 4a604216015..6ef8c3774b8 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font_fallback_list.h @@ -28,6 +28,7 @@ #include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h" #include "third_party/blink/renderer/platform/fonts/simple_font_data.h" #include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/forward.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" @@ -42,13 +43,13 @@ class PLATFORM_EXPORT FontFallbackList : public RefCounted<FontFallbackList> { USING_FAST_MALLOC(FontFallbackList); public: - static scoped_refptr<FontFallbackList> Create() { - return base::AdoptRef(new FontFallbackList()); + static scoped_refptr<FontFallbackList> Create(FontSelector* font_selector) { + return base::AdoptRef(new FontFallbackList(font_selector)); } ~FontFallbackList() { ReleaseFontData(); } bool IsValid() const; - void Invalidate(FontSelector*); + void Invalidate(); bool LoadingCustomFonts() const; bool ShouldSkipDrawing() const; @@ -58,7 +59,12 @@ class PLATFORM_EXPORT FontFallbackList : public RefCounted<FontFallbackList> { unsigned FontSelectorVersion() const { return font_selector_version_; } uint16_t Generation() const { return generation_; } - ShapeCache* GetShapeCache(const FontDescription& font_description) const { + ShapeCache* GetShapeCache(const FontDescription& font_description) { + if (RuntimeEnabledFeatures::CSSReducedFontLoadingInvalidationsEnabled()) { + if (!IsValid()) + Invalidate(); + } + if (!shape_cache_) { FallbackListCompositeKey key = CompositeKey(font_description); shape_cache_ = @@ -72,6 +78,11 @@ class PLATFORM_EXPORT FontFallbackList : public RefCounted<FontFallbackList> { const SimpleFontData* PrimarySimpleFontData( const FontDescription& font_description) { + if (RuntimeEnabledFeatures::CSSReducedFontLoadingInvalidationsEnabled()) { + if (!IsValid()) + Invalidate(); + } + if (!cached_primary_simple_font_data_) { cached_primary_simple_font_data_ = DeterminePrimarySimpleFontData(font_description); @@ -79,28 +90,37 @@ class PLATFORM_EXPORT FontFallbackList : public RefCounted<FontFallbackList> { } return cached_primary_simple_font_data_; } - const FontData* FontDataAt(const FontDescription&, unsigned index) const; + const FontData* FontDataAt(const FontDescription&, unsigned index); - FallbackListCompositeKey CompositeKey(const FontDescription&) const; + bool CanShapeWordByWord(const FontDescription&); + + void SetCanShapeWordByWordForTesting(bool b) { + can_shape_word_by_word_ = b; + can_shape_word_by_word_computed_ = true; + } private: - FontFallbackList(); + explicit FontFallbackList(FontSelector* font_selector); scoped_refptr<FontData> GetFontData(const FontDescription&, int& family_index) const; - const SimpleFontData* DeterminePrimarySimpleFontData( - const FontDescription&) const; + const SimpleFontData* DeterminePrimarySimpleFontData(const FontDescription&); + + FallbackListCompositeKey CompositeKey(const FontDescription&) const; void ReleaseFontData(); + bool ComputeCanShapeWordByWord(const FontDescription&); - mutable Vector<scoped_refptr<FontData>, 1> font_list_; - mutable const SimpleFontData* cached_primary_simple_font_data_; - Persistent<FontSelector> font_selector_; + Vector<scoped_refptr<FontData>, 1> font_list_; + const SimpleFontData* cached_primary_simple_font_data_; + const Persistent<FontSelector> font_selector_; unsigned font_selector_version_; - mutable int family_index_; + int family_index_; uint16_t generation_; - mutable bool has_loading_fallback_ : 1; - mutable base::WeakPtr<ShapeCache> shape_cache_; + bool has_loading_fallback_ : 1; + bool can_shape_word_by_word_ : 1; + bool can_shape_word_by_word_computed_ : 1; + base::WeakPtr<ShapeCache> shape_cache_; DISALLOW_COPY_AND_ASSIGN(FontFallbackList); }; diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_global_context.cc b/chromium/third_party/blink/renderer/platform/fonts/font_global_context.cc index ff2fd249e6c..ee1cb3b220c 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_global_context.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font_global_context.cc @@ -20,7 +20,9 @@ FontGlobalContext* FontGlobalContext::Get(CreateIfNeeded create_if_needed) { return *font_persistent; } -FontGlobalContext::FontGlobalContext() : harfbuzz_font_funcs_(nullptr) {} +FontGlobalContext::FontGlobalContext() + : harfbuzz_font_funcs_skia_advances_(nullptr), + harfbuzz_font_funcs_harfbuzz_advances_(nullptr) {} FontGlobalContext::~FontGlobalContext() = default; @@ -32,6 +34,15 @@ FontUniqueNameLookup* FontGlobalContext::GetFontUniqueNameLookup() { return Get()->font_unique_name_lookup_.get(); } +HarfBuzzFontCache* FontGlobalContext::GetHarfBuzzFontCache() { + std::unique_ptr<HarfBuzzFontCache>& global_context_harfbuzz_font_cache = + Get()->harfbuzz_font_cache_; + if (!global_context_harfbuzz_font_cache) { + global_context_harfbuzz_font_cache = std::make_unique<HarfBuzzFontCache>(); + } + return global_context_harfbuzz_font_cache.get(); +} + void FontGlobalContext::ClearMemory() { if (!Get(kDoNotCreate)) return; diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_global_context.h b/chromium/third_party/blink/renderer/platform/fonts/font_global_context.h index 63c3c861d6e..37e743c2264 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_global_context.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font_global_context.h @@ -6,7 +6,6 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_GLOBAL_CONTEXT_H_ #include "third_party/blink/renderer/platform/fonts/font_cache.h" -#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/text/layout_locale.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -17,6 +16,7 @@ namespace blink { class FontCache; class FontUniqueNameLookup; +class HarfBuzzFontCache; enum CreateIfNeeded { kDoNotCreate, kCreate }; @@ -30,16 +30,27 @@ class PLATFORM_EXPORT FontGlobalContext { static inline FontCache& GetFontCache() { return Get()->font_cache_; } - static inline HarfBuzzFontCache& GetHarfBuzzFontCache() { - return Get()->harfbuzz_font_cache_; - } + static HarfBuzzFontCache* GetHarfBuzzFontCache(); + + enum HorizontalAdvanceSource { + kSkiaHorizontalAdvances, + kHarfBuzzHorizontalAdvances + }; - static hb_font_funcs_t* GetHarfBuzzFontFuncs() { - return Get()->harfbuzz_font_funcs_; + static hb_font_funcs_t* GetHarfBuzzFontFuncs( + HorizontalAdvanceSource advance_source) { + if (advance_source == kHarfBuzzHorizontalAdvances) { + return Get()->harfbuzz_font_funcs_harfbuzz_advances_; + } + return Get()->harfbuzz_font_funcs_skia_advances_; } - static void SetHarfBuzzFontFuncs(hb_font_funcs_t* funcs) { - Get()->harfbuzz_font_funcs_ = funcs; + static void SetHarfBuzzFontFuncs(HorizontalAdvanceSource advance_source, + hb_font_funcs_t* funcs) { + if (advance_source == kHarfBuzzHorizontalAdvances) { + Get()->harfbuzz_font_funcs_harfbuzz_advances_ = funcs; + } + Get()->harfbuzz_font_funcs_skia_advances_ = funcs; } static FontUniqueNameLookup* GetFontUniqueNameLookup(); @@ -54,8 +65,9 @@ class PLATFORM_EXPORT FontGlobalContext { ~FontGlobalContext(); FontCache font_cache_; - HarfBuzzFontCache harfbuzz_font_cache_; - hb_font_funcs_t* harfbuzz_font_funcs_; + std::unique_ptr<HarfBuzzFontCache> harfbuzz_font_cache_; + hb_font_funcs_t* harfbuzz_font_funcs_skia_advances_; + hb_font_funcs_t* harfbuzz_font_funcs_harfbuzz_advances_; std::unique_ptr<FontUniqueNameLookup> font_unique_name_lookup_; DISALLOW_COPY_AND_ASSIGN(FontGlobalContext); diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc b/chromium/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc index 5e1005a2e4d..a9a0511de03 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/font_matching_metrics.cc @@ -28,6 +28,18 @@ HashSet<T> Intersection(const HashSet<T>& a, const HashSet<T>& b) { namespace blink { +FontMatchingMetrics::FontMatchingMetrics(bool top_level, + ukm::UkmRecorder* ukm_recorder, + ukm::SourceId source_id) + : top_level_(top_level), + ukm_recorder_(ukm_recorder), + source_id_(source_id) { + // Estimate of average page font use from anecdotal browsing session. + constexpr unsigned kEstimatedFontCount = 7; + local_fonts_succeeded_.ReserveCapacityForSize(kEstimatedFontCount); + local_fonts_failed_.ReserveCapacityForSize(kEstimatedFontCount); +} + void FontMatchingMetrics::ReportSuccessfulFontFamilyMatch( const AtomicString& font_family_name) { successful_font_families_.insert(font_family_name); @@ -48,6 +60,16 @@ void FontMatchingMetrics::ReportWebFontFamily( web_font_families_.insert(font_family_name); } +void FontMatchingMetrics::ReportSuccessfulLocalFontMatch( + const AtomicString& font_name) { + local_fonts_succeeded_.insert(font_name); +} + +void FontMatchingMetrics::ReportFailedLocalFontMatch( + const AtomicString& font_name) { + local_fonts_failed_.insert(font_name); +} + void FontMatchingMetrics::PublishUkmMetrics() { ukm::builders::FontMatchAttempts(source_id_) .SetLoadContext(top_level_ ? kTopLevel : kSubFrame) @@ -63,6 +85,10 @@ void FontMatchingMetrics::PublishUkmMetrics() { .SetWebFontFamilyFailures(ukm::GetExponentialBucketMin( Intersection(failed_font_families_, web_font_families_).size(), kUkmFontLoadCountBucketSpacing)) + .SetLocalFontFailures(ukm::GetExponentialBucketMin( + local_fonts_failed_.size(), kUkmFontLoadCountBucketSpacing)) + .SetLocalFontSuccesses(ukm::GetExponentialBucketMin( + local_fonts_succeeded_.size(), kUkmFontLoadCountBucketSpacing)) .Record(ukm_recorder_); } diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_matching_metrics.h b/chromium/third_party/blink/renderer/platform/fonts/font_matching_metrics.h index c31be1c51b5..59827c38e53 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_matching_metrics.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font_matching_metrics.h @@ -27,10 +27,7 @@ class PLATFORM_EXPORT FontMatchingMetrics { public: FontMatchingMetrics(bool top_level, ukm::UkmRecorder* ukm_recorder, - ukm::SourceId source_id) - : top_level_(top_level), - ukm_recorder_(ukm_recorder), - source_id_(source_id) {} + ukm::SourceId source_id); // Called when a page attempts to match a font family, and the font family is // available. @@ -46,6 +43,14 @@ class PLATFORM_EXPORT FontMatchingMetrics { // Called when a page attempts to match a web font family. void ReportWebFontFamily(const AtomicString& font_family_name); + // Reports a font listed in a @font-face src:local rule that successfully + // matched. + void ReportSuccessfulLocalFontMatch(const AtomicString& font_name); + + // Reports a font listed in a @font-face src:local rule that didn't + // successfully match. + void ReportFailedLocalFontMatch(const AtomicString& font_name); + // Publishes the number of font family matches attempted (both successful and // otherwise) to UKM. Called at page unload. void PublishUkmMetrics(); @@ -63,6 +68,12 @@ class PLATFORM_EXPORT FontMatchingMetrics { // Web font families the page attempted to match. HashSet<AtomicString> web_font_families_; + // @font-face src:local fonts that successfully matched. + HashSet<AtomicString> local_fonts_succeeded_; + + // @font-face src:local fonts that didn't successfully match. + HashSet<AtomicString> local_fonts_failed_; + // True if this FontMatchingMetrics instance is for a top-level frame, false // otherwise. const bool top_level_ = false; diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selector.h b/chromium/third_party/blink/renderer/platform/fonts/font_selector.h index 0eaaf9e94fb..0861e6e15c8 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_selector.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font_selector.h @@ -71,6 +71,15 @@ class PLATFORM_EXPORT FontSelector : public FontCacheClient { virtual void ReportFailedFontFamilyMatch( const AtomicString& font_family_name) = 0; + // Called when a page attempts to match a font name via a @font-face src:local + // rule, and the font is available. + virtual void ReportSuccessfulLocalFontMatch( + const AtomicString& font_name) = 0; + + // Called when a page attempts to match a font name via a @font-face src:local + // rule, and the font is not available. + virtual void ReportFailedLocalFontMatch(const AtomicString& font_name) = 0; + virtual void RegisterForInvalidationCallbacks(FontSelectorClient*) = 0; virtual void UnregisterForInvalidationCallbacks(FontSelectorClient*) = 0; diff --git a/chromium/third_party/blink/renderer/platform/fonts/font_selector_client.h b/chromium/third_party/blink/renderer/platform/fonts/font_selector_client.h index 649b054fb68..b85832903ab 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/font_selector_client.h +++ b/chromium/third_party/blink/renderer/platform/fonts/font_selector_client.h @@ -17,7 +17,7 @@ class FontSelectorClient : public GarbageCollectedMixin { virtual void FontsNeedUpdate(FontSelector*) = 0; - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/linux/font_unique_name_lookup_linux.cc b/chromium/third_party/blink/renderer/platform/fonts/linux/font_unique_name_lookup_linux.cc index 62d92b7f412..604a895c075 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/linux/font_unique_name_lookup_linux.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/linux/font_unique_name_lookup_linux.cc @@ -19,7 +19,6 @@ sk_sp<SkTypeface> FontUniqueNameLookupLinux::MatchUniqueName( if (!Platform::Current()->GetSandboxSupport()) { LOG(ERROR) << "@font-face src: local() instantiation only available when " "connected to browser process."; - DCHECK(Platform::Current()->GetSandboxSupport()); return nullptr; } diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/core_text_font_format_support.h b/chromium/third_party/blink/renderer/platform/fonts/mac/core_text_font_format_support.h index 1816f6f49d7..b5f3ffe9738 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/mac/core_text_font_format_support.h +++ b/chromium/third_party/blink/renderer/platform/fonts/mac/core_text_font_format_support.h @@ -9,6 +9,7 @@ namespace blink { bool CoreTextVersionSupportsVariations(); bool CoreTextVersionSupportsColrCpal(); + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_CORE_TEXT_FONT_FORMAT_SUPPORT_H_ diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm b/chromium/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm index f50fc36281d..fedd1b91424 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm +++ b/chromium/third_party/blink/renderer/platform/fonts/mac/font_cache_mac.mm @@ -29,8 +29,11 @@ #import "third_party/blink/renderer/platform/fonts/font_cache.h" -#import <AppKit/AppKit.h> #include <memory> + +#import <AppKit/AppKit.h> +#import <CoreText/CoreText.h> + #include "base/location.h" #include "base/mac/foundation_util.h" #include "third_party/blink/public/platform/platform.h" @@ -59,6 +62,22 @@ inLanguage:(id)useNil; @end +namespace { + +NSString* GetLocalizedString(CTFontDescriptorRef fd, CFStringRef attribute) { + base::ScopedCFTypeRef<CFStringRef> cf_str(base::mac::CFCast<CFStringRef>( + CTFontDescriptorCopyLocalizedAttribute(fd, attribute, nullptr))); + return [base::mac::CFToNSCast(cf_str.release()) autorelease]; +} + +NSString* GetString(CTFontDescriptorRef fd, CFStringRef attribute) { + base::ScopedCFTypeRef<CFStringRef> cf_str(base::mac::CFCast<CFStringRef>( + CTFontDescriptorCopyAttribute(fd, attribute))); + return [base::mac::CFToNSCast(cf_str.release()) autorelease]; +} + +} // namespace + namespace blink { const char kColorEmojiFontMac[] = "Apple Color Emoji"; @@ -221,9 +240,12 @@ scoped_refptr<SimpleFontData> FontCache::PlatformFallbackFontForCharacter( substitute_font, platform_data.size(), synthetic_bold, (traits & NSFontItalicTrait) && !(substitute_font_traits & NSFontItalicTrait), - platform_data.Orientation(), + platform_data.Orientation(), font_description.FontOpticalSizing(), nullptr); // No variation paramaters in fallback. + if (!alternate_font) + return nullptr; + return FontDataFromFontPlatformData(alternate_font.get(), kDoNotRetain); } @@ -295,11 +317,43 @@ std::unique_ptr<FontPlatformData> FontCache::CreateFontPlatformData( // the returned FontPlatformData since it will not have a valid SkTypeface. std::unique_ptr<FontPlatformData> platform_data = FontPlatformDataFromNSFont( platform_font, size, synthetic_bold, synthetic_italic, - font_description.Orientation(), font_description.VariationSettings()); - if (!platform_data->Typeface()) { + font_description.Orientation(), font_description.FontOpticalSizing(), + font_description.VariationSettings()); + if (!platform_data || !platform_data->Typeface()) { return nullptr; } return platform_data; } +std::vector<FontEnumerationEntry> FontCache::EnumeratePlatformAvailableFonts() { + DCHECK(RuntimeEnabledFeatures::FontAccessEnabled()); + @autoreleasepool { + std::vector<FontEnumerationEntry> output; + + CFTypeRef values[1] = {kCFBooleanTrue}; + base::ScopedCFTypeRef<CFDictionaryRef> options(CFDictionaryCreate( + kCFAllocatorDefault, + (const void**)kCTFontCollectionRemoveDuplicatesOption, + (const void**)&values, + /*numValues=*/1, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + base::ScopedCFTypeRef<CTFontCollectionRef> collection( + CTFontCollectionCreateFromAvailableFonts(options)); + + base::ScopedCFTypeRef<CFArrayRef> font_descs( + CTFontCollectionCreateMatchingFontDescriptors(collection)); + + for (CFIndex i = 0; i < CFArrayGetCount(font_descs); ++i) { + CTFontDescriptorRef fd = base::mac::CFCast<CTFontDescriptorRef>( + CFArrayGetValueAtIndex(font_descs, i)); + NSString* postscript_name = GetString(fd, kCTFontNameAttribute); + NSString* full_name = GetLocalizedString(fd, kCTFontDisplayNameAttribute); + NSString* family = GetLocalizedString(fd, kCTFontFamilyNameAttribute); + output.push_back(FontEnumerationEntry{String(postscript_name), + String(full_name), String(family)}); + } + return output; + } +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.mm b/chromium/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.mm index 8d01704041c..cf25ba022f2 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.mm +++ b/chromium/third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.mm @@ -46,7 +46,7 @@ namespace { -static CGFloat toYosemiteFontWeight(blink::FontSelectionValue font_weight) { +static CGFloat toFontWeight(blink::FontSelectionValue font_weight) { static uint64_t ns_font_weights[] = { 0xbfe99999a0000000, // NSFontWeightUltraLight 0xbfe3333340000000, // NSFontWeightThin @@ -181,8 +181,7 @@ NSFont* MatchNSFontFamily(const AtomicString& desired_family_string, // On OSX 10.10+, the default system font has more weights. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunguarded-availability" - font = [NSFont systemFontOfSize:size - weight:toYosemiteFontWeight(desired_weight)]; + font = [NSFont systemFontOfSize:size weight:toFontWeight(desired_weight)]; #pragma clang diagnostic pop if (desired_traits & IMPORTANT_FONT_TRAITS) diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.h b/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.h index b1224beff2c..cd0e338cdb5 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.h +++ b/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.h @@ -31,6 +31,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_PLATFORM_DATA_MAC_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_MAC_FONT_PLATFORM_DATA_MAC_H_ +#include "third_party/blink/renderer/platform/fonts/font_optical_sizing.h" + #include <memory> @class NSFont; @@ -47,6 +49,7 @@ std::unique_ptr<FontPlatformData> FontPlatformDataFromNSFont( bool synthetic_bold, bool synthetic_italic, FontOrientation, + OpticalSizing, FontVariationSettings*); } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm b/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm index 16bcce4ba40..9bc59e46e5e 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm +++ b/chromium/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm @@ -27,21 +27,26 @@ #import <AvailabilityMacros.h> #include "base/mac/foundation_util.h" -#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsobject.h" #include "base/stl_util.h" #import "third_party/blink/public/platform/mac/web_sandbox_support.h" #import "third_party/blink/public/platform/platform.h" #import "third_party/blink/renderer/platform/fonts/font.h" #import "third_party/blink/renderer/platform/fonts/font_platform_data.h" +#import "third_party/blink/renderer/platform/fonts/mac/core_text_font_format_support.h" #import "third_party/blink/renderer/platform/fonts/opentype/font_settings.h" #import "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h" #import "third_party/blink/renderer/platform/web_test_support.h" #import "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #import "third_party/skia/include/core/SkFont.h" #import "third_party/skia/include/core/SkStream.h" +#import "third_party/skia/include/core/SkTypes.h" #import "third_party/skia/include/ports/SkTypeface_mac.h" +namespace { +constexpr SkFourByteTag kOpszTag = SkSetFourByteTag('o', 'p', 's', 'z'); +} + namespace blink { static bool CanLoadInProcess(NSFont* ns_font) { @@ -53,10 +58,10 @@ static bool CanLoadInProcess(NSFont* ns_font) { return ![font_name isEqualToString:@"LastResort"]; } -static CTFontDescriptorRef CascadeToLastResortFontDescriptor() { - static CTFontDescriptorRef descriptor; - if (descriptor) - return descriptor; +static CFDictionaryRef CascadeToLastResortFontAttributes() { + static CFDictionaryRef attributes; + if (attributes) + return attributes; base::ScopedCFTypeRef<CTFontDescriptorRef> last_resort( CTFontDescriptorCreateWithNameAndSize(CFSTR("LastResort"), 0)); @@ -67,13 +72,10 @@ static CTFontDescriptorRef CascadeToLastResortFontDescriptor() { const void* keys[] = {kCTFontCascadeListAttribute}; const void* values[] = {values_array}; - base::ScopedCFTypeRef<CFDictionaryRef> attributes(CFDictionaryCreate( + attributes = CFDictionaryCreate( kCFAllocatorDefault, keys, values, base::size(keys), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); - - descriptor = CTFontDescriptorCreateWithAttributes(attributes); - - return descriptor; + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + return attributes; } static sk_sp<SkTypeface> LoadFromBrowserProcess(NSFont* ns_font, @@ -88,20 +90,23 @@ static sk_sp<SkTypeface> LoadFromBrowserProcess(NSFont* ns_font, return nullptr; } - CGFontRef loaded_cg_font; + base::ScopedCFTypeRef<CTFontDescriptorRef> loaded_data_descriptor; uint32_t font_id; if (!sandbox_support->LoadFont(base::mac::NSToCFCast(ns_font), - &loaded_cg_font, &font_id)) { + &loaded_data_descriptor, &font_id)) { // TODO crbug.com/461279: Make this appear in the inspector console? DLOG(ERROR) << "Loading user font \"" << [[ns_font familyName] UTF8String] << "\" from non system location failed. Corrupt or missing font file?"; return nullptr; } - base::ScopedCFTypeRef<CGFontRef> cg_font(loaded_cg_font); - base::ScopedCFTypeRef<CTFontRef> ct_font(CTFontCreateWithGraphicsFont( - cg_font, text_size, 0, CascadeToLastResortFontDescriptor())); - sk_sp<SkTypeface> return_font(SkCreateTypefaceFromCTFont(ct_font, cg_font)); + + base::ScopedCFTypeRef<CTFontDescriptorRef> data_descriptor_with_cascade( + CTFontDescriptorCreateCopyWithAttributes( + loaded_data_descriptor, CascadeToLastResortFontAttributes())); + base::ScopedCFTypeRef<CTFontRef> ct_font(CTFontCreateWithFontDescriptor( + data_descriptor_with_cascade.get(), text_size, 0)); + sk_sp<SkTypeface> return_font = SkMakeTypefaceFromCTFont(ct_font); if (!return_font.get()) // TODO crbug.com/461279: Make this appear in the inspector console? @@ -117,11 +122,12 @@ std::unique_ptr<FontPlatformData> FontPlatformDataFromNSFont( bool synthetic_bold, bool synthetic_italic, FontOrientation orientation, + OpticalSizing optical_sizing, FontVariationSettings* variation_settings) { DCHECK(ns_font); sk_sp<SkTypeface> typeface; if (CanLoadInProcess(ns_font)) { - typeface.reset(SkCreateTypefaceFromCTFont(base::mac::NSToCFCast(ns_font))); + typeface = SkMakeTypefaceFromCTFont(base::mac::NSToCFCast(ns_font)); } else { // In process loading fails for cases where third party font manager // software registers fonts in non system locations such as /Library/Fonts @@ -129,26 +135,79 @@ std::unique_ptr<FontPlatformData> FontPlatformDataFromNSFont( typeface = LoadFromBrowserProcess(ns_font, size); } - if (variation_settings && variation_settings->size() < UINT16_MAX) { - SkFontArguments::Axis axes[variation_settings->size()]; - for (size_t i = 0; i < variation_settings->size(); ++i) { - AtomicString feature_tag = variation_settings->at(i).Tag(); - axes[i] = {AtomicStringToFourByteTag(feature_tag), - SkFloatToScalar(variation_settings->at(i).Value())}; + auto make_typeface_fontplatformdata = [&typeface, &size, &synthetic_bold, + &synthetic_italic, &orientation]() { + return std::make_unique<FontPlatformData>( + std::move(typeface), std::string(), size, synthetic_bold, + synthetic_italic, orientation); + }; + + wtf_size_t valid_configured_axes = + variation_settings && variation_settings->size() < UINT16_MAX + ? variation_settings->size() + : 0; + + // No variable font requested, return static font. + if (!valid_configured_axes && optical_sizing == kNoneOpticalSizing) + return make_typeface_fontplatformdata(); + + if (!typeface) + return nullptr; + + int existing_axes = typeface->getVariationDesignPosition(nullptr, 0); + // Don't apply variation parameters if the font does not have axes or we + // fail to retrieve the existing ones. + if (existing_axes <= 0) + return make_typeface_fontplatformdata(); + + Vector<SkFontArguments::VariationPosition::Coordinate> coordinates_to_set; + coordinates_to_set.resize(existing_axes); + + if (typeface->getVariationDesignPosition(coordinates_to_set.data(), + existing_axes) != existing_axes) { + return make_typeface_fontplatformdata(); + } + + // Iterate over the font's axes and find a missing tag from variation + // settings, special case opsz, track the number of axes reconfigured. + bool axes_reconfigured = false; + for (auto& coordinate : coordinates_to_set) { + // Set opsz to font size but allow having it overriden by + // font-variation-settings in case it has 'opsz'. + if (coordinate.axis == kOpszTag && optical_sizing == kAutoOpticalSizing) { + if (coordinate.value != SkFloatToScalar(size)) { + coordinate.value = SkFloatToScalar(size); + axes_reconfigured = true; + } + } + FontVariationAxis found_variation_setting(AtomicString(), 0); + if (variation_settings && + variation_settings->FindPair(FourByteTagToAtomicString(coordinate.axis), + &found_variation_setting)) { + if (coordinate.value != found_variation_setting.Value()) { + coordinate.value = found_variation_setting.Value(); + axes_reconfigured = true; + } } - sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); - // TODO crbug.com/670246: Refactor this to a future Skia API that acccepts - // axis parameters on system fonts directly. - typeface = fm->makeFromStream( - typeface->openStream(nullptr)->duplicate(), - SkFontArguments().setAxes(axes, variation_settings->size())); } - return std::make_unique<FontPlatformData>( - std::move(typeface), - std::string(), // family_ doesn't exist on Mac, this avoids conversion - // from NSString which requires including a //base header - size, synthetic_bold, synthetic_italic, orientation); + if (!axes_reconfigured) { + // No variable axes touched, return the previous typeface. + return make_typeface_fontplatformdata(); + } + + SkFontArguments::VariationPosition variation_design_position{ + coordinates_to_set.data(), coordinates_to_set.size()}; + + sk_sp<SkTypeface> cloned_typeface(typeface->makeClone( + SkFontArguments().setVariationDesignPosition(variation_design_position))); + + if (!cloned_typeface) { + // Applying varition parameters failed, return original typeface. + return make_typeface_fontplatformdata(); + } + typeface = cloned_typeface; + return make_typeface_fontplatformdata(); } void FontPlatformData::SetupSkFont(SkFont* skfont, diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.cc index 964943fa0b2..2c11652ffcf 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_format_check.cc @@ -4,32 +4,21 @@ #include "third_party/blink/renderer/platform/fonts/opentype/font_format_check.h" -#include "third_party/blink/renderer/platform/wtf/vector.h" -#include "third_party/skia/include/core/SkTypeface.h" - // Include HarfBuzz to have a cross-platform way to retrieve table tags without // having to rely on the platform being able to instantiate this font format. #include <hb.h> -namespace blink { - -namespace { - -struct HarfbuzzBlobDestroyer { - inline void operator()(hb_blob_t* blob) { hb_blob_destroy(blob); } -}; +#include "third_party/blink/renderer/platform/wtf/vector.h" +#include "third_party/harfbuzz-ng/utils/hb_scoped.h" +#include "third_party/skia/include/core/SkTypeface.h" -struct HarfbuzzFaceDestroyer { - inline void operator()(hb_face_t* face) { hb_face_destroy(face); } -}; -} // namespace +namespace blink { FontFormatCheck::FontFormatCheck(sk_sp<SkData> sk_data) { - std::unique_ptr<hb_blob_t, HarfbuzzBlobDestroyer> font_blob(hb_blob_create( + HbScoped<hb_blob_t> font_blob(hb_blob_create( reinterpret_cast<const char*>(sk_data->bytes()), sk_data->size(), HB_MEMORY_MODE_READONLY, nullptr, nullptr)); - std::unique_ptr<hb_face_t, HarfbuzzFaceDestroyer> face( - hb_face_create(font_blob.get(), 0)); + HbScoped<hb_face_t> face(hb_face_create(font_blob.get(), 0)); unsigned table_count = 0; table_count = hb_face_get_table_tags(face.get(), 0, nullptr, nullptr); diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.cc index 733d76d0db4..84c34be7a02 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.cc @@ -5,6 +5,7 @@ #include "third_party/blink/renderer/platform/fonts/opentype/font_settings.h" #include "third_party/blink/renderer/platform/wtf/hash_functions.h" +#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h" #include "third_party/blink/renderer/platform/wtf/text/string_hash.h" #include "third_party/blink/renderer/platform/wtf/text/string_hasher.h" @@ -16,6 +17,13 @@ uint32_t AtomicStringToFourByteTag(AtomicString tag) { return (((tag[0]) << 24) | ((tag[1]) << 16) | ((tag[2]) << 8) | (tag[3])); } +AtomicString FourByteTagToAtomicString(uint32_t tag) { + constexpr size_t tag_size = 4; + LChar tag_string[tag_size] = {(tag >> 24) & 0xFF, (tag >> 16) & 0xFF, + (tag >> 8) & 0xFF, tag & 0xFF}; + return AtomicString(tag_string, tag_size); +} + unsigned FontVariationSettings::GetHash() const { unsigned computed_hash = size() ? 5381 : 0; unsigned num_features = size(); diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.h b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.h index 10ab2aec21e..ea5b8c942f8 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.h +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings.h @@ -17,6 +17,7 @@ namespace blink { uint32_t AtomicStringToFourByteTag(AtomicString tag); +AtomicString FourByteTagToAtomicString(uint32_t tag); template <typename T> class FontTagValuePair { @@ -34,7 +35,7 @@ class FontTagValuePair { private: AtomicString tag_; - const T value_; + T value_; }; template <typename T> @@ -47,6 +48,7 @@ class FontSettings { bool operator==(const FontSettings& other) const { return list_ == other.list_; } + bool operator!=(const FontSettings& other) const { return !(*this == other); } String ToString() const { StringBuilder builder; wtf_size_t num_features = size(); @@ -60,6 +62,20 @@ class FontSettings { } return builder.ToString(); } + + bool FindPair(AtomicString tag, T* found_pair) const { + if (!found_pair) + return false; + + for (auto& pair : list_) { + if (pair.Tag() == tag) { + *found_pair = pair; + return true; + } + } + return false; + } + const T* begin() const { return list_.begin(); } const T* end() const { return list_.end(); } T* begin() { return list_.begin(); } diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings_test.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings_test.cc index 5c2fa7d94e8..6a3e0457ac3 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings_test.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/font_settings_test.cc @@ -61,4 +61,30 @@ TEST(FontSettingsTest, ToString) { } } +TEST(FontSettingsTest, FindTest) { + { + scoped_refptr<FontVariationSettings> settings = + MakeSettings<FontVariationSettings, FontVariationAxis>( + {FontVariationAxis{"a", 42}, FontVariationAxis{"b", 8118}}); + FontVariationAxis found_axis(AtomicString(), 0); + ASSERT_FALSE(settings->FindPair("c", &found_axis)); + ASSERT_FALSE(settings->FindPair("ddddd", &found_axis)); + ASSERT_FALSE(settings->FindPair("", &found_axis)); + ASSERT_EQ(found_axis.Value(), 0); + ASSERT_TRUE(settings->FindPair("a", &found_axis)); + ASSERT_EQ(found_axis.Tag(), AtomicString("a")); + ASSERT_EQ(found_axis.Value(), 42); + ASSERT_TRUE(settings->FindPair("b", &found_axis)); + ASSERT_EQ(found_axis.Tag(), AtomicString("b")); + ASSERT_EQ(found_axis.Value(), 8118); + } +} + +TEST(FontSettingsTest, FindTestEmpty) { + scoped_refptr<FontVariationSettings> settings = + MakeSettings<FontVariationSettings, FontVariationAxis>({}); + FontVariationAxis found_axis(AtomicString(), 0); + ASSERT_FALSE(settings->FindPair("a", &found_axis)); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc index 4c18c37138f..542975a52ba 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc @@ -2,10 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h" - -#include <hb-aat.h> +// clang-format off #include <hb.h> +#include <hb-aat.h> +// clang-format on + +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h" +#include "third_party/harfbuzz-ng/utils/hb_scoped.h" namespace blink { @@ -138,12 +141,10 @@ OpenTypeCapsSupport::FontFormat OpenTypeCapsSupport::GetFontFormat() const { hb_face_t* hb_face = hb_font_get_face( harfbuzz_face_->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout)); - std::unique_ptr<hb_blob_t, decltype(&hb_blob_destroy)> morx_blob( - hb_face_reference_table(hb_face, HB_TAG('m', 'o', 'r', 'x')), - hb_blob_destroy); - std::unique_ptr<hb_blob_t, decltype(&hb_blob_destroy)> mort_blob( - hb_face_reference_table(hb_face, HB_TAG('m', 'o', 'r', 't')), - hb_blob_destroy); + HbScoped<hb_blob_t> morx_blob( + hb_face_reference_table(hb_face, HB_TAG('m', 'o', 'r', 'x'))); + HbScoped<hb_blob_t> mort_blob( + hb_face_reference_table(hb_face, HB_TAG('m', 'o', 'r', 't'))); // TODO(crbug.com/911149): Use hb_aat_layout_has_substitution() for // has_morx_or_mort and hb_ot_layout_has_substitution() for has_gsub once is diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_stretch_data.h b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_stretch_data.h new file mode 100644 index 00000000000..af3cc059903 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_stretch_data.h @@ -0,0 +1,45 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_MATH_STRETCH_DATA_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_MATH_STRETCH_DATA_H_ + +#include "base/optional.h" +#include "third_party/blink/renderer/platform/fonts/glyph.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +class PLATFORM_EXPORT OpenTypeMathStretchData { + public: + enum StretchAxis : uint8_t { Horizontal = 0, Vertical = 1 }; + + // https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathGlyphVariantRecordFormat + // Note: Only variantGlyph is considered as using advanceMeasurement can lead + // to inconsistent values compared to what SimpleFontData returns. + using GlyphVariantRecord = Glyph; + + // https://docs.microsoft.com/en-us/typography/opentype/spec/math#glyphPartRecord + struct GlyphPartRecord { + Glyph glyph; + float start_connector_length; + float end_connector_length; + float full_advance; + bool is_extender; + }; + + // https://mathml-refresh.github.io/mathml-core/#the-glyphassembly-table + struct AssemblyParameters { + float connector_overlap{0}; + unsigned repetition_count{0}; + unsigned glyph_count{0}; + float stretch_size{0}; + Vector<GlyphPartRecord> parts; + }; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_MATH_STRETCH_DATA_H_ diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.cc new file mode 100644 index 00000000000..80d94665ab7 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.cc @@ -0,0 +1,258 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h" + +// clang-format off +#include <hb.h> +#include <hb-ot.h> +// clang-format on + +#include "base/bind.h" +#include "base/callback.h" +#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h" + +namespace { +// HarfBuzz' hb_position_t is a 16.16 fixed-point value. +float HarfBuzzUnitsToFloat(hb_position_t value) { + static const float kFloatToHbRatio = 1.0f / (1 << 16); + return kFloatToHbRatio * value; +} + +// Latin Modern, STIX Two, XITS, Asana, Deja Vu, Libertinus and TeX Gyre fonts +// provide at most 13 size variant and 5 assembly parts. +// See https://chromium-review.googlesource.com/c/chromium/src/+/2074678 +unsigned kMaxHarfBuzzRecords = 20; + +hb_direction_t HarfBuzzDirection( + blink::OpenTypeMathStretchData::StretchAxis stretch_axis) { + return stretch_axis == blink::OpenTypeMathStretchData::StretchAxis::Horizontal + ? HB_DIRECTION_LTR + : HB_DIRECTION_BTT; +} + +} // namespace + +namespace blink { + +bool OpenTypeMathSupport::HasMathData(const HarfBuzzFace* harfbuzz_face) { + if (!harfbuzz_face) + return false; + + hb_font_t* font = + harfbuzz_face->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout); + DCHECK(font); + hb_face_t* face = hb_font_get_face(font); + DCHECK(face); + + return hb_ot_math_has_data(face); +} + +base::Optional<float> OpenTypeMathSupport::MathConstant( + const HarfBuzzFace* harfbuzz_face, + MathConstants constant) { + if (!HasMathData(harfbuzz_face)) + return base::nullopt; + + hb_font_t* font = + harfbuzz_face->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout); + DCHECK(font); + + hb_position_t harfbuzz_value = hb_ot_math_get_constant( + font, static_cast<hb_ot_math_constant_t>(constant)); + + switch (constant) { + case kScriptPercentScaleDown: + case kScriptScriptPercentScaleDown: + case kRadicalDegreeBottomRaisePercent: + return base::Optional<float>(harfbuzz_value / 100.0); + case kDelimitedSubFormulaMinHeight: + case kDisplayOperatorMinHeight: + case kMathLeading: + case kAxisHeight: + case kAccentBaseHeight: + case kFlattenedAccentBaseHeight: + case kSubscriptShiftDown: + case kSubscriptTopMax: + case kSubscriptBaselineDropMin: + case kSuperscriptShiftUp: + case kSuperscriptShiftUpCramped: + case kSuperscriptBottomMin: + case kSuperscriptBaselineDropMax: + case kSubSuperscriptGapMin: + case kSuperscriptBottomMaxWithSubscript: + case kSpaceAfterScript: + case kUpperLimitGapMin: + case kUpperLimitBaselineRiseMin: + case kLowerLimitGapMin: + case kLowerLimitBaselineDropMin: + case kStackTopShiftUp: + case kStackTopDisplayStyleShiftUp: + case kStackBottomShiftDown: + case kStackBottomDisplayStyleShiftDown: + case kStackGapMin: + case kStackDisplayStyleGapMin: + case kStretchStackTopShiftUp: + case kStretchStackBottomShiftDown: + case kStretchStackGapAboveMin: + case kStretchStackGapBelowMin: + case kFractionNumeratorShiftUp: + case kFractionNumeratorDisplayStyleShiftUp: + case kFractionDenominatorShiftDown: + case kFractionDenominatorDisplayStyleShiftDown: + case kFractionNumeratorGapMin: + case kFractionNumDisplayStyleGapMin: + case kFractionRuleThickness: + case kFractionDenominatorGapMin: + case kFractionDenomDisplayStyleGapMin: + case kSkewedFractionHorizontalGap: + case kSkewedFractionVerticalGap: + case kOverbarVerticalGap: + case kOverbarRuleThickness: + case kOverbarExtraAscender: + case kUnderbarVerticalGap: + case kUnderbarRuleThickness: + case kUnderbarExtraDescender: + case kRadicalVerticalGap: + case kRadicalDisplayStyleVerticalGap: + case kRadicalRuleThickness: + case kRadicalExtraAscender: + case kRadicalKernBeforeDegree: + case kRadicalKernAfterDegree: + return base::Optional<float>(HarfBuzzUnitsToFloat(harfbuzz_value)); + default: + NOTREACHED(); + } + return base::nullopt; +} + +base::Optional<float> OpenTypeMathSupport::MathItalicCorrection( + const HarfBuzzFace* harfbuzz_face, + Glyph glyph) { + if (!harfbuzz_face) + return base::nullopt; + + hb_font_t* font = + harfbuzz_face->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout); + + return base::Optional<float>(HarfBuzzUnitsToFloat( + hb_ot_math_get_glyph_italics_correction(font, glyph))); +} + +template <typename HarfBuzzRecordType> +using GetHarfBuzzMathRecordGetter = + base::OnceCallback<unsigned int(hb_font_t* font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int* record_count, + HarfBuzzRecordType* record_array)>; + +template <typename HarfBuzzRecordType, typename RecordType> +using HarfBuzzMathRecordConverter = + base::RepeatingCallback<RecordType(HarfBuzzRecordType)>; + +template <typename HarfBuzzRecordType, typename RecordType> +Vector<RecordType> GetHarfBuzzMathRecord( + const HarfBuzzFace* harfbuzz_face, + Glyph base_glyph, + OpenTypeMathStretchData::StretchAxis stretch_axis, + GetHarfBuzzMathRecordGetter<HarfBuzzRecordType> getter, + HarfBuzzMathRecordConverter<HarfBuzzRecordType, RecordType> converter, + base::Optional<RecordType> prepended_record) { + hb_font_t* hb_font = + harfbuzz_face->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout); + DCHECK(hb_font); + + hb_direction_t hb_stretch_axis = HarfBuzzDirection(stretch_axis); + + // In practice, math fonts have, for a given base glyph and stretch axis only + // provide a few GlyphVariantRecords (size variants of increasing sizes) and + // GlyphPartRecords (parts of a glyph assembly) so it is safe to truncate + // the result vector to a small size. + HarfBuzzRecordType chunk[kMaxHarfBuzzRecords]; + unsigned int count = kMaxHarfBuzzRecords; + std::move(getter).Run(hb_font, base_glyph, hb_stretch_axis, + 0 /* start_offset */, &count, chunk); + + // Create the vector to the determined size and initialize it with the results + // converted from HarfBuzz's ones, prepending any optional record. + Vector<RecordType> result; + result.ReserveInitialCapacity(prepended_record ? count + 1 : count); + if (prepended_record) + result.push_back(*prepended_record); + for (unsigned i = 0; i < count; i++) { + result.push_back(converter.Run(chunk[i])); + } + return result; +} + +Vector<OpenTypeMathStretchData::GlyphVariantRecord> +OpenTypeMathSupport::GetGlyphVariantRecords( + const HarfBuzzFace* harfbuzz_face, + Glyph base_glyph, + OpenTypeMathStretchData::StretchAxis stretch_axis) { + DCHECK(harfbuzz_face); + DCHECK(base_glyph); + + auto getter = base::BindOnce(&hb_ot_math_get_glyph_variants); + auto converter = + base::BindRepeating([](hb_ot_math_glyph_variant_t record) + -> OpenTypeMathStretchData::GlyphVariantRecord { + return record.glyph; + }); + return GetHarfBuzzMathRecord( + harfbuzz_face, base_glyph, stretch_axis, std::move(getter), + std::move(converter), + base::Optional<OpenTypeMathStretchData::GlyphVariantRecord>(base_glyph)); +} + +Vector<OpenTypeMathStretchData::GlyphPartRecord> +OpenTypeMathSupport::GetGlyphPartRecords( + const HarfBuzzFace* harfbuzz_face, + Glyph base_glyph, + OpenTypeMathStretchData::StretchAxis stretch_axis, + float* italic_correction) { + DCHECK(harfbuzz_face); + DCHECK(base_glyph); + + auto getter = base::BindOnce( + [](hb_font_t* font, hb_codepoint_t glyph, hb_direction_t direction, + unsigned int start_offset, unsigned int* parts_count, + hb_ot_math_glyph_part_t* parts) { + hb_position_t italic_correction; + return hb_ot_math_get_glyph_assembly(font, glyph, direction, + start_offset, parts_count, parts, + &italic_correction); + }); + auto converter = + base::BindRepeating([](hb_ot_math_glyph_part_t record) + -> OpenTypeMathStretchData::GlyphPartRecord { + return {record.glyph, + HarfBuzzUnitsToFloat(record.start_connector_length), + HarfBuzzUnitsToFloat(record.end_connector_length), + HarfBuzzUnitsToFloat(record.full_advance), + record.flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER}; + }); + Vector<OpenTypeMathStretchData::GlyphPartRecord> parts = + GetHarfBuzzMathRecord( + harfbuzz_face, base_glyph, stretch_axis, std::move(getter), + std::move(converter), + base::Optional<OpenTypeMathStretchData::GlyphPartRecord>()); + if (italic_correction && !parts.IsEmpty()) { + hb_font_t* hb_font = + harfbuzz_face->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout); + // A GlyphAssembly subtable exists for the specified font, glyph and stretch + // axis since it has been possible to retrieve the GlyphPartRecords. This + // means that the following call is guaranteed to get an italic correction. + hb_position_t harfbuzz_italic_correction; + hb_ot_math_get_glyph_assembly(hb_font, base_glyph, + HarfBuzzDirection(stretch_axis), 0, nullptr, + nullptr, &harfbuzz_italic_correction); + *italic_correction = HarfBuzzUnitsToFloat(harfbuzz_italic_correction); + } + return parts; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h new file mode 100644 index 00000000000..633a4d11f2b --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h @@ -0,0 +1,120 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_MATH_SUPPORT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_MATH_SUPPORT_H_ + +#include "base/optional.h" +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_math_stretch_data.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +class HarfBuzzFace; + +class PLATFORM_EXPORT OpenTypeMathSupport { + public: + static bool HasMathData(const HarfBuzzFace*); + + // These constants are defined in the OpenType MATH table: + // https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathconstants-table + // Their values match the indices in the MathConstants subtable. + enum MathConstants { + kScriptPercentScaleDown = 0, + kScriptScriptPercentScaleDown = 1, + kDelimitedSubFormulaMinHeight = 2, + kDisplayOperatorMinHeight = 3, + kMathLeading = 4, + kAxisHeight = 5, + kAccentBaseHeight = 6, + kFlattenedAccentBaseHeight = 7, + kSubscriptShiftDown = 8, + kSubscriptTopMax = 9, + kSubscriptBaselineDropMin = 10, + kSuperscriptShiftUp = 11, + kSuperscriptShiftUpCramped = 12, + kSuperscriptBottomMin = 13, + kSuperscriptBaselineDropMax = 14, + kSubSuperscriptGapMin = 15, + kSuperscriptBottomMaxWithSubscript = 16, + kSpaceAfterScript = 17, + kUpperLimitGapMin = 18, + kUpperLimitBaselineRiseMin = 19, + kLowerLimitGapMin = 20, + kLowerLimitBaselineDropMin = 21, + kStackTopShiftUp = 22, + kStackTopDisplayStyleShiftUp = 23, + kStackBottomShiftDown = 24, + kStackBottomDisplayStyleShiftDown = 25, + kStackGapMin = 26, + kStackDisplayStyleGapMin = 27, + kStretchStackTopShiftUp = 28, + kStretchStackBottomShiftDown = 29, + kStretchStackGapAboveMin = 30, + kStretchStackGapBelowMin = 31, + kFractionNumeratorShiftUp = 32, + kFractionNumeratorDisplayStyleShiftUp = 33, + kFractionDenominatorShiftDown = 34, + kFractionDenominatorDisplayStyleShiftDown = 35, + kFractionNumeratorGapMin = 36, + kFractionNumDisplayStyleGapMin = 37, + kFractionRuleThickness = 38, + kFractionDenominatorGapMin = 39, + kFractionDenomDisplayStyleGapMin = 40, + kSkewedFractionHorizontalGap = 41, + kSkewedFractionVerticalGap = 42, + kOverbarVerticalGap = 43, + kOverbarRuleThickness = 44, + kOverbarExtraAscender = 45, + kUnderbarVerticalGap = 46, + kUnderbarRuleThickness = 47, + kUnderbarExtraDescender = 48, + kRadicalVerticalGap = 49, + kRadicalDisplayStyleVerticalGap = 50, + kRadicalRuleThickness = 51, + kRadicalExtraAscender = 52, + kRadicalKernBeforeDegree = 53, + kRadicalKernAfterDegree = 54, + kRadicalDegreeBottomRaisePercent = 55 + }; + + // Returns the value of the requested math constant or null if the font does + // not have any OpenType MATH table. All values are 16.16 fixed-point values + // converted to float except percentages (kScriptPercentScaleDown, + // kScriptScriptPercentScaleDown and kRadicalDegreeBottomRaisePercent) which + // are represented by a number between 0 and 1. + // https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathconstants-table + static base::Optional<float> MathConstant(const HarfBuzzFace*, MathConstants); + + // Returns the italic correction corresponding to the specified glyph or null + // if the font does not have any OpenType MATH table. This value provides an + // estimation of how much the glyph is slanted, which can be used e.g. when + // attaching scripts to the glyph. + // https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathitalicscorrectioninfo-table + static base::Optional<float> MathItalicCorrection(const HarfBuzzFace*, Glyph); + + // Returns a vector of GlyphVariantRecords corresponding to the specified + // glyph and stretch axis. The base glyph is always added as the first item. + // https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathvariants-table + static Vector<OpenTypeMathStretchData::GlyphVariantRecord> + GetGlyphVariantRecords(const HarfBuzzFace*, + Glyph base_glyph, + OpenTypeMathStretchData::StretchAxis); + + // Returns a vector of GlyphPartRecords corresponding to the specified + // glyph and stretch axis or an empty vector if there is no such construction. + // If the italic_correction parameter is specified and a construction is + // available, then it is set to the italic correction of the glyph assembly. + // https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathvariants-table + static Vector<OpenTypeMathStretchData::GlyphPartRecord> GetGlyphPartRecords( + const HarfBuzzFace*, + Glyph base_glyph, + OpenTypeMathStretchData::StretchAxis, + float* italic_correction = nullptr); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_OPENTYPE_OPEN_TYPE_MATH_SUPPORT_H_ diff --git a/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support_test.cc b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support_test.cc new file mode 100644 index 00000000000..c6493f48399 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/fonts/opentype/open_type_math_support_test.cc @@ -0,0 +1,466 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h" +#include "base/memory/scoped_refptr.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/fonts/font.h" +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_types.h" +#include "third_party/blink/renderer/platform/testing/font_test_helpers.h" +#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" + +namespace { +const UChar32 kLeftBraceCodePoint = '{'; +const UChar32 kOverBraceCodePoint = 0x23DE; +const UChar32 kArabicMathOperatorHahWithDalCodePoint = 0x1EEF1; +const UChar32 kNAryWhiteVerticalBarCodePoint = 0x2AFF; +} // namespace + +namespace blink { + +class OpenTypeMathSupportTest : public testing::Test { + protected: + void SetUp() override { + font_description.SetComputedSize(10.0); + font = Font(font_description); + } + + void TearDown() override {} + + Font CreateMathFont(const String& name, float size = 1000) { + FontDescription::VariantLigatures ligatures; + return blink::test::CreateTestFont( + "MathTestFont", + blink::test::BlinkWebTestsFontsTestDataPath(String("math/") + name), + size, &ligatures); + } + + bool HasMathData(const String& name) { + return OpenTypeMathSupport::HasMathData( + CreateMathFont(name).PrimaryFont()->PlatformData().GetHarfBuzzFace()); + } + + base::Optional<float> MathConstant( + const String& name, + OpenTypeMathSupport::MathConstants constant) { + Font math = CreateMathFont(name); + return OpenTypeMathSupport::MathConstant( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), constant); + } + + FontDescription font_description; + Font font; +}; + +TEST_F(OpenTypeMathSupportTest, HasMathData) { + // Null parameter. + EXPECT_FALSE(OpenTypeMathSupport::HasMathData(nullptr)); + + // Font without a MATH table. + EXPECT_FALSE(HasMathData("math-text.woff")); + + // Font with a MATH table. + EXPECT_TRUE(HasMathData("axisheight5000-verticalarrow14000.woff")); +} + +TEST_F(OpenTypeMathSupportTest, MathConstantNullOpt) { + Font math_text = CreateMathFont("math-text.woff"); + + for (int i = OpenTypeMathSupport::MathConstants::kScriptPercentScaleDown; + i <= + OpenTypeMathSupport::MathConstants::kRadicalDegreeBottomRaisePercent; + i++) { + auto math_constant = static_cast<OpenTypeMathSupport::MathConstants>(i); + + // Null parameter. + EXPECT_FALSE(OpenTypeMathSupport::MathConstant(nullptr, math_constant)); + + // Font without a MATH table. + EXPECT_FALSE(OpenTypeMathSupport::MathConstant( + math_text.PrimaryFont()->PlatformData().GetHarfBuzzFace(), + math_constant)); + } +} + +// See third_party/blink/web_tests/external/wpt/mathml/tools/percentscaledown.py +TEST_F(OpenTypeMathSupportTest, MathConstantPercentScaleDown) { + { + auto result = MathConstant( + "scriptpercentscaledown80-scriptscriptpercentscaledown0.woff", + OpenTypeMathSupport::MathConstants::kScriptPercentScaleDown); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, .8); + } + + { + auto result = MathConstant( + "scriptpercentscaledown0-scriptscriptpercentscaledown40.woff", + OpenTypeMathSupport::MathConstants::kScriptScriptPercentScaleDown); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, .4); + } +} + +// See third_party/blink/web_tests/external/wpt/mathml/tools/fractions.py +TEST_F(OpenTypeMathSupportTest, MathConstantFractions) { + { + auto result = MathConstant( + "fraction-numeratorshiftup11000-axisheight1000-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kFractionNumeratorShiftUp); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 11000); + } + + { + auto result = MathConstant( + "fraction-numeratordisplaystyleshiftup2000-axisheight1000-" + "rulethickness1000.woff", + OpenTypeMathSupport::MathConstants:: + kFractionNumeratorDisplayStyleShiftUp); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 2000); + } + + { + auto result = MathConstant( + "fraction-denominatorshiftdown3000-axisheight1000-rulethickness1000." + "woff", + OpenTypeMathSupport::MathConstants::kFractionDenominatorShiftDown); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 3000); + } + + { + auto result = MathConstant( + "fraction-denominatordisplaystyleshiftdown6000-axisheight1000-" + "rulethickness1000.woff", + OpenTypeMathSupport::MathConstants:: + kFractionDenominatorDisplayStyleShiftDown); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 6000); + } + + { + auto result = MathConstant( + "fraction-numeratorgapmin9000-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kFractionNumeratorGapMin); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 9000); + } + + { + auto result = MathConstant( + "fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kFractionNumDisplayStyleGapMin); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 8000); + } + + { + auto result = MathConstant( + "fraction-rulethickness10000.woff", + OpenTypeMathSupport::MathConstants::kFractionRuleThickness); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 10000); + } + + { + auto result = MathConstant( + "fraction-denominatorgapmin4000-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kFractionDenominatorGapMin); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 4000); + } + + { + auto result = MathConstant( + "fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kFractionDenomDisplayStyleGapMin); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 5000); + } +} + +// See third_party/blink/web_tests/external/wpt/mathml/tools/radicals.py +TEST_F(OpenTypeMathSupportTest, MathConstantRadicals) { + { + auto result = MathConstant( + "radical-degreebottomraisepercent25-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kRadicalDegreeBottomRaisePercent); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, .25); + } + + { + auto result = + MathConstant("radical-verticalgap6000-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kRadicalVerticalGap); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 6000); + } + + { + auto result = MathConstant( + "radical-displaystyleverticalgap7000-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kRadicalDisplayStyleVerticalGap); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 7000); + } + + { + auto result = + MathConstant("radical-rulethickness8000.woff", + OpenTypeMathSupport::MathConstants::kRadicalRuleThickness); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 8000); + } + + { + auto result = + MathConstant("radical-extraascender3000-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kRadicalExtraAscender); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 3000); + } + + { + auto result = MathConstant( + "radical-kernbeforedegree4000-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kRadicalKernBeforeDegree); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, 4000); + } + + { + auto result = MathConstant( + "radical-kernafterdegreeminus5000-rulethickness1000.woff", + OpenTypeMathSupport::MathConstants::kRadicalKernAfterDegree); + EXPECT_TRUE(result); + EXPECT_FLOAT_EQ(*result, -5000); + } +} + +TEST_F(OpenTypeMathSupportTest, MathVariantsWithoutTable) { + Font math = CreateMathFont("math-text.woff"); + auto glyph = math.PrimaryFont()->GlyphForCharacter('A'); + + // Horizontal variants. + { + auto variants = OpenTypeMathSupport::GetGlyphVariantRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), glyph, + OpenTypeMathStretchData::StretchAxis::Horizontal); + EXPECT_EQ(variants.size(), 1u); + EXPECT_EQ(variants[0], glyph); + } + + // Vertical variants. + { + auto variants = OpenTypeMathSupport::GetGlyphVariantRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), glyph, + OpenTypeMathStretchData::StretchAxis::Vertical); + EXPECT_EQ(variants.size(), 1u); + EXPECT_EQ(variants[0], glyph); + } + + // Horizontal parts. + { + auto parts = OpenTypeMathSupport::GetGlyphPartRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), glyph, + OpenTypeMathStretchData::StretchAxis::Horizontal); + EXPECT_TRUE(parts.IsEmpty()); + } + + // // Vertical parts. + { + auto parts = OpenTypeMathSupport::GetGlyphPartRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), glyph, + OpenTypeMathStretchData::StretchAxis::Vertical); + EXPECT_TRUE(parts.IsEmpty()); + } +} + +TEST_F(OpenTypeMathSupportTest, MathVariantsWithTable) { + // operators.woff contains stretchy operators from the MathML operator + // dictionary (including left and over braces) represented by squares. + // It also contains glyphs h0, h1, h2, h3 and v0, v1, v2, v3 that are + // respectively horizontal and vertical rectangles of increasing size. + // The MathVariants table contains the following data for horizontal + // (respectively vertical) operators: + // - Glyph variants: h0, h1, h2, h3 (respectively v0, v1, v2, v3). + // - Glyph parts: non-extender h2 and extender h1 (respectively v2 and v1). + // For details, see createSizeVariants() and createStretchy() from + // third_party/blink/web_tests/external/wpt/mathml/tools/operator-dictionary.py + + Font math = CreateMathFont("operators.woff"); + auto left_brace = math.PrimaryFont()->GlyphForCharacter(kLeftBraceCodePoint); + auto over_brace = math.PrimaryFont()->GlyphForCharacter(kOverBraceCodePoint); + + // Calculate glyph indices from the last unicode character in the font. + // TODO(https://crbug.com/1057596): Find a better way to access these glyph + // indices. + auto v0 = math.PrimaryFont()->GlyphForCharacter( + kArabicMathOperatorHahWithDalCodePoint) + + 1; + auto h0 = v0 + 1; + auto v1 = h0 + 1; + auto h1 = v1 + 1; + auto v2 = h1 + 1; + auto h2 = v2 + 1; + auto v3 = h2 + 1; + auto h3 = v3 + 1; + + // Vertical variants for vertical operator. + { + auto variants = OpenTypeMathSupport::GetGlyphVariantRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), left_brace, + OpenTypeMathStretchData::StretchAxis::Vertical); + EXPECT_EQ(variants.size(), 5u); + EXPECT_EQ(variants[0], left_brace); + EXPECT_EQ(variants[1], v0); + EXPECT_EQ(variants[2], v1); + EXPECT_EQ(variants[3], v2); + EXPECT_EQ(variants[4], v3); + } + + // Horizontal variants for vertical operator. + { + auto variants = OpenTypeMathSupport::GetGlyphVariantRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), left_brace, + OpenTypeMathStretchData::StretchAxis::Horizontal); + EXPECT_EQ(variants.size(), 1u); + EXPECT_EQ(variants[0], left_brace); + } + + // Horizontal variants for horizontal operator. + { + auto variants = OpenTypeMathSupport::GetGlyphVariantRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), over_brace, + OpenTypeMathStretchData::StretchAxis::Horizontal); + EXPECT_EQ(variants.size(), 5u); + EXPECT_EQ(variants[0], over_brace); + EXPECT_EQ(variants[1], h0); + EXPECT_EQ(variants[2], h1); + EXPECT_EQ(variants[3], h2); + EXPECT_EQ(variants[4], h3); + } + + // Vertical variants for horizontal operator. + { + auto variants = OpenTypeMathSupport::GetGlyphVariantRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), over_brace, + OpenTypeMathStretchData::StretchAxis::Vertical); + EXPECT_EQ(variants.size(), 1u); + EXPECT_EQ(variants[0], over_brace); + } + + // Vertical parts for vertical operator. + { + auto parts = OpenTypeMathSupport::GetGlyphPartRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), left_brace, + OpenTypeMathStretchData::StretchAxis::Vertical); + EXPECT_EQ(parts.size(), 2u); + EXPECT_EQ(parts[0].glyph, v2); + EXPECT_FLOAT_EQ(parts[0].start_connector_length, 0); + EXPECT_FLOAT_EQ(parts[0].end_connector_length, 1000); + EXPECT_FLOAT_EQ(parts[0].full_advance, 3000); + EXPECT_EQ(parts[0].is_extender, false); + EXPECT_EQ(parts[1].glyph, v1); + EXPECT_FLOAT_EQ(parts[1].start_connector_length, 1000); + EXPECT_FLOAT_EQ(parts[1].end_connector_length, 1000); + EXPECT_FLOAT_EQ(parts[1].full_advance, 2000); + EXPECT_EQ(parts[1].is_extender, true); + } + + // Horizontal parts for vertical operator. + { + auto parts = OpenTypeMathSupport::GetGlyphPartRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), left_brace, + OpenTypeMathStretchData::StretchAxis::Horizontal); + EXPECT_TRUE(parts.IsEmpty()); + } + + // Horizontal parts for horizontal operator. + { + auto parts = OpenTypeMathSupport::GetGlyphPartRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), over_brace, + OpenTypeMathStretchData::StretchAxis::Horizontal); + + EXPECT_EQ(parts.size(), 2u); + EXPECT_EQ(parts[0].glyph, h2); + EXPECT_FLOAT_EQ(parts[0].start_connector_length, 0); + EXPECT_FLOAT_EQ(parts[0].end_connector_length, 1000); + EXPECT_FLOAT_EQ(parts[0].full_advance, 3000); + EXPECT_EQ(parts[0].is_extender, false); + + EXPECT_EQ(parts[1].glyph, h1); + EXPECT_FLOAT_EQ(parts[1].start_connector_length, 1000); + EXPECT_FLOAT_EQ(parts[1].end_connector_length, 1000); + EXPECT_FLOAT_EQ(parts[1].full_advance, 2000); + EXPECT_EQ(parts[1].is_extender, true); + } + + // Vertical parts for horizontal operator. + { + auto parts = OpenTypeMathSupport::GetGlyphPartRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), over_brace, + OpenTypeMathStretchData::StretchAxis::Vertical); + EXPECT_TRUE(parts.IsEmpty()); + } +} + +// See third_party/blink/web_tests/external/wpt/mathml/tools/largeop.py +TEST_F(OpenTypeMathSupportTest, MathItalicCorrection) { + { + Font math = CreateMathFont( + "largeop-displayoperatorminheight2000-2AFF-italiccorrection3000.woff"); + Glyph base_glyph = + math.PrimaryFont()->GlyphForCharacter(kNAryWhiteVerticalBarCodePoint); + + // Retrieve the glyph with italic correction. + Vector<OpenTypeMathStretchData::GlyphVariantRecord> variants = + OpenTypeMathSupport::GetGlyphVariantRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), base_glyph, + OpenTypeMathStretchData::StretchAxis::Vertical); + EXPECT_EQ(variants.size(), 3u); + EXPECT_EQ(variants[0], base_glyph); + EXPECT_EQ(variants[1], base_glyph); + Glyph glyph_with_italic_correction = variants[2]; + + // MathItalicCorrection with a value. + base::Optional<float> glyph_with_italic_correction_value = + OpenTypeMathSupport::MathItalicCorrection( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), + glyph_with_italic_correction); + EXPECT_TRUE(glyph_with_italic_correction_value); + EXPECT_FLOAT_EQ(*glyph_with_italic_correction_value, 3000); + + // GetGlyphPartRecords does not set italic correction when there is no + // construction available. + float italic_correction = -1000; + Vector<OpenTypeMathStretchData::GlyphPartRecord> parts = + OpenTypeMathSupport::GetGlyphPartRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), base_glyph, + OpenTypeMathStretchData::StretchAxis::Vertical, &italic_correction); + EXPECT_TRUE(parts.IsEmpty()); + EXPECT_FLOAT_EQ(italic_correction, -1000); + } + + { + Font math = CreateMathFont( + "largeop-displayoperatorminheight7000-2AFF-italiccorrection5000.woff"); + Glyph base_glyph = + math.PrimaryFont()->GlyphForCharacter(kNAryWhiteVerticalBarCodePoint); + + // OpenTypeMathSupport::GetGlyphPartRecords sets italic correction. + float italic_correction = -1000; + Vector<OpenTypeMathStretchData::GlyphPartRecord> parts = + OpenTypeMathSupport::GetGlyphPartRecords( + math.PrimaryFont()->PlatformData().GetHarfBuzzFace(), base_glyph, + OpenTypeMathStretchData::StretchAxis::Vertical, &italic_correction); + EXPECT_EQ(parts.size(), 3u); + EXPECT_FLOAT_EQ(italic_correction, 5000); + } +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.h b/chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.h index 61b2872c97b..17cc3ed89ec 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.h +++ b/chromium/third_party/blink/renderer/platform/fonts/segmented_font_data.h @@ -29,6 +29,7 @@ #include "third_party/blink/renderer/platform/fonts/font_data.h" #include "third_party/blink/renderer/platform/fonts/font_data_for_range_set.h" #include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" class SimpleFontData; @@ -61,7 +62,12 @@ class PLATFORM_EXPORT SegmentedFontData : public FontData { Vector<scoped_refptr<FontDataForRangeSet>, 1> faces_; }; -DEFINE_FONT_DATA_TYPE_CASTS(SegmentedFontData, true); +template <> +struct DowncastTraits<SegmentedFontData> { + static bool AllowFrom(const FontData& fontData) { + return fontData.IsSegmented(); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc index 5b020364b1a..a9886bcd928 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc @@ -13,8 +13,8 @@ scoped_refptr<const ShapeResult> CachingWordShapeIterator::ShapeWordWithoutSpacing(const TextRun& word_run, const Font* font) { ShapeCacheEntry* cache_entry = shape_cache_->Add(word_run, ShapeCacheEntry()); - if (cache_entry && cache_entry->shape_result_) - return cache_entry->shape_result_; + if (cache_entry && *cache_entry) + return *cache_entry; const String word_text = word_run.NormalizedUTF16(); HarfBuzzShaper shaper(word_text); @@ -25,7 +25,7 @@ CachingWordShapeIterator::ShapeWordWithoutSpacing(const TextRun& word_run, shape_result->SetDeprecatedInkBounds(shape_result->ComputeInkBounds()); if (cache_entry) - cache_entry->shape_result_ = shape_result; + *cache_entry = shape_result; return shape_result; } diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc index 7e4e2bc6398..68972169da9 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc @@ -37,7 +37,7 @@ namespace blink { ShapeCache* CachingWordShaper::GetShapeCache() const { - return font_.font_fallback_list_->GetShapeCache(font_.font_description_); + return font_.GetShapeCache(); } // Returns the total advance width of the TextRun run. If glyph_bounds @@ -157,7 +157,7 @@ GlyphData CachingWordShaper::EmphasisMarkGlyphData( ShapeResultBuffer buffer; ShapeResultsForRun(GetShapeCache(), &font_, emphasis_mark_run, &buffer); - return buffer.EmphasisMarkGlyphData(font_.font_description_); + return buffer.EmphasisMarkGlyphData(font_.GetFontDescription()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc index 89fea7e10c3..46137b2efdd 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper_test.cc @@ -24,7 +24,6 @@ class CachingWordShaperTest : public testing::Test { font_description.SetGenericFamily(FontDescription::kStandardFamily); font = Font(font_description); - font.Update(nullptr); ASSERT_TRUE(font.CanShapeWordByWord()); fallback_fonts = nullptr; cache = std::make_unique<ShapeCache>(); @@ -390,7 +389,6 @@ TEST_F(CachingWordShaperTest, TextOrientationFallbackShouldNotInFallbackList) { font_description.SetOrientation(FontOrientation::kVerticalMixed); Font vertical_mixed_font = Font(font_description); - vertical_mixed_font.Update(nullptr); ASSERT_TRUE(vertical_mixed_font.CanShapeWordByWord()); CachingWordShaper shaper(vertical_mixed_font); diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc index 4d4a8227312..97fa173bb8b 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc @@ -30,8 +30,10 @@ #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h" -#include <hb-ot.h> +// clang-format off #include <hb.h> +#include <hb-ot.h> +// clang-format on #include <memory> @@ -50,6 +52,7 @@ #include "third_party/blink/renderer/platform/wtf/hash_map.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" +#include "third_party/harfbuzz-ng/utils/hb_scoped.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPoint.h" @@ -59,34 +62,40 @@ namespace blink { -void HbFontDeleter::operator()(hb_font_t* font) { - if (font) - hb_font_destroy(font); -} +namespace { -void HbFaceDeleter::operator()(hb_face_t* face) { - if (face) - hb_face_destroy(face); -} +#if defined(OS_MACOSX) +void DetermineTrakSbix(SkTypeface* typeface, bool* has_trak, bool* has_sbix) { + int num_tags = typeface->countTables(); + + SkFontTableTag tags[num_tags]; + + int returned_tags = typeface->getTableTags(tags); + DCHECK_EQ(num_tags, returned_tags); -struct HbSetDeleter { - void operator()(hb_set_t* set) { - if (set) - hb_set_destroy(set); + for (auto& tag : tags) { + if (tag == SkSetFourByteTag('t', 'r', 'a', 'k')) + *has_trak = true; + if (tag == SkSetFourByteTag('s', 'b', 'i', 'x')) + *has_sbix = true; } -}; +} +#endif -using HbSetUniquePtr = std::unique_ptr<hb_set_t, HbSetDeleter>; +} // namespace -static scoped_refptr<HbFontCacheEntry> CreateHbFontCacheEntry(hb_face_t*); +static scoped_refptr<HbFontCacheEntry> CreateHbFontCacheEntry( + hb_face_t*, + SkTypeface* typefaces); HarfBuzzFace::HarfBuzzFace(FontPlatformData* platform_data, uint64_t unique_id) : platform_data_(platform_data), unique_id_(unique_id) { HarfBuzzFontCache::AddResult result = - FontGlobalContext::GetHarfBuzzFontCache().insert(unique_id_, nullptr); + FontGlobalContext::GetHarfBuzzFontCache()->insert(unique_id_, nullptr); if (result.is_new_entry) { - HbFaceUniquePtr face(CreateFace()); - result.stored_value->value = CreateHbFontCacheEntry(face.get()); + HbScoped<hb_face_t> face(CreateFace()); + result.stored_value->value = + CreateHbFontCacheEntry(face.get(), platform_data->Typeface()); } result.stored_value->value->AddRef(); unscaled_font_ = result.stored_value->value->HbFont(); @@ -94,13 +103,14 @@ HarfBuzzFace::HarfBuzzFace(FontPlatformData* platform_data, uint64_t unique_id) } HarfBuzzFace::~HarfBuzzFace() { - HarfBuzzFontCache::iterator result = - FontGlobalContext::GetHarfBuzzFontCache().find(unique_id_); - SECURITY_DCHECK(result != FontGlobalContext::GetHarfBuzzFontCache().end()); + HarfBuzzFontCache* harfbuzz_font_cache = + FontGlobalContext::GetHarfBuzzFontCache(); + HarfBuzzFontCache::iterator result = harfbuzz_font_cache->find(unique_id_); + SECURITY_DCHECK(result != harfbuzz_font_cache->end()); DCHECK(!result.Get()->value->HasOneRef()); result.Get()->value->Release(); if (result.Get()->value->HasOneRef()) - FontGlobalContext::GetHarfBuzzFontCache().erase(unique_id_); + harfbuzz_font_cache->erase(unique_id_); } static hb_bool_t HarfBuzzGetGlyph(hb_font_t* hb_font, @@ -228,7 +238,7 @@ bool HarfBuzzFace::HasSpaceInLigaturesOrKerning(TypesettingFeatures features) { const hb_codepoint_t kInvalidCodepoint = static_cast<hb_codepoint_t>(-1); hb_codepoint_t space = kInvalidCodepoint; - HbSetUniquePtr glyphs(hb_set_create()); + HbScoped<hb_set_t> glyphs(hb_set_create()); // Check whether computing is needed and compute for gpos/gsub. if (features & kKerning && @@ -280,31 +290,43 @@ bool HarfBuzzFace::ShouldSubpixelPosition() { return harfbuzz_font_data_->font_.isSubpixel(); } -static hb_font_funcs_t* HarfBuzzSkiaGetFontFuncs() { - hb_font_funcs_t* funcs = FontGlobalContext::GetHarfBuzzFontFuncs(); +static hb_font_funcs_t* create_populated_hb_font_funcs( + FontGlobalContext::HorizontalAdvanceSource horizontal_advance_source) { + hb_font_funcs_t* funcs = hb_font_funcs_create(); - // We don't set callback functions which we can't support. - // HarfBuzz will use the fallback implementation if they aren't set. - if (!funcs) { - funcs = hb_font_funcs_create(); - hb_font_funcs_set_variation_glyph_func(funcs, HarfBuzzGetGlyph, nullptr, - nullptr); - hb_font_funcs_set_nominal_glyph_func(funcs, HarfBuzzGetNominalGlyph, - nullptr, nullptr); + if (horizontal_advance_source == FontGlobalContext::kSkiaHorizontalAdvances) { hb_font_funcs_set_glyph_h_advance_func( funcs, HarfBuzzGetGlyphHorizontalAdvance, nullptr, nullptr); hb_font_funcs_set_glyph_h_advances_func( funcs, HarfBuzzGetGlyphHorizontalAdvances, nullptr, nullptr); - // TODO(https://crbug.com/899718): Replace vertical metrics callbacks with - // HarfBuzz VORG/VMTX internal implementation by deregistering those. - hb_font_funcs_set_glyph_v_advance_func( - funcs, HarfBuzzGetGlyphVerticalAdvance, nullptr, nullptr); - hb_font_funcs_set_glyph_v_origin_func(funcs, HarfBuzzGetGlyphVerticalOrigin, - nullptr, nullptr); - hb_font_funcs_set_glyph_extents_func(funcs, HarfBuzzGetGlyphExtents, + } + hb_font_funcs_set_variation_glyph_func(funcs, HarfBuzzGetGlyph, nullptr, + nullptr); + hb_font_funcs_set_nominal_glyph_func(funcs, HarfBuzzGetNominalGlyph, nullptr, + nullptr); + // TODO(https://crbug.com/899718): Replace vertical metrics callbacks with + // HarfBuzz VORG/VMTX internal implementation by deregistering those. + hb_font_funcs_set_glyph_v_advance_func(funcs, HarfBuzzGetGlyphVerticalAdvance, nullptr, nullptr); - hb_font_funcs_make_immutable(funcs); - FontGlobalContext::SetHarfBuzzFontFuncs(funcs); + hb_font_funcs_set_glyph_v_origin_func(funcs, HarfBuzzGetGlyphVerticalOrigin, + nullptr, nullptr); + hb_font_funcs_set_glyph_extents_func(funcs, HarfBuzzGetGlyphExtents, nullptr, + nullptr); + + hb_font_funcs_make_immutable(funcs); + return funcs; +} + +static hb_font_funcs_t* HarfBuzzSkiaGetFontFuncs( + FontGlobalContext::HorizontalAdvanceSource advance_source) { + hb_font_funcs_t* funcs = + FontGlobalContext::GetHarfBuzzFontFuncs(advance_source); + + // We don't set callback functions which we can't support. + // HarfBuzz will use the fallback implementation if they aren't set. + if (!funcs) { + funcs = create_populated_hb_font_funcs(advance_source); + FontGlobalContext::SetHarfBuzzFontFuncs(advance_source, funcs); } DCHECK(funcs); return funcs; @@ -360,11 +382,10 @@ hb_face_t* HarfBuzzFace::CreateFace() { if (tf_stream && tf_stream->getMemoryBase()) { const void* tf_memory = tf_stream->getMemoryBase(); size_t tf_size = tf_stream->getLength(); - std::unique_ptr<hb_blob_t, void (*)(hb_blob_t*)> face_blob( + HbScoped<hb_blob_t> face_blob( hb_blob_create(reinterpret_cast<const char*>(tf_memory), SafeCast<unsigned int>(tf_size), HB_MEMORY_MODE_READONLY, - tf_stream.release(), DeleteTypefaceStream), - hb_blob_destroy); + tf_stream.release(), DeleteTypefaceStream)); face = hb_face_create(face_blob.get(), ttc_index); } #endif @@ -382,15 +403,39 @@ hb_face_t* HarfBuzzFace::CreateFace() { return face; } -scoped_refptr<HbFontCacheEntry> CreateHbFontCacheEntry(hb_face_t* face) { - HbFontUniquePtr ot_font(hb_font_create(face)); +scoped_refptr<HbFontCacheEntry> CreateHbFontCacheEntry(hb_face_t* face, + SkTypeface* typeface) { + HbScoped<hb_font_t> ot_font(hb_font_create(face)); hb_ot_font_set_funcs(ot_font.get()); + + int axis_count = typeface->getVariationDesignPosition(nullptr, 0); + if (axis_count > 0) { + Vector<SkFontArguments::VariationPosition::Coordinate> axis_values; + axis_values.resize(axis_count); + if (typeface->getVariationDesignPosition(axis_values.data(), + axis_values.size()) > 0) { + hb_font_set_variations( + ot_font.get(), reinterpret_cast<hb_variation_t*>(axis_values.data()), + axis_values.size()); + } + } + // Creating a sub font means that non-available functions // are found from the parent. hb_font_t* unscaled_font = hb_font_create_sub_font(ot_font.get()); scoped_refptr<HbFontCacheEntry> cache_entry = HbFontCacheEntry::Create(unscaled_font); - hb_font_set_funcs(unscaled_font, HarfBuzzSkiaGetFontFuncs(), + + FontGlobalContext::HorizontalAdvanceSource advance_source = + FontGlobalContext::kSkiaHorizontalAdvances; +#if defined(OS_MACOSX) + bool has_trak = false; + bool has_sbix = false; + DetermineTrakSbix(typeface, &has_trak, &has_sbix); + if (has_trak && !has_sbix) + advance_source = FontGlobalContext::kHarfBuzzHorizontalAdvances; +#endif + hb_font_set_funcs(unscaled_font, HarfBuzzSkiaGetFontFuncs(advance_source), cache_entry->HbFontData(), nullptr); return cache_entry; } @@ -423,19 +468,6 @@ hb_font_t* HarfBuzzFace::GetScaledFont( // equivalent of CSS pixels here. hb_font_set_ptem(unscaled_font_, platform_data_->size()); - SkTypeface* typeface = harfbuzz_font_data_->font_.getTypeface(); - int axis_count = typeface->getVariationDesignPosition(nullptr, 0); - if (axis_count > 0) { - Vector<SkFontArguments::VariationPosition::Coordinate> axis_values; - axis_values.resize(axis_count); - if (typeface->getVariationDesignPosition(axis_values.data(), - axis_values.size()) > 0) { - hb_font_set_variations( - unscaled_font_, reinterpret_cast<hb_variation_t*>(axis_values.data()), - axis_values.size()); - } - } - return unscaled_font_; } diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.cc index 02690dd9854..40ad779aaed 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.cc @@ -8,7 +8,7 @@ namespace blink { HbFontCacheEntry::HbFontCacheEntry(hb_font_t* font) - : hb_font_(HbFontUniquePtr(font)), + : hb_font_(HbScoped<hb_font_t>(font)), hb_font_data_(std::make_unique<HarfBuzzFontData>()) {} HbFontCacheEntry::~HbFontCacheEntry() = default; diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h index 5826f0a6bba..d8526a9352a 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h @@ -5,30 +5,18 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_FONT_CACHE_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_FONT_CACHE_H_ +#include <hb.h> + #include <memory> #include "third_party/blink/renderer/platform/fonts/font_metrics.h" #include "third_party/blink/renderer/platform/fonts/unicode_range_set.h" - -struct hb_font_t; -struct hb_face_t; +#include "third_party/harfbuzz-ng/utils/hb_scoped.h" namespace blink { struct HarfBuzzFontData; -struct HbFontDeleter { - void operator()(hb_font_t* font); -}; - -using HbFontUniquePtr = std::unique_ptr<hb_font_t, HbFontDeleter>; - -struct HbFaceDeleter { - void operator()(hb_face_t* face); -}; - -using HbFaceUniquePtr = std::unique_ptr<hb_face_t, HbFaceDeleter>; - // Though we have FontCache class, which provides the cache mechanism for // WebKit's font objects, we also need additional caching layer for HarfBuzz to // reduce the number of hb_font_t objects created. Without it, we would create @@ -50,15 +38,17 @@ class HbFontCacheEntry : public RefCounted<HbFontCacheEntry> { private: explicit HbFontCacheEntry(hb_font_t* font); - HbFontUniquePtr hb_font_; + HbScoped<hb_font_t> hb_font_; std::unique_ptr<HarfBuzzFontData> hb_font_data_; }; -typedef HashMap<uint64_t, - scoped_refptr<HbFontCacheEntry>, - WTF::IntHash<uint64_t>, - WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> - HarfBuzzFontCache; +// Declare as derived class in order to be able to forward-declare it as class +// in FontGlobalContext. +class HarfBuzzFontCache + : public HashMap<uint64_t, + scoped_refptr<HbFontCacheEntry>, + WTF::IntHash<uint64_t>, + WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> {}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc index 186cb357c9c..b63c48d9513 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc @@ -36,9 +36,8 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { Font font(font_description); // Set font size to something other than the default 0 size in // FontDescription, 16 matches the default text size in HTML. + // We don't use a FontSelector here. Only look for system fonts for now. font_description.SetComputedSize(16.0f); - // Only look for system fonts for now. - font.Update(nullptr); HarfBuzzShaper shaper(String(converted_input_buffer, converted_length)); scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr); diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc index 9cca0e58d2b..6c03756d4c4 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc @@ -83,7 +83,6 @@ class HarfBuzzShaperTest : public testing::Test { void SetUp() override { font_description.SetComputedSize(12.0); font = Font(font_description); - font.Update(nullptr); } void TearDown() override {} @@ -101,7 +100,6 @@ class HarfBuzzShaperTest : public testing::Test { font_description.SetFamily(devanagari_family); font = Font(font_description); - font.Update(nullptr); } Font CreateAhem(float size) { @@ -472,7 +470,6 @@ TEST_F(HarfBuzzShaperTest, ShapeTabulationCharacters) { TEST_F(HarfBuzzShaperTest, ShapeVerticalUpright) { font_description.SetOrientation(FontOrientation::kVerticalUpright); font = Font(font_description); - font.Update(nullptr); // This string should create 2 runs, ideographic and Latin, both in upright. String string(u"\u65E5\u65E5\u65E5lllll"); @@ -496,7 +493,6 @@ TEST_F(HarfBuzzShaperTest, ShapeVerticalUpright) { TEST_F(HarfBuzzShaperTest, ShapeVerticalUprightIdeograph) { font_description.SetOrientation(FontOrientation::kVerticalUpright); font = Font(font_description); - font.Update(nullptr); // This string should create one ideograph run. String string(u"\u65E5\u65E6\u65E0\u65D3\u65D0"); @@ -527,7 +523,6 @@ TEST_F(HarfBuzzShaperTest, RangeShapeSmallCaps) { font_description.SetVariantCaps(FontDescription::kSmallCaps); font_description.SetComputedSize(12.0); Font font(font_description); - font.Update(nullptr); // Shaping index 2 to 3 means that case splitting for small caps splits before // character index 2 since the initial 'a' needs to be uppercased, but the @@ -563,7 +558,6 @@ TEST_F(HarfBuzzShaperTest, RangeShapeSmallCaps) { TEST_F(HarfBuzzShaperTest, ShapeVerticalMixed) { font_description.SetOrientation(FontOrientation::kVerticalMixed); font = Font(font_description); - font.Update(nullptr); // This string should create 2 runs, ideographic in upright and Latin in // rotated horizontal. @@ -1634,10 +1628,7 @@ static bool KerningIsHappening(const FontDescription& font_description, kern.SetKerning(FontDescription::kAutoKerning); Font font_no_kern(no_kern); - font_no_kern.Update(nullptr); - Font font_kern(kern); - font_kern.Update(nullptr); HarfBuzzShaper shaper(str); @@ -1749,7 +1740,6 @@ TEST_F(HarfBuzzShaperTest, ShapeVerticalWithoutSubpixelPositionIsRounded) { font_description.SetOrientation(FontOrientation::kVerticalUpright); font = Font(font_description); - font.Update(nullptr); String string(u"\u65E5\u65E5\u65E5"); TextDirection direction = TextDirection::kLtr; @@ -1769,7 +1759,6 @@ TEST_F(HarfBuzzShaperTest, ShapeVerticalWithSubpixelPositionIsRounded) { font_description.SetOrientation(FontOrientation::kVerticalUpright); font = Font(font_description); - font.Update(nullptr); String string(u"\u65E5\u65E5\u65E5"); TextDirection direction = TextDirection::kLtr; diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h index b6a3ff992b0..9dfd507dd0b 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h @@ -39,11 +39,7 @@ namespace blink { -struct ShapeCacheEntry { - DISALLOW_NEW(); - ShapeCacheEntry() { shape_result_ = nullptr; } - scoped_refptr<const ShapeResult> shape_result_; -}; +using ShapeCacheEntry = scoped_refptr<const ShapeResult>; class ShapeCache { USING_FAST_MALLOC(ShapeCache); @@ -118,7 +114,7 @@ class ShapeCache { if (run.length() > SmallStringKey::Capacity()) return nullptr; - return AddSlowCase(run, entry); + return AddSlowCase(run, std::move(entry)); } void ClearIfVersionChanged(unsigned version) { @@ -140,10 +136,10 @@ class ShapeCache { size_t ByteSize() const { size_t self_byte_size = 0; for (auto cache_entry : single_char_map_) { - self_byte_size += cache_entry.value.shape_result_->ByteSize(); + self_byte_size += cache_entry.value->ByteSize(); } for (auto cache_entry : short_string_map_) { - self_byte_size += cache_entry.value.shape_result_->ByteSize(); + self_byte_size += cache_entry.value->ByteSize(); } return self_byte_size; } @@ -160,7 +156,8 @@ class ShapeCache { // as such use bit 31 (zero-based) to indicate direction. if (run.Direction() == TextDirection::kRtl) key |= (1u << 31); - SingleCharMap::AddResult add_result = single_char_map_.insert(key, entry); + SingleCharMap::AddResult add_result = + single_char_map_.insert(key, std::move(entry)); is_new_entry = add_result.is_new_entry; value = &add_result.stored_value->value; } else { @@ -170,9 +167,8 @@ class ShapeCache { } else { small_string_key = SmallStringKey(run.Span16(), run.Direction()); } - SmallStringMap::AddResult add_result = - short_string_map_.insert(small_string_key, entry); + short_string_map_.insert(small_string_key, std::move(entry)); is_new_entry = add_result.is_new_entry; value = &add_result.stored_value->value; } @@ -202,6 +198,7 @@ class ShapeCache { struct SmallStringKeyHashTraits : WTF::SimpleClassHashTraits<SmallStringKey> { STATIC_ONLY(SmallStringKeyHashTraits); static const bool kHasIsEmptyValueFunction = true; + static const bool kEmptyValueIsZero = false; static bool IsEmptyValue(const SmallStringKey& key) { return key.IsHashTableEmptyValue(); } diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc index 3a73c9a9d8e..81864362264 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc @@ -565,7 +565,7 @@ unsigned ShapeResult::OffsetToFit(float x, TextDirection line_direction) const { if (IsLtr(line_direction)) return result.left_character_index; - if (x == result.origin_x && IsRtl(Direction())) + if (x == result.origin_x) return result.left_character_index; return result.right_character_index; } @@ -1450,6 +1450,96 @@ scoped_refptr<ShapeResult> ShapeResult::CreateForSpaces(const Font* font, return result; } +scoped_refptr<ShapeResult> ShapeResult::CreateForStretchyMathOperator( + const Font* font, + TextDirection direction, + OpenTypeMathStretchData::StretchAxis stretch_axis, + Glyph glyph_variant, + float stretch_size) { + bool is_horizontal_assembly = + stretch_axis == OpenTypeMathStretchData::StretchAxis::Horizontal; + unsigned start_index = 0; + unsigned num_characters = 1; + scoped_refptr<ShapeResult> result = + ShapeResult::Create(font, start_index, num_characters, direction); + + hb_direction_t hb_direction = + is_horizontal_assembly ? HB_DIRECTION_LTR : HB_DIRECTION_TTB; + unsigned glyph_index = 0; + scoped_refptr<ShapeResult::RunInfo> run = RunInfo::Create( + font->PrimaryFont(), hb_direction, CanvasRotationInVertical::kRegular, + HB_SCRIPT_COMMON, start_index, 1 /* num_glyph */, num_characters); + run->glyph_data_[glyph_index] = {glyph_variant, 0 /* character index */, + true /* IsSafeToBreakBefore */, + stretch_size}; + run->width_ = std::max(0.0f, stretch_size); + + result->width_ = run->width_; + result->num_glyphs_ = run->NumGlyphs(); + result->runs_.push_back(std::move(run)); + + return result; +} + +scoped_refptr<ShapeResult> ShapeResult::CreateForStretchyMathOperator( + const Font* font, + TextDirection direction, + OpenTypeMathStretchData::StretchAxis stretch_axis, + const OpenTypeMathStretchData::AssemblyParameters& assembly_parameters) { + DCHECK(!assembly_parameters.parts.IsEmpty()); + DCHECK_LE(assembly_parameters.glyph_count, HarfBuzzRunGlyphData::kMaxGlyphs); + + bool is_horizontal_assembly = + stretch_axis == OpenTypeMathStretchData::StretchAxis::Horizontal; + unsigned start_index = 0; + unsigned num_characters = 1; + scoped_refptr<ShapeResult> result = + ShapeResult::Create(font, start_index, num_characters, direction); + + hb_direction_t hb_direction = + is_horizontal_assembly ? HB_DIRECTION_LTR : HB_DIRECTION_TTB; + scoped_refptr<ShapeResult::RunInfo> run = RunInfo::Create( + font->PrimaryFont(), hb_direction, CanvasRotationInVertical::kRegular, + HB_SCRIPT_COMMON, start_index, assembly_parameters.glyph_count, + num_characters); + + float overlap = assembly_parameters.connector_overlap; + unsigned part_index = 0; + for (const auto& part : assembly_parameters.parts) { + unsigned repetition_count = + part.is_extender ? assembly_parameters.repetition_count : 1; + if (!repetition_count) + continue; + DCHECK(part_index < assembly_parameters.glyph_count); + for (unsigned repetition_index = 0; repetition_index < repetition_count; + repetition_index++) { + unsigned glyph_index = + is_horizontal_assembly + ? part_index + : assembly_parameters.glyph_count - 1 - part_index; + float full_advance = glyph_index == assembly_parameters.glyph_count - 1 + ? part.full_advance + : part.full_advance - overlap; + run->glyph_data_[glyph_index] = {part.glyph, 0 /* character index */, + !glyph_index /* IsSafeToBreakBefore */, + full_advance}; + if (!is_horizontal_assembly) { + GlyphOffset glyph_offset( + 0, -assembly_parameters.stretch_size + part.full_advance); + run->glyph_data_.SetOffsetAt(glyph_index, glyph_offset); + result->has_vertical_offsets_ |= (glyph_offset.Height() != 0); + } + part_index++; + } + } + run->width_ = std::max(0.0f, assembly_parameters.stretch_size); + + result->width_ = run->width_; + result->num_glyphs_ = run->NumGlyphs(); + result->runs_.push_back(std::move(run)); + return result; +} + void ShapeResult::ToString(StringBuilder* output) const { output->Append("#chars="); output->AppendNumber(num_characters_); diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.h index eeb7e99b05f..d63fdd62f44 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.h +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result.h @@ -35,6 +35,7 @@ #include "base/containers/span.h" #include "third_party/blink/renderer/platform/fonts/canvas_rotation_in_vertical.h" #include "third_party/blink/renderer/platform/fonts/glyph.h" +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_math_stretch_data.h" #include "third_party/blink/renderer/platform/fonts/simple_font_data.h" #include "third_party/blink/renderer/platform/geometry/float_rect.h" #include "third_party/blink/renderer/platform/geometry/layout_unit.h" @@ -142,6 +143,17 @@ class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> { unsigned start_index, unsigned length, float width); + static scoped_refptr<ShapeResult> CreateForStretchyMathOperator( + const Font*, + TextDirection, + OpenTypeMathStretchData::StretchAxis, + Glyph, + float stretch_size); + static scoped_refptr<ShapeResult> CreateForStretchyMathOperator( + const Font*, + TextDirection, + OpenTypeMathStretchData::StretchAxis, + const OpenTypeMathStretchData::AssemblyParameters&); ~ShapeResult(); // Returns a mutable unique instance. If |this| has more than 1 ref count, @@ -495,6 +507,7 @@ class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> { friend class ShapeResultBloberizer; friend class ShapeResultView; friend class ShapeResultTest; + friend class StretchyOperatorShaper; template <bool has_non_zero_glyph_offsets> float ForEachGlyphImpl(float initial_advance, diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc index 17be9dacea0..91a4ccffadc 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc @@ -39,7 +39,6 @@ class ShapeResultBloberizerTest : public testing::Test { font_description.SetGenericFamily(FontDescription::kStandardFamily); font = Font(font_description); - font.Update(nullptr); ASSERT_TRUE(font.CanShapeWordByWord()); fallback_fonts = nullptr; cache = std::make_unique<ShapeCache>(); @@ -267,7 +266,6 @@ TEST_F(ShapeResultBloberizerTest, CommonAccentLeftToRightFillGlyphBuffer) { bloberizer.FillGlyphs(run_info, buffer); Font reference_font(font_description); - reference_font.Update(nullptr); reference_font.SetCanShapeWordByWordForTesting(false); ShapeResultBloberizer reference_bloberizer(reference_font, 1); @@ -305,7 +303,6 @@ TEST_F(ShapeResultBloberizerTest, CommonAccentRightToLeftFillGlyphBuffer) { bloberizer.FillGlyphs(run_info, buffer); Font reference_font(font_description); - reference_font.Update(nullptr); reference_font.SetCanShapeWordByWordForTesting(false); ShapeResultBloberizer reference_bloberizer(reference_font, 1); diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc index 73f8a4c0fe3..758065226be 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc @@ -130,7 +130,7 @@ Vector<CharacterRange> ShapeResultBuffer::IndividualCharacterRanges( float total_width) const { Vector<CharacterRange> ranges; float current_x = direction == TextDirection::kRtl ? total_width : 0; - for (const scoped_refptr<const ShapeResult> result : results_) + for (const scoped_refptr<const ShapeResult>& result : results_) current_x = result->IndividualCharacterRanges(&ranges, current_x); return ranges; } @@ -192,7 +192,7 @@ Vector<double> ShapeResultBuffer::IndividualCharacterAdvances( Vector<double> advances; double current_x = direction == TextDirection::kRtl ? total_width : 0; - for (const scoped_refptr<const ShapeResult> result : results_) { + for (const scoped_refptr<const ShapeResult>& result : results_) { unsigned run_count = result->runs_.size(); result->EnsureGraphemes( diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h index a1a6708907c..32fa8f2ab73 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h @@ -211,7 +211,7 @@ struct ShapeResult::RunInfo : public RefCounted<ShapeResult::RunInfo> { } if (!Rtl()) - end = num_characters_; + end = offset + num_characters_; else end = start; start = index; diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test.cc index 3738a170f1d..8dac0939e7b 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_test.cc @@ -21,7 +21,6 @@ class ShapeResultTest : public testing::Test { void SetUp() override { font_description.SetComputedSize(12.0); font = Font(font_description); - font.Update(nullptr); FontDescription::VariantLigatures ligatures; arabic_font = blink::test::CreateTestFont( diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc index 9e558346965..dd4998fb3c0 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc @@ -83,6 +83,15 @@ struct ShapeResultView::RunInfoPart { // |base::span<RunInfoPart>|. const RunInfoPart* get() const { return this; } + void ExpandRangeToIncludePartialGlyphs(unsigned offset, + unsigned* from, + unsigned* to) const { + DCHECK_GE(offset + start_index_, offset_); + unsigned part_offset = offset + start_index_ - offset_; + run_->ExpandRangeToIncludePartialGlyphs( + part_offset, reinterpret_cast<int*>(from), reinterpret_cast<int*>(to)); + } + scoped_refptr<const ShapeResult::RunInfo> run_; ShapeResult::RunInfo::GlyphDataRange range_; @@ -633,4 +642,13 @@ FloatRect ShapeResultView::ComputeInkBounds() const { return ink_bounds; } +void ShapeResultView::ExpandRangeToIncludePartialGlyphs(unsigned* from, + unsigned* to) const { + unsigned accumulated_offset = char_index_offset_; + for (const auto& part : Parts()) { + part.ExpandRangeToIncludePartialGlyphs(accumulated_offset, from, to); + accumulated_offset += part.NumCharacters(); + } +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h index 591dff49962..05c7fa1563d 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h @@ -145,9 +145,11 @@ class PLATFORM_EXPORT ShapeResultView final } void GetRunFontData(Vector<ShapeResult::RunFontData>*) const; + void ExpandRangeToIncludePartialGlyphs(unsigned* from, unsigned* to) const; + private: template <class ShapeResultType> - ShapeResultView(const ShapeResultType*); + explicit ShapeResultView(const ShapeResultType*); struct RunInfoPart; template <class ShapeResultType> diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view_test.cc index ce0f6ccb82b..85c46054789 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view_test.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shape_result_view_test.cc @@ -21,7 +21,6 @@ class ShapeResultViewTest : public testing::Test { void SetUp() override { font_description.SetComputedSize(12.0); font = Font(font_description); - font.Update(nullptr); } void TearDown() override {} diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc index 82eeefad31b..b0296b7f786 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc @@ -52,7 +52,6 @@ class ShapingLineBreakerTest : public testing::Test { void SetUp() override { font_description.SetComputedSize(12.0); font = Font(font_description); - font.Update(nullptr); } void TearDown() override {} diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper.cc new file mode 100644 index 00000000000..99c80b364f4 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper.cc @@ -0,0 +1,223 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper.h" + +#include <hb-ot.h> +#include <hb.h> +#include <unicode/uchar.h> + +#include "third_party/blink/renderer/platform/fonts/canvas_rotation_in_vertical.h" +#include "third_party/blink/renderer/platform/fonts/font.h" +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h" +#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h" +#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h" +#include "third_party/blink/renderer/platform/geometry/float_rect.h" +#include "third_party/blink/renderer/platform/wtf/text/unicode.h" +#include "ui/gfx/skia_util.h" + +namespace blink { + +namespace { + +// HarfBuzz' hb_position_t is a 16.16 fixed-point value. +inline float HarfBuzzUnitsToFloat(hb_position_t value) { + static const float kFloatToHbRatio = 1.0f / (1 << 16); + return kFloatToHbRatio * value; +} + +inline float GetGlyphStretchSize( + FloatRect bounds, + OpenTypeMathStretchData::StretchAxis stretch_axis) { + return stretch_axis == OpenTypeMathStretchData::StretchAxis::Horizontal + ? bounds.Width() + : bounds.Height(); +} + +inline StretchyOperatorShaper::Metrics ToMetrics(FloatRect bounds) { + return {bounds.Width(), -bounds.Y(), bounds.MaxY()}; +} + +base::Optional<OpenTypeMathStretchData::AssemblyParameters> +GetAssemblyParameters(const HarfBuzzFace* harfbuzz_face, + Glyph base_glyph, + OpenTypeMathStretchData::StretchAxis stretch_axis, + float target_size) { + Vector<OpenTypeMathStretchData::GlyphPartRecord> parts = + OpenTypeMathSupport::GetGlyphPartRecords(harfbuzz_face, base_glyph, + stretch_axis); + if (parts.IsEmpty()) + return base::nullopt; + + hb_font_t* hb_font = + harfbuzz_face->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout); + + auto hb_stretch_axis = + stretch_axis == OpenTypeMathStretchData::StretchAxis::Horizontal + ? HB_DIRECTION_LTR + : HB_DIRECTION_BTT; + + // Go over the assembly parts and determine parameters used below. + // https://mathml-refresh.github.io/mathml-core/#the-glyphassembly-table + float min_connector_overlap = HarfBuzzUnitsToFloat( + hb_ot_math_get_min_connector_overlap(hb_font, hb_stretch_axis)); + float max_connector_overlap = std::numeric_limits<float>::max(); + float non_extender_advance_sum = 0, extender_advance_sum = 0; + unsigned non_extender_count = 0, extender_count = 0; + + for (auto& part : parts) { + // Calculate the count and advance sums of extender and non-extender glyphs. + if (part.is_extender) { + extender_count++; + extender_advance_sum += part.full_advance; + } else { + non_extender_count++; + non_extender_advance_sum += part.full_advance; + } + + // Take into account start connector length for all but the first glyph. + if (part.is_extender || &part != &parts.front()) { + max_connector_overlap = + std::min(max_connector_overlap, part.start_connector_length); + } + + // Take into account end connector length for all but the last glyph. + if (part.is_extender || &part != &parts.back()) { + max_connector_overlap = + std::min(max_connector_overlap, part.end_connector_length); + } + } + + // Check validity conditions indicated in MathML core. + float extender_non_overlapping_advance_sum = + extender_advance_sum - min_connector_overlap * extender_count; + if (extender_count == 0 || max_connector_overlap < min_connector_overlap || + extender_non_overlapping_advance_sum <= 0) + return base::nullopt; + + // Calculate the minimal number of repetitions needed to obtain an assembly + // size of size at least target size (r_min in MathML Core). + unsigned repetition_count = std::max<float>( + std::ceil((target_size - non_extender_advance_sum + + min_connector_overlap * (non_extender_count - 1)) / + extender_non_overlapping_advance_sum), + 0); + + // Calculate the number of glyphs, limiting repetition_count to ensure the + // assembly does not have more than HarfBuzzRunGlyphData::kMaxGlyphs. + DCHECK_LE(non_extender_count, HarfBuzzRunGlyphData::kMaxGlyphs); + repetition_count = std::min<unsigned>( + repetition_count, + (HarfBuzzRunGlyphData::kMaxGlyphs - non_extender_count) / extender_count); + unsigned glyph_count = non_extender_count + repetition_count * extender_count; + DCHECK_LE(glyph_count, HarfBuzzRunGlyphData::kMaxGlyphs); + + // Calculate the maximum overlap (called o_max in MathML Core) and the number + // of glyph in such an assembly (called N in MathML Core). + float connector_overlap = max_connector_overlap; + if (glyph_count > 1) { + float max_connector_overlap_theorical = + (non_extender_advance_sum + repetition_count * extender_advance_sum - + target_size) / + (glyph_count - 1); + connector_overlap = + std::max(min_connector_overlap, + std::min(connector_overlap, max_connector_overlap_theorical)); + } + + // Calculate the assembly size (called AssemblySize(o, r) in MathML Core). + float stretch_size = non_extender_advance_sum + + repetition_count * extender_advance_sum - + connector_overlap * (glyph_count - 1); + + return base::Optional<OpenTypeMathStretchData::AssemblyParameters>( + {connector_overlap, repetition_count, glyph_count, stretch_size, + std::move(parts)}); +} + +} // namespace + +StretchyOperatorShaper::Metrics StretchyOperatorShaper::GetMetrics( + const Font* font, + float target_size) const { + const SimpleFontData* primary_font = font->PrimaryFont(); + const HarfBuzzFace* harfbuzz_face = + primary_font->PlatformData().GetHarfBuzzFace(); + Glyph base_glyph = primary_font->GlyphForCharacter(stretchy_character_); + + FloatRect bounds; + + // Try different glyph variants. + for (auto& variant : OpenTypeMathSupport::GetGlyphVariantRecords( + harfbuzz_face, base_glyph, stretch_axis_)) { + bounds = primary_font->BoundsForGlyph(variant); + if (GetGlyphStretchSize(bounds, stretch_axis_) >= target_size) + return ToMetrics(bounds); + } + + // Try a glyph assembly. + auto params = GetAssemblyParameters(harfbuzz_face, base_glyph, stretch_axis_, + target_size); + if (!params) + return ToMetrics(bounds); + + bounds = stretch_axis_ == OpenTypeMathStretchData::StretchAxis::Horizontal + ? FloatRect(0, 0, params->stretch_size, 0) + : FloatRect(0, -params->stretch_size, 0, params->stretch_size); + + for (auto& part : params->parts) { + // Include dimension of the part, orthogonal to the stretch axis. + auto glyph_bounds = primary_font->BoundsForGlyph(part.glyph); + if (stretch_axis_ == OpenTypeMathStretchData::StretchAxis::Horizontal) { + glyph_bounds.SetX(0); + glyph_bounds.SetWidth(0); + } else { + glyph_bounds.SetY(0); + glyph_bounds.SetHeight(0); + } + bounds.UniteEvenIfEmpty(glyph_bounds); + } + + return ToMetrics(bounds); +} + +scoped_refptr<ShapeResult> StretchyOperatorShaper::Shape( + const Font* font, + float target_size) const { + const SimpleFontData* primary_font = font->PrimaryFont(); + const HarfBuzzFace* harfbuzz_face = + primary_font->PlatformData().GetHarfBuzzFace(); + Glyph base_glyph = primary_font->GlyphForCharacter(stretchy_character_); + + Glyph glyph_variant; + float glyph_variant_stretch_size; + TextDirection direction = TextDirection::kLtr; + + // Try different glyph variants. + for (auto& variant : OpenTypeMathSupport::GetGlyphVariantRecords( + harfbuzz_face, base_glyph, stretch_axis_)) { + glyph_variant = variant; + auto bounds = primary_font->BoundsForGlyph(glyph_variant); + glyph_variant_stretch_size = GetGlyphStretchSize(bounds, stretch_axis_); + if (glyph_variant_stretch_size >= target_size) { + return ShapeResult::CreateForStretchyMathOperator( + font, direction, stretch_axis_, glyph_variant, + glyph_variant_stretch_size); + } + } + + // Try a glyph assembly. + auto params = GetAssemblyParameters(harfbuzz_face, base_glyph, stretch_axis_, + target_size); + if (!params) { + return ShapeResult::CreateForStretchyMathOperator( + font, direction, stretch_axis_, glyph_variant, + glyph_variant_stretch_size); + } + + return ShapeResult::CreateForStretchyMathOperator( + font, direction, stretch_axis_, std::move(*params)); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper.h b/chromium/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper.h new file mode 100644 index 00000000000..4df0edaed15 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper.h @@ -0,0 +1,59 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_STRETCHY_OPERATOR_SHAPER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_STRETCHY_OPERATOR_SHAPER_H_ + +#include <base/memory/scoped_refptr.h> +#include <unicode/uchar.h> +#include "third_party/blink/renderer/platform/fonts/glyph.h" +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h" +#include "third_party/blink/renderer/platform/text/text_direction.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +namespace blink { + +class Font; +class ShapeResult; +class StretchyOperatorShaper; + +// TODO(https://crbug.com/1057589): Add a TextDirection parameter, so that it's +// possible to perform glyph-level (rtlm feature) or character-level mirroring +// before stretching. +// https://mathml-refresh.github.io/mathml-core/#algorithms-for-glyph-stretching +class PLATFORM_EXPORT StretchyOperatorShaper final { + DISALLOW_NEW(); + + public: + StretchyOperatorShaper(UChar stretchy_character, + OpenTypeMathStretchData::StretchAxis stretch_axis) + : stretchy_character_(stretchy_character), stretch_axis_(stretch_axis) {} + + // Returns the metrics of the stretched operator for layout purpose. + // May be called multiple times; font and direction may vary between calls. + // https://mathml-refresh.github.io/mathml-core/#dfn-box-metrics-of-a-stretchy-glyph + struct Metrics { + float advance; + float ascent; + float descent; + // TODO(https://crbug.com/1057592): Add italic correction. + }; + Metrics GetMetrics(const Font*, float target_size) const; + + // Shape the stretched operator. The coordinates of the glyph(s) use the same + // origin as the rectangle returned by GetMetrics. + // May be called multiple times; font and direction may vary between calls. + // https://mathml-refresh.github.io/mathml-core/#dfn-shape-a-stretchy-glyph + scoped_refptr<ShapeResult> Shape(const Font*, float target_size) const; + + ~StretchyOperatorShaper() = default; + + private: + const UChar stretchy_character_; + const OpenTypeMathStretchData::StretchAxis stretch_axis_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_STRETCHY_OPERATOR_SHAPER_H_ diff --git a/chromium/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper_test.cc b/chromium/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper_test.cc new file mode 100644 index 00000000000..1fd1aeaf8bb --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper_test.cc @@ -0,0 +1,264 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/fonts/shaping/stretchy_operator_shaper.h" +#include "base/memory/scoped_refptr.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/fonts/font.h" +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_types.h" +#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h" +#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h" +#include "third_party/blink/renderer/platform/testing/font_test_helpers.h" +#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" + +namespace blink { + +namespace { + +const UChar32 kLeftBraceCodePoint = '{'; +const UChar32 kOverBraceCodePoint = 0x23DE; +const UChar32 kArabicMathOperatorHahWithDalCodePoint = 0x1EEF1; +float kSizeError = .1; + +ShapeResultTestInfo* TestInfo(const scoped_refptr<ShapeResult>& result) { + return static_cast<ShapeResultTestInfo*>(result.get()); +} + +} // namespace + +class StretchyOperatorShaperTest : public testing::Test { + protected: + void SetUp() override { + font_description.SetComputedSize(10.0); + font = Font(font_description); + } + + void TearDown() override {} + + Font CreateMathFont(const String& name, float size = 1000) { + FontDescription::VariantLigatures ligatures; + return blink::test::CreateTestFont( + "MathTestFont", + blink::test::BlinkWebTestsFontsTestDataPath(String("math/") + name), + size, &ligatures); + } + + FontDescription font_description; + Font font; +}; + +// See createStretchy() in +// third_party/blink/web_tests/external/wpt/mathml/tools/operator-dictionary.py +TEST_F(StretchyOperatorShaperTest, GlyphVariants) { + Font math = CreateMathFont("operators.woff"); + + StretchyOperatorShaper vertical_shaper( + kLeftBraceCodePoint, OpenTypeMathStretchData::StretchAxis::Vertical); + StretchyOperatorShaper horizontal_shaper( + kOverBraceCodePoint, OpenTypeMathStretchData::StretchAxis::Horizontal); + + auto left_brace = math.PrimaryFont()->GlyphForCharacter(kLeftBraceCodePoint); + auto over_brace = math.PrimaryFont()->GlyphForCharacter(kOverBraceCodePoint); + + // Calculate glyph indices from the last unicode character in the font. + // TODO(https://crbug.com/1057596): Find a better way to access these glyph + // indices. + auto v0 = math.PrimaryFont()->GlyphForCharacter( + kArabicMathOperatorHahWithDalCodePoint) + + 1; + auto h0 = v0 + 1; + auto v1 = h0 + 1; + auto h1 = v1 + 1; + auto v2 = h1 + 1; + auto h2 = v2 + 1; + + // Stretch operators to target sizes (in font units) 125, 250, 375, 500, 625, + // 750, 875, 1000, 1125, ..., 3750, 3875, 4000. + // + // Shaper tries glyphs over_brace/left_brace, h0/v0, h1/v1, h2/v2, h3/v3 of + // respective sizes 1000, 1000, 2000, 3000 and 4000. It returns the smallest + // glyph larger than the target size. + const unsigned size_count = 4; + const unsigned subdivision = 8; + for (unsigned i = 0; i < size_count; i++) { + for (unsigned j = 1; j <= subdivision; j++) { + // Due to floating-point errors, the actual metrics of the size variants + // might actually be slightly smaller than expected. Reduce the + // target_size by kSizeError to ensure that the shaper picks the desired + // size variant. + float target_size = i * 1000 + (j * 1000 / subdivision) - kSizeError; + + // Metrics of horizontal size variants. + { + auto metrics = horizontal_shaper.GetMetrics(&math, target_size); + EXPECT_NEAR(metrics.advance, (i + 1) * 1000, kSizeError); + EXPECT_NEAR(metrics.ascent, 1000, kSizeError); + EXPECT_FLOAT_EQ(metrics.descent, 0); + } + + // Metrics of vertical size variants. + + { + auto metrics = vertical_shaper.GetMetrics(&math, target_size); + EXPECT_NEAR(metrics.advance, 1000, kSizeError); + EXPECT_NEAR(metrics.ascent, (i + 1) * 1000, kSizeError); + EXPECT_FLOAT_EQ(metrics.descent, 0); + } + + // Shaping of horizontal size variants. + { + scoped_refptr<ShapeResult> result = + horizontal_shaper.Shape(&math, target_size); + EXPECT_EQ(TestInfo(result)->NumberOfRunsForTesting(), 1u); + EXPECT_EQ(TestInfo(result)->RunInfoForTesting(0).NumGlyphs(), 1u); + Glyph expected_variant = i ? h0 + 2 * i : over_brace; + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, 0), expected_variant); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, 0), (i + 1) * 1000, + kSizeError); + } + + // Shaping of vertical size variants. + { + scoped_refptr<ShapeResult> result = + vertical_shaper.Shape(&math, target_size); + EXPECT_EQ(TestInfo(result)->NumberOfRunsForTesting(), 1u); + EXPECT_EQ(TestInfo(result)->RunInfoForTesting(0).NumGlyphs(), 1u); + Glyph expected_variant = i ? v0 + 2 * i : left_brace; + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, 0), expected_variant); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, 0), (i + 1) * 1000, + kSizeError); + } + } + } + + // Stretch an operator to target sizes (in font units) much larger than 4000. + // + // This will force an assembly with the following parts: + // _____________________________________________________________ + // Part | MaxStartOverlap | MaxEndOverlap | Advance | Extender | + // h2/v2 | 0 | 1000 | 3000 | false | + // h1/v1 | 1000 | 1000 | 2000 | true | + // + // For an assembly made of one non-extender glyph h2/v2 and repetition_count + // copies of extenders h1/v1, the size is + // advance(h2/v2) + repetition_count * (advance(h1/v1) - overlap). + // + // For repetition_count = k and overlap = 750, the size is X = 1250k + 3000. + // + // Since the font min overlap is 500, for repetition_count = k - 1 the size + // is at most Y = 1500k + 1500. + // + // Since the max overlap of parts is 1000, for repetition_count = k + 1 the + // size is at least Z = 1000k + 4000. + // + // { X - 4000 = 1250k - 1000 >= 250 >> kSizeError for k >= 1. + // { X - Y = 1500 - 250k >= 250 >> kSizeError for k <= 5. + // Hence setting the target size to 1250k + 3000 will ensure an assembly of + // k + 1 glyphs and overlap close to 750 for 1 <= k <= 5. + // + // Additionally, X - Z = 250k - 1000 = 250 >> kSizeError for k = 5 so this + // case also verifies that the minimal number of repetitions is actually used. + // + for (unsigned repetition_count = 1; repetition_count <= 5; + repetition_count++) { + // It is not necessary to decrease the target_size by kSizeError here. The + // shaper can just increase overlap by kSizeError / repetition_count to + // reduce the actual size of the assembly. + float overlap = 750; + float target_size = 3000 + repetition_count * (2000 - overlap); + + // Metrics of horizontal assembly. + { + auto metrics = horizontal_shaper.GetMetrics(&math, target_size); + EXPECT_NEAR(metrics.advance, target_size, kSizeError); + EXPECT_NEAR(metrics.ascent, 1000, kSizeError); + EXPECT_FLOAT_EQ(metrics.descent, 0); + } + + // Metrics of vertical assembly. + { + auto metrics = vertical_shaper.GetMetrics(&math, target_size); + EXPECT_NEAR(metrics.advance, 1000, kSizeError); + EXPECT_NEAR(metrics.ascent, target_size, kSizeError); + EXPECT_FLOAT_EQ(metrics.descent, 0); + } + + // Shaping of horizontal assembly. + // From left to right: h2, h1, h1, h1, ... + { + scoped_refptr<ShapeResult> result = + horizontal_shaper.Shape(&math, target_size); + + EXPECT_EQ(TestInfo(result)->NumberOfRunsForTesting(), 1u); + EXPECT_EQ(TestInfo(result)->RunInfoForTesting(0).NumGlyphs(), + repetition_count + 1); + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, 0), h2); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, 0), 3000 - overlap, + kSizeError); + for (unsigned i = 0; i < repetition_count - 1; i++) { + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, i + 1), h1); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, i + 1), + 2000 - overlap, kSizeError); + } + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, repetition_count), h1); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, repetition_count), + 2000, kSizeError); + } + + // Shaping of vertical assembly. + // From bottom to top: v2, v1, v1, v1, ... + { + scoped_refptr<ShapeResult> result = + vertical_shaper.Shape(&math, target_size); + + EXPECT_EQ(TestInfo(result)->NumberOfRunsForTesting(), 1u); + EXPECT_EQ(TestInfo(result)->RunInfoForTesting(0).NumGlyphs(), + repetition_count + 1); + for (unsigned i = 0; i < repetition_count; i++) { + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, i), v1); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, i), 2000 - overlap, + kSizeError); + } + EXPECT_EQ(TestInfo(result)->GlyphForTesting(0, repetition_count), v2); + EXPECT_NEAR(TestInfo(result)->AdvanceForTesting(0, repetition_count), + 3000, kSizeError); + } + } + + // Stretch an operator to edge target size values. + // + // These tests verify that it does not cause any assertion or crashes. + { + // Zero. + float target_size = 0; + horizontal_shaper.Shape(&math, target_size); + vertical_shaper.Shape(&math, target_size); + + // Negative. + target_size = -5500; + horizontal_shaper.Shape(&math, target_size); + vertical_shaper.Shape(&math, target_size); + + // Max limit. + target_size = std::numeric_limits<float>::max(); + horizontal_shaper.Shape(&math, target_size); + vertical_shaper.Shape(&math, target_size); + + // Min limit. + target_size = std::numeric_limits<float>::min(); + horizontal_shaper.Shape(&math, target_size); + vertical_shaper.Shape(&math, target_size); + + // More than the max number of glyphs. + // The size of an assembly with one non-extender v2/h2 and k - 1 extenders + // h1/v1 and minimal overlap 500 is Y = 1500k + 1500. + // So target_size - Y >= 250 >> kSizeError if the assembly does not have + // more than the max number of glyphs. + target_size = 1500 * HarfBuzzRunGlyphData::kMaxGlyphs + 1750; + horizontal_shaper.Shape(&math, target_size); + vertical_shaper.Shape(&math, target_size); + } +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/simple_font_data.h b/chromium/third_party/blink/renderer/platform/fonts/simple_font_data.h index 7c5e9561c83..070cde2e8ba 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/simple_font_data.h +++ b/chromium/third_party/blink/renderer/platform/fonts/simple_font_data.h @@ -39,6 +39,7 @@ #include "third_party/blink/renderer/platform/fonts/typesetting_features.h" #include "third_party/blink/renderer/platform/geometry/float_rect.h" #include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" #include "third_party/blink/renderer/platform/wtf/text/string_hash.h" #include "third_party/skia/include/core/SkFont.h" @@ -245,7 +246,12 @@ ALWAYS_INLINE float SimpleFontData::WidthForGlyph(Glyph glyph) const { #endif } -DEFINE_FONT_DATA_TYPE_CASTS(SimpleFontData, false); +template <> +struct DowncastTraits<SimpleFontData> { + static bool AllowFrom(const FontData& fontData) { + return !fontData.IsSegmented(); + } +}; } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SIMPLE_FONT_DATA_H_ diff --git a/chromium/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc b/chromium/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc index 51bdbb7510c..7537fb4d036 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc @@ -33,6 +33,7 @@ #include <memory> #include <utility> +#include "base/logging.h" #include "build/build_config.h" #include "third_party/blink/public/platform/linux/web_sandbox_support.h" #include "third_party/blink/public/platform/platform.h" @@ -245,6 +246,13 @@ sk_sp<SkTypeface> FontCache::CreateTypeface( name.c_str(), font_description.SkiaFontStyle()); } +#if !defined(OS_MACOSX) +std::vector<FontEnumerationEntry> FontCache::EnumeratePlatformAvailableFonts() { + NOTIMPLEMENTED(); + return std::vector<FontEnumerationEntry>(); +} +#endif // !defined(OS_MACOSX) + #if !defined(OS_WIN) std::unique_ptr<FontPlatformData> FontCache::CreateFontPlatformData( const FontDescription& font_description, diff --git a/chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h b/chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h index 6a72122ee22..f638f1c8393 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h +++ b/chromium/third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h @@ -15,6 +15,10 @@ class SkFont; namespace blink { +// TODO: Width functions are affected by issue +// https://bugs.chromium.org/p/skia/issues/detail?id=10123 in Skia, which +// currently does not return trak-free advances on Mac OS 10.15. + void SkFontGetGlyphWidthForHarfBuzz(const SkFont&, hb_codepoint_t, hb_position_t* width); diff --git a/chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.cc b/chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.cc index 9b5a8ed881f..e72f801016a 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/web_font_decoder.cc @@ -30,15 +30,12 @@ #include "third_party/blink/renderer/platform/fonts/web_font_decoder.h" -#include "base/timer/elapsed_timer.h" #include "build/build_config.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/fonts/font_cache.h" #include "third_party/blink/renderer/platform/fonts/web_font_typeface_factory.h" -#include "third_party/blink/renderer/platform/instrumentation/histogram.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/wtf/shared_buffer.h" -#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" #include "third_party/ots/include/ots-memory-stream.h" #include "third_party/skia/include/core/SkStream.h" @@ -145,38 +142,6 @@ ots::TableAction BlinkOTSContext::GetTableAction(uint32_t tag) { } } -void RecordDecodeSpeedHistogram(const char* data, - size_t length, - double decode_time, - size_t decoded_size) { - if (decode_time <= 0) - return; - - double kb_per_second = decoded_size / (1000 * decode_time); - if (length >= 4) { - if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') { - DEFINE_THREAD_SAFE_STATIC_LOCAL( - CustomCountHistogram, woff_histogram, - ("WebFont.DecodeSpeed.WOFF", 1000, 300000, 50)); - woff_histogram.Count(kb_per_second); - return; - } - - if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == '2') { - DEFINE_THREAD_SAFE_STATIC_LOCAL( - CustomCountHistogram, woff2_histogram, - ("WebFont.DecodeSpeed.WOFF2", 1000, 300000, 50)); - woff2_histogram.Count(kb_per_second); - return; - } - } - - DEFINE_THREAD_SAFE_STATIC_LOCAL( - CustomCountHistogram, sfnt_histogram, - ("WebFont.DecodeSpeed.SFNT", 1000, 300000, 50)); - sfnt_histogram.Count(kb_per_second); -} - } // namespace sk_sp<SkTypeface> WebFontDecoder::Decode(SharedBuffer* buffer) { @@ -196,14 +161,13 @@ sk_sp<SkTypeface> WebFontDecoder::Decode(SharedBuffer* buffer) { // Most web fonts are compressed, so the result can be much larger than // the original. ots::ExpandingMemoryStream output(buffer->size(), kMaxWebFontSize); - base::ElapsedTimer timer; BlinkOTSContext ots_context; SharedBuffer::DeprecatedFlatData flattened_buffer(buffer); - const char* data = flattened_buffer.Data(); TRACE_EVENT_BEGIN0("blink", "DecodeFont"); - bool ok = ots_context.Process(&output, reinterpret_cast<const uint8_t*>(data), - buffer->size()); + bool ok = ots_context.Process( + &output, reinterpret_cast<const uint8_t*>(flattened_buffer.Data()), + buffer->size()); TRACE_EVENT_END0("blink", "DecodeFont"); if (!ok) { @@ -212,9 +176,6 @@ sk_sp<SkTypeface> WebFontDecoder::Decode(SharedBuffer* buffer) { } const size_t decoded_length = SafeCast<size_t>(output.Tell()); - RecordDecodeSpeedHistogram(data, buffer->size(), timer.Elapsed().InSecondsF(), - decoded_length); - sk_sp<SkData> sk_data = SkData::MakeWithCopy(output.get(), decoded_length); sk_sp<SkTypeface> new_typeface; diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.cc b/chromium/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.cc index a563a80074d..9544a0ed0ab 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.cc @@ -49,12 +49,11 @@ void FallbackFamilyStyleCache::Put( String cache_key = makeCacheKey(generic_family, bcp47_language_tag, fallback_priority); - FallbackLruCache::TypefaceVector* existing_typefaces = - recent_fallback_fonts_.Get(cache_key); + TypefaceVector* existing_typefaces = recent_fallback_fonts_.Get(cache_key); if (existing_typefaces) { existing_typefaces->insert(0, sk_ref_sp(typeface)); } else { - FallbackLruCache::TypefaceVector typefaces; + TypefaceVector typefaces; typefaces.push_back(sk_ref_sp(typeface)); recent_fallback_fonts_.Put(std::move(cache_key), std::move(typefaces)); } @@ -67,7 +66,7 @@ void FallbackFamilyStyleCache::Get( UChar32 character, String* fallback_family, SkFontStyle* fallback_style) { - FallbackLruCache::TypefaceVector* typefaces = recent_fallback_fonts_.Get( + TypefaceVector* typefaces = recent_fallback_fonts_.Get( makeCacheKey(generic_family, bcp47_language_tag, fallback_priority)); if (!typefaces) return; diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h b/chromium/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h index 5e8f86ebcf9..770702feb39 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h +++ b/chromium/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h @@ -7,12 +7,15 @@ #include "third_party/blink/renderer/platform/fonts/font_description.h" #include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h" -#include "third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win.h" +#include "third_party/blink/renderer/platform/wtf/lru_cache.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkTypeface.h" namespace blink { +using TypefaceVector = Vector<sk_sp<SkTypeface>>; +using FallbackLruCache = WTF::LruCache<String, TypefaceVector>; + class FallbackFamilyStyleCache { USING_FAST_MALLOC(FallbackFamilyStyleCache); diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win.cc b/chromium/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win.cc deleted file mode 100644 index 37b6a152785..00000000000 --- a/chromium/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win.h" - -#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" -#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" - -namespace blink { - -FallbackLruCache::FallbackLruCache(size_t max_size) : max_size_(max_size) { - DCHECK_GT(max_size_, 0u); -} - -FallbackLruCache::TypefaceVector* FallbackLruCache::Get(const String& key) { - HashMapType::iterator find_result = map_.find(key); - if (find_result == map_.end()) - return nullptr; - - // Move result to beginning of list. - KeyListNode* node = find_result->value.ListNode(); - ordering_.Remove(node); - ordering_.Push(node); - return find_result->value.value(); -} - -void FallbackLruCache::Put(String&& key, TypefaceVector&& arg) { - HashMapType::iterator find_result = map_.find(key); - if (find_result != map_.end()) { - ordering_.Remove(find_result->value.ListNode()); - map_.erase(find_result); - } - - if (map_.size() >= max_size_) { - RemoveLeastRecentlyUsed(); - } - - std::unique_ptr<KeyListNode> list_node = std::make_unique<KeyListNode>(key); - HashMapType::AddResult add_result = map_.insert( - std::move(key), MappedWithListNode(std::move(arg), std::move(list_node))); - DCHECK(add_result.is_new_entry); - ordering_.Push(add_result.stored_value->value.ListNode()); -} - -void FallbackLruCache::Clear() { - map_.clear(); - ordering_.Clear(); -} - -void FallbackLruCache::RemoveLeastRecentlyUsed() { - KeyListNode* tail = ordering_.Tail(); - ordering_.Remove(tail); - map_.erase(tail->key()); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win.h b/chromium/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win.h deleted file mode 100644 index 90048e3319a..00000000000 --- a/chromium/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/wtf/doubly_linked_list.h" -#include "third_party/blink/renderer/platform/wtf/hash_map.h" -#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h" -#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" -#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" -#include "third_party/skia/include/core/SkRefCnt.h" -#include "third_party/skia/include/core/SkTypeface.h" - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WIN_FALLBACK_LRU_CACHE_WIN_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WIN_FALLBACK_LRU_CACHE_WIN_H_ - -namespace blink { - -/* A LRU cache for storing a a vector of typefaces for a particular key string, - * which would usually be a locale plus potential additional parameters. Uses a - * HashMap for storage and access and a DoublyLinkedList for managing age of - * entries. TODO(https://crbug.com/1010925): Potentially move this to a generic - * LRU Cache implementation once we have such in WTF. */ -class PLATFORM_EXPORT FallbackLruCache { - USING_FAST_MALLOC(FallbackLruCache); - - public: - FallbackLruCache(size_t max_size); - - using TypefaceVector = Vector<sk_sp<SkTypeface>>; - - TypefaceVector* Get(const String& key); - void Put(String&& key, TypefaceVector&& arg); - - void Clear(); - - size_t size() const { return map_.size(); } - - private: - class KeyListNode final : public DoublyLinkedListNode<KeyListNode> { - USING_FAST_MALLOC(KeyListNode); - - public: - friend class DoublyLinkedListNode<KeyListNode>; - KeyListNode(const String& key) : key_(key) {} - - const String& key() const { return key_; } - - private: - String key_; - KeyListNode* prev_{nullptr}; - KeyListNode* next_{nullptr}; - }; - - class MappedWithListNode { - USING_FAST_MALLOC(MappedWithListNode); - - public: - MappedWithListNode(TypefaceVector&& mapped_arg, - std::unique_ptr<KeyListNode>&& list_node) - : mapped_value_(std::move(mapped_arg)), - list_node_(std::move(list_node)) {} - - MappedWithListNode(WTF::HashTableDeletedValueType) { - list_node_.reset(reinterpret_cast<KeyListNode*>(-1)); - } - - TypefaceVector* value() { return &mapped_value_; } - KeyListNode* ListNode() { return list_node_.get(); } - - private: - TypefaceVector mapped_value_; - std::unique_ptr<KeyListNode> list_node_; - }; - - void RemoveLeastRecentlyUsed(); - - using HashMapType = HashMap<String, - MappedWithListNode, - DefaultHash<String>::Hash, - HashTraits<String>, - SimpleClassHashTraits<MappedWithListNode>>; - - HashMapType map_; - DoublyLinkedList<KeyListNode> ordering_; - size_t max_size_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WIN_FALLBACK_LRU_CACHE_WIN_H_ diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win_test.cc b/chromium/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win_test.cc index 92f78aa9e38..917f0111235 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win_test.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win_test.cc @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win.h" +#include "third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/text/character_names.h" +#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/skia/include/core/SkFontMgr.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkTypeface.h" @@ -29,7 +32,7 @@ void fillCacheWithDummies(blink::FallbackLruCache& lru_cache, const char* format_string, size_t count) { for (size_t i = 0; i < count; ++i) { - blink::FallbackLruCache::TypefaceVector dummy_typefaces; + blink::TypefaceVector dummy_typefaces; dummy_typefaces.push_back( SkTypeface::MakeFromName(kFontFamilyNameArial, SkFontStyle())); lru_cache.Put(String::Format(format_string, i), std::move(dummy_typefaces)); @@ -48,7 +51,7 @@ TEST(FallbackLruCacheTest, KeepChineseWhenFetched) { // the Chinese font and ensure it's gone. FallbackLruCache lru_cache(kLruCacheTestSize); EXPECT_EQ(lru_cache.size(), 0u); - FallbackLruCache::TypefaceVector fallback_typefaces_zh; + TypefaceVector fallback_typefaces_zh; fallback_typefaces_zh.push_back( fallbackForLocale(kHanSimplifiedLocale, kFirstCJKIdeograph)); lru_cache.Put(kHanSimplifiedLocale, std::move(fallback_typefaces_zh)); @@ -56,8 +59,7 @@ TEST(FallbackLruCacheTest, KeepChineseWhenFetched) { EXPECT_EQ(lru_cache.size(), 1u); fillCacheWithDummies(lru_cache, "dummy_locale_%zu", kLruCacheTestSize - 1); - FallbackLruCache::TypefaceVector* chinese_typefaces = - lru_cache.Get(kHanSimplifiedLocale); + TypefaceVector* chinese_typefaces = lru_cache.Get(kHanSimplifiedLocale); EXPECT_TRUE(chinese_typefaces); EXPECT_TRUE(chinese_typefaces->at(0)->unicharToGlyph(0x4E01)); EXPECT_EQ(lru_cache.size(), kLruCacheTestSize); diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc b/chromium/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc index 821a6a7afc3..52427643b20 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc @@ -42,7 +42,7 @@ #include "base/debug/alias.h" #include "base/stl_util.h" #include "base/trace_event/trace_event.h" -#include "third_party/blink/public/platform/interface_provider.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/fonts/bitmap_glyphs_block_list.h" #include "third_party/blink/renderer/platform/fonts/font_description.h" @@ -229,7 +229,7 @@ void FontCache::SetStatusFontMetrics(const wchar_t* family_name, void FontCache::EnsureServiceConnected() { if (service_) return; - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( service_.BindNewPipeAndPassReceiver()); } diff --git a/chromium/third_party/blink/renderer/platform/fonts/win/font_unique_name_lookup_win.cc b/chromium/third_party/blink/renderer/platform/fonts/win/font_unique_name_lookup_win.cc index 52555021f7b..13162712b2e 100644 --- a/chromium/third_party/blink/renderer/platform/fonts/win/font_unique_name_lookup_win.cc +++ b/chromium/third_party/blink/renderer/platform/fonts/win/font_unique_name_lookup_win.cc @@ -9,8 +9,8 @@ #include "base/files/file_path.h" #include "mojo/public/mojom/base/shared_memory.mojom-blink.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom-blink.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/instrumentation/histogram.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -165,7 +165,7 @@ bool FontUniqueNameLookupWin::IsFontUniqueNameLookupReadyForSyncLookup() { void FontUniqueNameLookupWin::EnsureServiceConnected() { if (service_) return; - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( service_.BindNewPipeAndPassReceiver()); } diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_point.h b/chromium/third_party/blink/renderer/platform/geometry/float_point.h index cd1ddb8bf56..91b2d129008 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/float_point.h +++ b/chromium/third_party/blink/renderer/platform/geometry/float_point.h @@ -34,6 +34,7 @@ #include "third_party/blink/renderer/platform/geometry/int_size.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/forward.h" +#include "third_party/blink/renderer/platform/wtf/hash_traits.h" #include "third_party/skia/include/core/SkPoint.h" #include "ui/gfx/geometry/point3_f.h" #include "ui/gfx/geometry/point_f.h" @@ -71,6 +72,11 @@ class PLATFORM_EXPORT FloatPoint { static FloatPoint NarrowPrecision(double x, double y); + bool IsValid() const { + return x_ != -std::numeric_limits<float>::infinity() && + y_ != -std::numeric_limits<float>::infinity(); + } + constexpr float X() const { return x_; } constexpr float Y() const { return y_; } @@ -139,6 +145,9 @@ class PLATFORM_EXPORT FloatPoint { private: float x_, y_; + + friend struct ::WTF::DefaultHash<blink::FloatSize>; + friend struct ::WTF::HashTraits<blink::FloatSize>; }; inline FloatPoint& operator+=(FloatPoint& a, const FloatSize& b) { @@ -217,6 +226,10 @@ inline IntPoint FlooredIntPoint(const FloatPoint& p) { return IntPoint(clampTo<int>(floorf(p.X())), clampTo<int>(floorf(p.Y()))); } +inline IntPoint FlooredIntPoint(const gfx::PointF& p) { + return IntPoint(clampTo<int>(floorf(p.x())), clampTo<int>(floorf(p.y()))); +} + inline IntPoint CeiledIntPoint(const FloatPoint& p) { return IntPoint(clampTo<int>(ceilf(p.X())), clampTo<int>(ceilf(p.Y()))); } @@ -243,4 +256,42 @@ PLATFORM_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, } // namespace blink -#endif +namespace WTF { + +template <> +struct DefaultHash<blink::FloatPoint> { + STATIC_ONLY(DefaultHash); + struct Hash { + STATIC_ONLY(Hash); + typedef typename IntTypes<sizeof(float)>::UnsignedType Bits; + static unsigned GetHash(const blink::FloatPoint& key) { + return HashInts(bit_cast<Bits>(key.X()), bit_cast<Bits>(key.Y())); + } + static bool Equal(const blink::FloatPoint& a, const blink::FloatPoint& b) { + return bit_cast<Bits>(a.X()) == bit_cast<Bits>(b.X()) && + bit_cast<Bits>(a.Y()) == bit_cast<Bits>(b.Y()); + } + static const bool safe_to_compare_to_empty_or_deleted = true; + }; +}; + +template <> +struct HashTraits<blink::FloatPoint> : GenericHashTraits<blink::FloatPoint> { + STATIC_ONLY(HashTraits); + static const bool kEmptyValueIsZero = false; + static blink::FloatPoint EmptyValue() { + return blink::FloatPoint(std::numeric_limits<float>::infinity(), + std::numeric_limits<float>::infinity()); + } + static void ConstructDeletedValue(blink::FloatPoint& slot, bool) { + slot = blink::FloatPoint(-std::numeric_limits<float>::infinity(), + -std::numeric_limits<float>::infinity()); + } + static bool IsDeletedValue(const blink::FloatPoint& value) { + return !value.IsValid(); + } +}; + +} // namespace WTF + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_POINT_H_ diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_rect.cc b/chromium/third_party/blink/renderer/platform/geometry/float_rect.cc index 3b0b298bf7b..f9c90559557 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/float_rect.cc +++ b/chromium/third_party/blink/renderer/platform/geometry/float_rect.cc @@ -63,6 +63,10 @@ bool FloatRect::EqualWithinEpsilon(const FloatRect& other, #endif +bool FloatRect::IsFinite() const { + return static_cast<SkRect>(*this).isFinite(); +} + bool FloatRect::IsExpressibleAsIntRect() const { return isWithinIntRange(X()) && isWithinIntRange(Y()) && isWithinIntRange(Width()) && isWithinIntRange(Height()) && diff --git a/chromium/third_party/blink/renderer/platform/geometry/float_rect.h b/chromium/third_party/blink/renderer/platform/geometry/float_rect.h index 1e3c7b0787a..4271528c4b0 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/float_rect.h +++ b/chromium/third_party/blink/renderer/platform/geometry/float_rect.h @@ -29,6 +29,7 @@ #include <iosfwd> +#include "base/compiler_specific.h" #include "base/numerics/clamped_math.h" #include "build/build_config.h" #include "third_party/blink/renderer/platform/geometry/float_point.h" @@ -92,6 +93,8 @@ class PLATFORM_EXPORT FloatRect { constexpr bool IsEmpty() const { return size_.IsEmpty(); } constexpr bool IsZero() const { return size_.IsZero(); } + // True if no member is infinite or NaN. + bool IsFinite() const; bool IsExpressibleAsIntRect() const; FloatPoint Center() const { @@ -130,8 +133,8 @@ class PLATFORM_EXPORT FloatRect { location_.Y() + size_.Height()); } // typically bottomRight - bool Intersects(const IntRect&) const; - bool Intersects(const FloatRect&) const; + WARN_UNUSED_RESULT bool Intersects(const IntRect&) const; + WARN_UNUSED_RESULT bool Intersects(const FloatRect&) const; bool Contains(const IntRect&) const; bool Contains(const FloatRect&) const; bool Contains(const FloatPoint&, ContainsMode = kInsideOrOnStroke) const; diff --git a/chromium/third_party/blink/renderer/platform/geometry/int_rect.h b/chromium/third_party/blink/renderer/platform/geometry/int_rect.h index 28a594d574e..9f891218aa4 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/int_rect.h +++ b/chromium/third_party/blink/renderer/platform/geometry/int_rect.h @@ -28,6 +28,7 @@ #include <iosfwd> +#include "base/compiler_specific.h" #include "base/numerics/clamped_math.h" #include "build/build_config.h" #include "third_party/blink/renderer/platform/geometry/int_point.h" @@ -133,7 +134,7 @@ class PLATFORM_EXPORT IntRect { location_.Y() + size_.Height()); } // typically bottomRight - bool Intersects(const IntRect&) const; + WARN_UNUSED_RESULT bool Intersects(const IntRect&) const; bool Contains(const IntRect&) const; // This checks to see if the rect contains x,y in the traditional sense. diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_rect.h b/chromium/third_party/blink/renderer/platform/geometry/layout_rect.h index f1b6f9b0798..6434c5d32f5 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/layout_rect.h +++ b/chromium/third_party/blink/renderer/platform/geometry/layout_rect.h @@ -32,6 +32,8 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_RECT_H_ #include <iosfwd> + +#include "base/compiler_specific.h" #include "third_party/blink/renderer/platform/geometry/float_rect.h" #include "third_party/blink/renderer/platform/geometry/int_rect.h" #include "third_party/blink/renderer/platform/geometry/layout_point.h" @@ -196,7 +198,7 @@ class PLATFORM_EXPORT LayoutRect { location_.Y() + size_.Height()); } - bool Intersects(const LayoutRect&) const; + WARN_UNUSED_RESULT bool Intersects(const LayoutRect&) const; bool Contains(const LayoutRect&) const; // This checks to see if the rect contains x,y in the traditional sense. diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_size.h b/chromium/third_party/blink/renderer/platform/geometry/layout_size.h index 472784b000d..32a0c93f776 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/layout_size.h +++ b/chromium/third_party/blink/renderer/platform/geometry/layout_size.h @@ -65,6 +65,8 @@ class PLATFORM_EXPORT LayoutSize { : width_(size.Width()), height_(size.Height()) {} constexpr explicit LayoutSize(const gfx::Size& size) : width_(size.width()), height_(size.height()) {} + constexpr explicit LayoutSize(const gfx::SizeF& size) + : width_(size.width()), height_(size.height()) {} constexpr explicit operator FloatSize() const { return FloatSize(width_.ToFloat(), height_.ToFloat()); diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_unit.h b/chromium/third_party/blink/renderer/platform/geometry/layout_unit.h index eaaff017795..8ffe3e11501 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/layout_unit.h +++ b/chromium/third_party/blink/renderer/platform/geometry/layout_unit.h @@ -723,7 +723,12 @@ inline float& operator/=(float& a, const LayoutUnit& b) { inline int SnapSizeToPixel(LayoutUnit size, LayoutUnit location) { LayoutUnit fraction = location.Fraction(); - return (fraction + size).Round() - fraction.Round(); + int result = (fraction + size).Round() - fraction.Round(); + if (UNLIKELY(result == 0 && + std::abs(size.ToFloat()) > LayoutUnit::Epsilon() * 4)) { + return size > 0 ? 1 : -1; + } + return result; } inline int RoundToInt(LayoutUnit value) { diff --git a/chromium/third_party/blink/renderer/platform/geometry/layout_unit_test.cc b/chromium/third_party/blink/renderer/platform/geometry/layout_unit_test.cc index db1fa1f610f..05d4a2f762e 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/layout_unit_test.cc +++ b/chromium/third_party/blink/renderer/platform/geometry/layout_unit_test.cc @@ -155,8 +155,20 @@ TEST(LayoutUnitTest, LayoutUnitSnapSizeToPixel) { EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.99))); EXPECT_EQ(2, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1))); - EXPECT_EQ(0, SnapSizeToPixel(LayoutUnit(0.5), LayoutUnit(1.5))); - EXPECT_EQ(0, SnapSizeToPixel(LayoutUnit(0.99), LayoutUnit(1.5))); + // 0.046875 is 3/64, lower than 4 * LayoutUnit::Epsilon() + EXPECT_EQ(0, SnapSizeToPixel(LayoutUnit(0.046875), LayoutUnit(0))); + // 0.078125 is 5/64, higher than 4 * LayoutUnit::Epsilon() + EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(0.078125), LayoutUnit(0))); + + // Negative versions + EXPECT_EQ(0, SnapSizeToPixel(LayoutUnit(-0.046875), LayoutUnit(0))); + EXPECT_EQ(-1, SnapSizeToPixel(LayoutUnit(-0.078125), LayoutUnit(0))); + + // The next 2 would snap to zero but for the requirement that we not snap + // sizes greater than 4 * LayoutUnit::Epsilon() to 0. + EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(0.5), LayoutUnit(1.5))); + EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(0.99), LayoutUnit(1.5))); + EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.0), LayoutUnit(1.5))); EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.49), LayoutUnit(1.5))); EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1.5))); diff --git a/chromium/third_party/blink/renderer/platform/geometry/length.h b/chromium/third_party/blink/renderer/platform/geometry/length.h index 72d3979fb1a..a77de90a46a 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/length.h +++ b/chromium/third_party/blink/renderer/platform/geometry/length.h @@ -60,7 +60,7 @@ class PLATFORM_EXPORT Length { kExtendToZoom, kDeviceWidth, kDeviceHeight, - kMaxSizeNone + kNone }; Length() : int_value_(0), quirk_(false), type_(kAuto), is_float_(false) {} @@ -114,7 +114,7 @@ class PLATFORM_EXPORT Length { bool operator==(const Length& o) const { return (type_ == o.type_) && (quirk_ == o.quirk_) && - (IsMaxSizeNone() || (GetFloatValue() == o.GetFloatValue()) || + (IsNone() || (GetFloatValue() == o.GetFloatValue()) || IsCalculatedEqual(o)); } bool operator!=(const Length& o) const { return !(*this == o); } @@ -145,7 +145,7 @@ class PLATFORM_EXPORT Length { static Length ExtendToZoom() { return Length(kExtendToZoom); } static Length DeviceWidth() { return Length(kDeviceWidth); } static Length DeviceHeight() { return Length(kDeviceHeight); } - static Length MaxSizeNone() { return Length(kMaxSizeNone); } + static Length None() { return Length(kNone); } static Length FitContent() { return Length(kFitContent); } template <typename NUMBER_TYPE> static Length Percent(NUMBER_TYPE number) { @@ -191,21 +191,21 @@ class PLATFORM_EXPORT Length { void SetQuirk(bool quirk) { quirk_ = quirk; } - bool IsMaxSizeNone() const { return GetType() == kMaxSizeNone; } + bool IsNone() const { return GetType() == kNone; } // FIXME calc: https://bugs.webkit.org/show_bug.cgi?id=80357. A calculated // Length always contains a percentage, and without a maxValue passed to these // functions it's impossible to determine the sign or zero-ness. We assume all // calc values are positive and non-zero for now. bool IsZero() const { - DCHECK(!IsMaxSizeNone()); + DCHECK(!IsNone()); if (IsCalculated()) return false; return is_float_ ? !float_value_ : !int_value_; } bool IsPositive() const { - if (IsMaxSizeNone()) + if (IsNone()) return false; if (IsCalculated()) return true; @@ -213,7 +213,7 @@ class PLATFORM_EXPORT Length { return GetFloatValue() > 0; } bool IsNegative() const { - if (IsMaxSizeNone() || IsCalculated()) + if (IsNone() || IsCalculated()) return false; return GetFloatValue() < 0; @@ -272,7 +272,7 @@ class PLATFORM_EXPORT Length { } float GetFloatValue() const { - DCHECK(!IsMaxSizeNone()); + DCHECK(!IsNone()); return is_float_ ? float_value_ : int_value_; } float NonNanCalculatedValue(LayoutUnit max_value) const; @@ -283,7 +283,7 @@ class PLATFORM_EXPORT Length { private: int GetIntValue() const { - DCHECK(!IsMaxSizeNone()); + DCHECK(!IsNone()); return is_float_ ? static_cast<int>(float_value_) : int_value_; } diff --git a/chromium/third_party/blink/renderer/platform/geometry/length_functions.cc b/chromium/third_party/blink/renderer/platform/geometry/length_functions.cc index 6aef97e527d..dd5a6fed930 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/length_functions.cc +++ b/chromium/third_party/blink/renderer/platform/geometry/length_functions.cc @@ -52,7 +52,7 @@ float FloatValueForLength(const Length& length, float maximum_value) { case Length::kExtendToZoom: case Length::kDeviceWidth: case Length::kDeviceHeight: - case Length::kMaxSizeNone: + case Length::kNone: NOTREACHED(); return 0; } @@ -80,7 +80,7 @@ LayoutUnit MinimumValueForLengthInternal(const Length& length, case Length::kExtendToZoom: case Length::kDeviceWidth: case Length::kDeviceHeight: - case Length::kMaxSizeNone: + case Length::kNone: NOTREACHED(); return LayoutUnit(); } @@ -103,7 +103,7 @@ LayoutUnit ValueForLength(const Length& length, LayoutUnit maximum_value) { case Length::kExtendToZoom: case Length::kDeviceWidth: case Length::kDeviceHeight: - case Length::kMaxSizeNone: + case Length::kNone: NOTREACHED(); return LayoutUnit(); } diff --git a/chromium/third_party/blink/renderer/platform/geometry/length_size.h b/chromium/third_party/blink/renderer/platform/geometry/length_size.h index 8da2e50bbe9..a03152f6e2b 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/length_size.h +++ b/chromium/third_party/blink/renderer/platform/geometry/length_size.h @@ -38,6 +38,7 @@ class LengthSize { bool operator==(const LengthSize& o) const { return width_ == o.width_ && height_ == o.height_; } + bool operator!=(const LengthSize& o) const { return !(*this == o); } void SetWidth(const Length& width) { width_ = width; } const Length& Width() const { return width_; } diff --git a/chromium/third_party/blink/renderer/platform/geometry/region.cc b/chromium/third_party/blink/renderer/platform/geometry/region.cc index a90abdc2378..54a4ce33a34 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/region.cc +++ b/chromium/third_party/blink/renderer/platform/geometry/region.cc @@ -327,7 +327,7 @@ Region::Shape::SegmentIterator Region::Shape::SegmentsEnd( return segments_.data() + segment_index; } -#ifndef NDEBUG +#if DCHECK_IS_ON() void Region::Shape::Dump() const { for (Shape::SpanIterator span = SpansBegin(), end = SpansEnd(); span != end; ++span) { @@ -570,7 +570,7 @@ Region::Shape Region::Shape::SubtractShapes(const Shape& shape1, return ShapeOperation<SubtractOperation>(shape1, shape2); } -#ifndef NDEBUG +#if DCHECK_IS_ON() void Region::Dump() const { printf("Bounds: (%d, %d, %d, %d)\n", bounds_.X(), bounds_.Y(), bounds_.Width(), bounds_.Height()); diff --git a/chromium/third_party/blink/renderer/platform/geometry/region.h b/chromium/third_party/blink/renderer/platform/geometry/region.h index d34815c7ca1..581d4df4574 100644 --- a/chromium/third_party/blink/renderer/platform/geometry/region.h +++ b/chromium/third_party/blink/renderer/platform/geometry/region.h @@ -73,7 +73,11 @@ class PLATFORM_EXPORT Region { uint64_t Area() const; -#ifndef NDEBUG + wtf_size_t Complexity() const { + return shape_.SpansSize() + shape_.SegmentsSize(); + } + +#if DCHECK_IS_ON() void Dump() const; #endif @@ -130,7 +134,7 @@ class PLATFORM_EXPORT Region { static bool CompareShapes(const Shape& shape1, const Shape& shape2); void TrimCapacities(); -#ifndef NDEBUG +#if DCHECK_IS_ON() void Dump() const; #endif diff --git a/chromium/third_party/blink/renderer/platform/graphics/DEPS b/chromium/third_party/blink/renderer/platform/graphics/DEPS index 69ebb831274..9af9d13ba01 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/DEPS +++ b/chromium/third_party/blink/renderer/platform/graphics/DEPS @@ -19,9 +19,11 @@ include_rules = [ "+gpu/command_buffer/client/gpu_memory_buffer_manager.h", "+gpu/command_buffer/client/raster_interface.h", "+gpu/command_buffer/client/shared_image_interface.h", + "+gpu/command_buffer/common/gles2_cmd_copy_texture_chromium_utils.h", "+gpu/command_buffer/common/gpu_memory_buffer_support.h", "+gpu/command_buffer/common/capabilities.h", "+gpu/command_buffer/common/mailbox.h", + "+gpu/command_buffer/common/mailbox_holder.h", "+gpu/command_buffer/common/shared_image_usage.h", "+gpu/command_buffer/common/sync_token.h", "+gpu/ipc/common/mailbox.mojom-blink.h", @@ -62,4 +64,5 @@ specific_include_rules = { ".*_test.cc": [ "+components/viz/test", ], + "(graphics_context|skia_utils)\.cc" : [ "+ui/base/ui_base_features.h" ] } diff --git a/chromium/third_party/blink/renderer/platform/graphics/OWNERS b/chromium/third_party/blink/renderer/platform/graphics/OWNERS index 73a65352c7d..0bb32866bbe 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/OWNERS +++ b/chromium/third_party/blink/renderer/platform/graphics/OWNERS @@ -13,6 +13,7 @@ vollick@chromium.org # For surface ID propagation and synchronization samans@chromium.org +jonross@chromium.org # lowLatency canvas mcasas@chromium.org diff --git a/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc index 22135712d5d..08d74cb21d7 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.cc @@ -4,51 +4,88 @@ #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h" +#include <memory> +#include <utility> + #include "components/viz/common/resources/single_release_callback.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/client/raster_interface.h" +#include "gpu/command_buffer/client/shared_image_interface.h" #include "gpu/command_buffer/common/sync_token.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h" +#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h" #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" -#include "third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h" #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h" -#include "third_party/blink/renderer/platform/graphics/skia_texture_holder.h" #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/skia/include/core/SkImage.h" -#include "third_party/skia/include/gpu/GrTexture.h" - -#include <memory> -#include <utility> namespace blink { +namespace { -scoped_refptr<AcceleratedStaticBitmapImage> -AcceleratedStaticBitmapImage::CreateFromSkImage( - sk_sp<SkImage> image, +void ReleaseCallbackOnContextThread( + std::unique_ptr<viz::SingleReleaseCallback> callback, + const gpu::SyncToken sync_token) { + callback->Run(sync_token, /* is_lost = */ false); +} + +} // namespace + +AcceleratedStaticBitmapImage::MailboxRef::MailboxRef( + const gpu::SyncToken& sync_token, + base::PlatformThreadRef context_thread_ref, + scoped_refptr<base::SingleThreadTaskRunner> context_task_runner, + std::unique_ptr<viz::SingleReleaseCallback> release_callback) + : sync_token_(sync_token), + context_thread_ref_(context_thread_ref), + context_task_runner_(std::move(context_task_runner)), + release_callback_(std::move(release_callback)) { + DCHECK(!is_cross_thread() || sync_token_.verified_flush()); +} + +AcceleratedStaticBitmapImage::MailboxRef::~MailboxRef() { + if (context_thread_ref_ == base::PlatformThread::CurrentRef()) { + ReleaseCallbackOnContextThread(std::move(release_callback_), sync_token_); + } else { + context_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&ReleaseCallbackOnContextThread, + std::move(release_callback_), sync_token_)); + } +} + +const gpu::SyncToken& +AcceleratedStaticBitmapImage::MailboxRef::GetOrCreateSyncToken( base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper) { - CHECK(image && image->isTextureBacked()); - return base::AdoptRef(new AcceleratedStaticBitmapImage( - std::move(image), std::move(context_provider_wrapper))); + if (!sync_token_.HasData()) { + DCHECK(!is_cross_thread()); + DCHECK(context_provider_wrapper); + context_provider_wrapper->ContextProvider() + ->InterfaceBase() + ->GenUnverifiedSyncTokenCHROMIUM(sync_token_.GetData()); + } + return sync_token_; } -scoped_refptr<AcceleratedStaticBitmapImage> -AcceleratedStaticBitmapImage::CreateFromWebGLContextImage( - const gpu::Mailbox& mailbox, - const gpu::SyncToken& sync_token, - unsigned texture_id, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&& - context_provider_wrapper, - IntSize mailbox_size, - bool is_origin_top_left) { - return base::AdoptRef(new AcceleratedStaticBitmapImage( - mailbox, sync_token, texture_id, std::move(context_provider_wrapper), - mailbox_size, is_origin_top_left)); +// static +void AcceleratedStaticBitmapImage::ReleaseTexture(void* ctx) { + auto* release_ctx = static_cast<ReleaseContext*>(ctx); + if (release_ctx->context_provider_wrapper) { + if (release_ctx->texture_id) { + auto* ri = release_ctx->context_provider_wrapper->ContextProvider() + ->RasterInterface(); + ri->EndSharedImageAccessDirectCHROMIUM(release_ctx->texture_id); + ri->DeleteGpuRasterTexture(release_ctx->texture_id); + } + } + + delete release_ctx; } +// static scoped_refptr<AcceleratedStaticBitmapImage> AcceleratedStaticBitmapImage::CreateFromCanvasMailbox( const gpu::Mailbox& mailbox, @@ -56,38 +93,16 @@ AcceleratedStaticBitmapImage::CreateFromCanvasMailbox( GLuint shared_image_texture_id, const SkImageInfo& sk_image_info, GLenum texture_target, - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper, - PlatformThreadId context_thread_id, bool is_origin_top_left, + base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper, + base::PlatformThreadRef context_thread_ref, + scoped_refptr<base::SingleThreadTaskRunner> context_task_runner, std::unique_ptr<viz::SingleReleaseCallback> release_callback) { return base::AdoptRef(new AcceleratedStaticBitmapImage( mailbox, sync_token, shared_image_texture_id, sk_image_info, - texture_target, std::move(context_provider_wrapper), context_thread_id, - is_origin_top_left, std::move(release_callback))); -} - -AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage( - sk_sp<SkImage> image, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&& - context_provider_wrapper) - : paint_image_content_id_(cc::PaintImage::GetNextContentId()) { - CHECK(image && image->isTextureBacked()); - skia_texture_holder_ = std::make_unique<SkiaTextureHolder>( - std::move(image), std::move(context_provider_wrapper)); -} - -AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage( - const gpu::Mailbox& mailbox, - const gpu::SyncToken& sync_token, - unsigned texture_id, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&& - context_provider_wrapper, - IntSize mailbox_size, - bool is_origin_top_left) - : paint_image_content_id_(cc::PaintImage::GetNextContentId()) { - mailbox_texture_holder_ = std::make_unique<MailboxTextureHolder>( - mailbox, sync_token, texture_id, std::move(context_provider_wrapper), - mailbox_size, is_origin_top_left); + texture_target, is_origin_top_left, kDefaultImageOrientation, + std::move(context_provider_wrapper), context_thread_ref, + std::move(context_task_runner), std::move(release_callback))); } AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage( @@ -96,92 +111,44 @@ AcceleratedStaticBitmapImage::AcceleratedStaticBitmapImage( GLuint shared_image_texture_id, const SkImageInfo& sk_image_info, GLenum texture_target, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&& - context_provider_wrapper, - PlatformThreadId context_thread_id, bool is_origin_top_left, + const ImageOrientation& orientation, + base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper, + base::PlatformThreadRef context_thread_ref, + scoped_refptr<base::SingleThreadTaskRunner> context_task_runner, std::unique_ptr<viz::SingleReleaseCallback> release_callback) - : mailbox_ref_(base::MakeRefCounted<TextureHolder::MailboxRef>( - std::move(release_callback))), + : StaticBitmapImage(orientation), + mailbox_(mailbox), + sk_image_info_(sk_image_info), + texture_target_(texture_target), + is_origin_top_left_(is_origin_top_left), + context_provider_wrapper_(std::move(context_provider_wrapper)), + mailbox_ref_( + base::MakeRefCounted<MailboxRef>(sync_token, + context_thread_ref, + std::move(context_task_runner), + std::move(release_callback))), paint_image_content_id_(cc::PaintImage::GetNextContentId()) { - mailbox_texture_holder_ = std::make_unique<MailboxTextureHolder>( - mailbox, sync_token, std::move(context_provider_wrapper), mailbox_ref_, - context_thread_id, sk_image_info, texture_target, is_origin_top_left); - if (shared_image_texture_id) { - skia_texture_holder_ = std::make_unique<SkiaTextureHolder>( - mailbox_texture_holder_.get(), shared_image_texture_id); - } -} - -namespace { - -void DestroySkImageOnOriginalThread( - sk_sp<SkImage> image, - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper, - std::unique_ptr<gpu::SyncToken> sync_token) { - if (context_provider_wrapper && - image->isValid( - context_provider_wrapper->ContextProvider()->GetGrContext())) { - if (sync_token->HasData()) { - // To make sure skia does not recycle the texture while it is still in use - // by another context. - context_provider_wrapper->ContextProvider() - ->ContextGL() - ->WaitSyncTokenCHROMIUM(sync_token->GetData()); - } - // In case texture was used by compositor, which may have changed params. - image->getTexture()->textureParamsModified(); - } - image.reset(); -} - -} // namespace - -AcceleratedStaticBitmapImage::~AcceleratedStaticBitmapImage() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(mailbox_.IsSharedImage()); - // If the original SkImage was retained, it must be destroyed on the thread - // where it came from. In the same thread case, there is nothing to do because - // the regular destruction flow is fine. - if (original_skia_image_) { - std::unique_ptr<gpu::SyncToken> sync_token = - base::WrapUnique(new gpu::SyncToken(GetSyncToken())); - if (!original_skia_image_task_runner_->BelongsToCurrentThread()) { - PostCrossThreadTask( - *original_skia_image_task_runner_, FROM_HERE, - CrossThreadBindOnce( - &DestroySkImageOnOriginalThread, std::move(original_skia_image_), - std::move(original_skia_image_context_provider_wrapper_), - WTF::Passed(std::move(sync_token)))); - } else { - DestroySkImageOnOriginalThread( - std::move(original_skia_image_), - std::move(original_skia_image_context_provider_wrapper_), - std::move(sync_token)); - } - } + if (shared_image_texture_id) + InitializeSkImage(shared_image_texture_id); } -void AcceleratedStaticBitmapImage::RetainOriginalSkImage() { +AcceleratedStaticBitmapImage::~AcceleratedStaticBitmapImage() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - DCHECK(skia_texture_holder_); - original_skia_image_ = skia_texture_holder_->GetSkImage(); - original_skia_image_context_provider_wrapper_ = ContextProviderWrapper(); - DCHECK(original_skia_image_); - - original_skia_image_task_runner_ = Thread::Current()->GetTaskRunner(); } IntSize AcceleratedStaticBitmapImage::Size() const { - return texture_holder()->Size(); + return IntSize(sk_image_info_.width(), sk_image_info_.height()); } scoped_refptr<StaticBitmapImage> AcceleratedStaticBitmapImage::MakeUnaccelerated() { CreateImageFromMailboxIfNeeded(); return UnacceleratedStaticBitmapImage::Create( - skia_texture_holder_->GetSkImage()->makeNonTextureImage()); + sk_image_->makeNonTextureImage(), orientation_); } bool AcceleratedStaticBitmapImage::CopyToTexture( @@ -197,49 +164,34 @@ bool AcceleratedStaticBitmapImage::CopyToTexture( if (!IsValid()) return false; - // TODO(junov) : could reduce overhead by using kOrderingBarrier when we know - // that the source and destination context or on the same stream. - EnsureMailbox(kUnverifiedSyncToken, GL_NEAREST); - // This method should only be used for cross-context copying, otherwise it's // wasting overhead. - DCHECK(mailbox_texture_holder_->IsCrossThread() || - dest_gl != ContextProviderWrapper()->ContextProvider()->ContextGL()); - - bool is_shared_image = mailbox_texture_holder_->GetMailbox().IsSharedImage(); + DCHECK(mailbox_ref_->is_cross_thread() || + dest_gl != ContextProvider()->ContextGL()); + DCHECK(mailbox_.IsSharedImage()); // Get a texture id that |destProvider| knows about and copy from it. dest_gl->WaitSyncTokenCHROMIUM( - mailbox_texture_holder_->GetSyncToken().GetConstData()); - GLuint source_texture_id; - if (is_shared_image) { - source_texture_id = dest_gl->CreateAndTexStorage2DSharedImageCHROMIUM( - mailbox_texture_holder_->GetMailbox().name); - dest_gl->BeginSharedImageAccessDirectCHROMIUM( - source_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); - } else { - source_texture_id = dest_gl->CreateAndConsumeTextureCHROMIUM( - mailbox_texture_holder_->GetMailbox().name); - } + mailbox_ref_->GetOrCreateSyncToken(ContextProviderWrapper()) + .GetConstData()); + GLuint source_texture_id = + dest_gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox_.name); + dest_gl->BeginSharedImageAccessDirectCHROMIUM( + source_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); dest_gl->CopySubTextureCHROMIUM( source_texture_id, 0, dest_target, dest_texture_id, dest_level, dest_point.X(), dest_point.Y(), source_sub_rectangle.X(), source_sub_rectangle.Y(), source_sub_rectangle.Width(), source_sub_rectangle.Height(), unpack_flip_y ? GL_FALSE : GL_TRUE, GL_FALSE, unpack_premultiply_alpha ? GL_FALSE : GL_TRUE); - if (is_shared_image) { - dest_gl->EndSharedImageAccessDirectCHROMIUM(source_texture_id); - } - // This drops the |destGL| context's reference on our |m_mailbox|, but it's - // still held alive by our SkImage. + dest_gl->EndSharedImageAccessDirectCHROMIUM(source_texture_id); dest_gl->DeleteTextures(1, &source_texture_id); // We need to update the texture holder's sync token to ensure that when this - // image is deleted, the texture resource will not be recycled by skia before - // the above texture copy has completed. + // mailbox is recycled or deleted, it is done after the copy operation above. gpu::SyncToken sync_token; dest_gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); - mailbox_texture_holder_->UpdateSyncToken(sync_token); + mailbox_ref_->set_sync_token(sync_token); return true; } @@ -251,31 +203,22 @@ PaintImage AcceleratedStaticBitmapImage::PaintImageForCurrentFrame() { if (!IsValid()) return PaintImage(); - sk_sp<SkImage> image; - if (original_skia_image_ && - original_skia_image_task_runner_->BelongsToCurrentThread()) { - // We need to avoid consuming the mailbox in the context where it - // originated. This avoids swapping back and forth between TextureHolder - // types. - image = original_skia_image_; - } else { - CreateImageFromMailboxIfNeeded(); - image = skia_texture_holder_->GetSkImage(); - } + CreateImageFromMailboxIfNeeded(); return CreatePaintImageBuilder() - .set_image(image, paint_image_content_id_) + .set_image(sk_image_, paint_image_content_id_) .set_completion_state(PaintImage::CompletionState::DONE) .TakePaintImage(); } -void AcceleratedStaticBitmapImage::Draw(cc::PaintCanvas* canvas, - const cc::PaintFlags& flags, - const FloatRect& dst_rect, - const FloatRect& src_rect, - RespectImageOrientationEnum, - ImageClampingMode image_clamping_mode, - ImageDecodingMode decode_mode) { +void AcceleratedStaticBitmapImage::Draw( + cc::PaintCanvas* canvas, + const cc::PaintFlags& flags, + const FloatRect& dst_rect, + const FloatRect& src_rect, + RespectImageOrientationEnum should_respect_image_orientation, + ImageClampingMode image_clamping_mode, + ImageDecodingMode decode_mode) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); auto paint_image = PaintImageForCurrentFrame(); if (!paint_image) @@ -287,68 +230,185 @@ void AcceleratedStaticBitmapImage::Draw(cc::PaintCanvas* canvas, .TakePaintImage(); } StaticBitmapImage::DrawHelper(canvas, flags, dst_rect, src_rect, - image_clamping_mode, paint_image); + image_clamping_mode, + should_respect_image_orientation, paint_image); } bool AcceleratedStaticBitmapImage::IsValid() const { - return texture_holder()->IsValid(); + if (sk_image_ && (!skia_context_provider_wrapper_ || + !sk_image_->isValid(ContextProvider()->GetGrContext()))) { + return false; + } + + if (mailbox_ref_->is_cross_thread()) { + // If context is is from another thread, validity cannot be verified. Just + // assume valid. Potential problem will be detected later. + return true; + } + + return !!context_provider_wrapper_; } WebGraphicsContext3DProvider* AcceleratedStaticBitmapImage::ContextProvider() const { - return texture_holder()->ContextProvider(); + auto context = ContextProviderWrapper(); + return context ? context->ContextProvider() : nullptr; } base::WeakPtr<WebGraphicsContext3DProviderWrapper> AcceleratedStaticBitmapImage::ContextProviderWrapper() const { - if (!IsValid()) - return nullptr; - - return texture_holder()->ContextProviderWrapper(); + return sk_image_ ? skia_context_provider_wrapper_ : context_provider_wrapper_; } void AcceleratedStaticBitmapImage::CreateImageFromMailboxIfNeeded() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (skia_texture_holder_) + if (sk_image_) + return; + InitializeSkImage(0u); +} + +void AcceleratedStaticBitmapImage::InitializeSkImage( + GLuint shared_image_texture_id) { + DCHECK(!shared_image_texture_id || !mailbox_ref_->is_cross_thread()); + + auto context_provider_wrapper = SharedGpuContext::ContextProviderWrapper(); + if (!context_provider_wrapper) return; - DCHECK(mailbox_texture_holder_); - skia_texture_holder_ = - std::make_unique<SkiaTextureHolder>(mailbox_texture_holder_.get(), 0u); + gpu::raster::RasterInterface* shared_ri = + context_provider_wrapper->ContextProvider()->RasterInterface(); + GrContext* shared_gr_context = + context_provider_wrapper->ContextProvider()->GetGrContext(); + DCHECK(shared_ri && + shared_gr_context); // context isValid already checked in callers + + GLuint shared_context_texture_id = 0u; + bool should_delete_texture_on_release = true; + + if (shared_image_texture_id) { + shared_context_texture_id = shared_image_texture_id; + should_delete_texture_on_release = false; + } else { + shared_ri->WaitSyncTokenCHROMIUM( + mailbox_ref_->GetOrCreateSyncToken(context_provider_wrapper) + .GetConstData()); + shared_context_texture_id = + shared_ri->CreateAndConsumeForGpuRaster(mailbox_); + shared_ri->BeginSharedImageAccessDirectCHROMIUM( + shared_context_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); + } + + GrGLTextureInfo texture_info; + texture_info.fTarget = texture_target_; + texture_info.fID = shared_context_texture_id; + texture_info.fFormat = + CanvasColorParams(sk_image_info_).GLSizedInternalFormat(); + GrBackendTexture backend_texture(sk_image_info_.width(), + sk_image_info_.height(), GrMipMapped::kNo, + texture_info); + + GrSurfaceOrigin origin = IsOriginTopLeft() ? kTopLeft_GrSurfaceOrigin + : kBottomLeft_GrSurfaceOrigin; + + auto* release_ctx = new ReleaseContext; + release_ctx->mailbox_ref = mailbox_ref_; + if (should_delete_texture_on_release) + release_ctx->texture_id = shared_context_texture_id; + release_ctx->context_provider_wrapper = context_provider_wrapper; + + sk_image_ = SkImage::MakeFromTexture( + shared_gr_context, backend_texture, origin, sk_image_info_.colorType(), + sk_image_info_.alphaType(), sk_image_info_.refColorSpace(), + &ReleaseTexture, release_ctx); + if (!sk_image_) + ReleaseTexture(release_ctx); + else + skia_context_provider_wrapper_ = std::move(context_provider_wrapper); } -void AcceleratedStaticBitmapImage::EnsureMailbox(MailboxSyncMode mode, - GLenum filter) { +void AcceleratedStaticBitmapImage::EnsureSyncTokenVerified() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (!mailbox_texture_holder_) { - TRACE_EVENT0("blink", "AcceleratedStaticBitmapImage::EnsureMailbox"); - - if (!original_skia_image_) { - // To ensure that the texture resource stays alive we only really need - // to retain the source SkImage until the mailbox is consumed, but this - // works too. - RetainOriginalSkImage(); - } - mailbox_texture_holder_ = std::make_unique<MailboxTextureHolder>( - skia_texture_holder_.get(), filter); + if (mailbox_ref_->verified_flush()) + return; + + if (mailbox_ref_->is_cross_thread()) { + // Was originally created on another thread. Should already have a sync + // token from the original source context, already verified if needed. + NOTREACHED() << "Cross-thread SyncToken should already be verified."; + return; } - mailbox_texture_holder_->Sync(mode); + + if (!ContextProviderWrapper()) + return; + + auto sync_token = + mailbox_ref_->GetOrCreateSyncToken(ContextProviderWrapper()); + int8_t* token_data = sync_token.GetData(); + ContextProvider()->InterfaceBase()->VerifySyncTokensCHROMIUM(&token_data, 1); + sync_token.SetVerifyFlush(); + mailbox_ref_->set_sync_token(sync_token); +} + +gpu::MailboxHolder AcceleratedStaticBitmapImage::GetMailboxHolder() const { + if (!IsValid()) + return gpu::MailboxHolder(); + + return gpu::MailboxHolder( + mailbox_, mailbox_ref_->GetOrCreateSyncToken(ContextProviderWrapper()), + texture_target_); } void AcceleratedStaticBitmapImage::Transfer() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - EnsureMailbox(kVerifiedSyncToken, GL_NEAREST); + EnsureSyncTokenVerified(); - // Release the SkiaTextureHolder, this SkImage is no longer valid to use + // SkImage is bound to the current thread so is no longer valid to use // cross-thread. - skia_texture_holder_.reset(); + sk_image_.reset(); DETACH_FROM_THREAD(thread_checker_); } bool AcceleratedStaticBitmapImage::CurrentFrameKnownToBeOpaque() { - return texture_holder()->CurrentFrameKnownToBeOpaque(); + return sk_image_info_.isOpaque(); +} + +scoped_refptr<StaticBitmapImage> +AcceleratedStaticBitmapImage::ConvertToColorSpace( + sk_sp<SkColorSpace> color_space, + SkColorType color_type) { + DCHECK(color_space); + DCHECK(color_type == kRGBA_F16_SkColorType || + color_type == kRGBA_8888_SkColorType); + + if (!ContextProviderWrapper()) + return nullptr; + + sk_sp<SkImage> skia_image = PaintImageForCurrentFrame().GetSkImage(); + if (SkColorSpace::Equals(color_space.get(), skia_image->colorSpace()) && + color_type == skia_image->colorType()) { + return this; + } + + auto image_info = skia_image->imageInfo() + .makeColorSpace(color_space) + .makeColorType(color_type); + + auto usage_flags = ContextProviderWrapper() + ->ContextProvider() + ->SharedImageInterface() + ->UsageForMailbox(mailbox_); + auto provider = CanvasResourceProvider::CreateSharedImageProvider( + Size(), ContextProviderWrapper(), kLow_SkFilterQuality, + CanvasColorParams(image_info), IsOriginTopLeft(), + CanvasResourceProvider::RasterMode::kGPU, usage_flags); + if (!provider) { + return nullptr; + } + + provider->Canvas()->drawImage(PaintImageForCurrentFrame(), 0, 0, nullptr); + return provider->Snapshot(orientation_); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h index f89408d112d..2b04e048281 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h +++ b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h @@ -10,12 +10,9 @@ #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_checker.h" -#include "third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h" -#include "third_party/blink/renderer/platform/graphics/skia_texture_holder.h" #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h" #include "third_party/blink/renderer/platform/scheduler/public/thread.h" -class GrContext; struct SkImageInfo; namespace viz { @@ -30,29 +27,10 @@ class PLATFORM_EXPORT AcceleratedStaticBitmapImage final public: ~AcceleratedStaticBitmapImage() override; - // SkImage with a texture backing. - // DO NOT USE. This is in the process of being removed. See crbug.com/962630. - static scoped_refptr<AcceleratedStaticBitmapImage> CreateFromSkImage( - sk_sp<SkImage>, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>); - - // Can specify the GrContext that created the texture backing. Ideally all - // callers would use this option. - // The |mailbox| is a name for the texture backing, allowing other contexts to - // use the same backing. - static scoped_refptr<AcceleratedStaticBitmapImage> - CreateFromWebGLContextImage( - const gpu::Mailbox&, - const gpu::SyncToken&, - unsigned texture_id, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&, - IntSize mailbox_size, - bool is_origin_top_left); - - // Creates an image wrapping a shared image mailbox. |release_callback| is a - // callback to be invoked when this mailbox/texture can be safely destroyed. - // It can be invoked on any thread. Note that it is assumed that the mailbox - // can only be used for read operations, no writes are allowed. + // Creates an image wrapping a shared image mailbox. + // + // |sync_token| is the token that must be waited on before reading the + // contents of this mailbox. // // |shared_image_texture_id| is an optional texture bound to the shared image // mailbox imported into the provided context. If provided the caller must @@ -60,28 +38,42 @@ class PLATFORM_EXPORT AcceleratedStaticBitmapImage final // and has a read lock on the shared image until the |release_callback| is // invoked. // - // If the image is created on a different thread than |context_thread_id| then - // the provided sync_token must be verified and no |shared_image_texture_id| - // should be provided. + // |sk_image_info| provides the metadata associated with the backing. + // + // |texture_target| is the target that the texture should be bound to if the + // backing is used with GL. + // + // |is_origin_top_left| indicates whether the origin in texture space + // corresponds to the top-left content pixel. + // + // |context_provider| is the context that the mailbox was created with. + // |context_thread_ref| and |context_task_runner| refer to the thread the + // context is bound to. If the image is created on a different thread than + // |context_thread_ref| then the provided sync_token must be verified and no + // |shared_image_texture_id| should be provided. + // + // |release_callback| is a callback to be invoked when this mailbox can be + // safely destroyed. It is guaranteed to be invoked on the context thread. + // + // Note that it is assumed that the mailbox can only be used for read + // operations, no writes are allowed. static scoped_refptr<AcceleratedStaticBitmapImage> CreateFromCanvasMailbox( const gpu::Mailbox&, const gpu::SyncToken&, GLuint shared_image_texture_id, const SkImageInfo& sk_image_info, GLenum texture_target, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>, - PlatformThreadId context_thread_id, bool is_origin_top_left, + base::WeakPtr<WebGraphicsContext3DProviderWrapper>, + base::PlatformThreadRef context_thread_ref, + scoped_refptr<base::SingleThreadTaskRunner> context_task_runner, std::unique_ptr<viz::SingleReleaseCallback> release_callback); bool CurrentFrameKnownToBeOpaque() override; IntSize Size() const override; bool IsTextureBacked() const override { return true; } - scoped_refptr<StaticBitmapImage> MakeAccelerated( - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_wrapper) - override { - return this; - } + scoped_refptr<StaticBitmapImage> ConvertToColorSpace(sk_sp<SkColorSpace>, + SkColorType) override; void Draw(cc::PaintCanvas*, const cc::PaintFlags&, @@ -105,82 +97,90 @@ class PLATFORM_EXPORT AcceleratedStaticBitmapImage final bool unpack_flip_y, const IntPoint& dest_point, const IntRect& source_sub_rectangle) override; - - bool HasMailbox() const final { return !!mailbox_texture_holder_; } - // To be called on sender thread before performing a transfer + // To be called on sender thread before performing a transfer to a different + // thread. void Transfer() final; - void EnsureMailbox(MailboxSyncMode, GLenum filter) final; + // Makes sure that the sync token associated with this mailbox is verified. + void EnsureSyncTokenVerified() final; - const gpu::Mailbox& GetMailbox() const final { - static const gpu::Mailbox mailbox; - return mailbox_texture_holder_ ? mailbox_texture_holder_->GetMailbox() - : mailbox; - } - const gpu::SyncToken& GetSyncToken() const final { - static const gpu::SyncToken sync_token; - return mailbox_texture_holder_ ? mailbox_texture_holder_->GetSyncToken() - : sync_token; + // Updates the sync token that must be waited on before recycling or deleting + // the mailbox for this image. This must be set by callers using the mailbox + // externally to this class. + void UpdateSyncToken(const gpu::SyncToken& sync_token) final { + mailbox_ref_->set_sync_token(sync_token); } + // Provides the mailbox backing for this image. The caller must wait on the + // sync token before accessing this mailbox. + gpu::MailboxHolder GetMailboxHolder() const final; + bool IsOriginTopLeft() const final { return is_origin_top_left_; } + PaintImage PaintImageForCurrentFrame() override; private: - AcceleratedStaticBitmapImage( - sk_sp<SkImage>, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&); - AcceleratedStaticBitmapImage( - const gpu::Mailbox&, - const gpu::SyncToken&, - unsigned texture_id, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&, - IntSize mailbox_size, - bool is_origin_top_left); + class MailboxRef : public ThreadSafeRefCounted<MailboxRef> { + public: + MailboxRef(const gpu::SyncToken& sync_token, + base::PlatformThreadRef context_thread_ref, + scoped_refptr<base::SingleThreadTaskRunner> context_task_runner, + std::unique_ptr<viz::SingleReleaseCallback> release_callback); + ~MailboxRef(); + + bool is_cross_thread() const { + return base::PlatformThread::CurrentRef() != context_thread_ref_; + } + void set_sync_token(gpu::SyncToken token) { sync_token_ = token; } + const gpu::SyncToken& GetOrCreateSyncToken( + base::WeakPtr<WebGraphicsContext3DProviderWrapper>); + bool verified_flush() { return sync_token_.verified_flush(); } + + private: + gpu::SyncToken sync_token_; + const base::PlatformThreadRef context_thread_ref_; + const scoped_refptr<base::SingleThreadTaskRunner> context_task_runner_; + std::unique_ptr<viz::SingleReleaseCallback> release_callback_; + }; + + struct ReleaseContext { + scoped_refptr<MailboxRef> mailbox_ref; + GLuint texture_id = 0u; + base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper; + }; + + static void ReleaseTexture(void* ctx); + AcceleratedStaticBitmapImage( const gpu::Mailbox&, const gpu::SyncToken&, GLuint shared_image_texture_id, const SkImageInfo& sk_image_info, GLenum texture_target, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&, - PlatformThreadId context_thread_id, bool is_origin_top_left, + const ImageOrientation& orientation, + base::WeakPtr<WebGraphicsContext3DProviderWrapper>, + base::PlatformThreadRef context_thread_ref, + scoped_refptr<base::SingleThreadTaskRunner> context_task_runner, std::unique_ptr<viz::SingleReleaseCallback> release_callback); void CreateImageFromMailboxIfNeeded(); - void WaitSyncTokenIfNeeded(); - void RetainOriginalSkImage(); - - // TODO(khushalsagar): Its unclear what to use here for calls checking IsValid - // or querying the ContextProvider for the image. This can differ in the 2, - // for instance if the image was transferred between threads. - const TextureHolder* texture_holder() const { - if (skia_texture_holder_) - return skia_texture_holder_.get(); - return mailbox_texture_holder_.get(); - } + void InitializeSkImage(GLuint shared_image_texture_id); - scoped_refptr<TextureHolder::MailboxRef> mailbox_ref_; + const gpu::Mailbox mailbox_; + const SkImageInfo sk_image_info_; + const GLenum texture_target_; + const bool is_origin_top_left_; - // The image is created with one of the texture holders below while the other - // one is created lazily if needed and then persisted for the lifetime of the - // image on a particular thread. - // When Transfer is called, the image is detached from its current thread to - // allow it to be used on a different thread. We create(if needed) and cache - // the mailbox in this case, so the texture can be used with a different - // context. The skia texture holder is released since the mailbox needs to be - // imported into the GrContext on the new thread. - std::unique_ptr<SkiaTextureHolder> skia_texture_holder_; - std::unique_ptr<MailboxTextureHolder> mailbox_texture_holder_; + base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_; + scoped_refptr<MailboxRef> mailbox_ref_; - THREAD_CHECKER(thread_checker_); - PaintImage::ContentId paint_image_content_id_; - - // For RetainOriginalSkImageForCopyOnWrite() - sk_sp<SkImage> original_skia_image_; - scoped_refptr<base::SingleThreadTaskRunner> original_skia_image_task_runner_; + // The context this SkImage is bound to. base::WeakPtr<WebGraphicsContext3DProviderWrapper> - original_skia_image_context_provider_wrapper_; + skia_context_provider_wrapper_; + sk_sp<SkImage> sk_image_; + + PaintImage::ContentId paint_image_content_id_; + THREAD_CHECKER(thread_checker_); }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc index 8f2ff90fddf..5fbfd9887e2 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image_test.cc @@ -4,12 +4,17 @@ #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h" +#include "base/test/null_task_runner.h" #include "base/test/task_environment.h" +#include "components/viz/common/resources/single_release_callback.h" +#include "components/viz/test/test_gles2_interface.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h" #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" #include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h" #include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h" +#include "third_party/blink/renderer/platform/graphics/test/gpu_test_utils.h" #include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/skia/include/core/SkSurface.h" @@ -25,12 +30,16 @@ using testing::SetArgPointee; using testing::SetArrayArgument; using testing::Test; -class MockGLES2InterfaceWithSyncTokenSupport : public FakeGLES2Interface { +class MockGLES2InterfaceWithSyncTokenSupport : public viz::TestGLES2Interface { public: MOCK_METHOD1(GenUnverifiedSyncTokenCHROMIUM, void(GLbyte*)); MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte*)); }; +GLbyte SyncTokenMatcher(const gpu::SyncToken& token) { + return reinterpret_cast<const GLbyte*>(&token)[0]; +} + gpu::SyncToken GenTestSyncToken(GLbyte id) { gpu::SyncToken token; // Store id in the first byte @@ -38,89 +47,63 @@ gpu::SyncToken GenTestSyncToken(GLbyte id) { return token; } -GLbyte SyncTokenMatcher(const gpu::SyncToken& token) { - return reinterpret_cast<const GLbyte*>(&token)[0]; +scoped_refptr<StaticBitmapImage> CreateBitmap() { + auto mailbox = gpu::Mailbox::GenerateForSharedImage(); + auto release_callback = viz::SingleReleaseCallback::Create( + base::BindOnce([](const gpu::SyncToken&, bool) {})); + return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox( + mailbox, GenTestSyncToken(100), 0, SkImageInfo::MakeN32Premul(100, 100), + GL_TEXTURE_2D, true, SharedGpuContext::ContextProviderWrapper(), + base::PlatformThread::CurrentRef(), + base::MakeRefCounted<base::NullTaskRunner>(), + std::move(release_callback)); } class AcceleratedStaticBitmapImageTest : public Test { public: void SetUp() override { - auto factory = [](MockGLES2InterfaceWithSyncTokenSupport* gl, - bool* gpu_compositing_disabled) - -> std::unique_ptr<WebGraphicsContext3DProvider> { - *gpu_compositing_disabled = false; - return std::make_unique<FakeWebGraphicsContext3DProvider>(gl, nullptr); - }; - SharedGpuContext::SetContextProviderFactoryForTesting( - WTF::BindRepeating(factory, WTF::Unretained(&gl_))); + auto gl = std::make_unique<MockGLES2InterfaceWithSyncTokenSupport>(); + gl_ = gl.get(); + context_provider_ = viz::TestContextProvider::Create(std::move(gl)); + InitializeSharedGpuContext(context_provider_.get()); + } + void TearDown() override { + gl_ = nullptr; + SharedGpuContext::ResetForTesting(); } - void TearDown() override { SharedGpuContext::ResetForTesting(); } protected: base::test::TaskEnvironment task_environment_; - MockGLES2InterfaceWithSyncTokenSupport gl_; + MockGLES2InterfaceWithSyncTokenSupport* gl_; + scoped_refptr<viz::TestContextProvider> context_provider_; }; -TEST_F(AcceleratedStaticBitmapImageTest, NoTextureHolderThrashing) { - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper = - SharedGpuContext::ContextProviderWrapper(); - GrContext* gr = context_provider_wrapper->ContextProvider()->GetGrContext(); - SkImageInfo imageInfo = SkImageInfo::MakeN32Premul(100, 100); - - sk_sp<SkSurface> surface = - SkSurface::MakeRenderTarget(gr, SkBudgeted::kNo, imageInfo); - - SkPaint paint; - surface->getCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint); - - sk_sp<SkImage> image = surface->makeImageSnapshot(); - scoped_refptr<AcceleratedStaticBitmapImage> bitmap = - AcceleratedStaticBitmapImage::CreateFromSkImage(image, - context_provider_wrapper); +TEST_F(AcceleratedStaticBitmapImageTest, SkImageCached) { + auto bitmap = CreateBitmap(); sk_sp<SkImage> stored_image = bitmap->PaintImageForCurrentFrame().GetSkImage(); - EXPECT_EQ(stored_image.get(), image.get()); - - bitmap->EnsureMailbox(kUnverifiedSyncToken, GL_LINEAR); - - // Verify that calling PaintImageForCurrentFrame does not swap out of mailbox - // mode. It should use the cached original image instead. - stored_image = bitmap->PaintImageForCurrentFrame().GetSkImage(); - - EXPECT_EQ(stored_image.get(), image.get()); + auto stored_image2 = bitmap->PaintImageForCurrentFrame().GetSkImage(); + EXPECT_EQ(stored_image.get(), stored_image2.get()); } TEST_F(AcceleratedStaticBitmapImageTest, CopyToTextureSynchronization) { - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper = - SharedGpuContext::ContextProviderWrapper(); - GrContext* gr = context_provider_wrapper->ContextProvider()->GetGrContext(); - SkImageInfo imageInfo = SkImageInfo::MakeN32Premul(100, 100); - sk_sp<SkSurface> surface = - SkSurface::MakeRenderTarget(gr, SkBudgeted::kNo, imageInfo); - - sk_sp<SkImage> image = surface->makeImageSnapshot(); - scoped_refptr<AcceleratedStaticBitmapImage> bitmap = - AcceleratedStaticBitmapImage::CreateFromSkImage(image, - context_provider_wrapper); + auto bitmap = CreateBitmap(); MockGLES2InterfaceWithSyncTokenSupport destination_gl; - testing::Mock::VerifyAndClearExpectations(&gl_); + testing::Mock::VerifyAndClearExpectations(gl_); testing::Mock::VerifyAndClearExpectations(&destination_gl); InSequence s; // Indicate to gmock that order of EXPECT_CALLs is important - // Anterior synchronization - const gpu::SyncToken sync_token1 = GenTestSyncToken(1); - EXPECT_CALL(gl_, GenUnverifiedSyncTokenCHROMIUM(_)) - .WillOnce(SetArrayArgument<0>( - sync_token1.GetConstData(), - sync_token1.GetConstData() + sizeof(gpu::SyncToken))); - EXPECT_CALL(destination_gl, - WaitSyncTokenCHROMIUM(Pointee(SyncTokenMatcher(sync_token1)))); + // Anterior synchronization. Wait on the sync token for the mailbox on the + // dest context. + EXPECT_CALL(destination_gl, WaitSyncTokenCHROMIUM(Pointee(SyncTokenMatcher( + bitmap->GetMailboxHolder().sync_token)))); - // Posterior synchronization + // Posterior synchronization. Generate a sync token on the destination context + // to ensure mailbox is destroyed after the copy. const gpu::SyncToken sync_token2 = GenTestSyncToken(2); EXPECT_CALL(destination_gl, GenUnverifiedSyncTokenCHROMIUM(_)) .WillOnce(SetArrayArgument<0>( @@ -129,25 +112,16 @@ TEST_F(AcceleratedStaticBitmapImageTest, CopyToTextureSynchronization) { IntPoint dest_point(0, 0); IntRect source_sub_rectangle(0, 0, 10, 10); - bitmap->CopyToTexture( + ASSERT_TRUE(bitmap->CopyToTexture( &destination_gl, GL_TEXTURE_2D, 1 /*dest_texture_id*/, 0 /*dest_texture_level*/, false /*unpack_premultiply_alpha*/, - false /*unpack_flip_y*/, dest_point, source_sub_rectangle); + false /*unpack_flip_y*/, dest_point, source_sub_rectangle)); testing::Mock::VerifyAndClearExpectations(&gl_); testing::Mock::VerifyAndClearExpectations(&destination_gl); - // Note the following expectation is commented-out because the - // MailboxTextureHolder destructor skips it when the texture ID is 0. - // The ID is zero because skia detected that it is being used with a fake - // context, so this problem can't be solved by just mocking GenTextures to - // make it produce non-zero IDs. - // TODO(junov): fix this! - // Final wait is postponed until destruction. - // EXPECT_CALL(gl_, - // WaitSyncTokenCHROMIUM(Pointee(SyncTokenMatcher(sync_token2)))); bitmap = - // nullptr; + EXPECT_EQ(bitmap->GetMailboxHolder().sync_token, sync_token2); } } // namespace diff --git a/chromium/third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h b/chromium/third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h index a35b7834192..deaa02ab7c3 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h +++ b/chromium/third_party/blink/renderer/platform/graphics/animation_worklet_mutator.h @@ -19,7 +19,7 @@ class PLATFORM_EXPORT AnimationWorkletMutator : public GarbageCollectedMixin { // Runs the animation frame callback. virtual std::unique_ptr<AnimationWorkletOutput> Mutate( std::unique_ptr<AnimationWorkletInput>) = 0; - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc b/chromium/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc index 64144689835..e34a9cccf59 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/animation_worklet_mutator_dispatcher_impl.cc @@ -199,9 +199,9 @@ void AnimationWorkletMutatorDispatcherImpl::MutateAsynchronouslyInternal( DCHECK(host_queue_->BelongsToCurrentThread()); on_async_mutation_complete_ = std::move(done_callback); int next_async_mutation_id = GetNextAsyncMutationId(); - TRACE_EVENT_ASYNC_BEGIN0("cc", - "AnimationWorkletMutatorDispatcherImpl::MutateAsync", - next_async_mutation_id); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN0( + "cc", "AnimationWorkletMutatorDispatcherImpl::MutateAsync", + TRACE_ID_LOCAL(next_async_mutation_id)); CrossThreadOnceClosure on_done = CrossThreadBindOnce( [](scoped_refptr<base::SingleThreadTaskRunner> host_queue, @@ -239,9 +239,9 @@ void AnimationWorkletMutatorDispatcherImpl::AsyncMutationsDone( } // The trace event deos not include queuing time. It covers the interval // between dispatching the request and retrieving the results. - TRACE_EVENT_ASYNC_END0("cc", - "AnimationWorkletMutatorDispatcherImpl::MutateAsync", - async_mutation_id); + TRACE_EVENT_NESTABLE_ASYNC_END0( + "cc", "AnimationWorkletMutatorDispatcherImpl::MutateAsync", + TRACE_ID_LOCAL(async_mutation_id)); // The Async mutation duration is the total time between request and // completion, and thus includes queuing time. UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( diff --git a/chromium/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc b/chromium/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc index 9524ea33884..624f216a509 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc @@ -9,7 +9,7 @@ #include "base/bind.h" #include "base/single_thread_task_runner.h" #include "services/viz/public/mojom/compositing/frame_timing_details.mojom-blink.h" -#include "third_party/blink/public/platform/interface_provider.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "ui/gfx/mojom/presentation_feedback.mojom-blink.h" @@ -64,7 +64,7 @@ void BeginFrameProvider::CreateCompositorFrameSinkIfNeeded() { base::PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::DISPLAY); mojo::Remote<mojom::blink::EmbeddedFrameSinkProvider> provider; - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( provider.BindNewPipeAndPassReceiver()); scoped_refptr<base::SingleThreadTaskRunner> task_runner = diff --git a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc index effb974b2a2..e7faefdc617 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.cc @@ -115,8 +115,7 @@ PaintImage BitmapImage::PaintImageForTesting() { PaintImage BitmapImage::CreatePaintImage() { sk_sp<PaintImageGenerator> generator = - decoder_ ? decoder_->CreateGenerator(PaintImage::kDefaultFrameIndex) - : nullptr; + decoder_ ? decoder_->CreateGenerator() : nullptr; if (!generator) return PaintImage(); @@ -157,6 +156,11 @@ IntSize BitmapImage::SizeRespectingOrientation() const { return size_respecting_orientation_; } +bool BitmapImage::HasDefaultOrientation() const { + ImageOrientation orientation = CurrentFrameOrientation(); + return orientation == kDefaultImageOrientation; +} + bool BitmapImage::GetHotSpot(IntPoint& hot_spot) const { return decoder_ && decoder_->HotSpot(hot_spot); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h index b73b4caf454..b36599efbd7 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h +++ b/chromium/third_party/blink/renderer/platform/graphics/bitmap_image.h @@ -36,9 +36,9 @@ #include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h" #include "third_party/blink/renderer/platform/graphics/image.h" #include "third_party/blink/renderer/platform/graphics/image_animation_policy.h" -#include "third_party/blink/renderer/platform/graphics/image_orientation.h" #include "third_party/blink/renderer/platform/image-decoders/image_animation.h" #include "third_party/blink/renderer/platform/timer.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" #include "third_party/blink/renderer/platform/wtf/forward.h" #include "third_party/skia/include/core/SkRefCnt.h" @@ -65,7 +65,8 @@ class PLATFORM_EXPORT BitmapImage final : public Image { bool CurrentFrameHasSingleSecurityOrigin() const override; IntSize Size() const override; - IntSize SizeRespectingOrientation() const; + IntSize SizeRespectingOrientation() const override; + bool HasDefaultOrientation() const override; bool GetHotSpot(IntPoint&) const override; String FilenameExtension() const override; @@ -91,7 +92,7 @@ class PLATFORM_EXPORT BitmapImage final : public Image { bool CurrentFrameIsLazyDecoded() override; size_t FrameCount() override; PaintImage PaintImageForCurrentFrame() override; - ImageOrientation CurrentFrameOrientation() const; + ImageOrientation CurrentFrameOrientation() const override; PaintImage PaintImageForTesting(); void AdvanceAnimationForTesting() override { @@ -184,7 +185,10 @@ class PLATFORM_EXPORT BitmapImage final : public Image { PaintImage::AnimationSequenceId reset_animation_sequence_id_ = 0; }; -DEFINE_IMAGE_TYPE_CASTS(BitmapImage); +template <> +struct DowncastTraits<BitmapImage> { + static bool AllowFrom(const Image& image) { return image.IsBitmapImage(); } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc index 7960ab34ac8..dae80f756bc 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc @@ -38,7 +38,6 @@ #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/platform/platform.h" -#include "third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h" #include "third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h" @@ -75,17 +74,6 @@ Canvas2DLayerBridge::Canvas2DLayerBridge(const IntSize& size, // Used by browser tests to detect the use of a Canvas2DLayerBridge. TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation", TRACE_EVENT_SCOPE_GLOBAL); - StartRecording(); - - // Clear the background transparent or opaque. Similar code at - // CanvasResourceProvider::Clear(). - if (IsValid()) { - DCHECK(recorder_); - recorder_->getRecordingCanvas()->clear( - color_params_.GetOpacityMode() == kOpaque ? SK_ColorBLACK - : SK_ColorTRANSPARENT); - DidDraw(FloatRect(0.f, 0.f, size_.Width(), size_.Height())); - } } Canvas2DLayerBridge::~Canvas2DLayerBridge() { @@ -98,7 +86,6 @@ Canvas2DLayerBridge::~Canvas2DLayerBridge() { return; if (acceleration_mode_ != kDisableAcceleration) { - GraphicsLayer::UnregisterContentsLayer(layer_.get()); layer_->ClearTexture(); // Orphaning the layer is required to trigger the recreation of a new layer // in the case where destruction is caused by a canvas resize. Test: @@ -109,16 +96,12 @@ Canvas2DLayerBridge::~Canvas2DLayerBridge() { layer_ = nullptr; } -void Canvas2DLayerBridge::StartRecording() { - recorder_ = std::make_unique<PaintRecorder>(); - cc::PaintCanvas* canvas = - recorder_->beginRecording(size_.Width(), size_.Height()); - // Always save an initial frame, to support resetting the top level matrix - // and clip. - canvas->save(); +void Canvas2DLayerBridge::SetCanvasResourceHost(CanvasResourceHost* host) { + resource_host_ = host; - if (resource_host_) - resource_host_->RestoreCanvasMatrixClipStack(canvas); + if (resource_host_ && GetOrCreateResourceProvider()) { + EnsureCleared(); + } } void Canvas2DLayerBridge::ResetResourceProvider() { @@ -297,9 +280,11 @@ CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider( // circular callstack from HTMLCanvasElement. resource_provider = resource_host_->GetOrCreateCanvasResourceProviderImpl(adjusted_hint); - if (!resource_provider) + if (!resource_provider || !resource_provider->IsValid()) return nullptr; + EnsureCleared(); + if (IsAccelerated() && !layer_) { layer_ = cc::TextureLayer::CreateForMailbox(this); layer_->SetIsDrawable(true); @@ -308,7 +293,6 @@ CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider( layer_->SetBlendBackgroundColor(ColorParams().GetOpacityMode() != kOpaque); layer_->SetNearestNeighbor(resource_host_->FilterQuality() == kNone_SkFilterQuality); - GraphicsLayer::RegisterContentsLayer(layer_.get()); } if (!IsHibernating()) @@ -325,13 +309,10 @@ CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider( } } - PaintFlags copy_paint; - copy_paint.setBlendMode(SkBlendMode::kSrc); PaintImageBuilder builder = PaintImageBuilder::WithDefault(); builder.set_image(hibernation_image_, PaintImage::GetNextContentId()); builder.set_id(PaintImage::GetNextId()); - resource_provider->Canvas()->drawImage(builder.TakePaintImage(), 0, 0, - ©_paint); + resource_provider->RestoreBackBuffer(builder.TakePaintImage()); hibernation_image_.reset(); if (resource_host_) { @@ -341,9 +322,14 @@ CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider( return resource_provider; } -cc::PaintCanvas* Canvas2DLayerBridge::DrawingCanvas() { +cc::PaintCanvas* Canvas2DLayerBridge::GetPaintCanvas() { DCHECK(resource_host_); - return recorder_->getRecordingCanvas(); + // We avoid only using GetOrCreateResourceProvider() here to skip the + // IsValid/ContextLost checks since this is in hot code paths. The context + // does not need to be valid here since only the recording canvas is used. + if (!ResourceProvider() && !GetOrCreateResourceProvider()) + return nullptr; + return ResourceProvider()->Canvas(); } void Canvas2DLayerBridge::UpdateFilterQuality() { @@ -418,7 +404,7 @@ void Canvas2DLayerBridge::SetIsBeingDisplayed(bool displayed) { } void Canvas2DLayerBridge::DrawFullImage(const cc::PaintImage& image) { - DrawingCanvas()->drawImage(image, 0, 0); + GetPaintCanvas()->drawImage(image, 0, 0); } bool Canvas2DLayerBridge::WritePixels(const SkImageInfo& orig_info, @@ -440,30 +426,27 @@ bool Canvas2DLayerBridge::WritePixels(const SkImageInfo& orig_info, last_record_tainted_by_write_pixels_ = true; have_recorded_draw_commands_ = false; - // Add a save to initialize the transform/clip stack and then restore it after - // the draw. This is needed because each recording initializes and the resets - // this state after every flush. - cc::PaintCanvas* canvas = ResourceProvider()->Canvas(); - PaintCanvasAutoRestore auto_restore(canvas, true); - if (GetOrCreateResourceProvider()) { - resource_host_->RestoreCanvasMatrixClipStack(canvas); - } ResourceProvider()->WritePixels(orig_info, pixels, row_bytes, x, y); return true; } void Canvas2DLayerBridge::SkipQueuedDrawCommands() { - if (have_recorded_draw_commands_) { - recorder_->finishRecordingAsPicture(); - StartRecording(); - have_recorded_draw_commands_ = false; - } + ResourceProvider()->SkipQueuedDrawCommands(); + have_recorded_draw_commands_ = false; if (rate_limiter_) rate_limiter_->Reset(); } +void Canvas2DLayerBridge::EnsureCleared() { + if (cleared_) + return; + cleared_ = true; + ResourceProvider()->Clear(); + DidDraw(FloatRect(0.f, 0.f, size_.Width(), size_.Height())); +} + void Canvas2DLayerBridge::ClearPendingRasterTimers() { gpu::raster::RasterInterface* raster_interface = nullptr; if (IsAccelerated() && SharedGpuContext::ContextProviderWrapper() && @@ -546,8 +529,14 @@ void Canvas2DLayerBridge::FlushRecording() { // Sample one out of every kRasterMetricProbability frames to time // If the canvas is accelerated, we also need access to the raster_interface - bool measure_raster_metric = (raster_interface || !IsAccelerated()) && - bernoulli_distribution_(random_generator_); + + // We are using @dont_use_idle_scheduling_for_testing_ temporarily to always + // measure while testing. + const bool will_measure = dont_use_idle_scheduling_for_testing_ || + bernoulli_distribution_(random_generator_); + const bool measure_raster_metric = + (raster_interface || !IsAccelerated()) && will_measure; + RasterTimer rasterTimer; base::Optional<base::ElapsedTimer> timer; // Start Recording the raster duration @@ -561,16 +550,11 @@ void Canvas2DLayerBridge::FlushRecording() { timer.emplace(); } - { // Make a new scope so that PaintRecord gets deleted and that gets timed - cc::PaintCanvas* canvas = ResourceProvider()->Canvas(); - last_recording_ = recorder_->finishRecordingAsPicture(); - canvas->drawPicture(last_recording_); - last_record_tainted_by_write_pixels_ = false; - if (!clear_frame_ || !resource_host_ || !resource_host_->IsPrinting()) { - last_recording_ = nullptr; - clear_frame_ = false; - } - ResourceProvider()->FlushSkia(); + last_recording_ = ResourceProvider()->FlushCanvas(); + last_record_tainted_by_write_pixels_ = false; + if (!clear_frame_ || !resource_host_ || !resource_host_->IsPrinting()) { + last_recording_ = nullptr; + clear_frame_ = false; } // Finish up the timing operation @@ -594,7 +578,6 @@ void Canvas2DLayerBridge::FlushRecording() { if (GetOrCreateResourceProvider()) ResourceProvider()->ReleaseLockedImages(); - StartRecording(); have_recorded_draw_commands_ = false; } @@ -716,6 +699,8 @@ cc::Layer* Canvas2DLayerBridge::Layer() { } void Canvas2DLayerBridge::DidDraw(const FloatRect& /* rect */) { + if (ResourceProvider() && ResourceProvider()->needs_flush()) + FinalizeFrame(); have_recorded_draw_commands_ = true; } diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h index 16a2bcf63d9..8daad03dd61 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h @@ -44,7 +44,6 @@ #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource_host.h" #include "third_party/blink/renderer/platform/graphics/graphics_types.h" -#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/deque.h" @@ -113,7 +112,8 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { virtual void DidRestoreCanvasMatrixClipStack(cc::PaintCanvas*) {} virtual bool IsAccelerated() const; - cc::PaintCanvas* DrawingCanvas(); + // This may recreate CanvasResourceProvider + cc::PaintCanvas* GetPaintCanvas(); bool IsValid(); bool WritePixels(const SkImageInfo&, const void* pixels, @@ -123,9 +123,7 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { void DontUseIdleSchedulingForTesting() { dont_use_idle_scheduling_for_testing_ = true; } - void SetCanvasResourceHost(CanvasResourceHost* host) { - resource_host_ = host; - } + void SetCanvasResourceHost(CanvasResourceHost* host); void Hibernate(); bool IsHibernating() const { return hibernation_image_ != nullptr; } @@ -137,14 +135,6 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { cc::TextureLayer* layer_for_testing() { return layer_.get(); } - // TODO(jochin): Remove this function completely once recorder_ has been - // moved into CanvasResourceProvider. - sk_sp<cc::PaintRecord> record_for_testing() { - sk_sp<cc::PaintRecord> record = recorder_->finishRecordingAsPicture(); - StartRecording(); - return record; - } - // The values of the enum entries must not change because they are used for // usage metrics histograms. New values can be added to the end. enum HibernationEvent { @@ -198,12 +188,11 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { bool CheckResourceProviderValid(); void ResetResourceProvider(); - void StartRecording(); void SkipQueuedDrawCommands(); + void EnsureCleared(); bool ShouldAccelerate(AccelerationHint) const; - std::unique_ptr<PaintRecorder> recorder_; sk_sp<SkImage> hibernation_image_; scoped_refptr<cc::TextureLayer> layer_; std::unique_ptr<SharedContextRateLimiter> rate_limiter_; @@ -255,6 +244,10 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { sk_sp<cc::PaintRecord> last_recording_; + // This tracks whether the canvas has been cleared once after + // this bridge was created. + bool cleared_ = false; + base::WeakPtrFactory<Canvas2DLayerBridge> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(Canvas2DLayerBridge); diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc index 438e238d047..fa1c188fb95 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc @@ -186,7 +186,7 @@ TEST_F(Canvas2DLayerBridgeTest, NoDrawOnContextLost) { EXPECT_TRUE(bridge->IsValid()); PaintFlags flags; uint32_t gen_id = bridge->GetOrCreateResourceProvider()->ContentUniqueID(); - bridge->DrawingCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); + bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); EXPECT_EQ(gen_id, bridge->GetOrCreateResourceProvider()->ContentUniqueID()); test_context_provider_->TestContextGL()->set_context_lost(true); EXPECT_EQ(nullptr, bridge->GetOrCreateResourceProvider()); @@ -306,7 +306,7 @@ TEST_F(Canvas2DLayerBridgeTest, AccelerationHint) { MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams()); PaintFlags flags; - bridge->DrawingCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); + bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot(kPreferAcceleration); EXPECT_TRUE(bridge->IsValid()); @@ -318,7 +318,7 @@ TEST_F(Canvas2DLayerBridgeTest, AccelerationHint) { MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams()); PaintFlags flags; - bridge->DrawingCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); + bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot(kPreferNoAcceleration); EXPECT_TRUE(bridge->IsValid()); @@ -330,7 +330,7 @@ TEST_F(Canvas2DLayerBridgeTest, AccelerationHint) { MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kDisableAcceleration, CanvasColorParams()); PaintFlags flags; - bridge->DrawingCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); + bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot(kPreferAcceleration); EXPECT_TRUE(bridge->IsValid()); @@ -342,7 +342,7 @@ TEST_F(Canvas2DLayerBridgeTest, AccelerationHint) { MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kDisableAcceleration, CanvasColorParams()); PaintFlags flags; - bridge->DrawingCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); + bridge->GetPaintCanvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), flags); scoped_refptr<StaticBitmapImage> image = bridge->NewImageSnapshot(kPreferNoAcceleration); EXPECT_TRUE(bridge->IsValid()); @@ -359,6 +359,13 @@ TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareIfContextLost) { EXPECT_FALSE(bridge->IsAccelerated()); } +void DrawSomething(Canvas2DLayerBridge* bridge) { + bridge->DidDraw(FloatRect(0, 0, 1, 1)); + bridge->FinalizeFrame(); + // Grabbing an image forces a flush + bridge->NewImageSnapshot(kPreferAcceleration); +} + TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareOnFailedTextureAlloc) { { // No fallback case. @@ -379,14 +386,19 @@ TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareOnFailedTextureAlloc) { ->ContextProvider() ->GetGrContext(); std::unique_ptr<Canvas2DLayerBridge> bridge = - MakeBridge(IntSize(300, 150), Canvas2DLayerBridge::kEnableAcceleration, - CanvasColorParams()); + std::make_unique<Canvas2DLayerBridge>( + IntSize(300, 150), Canvas2DLayerBridge::kEnableAcceleration, + CanvasColorParams()); + bridge->DontUseIdleSchedulingForTesting(); EXPECT_TRUE(bridge->IsValid()); EXPECT_TRUE(bridge->IsAccelerated()); // We don't yet know that // allocation will fail. // This will cause SkSurface_Gpu creation to fail without // Canvas2DLayerBridge otherwise detecting that anything was disabled. gr->abandonContext(); + host_ = std::make_unique<FakeCanvasResourceHost>(IntSize(300, 150)); + bridge->SetCanvasResourceHost(host_.get()); + DrawSomething(bridge.get()); scoped_refptr<StaticBitmapImage> snapshot = bridge->NewImageSnapshot(kPreferAcceleration); EXPECT_FALSE(bridge->IsAccelerated()); @@ -402,13 +414,6 @@ class MockLogger : public Canvas2DLayerBridge::Logger { ~MockLogger() override = default; }; -void DrawSomething(Canvas2DLayerBridge* bridge) { - bridge->DidDraw(FloatRect(0, 0, 1, 1)); - bridge->FinalizeFrame(); - // Grabbing an image forces a flush - bridge->NewImageSnapshot(kPreferAcceleration); -} - #if CANVAS2D_HIBERNATION_ENABLED TEST_F(Canvas2DLayerBridgeTest, HibernationLifeCycle) #else @@ -844,13 +849,17 @@ TEST_F(Canvas2DLayerBridgeTest, ResourceRecycling) { viz::TransferableResource resources[3]; std::unique_ptr<viz::SingleReleaseCallback> callbacks[3]; + PaintFlags flags; std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge( IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting, CanvasColorParams()); + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[0], &callbacks[0])); + + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[1], &callbacks[1])); @@ -860,6 +869,7 @@ TEST_F(Canvas2DLayerBridgeTest, ResourceRecycling) { // Now release the first resource and draw again. It should be reused due to // recycling. callbacks[0]->Run(gpu::SyncToken(), false); + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[2], &callbacks[2])); @@ -879,13 +889,16 @@ TEST_F(Canvas2DLayerBridgeTest, NoResourceRecyclingWhenPageHidden) { viz::TransferableResource resources[2]; std::unique_ptr<viz::SingleReleaseCallback> callbacks[2]; + PaintFlags flags; std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge( IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting, CanvasColorParams()); + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[0], &callbacks[0])); + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[1], &callbacks[1])); @@ -932,9 +945,8 @@ TEST_F(Canvas2DLayerBridgeTest, ReleaseResourcesAfterBridgeDestroyed) { } TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUse) { - auto color_params = - CanvasColorParams(CanvasColorSpace::kSRGB, CanvasPixelFormat::kF16, - kOpaque, CanvasForceRGBA::kNotForced); + auto color_params = CanvasColorParams(CanvasColorSpace::kSRGB, + CanvasPixelFormat::kF16, kOpaque); std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration, @@ -948,8 +960,8 @@ TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUse) { SkIRect::MakeWH(5, 5), kNone_SkFilterQuality, SkMatrix::I(), 0u, expected_color_space)}; - bridge->DrawingCanvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr); - bridge->DrawingCanvas()->drawImageRect( + bridge->GetPaintCanvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr); + bridge->GetPaintCanvas()->drawImageRect( images[1].paint_image(), SkRect::MakeWH(5u, 5u), SkRect::MakeWH(5u, 5u), nullptr, cc::PaintCanvas::kFast_SrcRectConstraint); bridge->NewImageSnapshot(kPreferAcceleration); @@ -958,9 +970,9 @@ TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUse) { } TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUseWithColorConversion) { - auto color_params = - CanvasColorParams(CanvasColorSpace::kSRGB, CanvasPixelFormat::kRGBA8, - kOpaque, CanvasForceRGBA::kNotForced); + auto color_params = CanvasColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kOpaque); std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration, color_params); @@ -972,8 +984,8 @@ TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUseWithColorConversion) { SkIRect::MakeWH(5, 5), kNone_SkFilterQuality, SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace())}; - bridge->DrawingCanvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr); - bridge->DrawingCanvas()->drawImageRect( + bridge->GetPaintCanvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr); + bridge->GetPaintCanvas()->drawImageRect( images[1].paint_image(), SkRect::MakeWH(5u, 5u), SkRect::MakeWH(5u, 5u), nullptr, cc::PaintCanvas::kFast_SrcRectConstraint); bridge->NewImageSnapshot(kPreferAcceleration); @@ -982,9 +994,8 @@ TEST_F(Canvas2DLayerBridgeTest, EnsureCCImageCacheUseWithColorConversion) { } TEST_F(Canvas2DLayerBridgeTest, ImagesLockedUntilCacheLimit) { - auto color_params = - CanvasColorParams(CanvasColorSpace::kSRGB, CanvasPixelFormat::kF16, - kOpaque, CanvasForceRGBA::kNotForced); + auto color_params = CanvasColorParams(CanvasColorSpace::kSRGB, + CanvasPixelFormat::kF16, kOpaque); std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration, color_params); @@ -1001,19 +1012,16 @@ TEST_F(Canvas2DLayerBridgeTest, ImagesLockedUntilCacheLimit) { 0u, color_params.GetStorageGfxColorSpace())}; // First 2 images are budgeted, they should remain locked after the op. - bridge->DrawingCanvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr); - bridge->DrawingCanvas()->drawImage(images[1].paint_image(), 0u, 0u, nullptr); - // TODO(jochin): Can just call provider::FlushSkia() once we move recorder_ - // to the resource provider. The following is a temp workaround. - cc::PaintCanvas* canvas = bridge->GetOrCreateResourceProvider()->Canvas(); - canvas->drawPicture(bridge->record_for_testing()); + bridge->GetPaintCanvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr); + bridge->GetPaintCanvas()->drawImage(images[1].paint_image(), 0u, 0u, nullptr); + bridge->GetOrCreateResourceProvider()->FlushCanvas(); EXPECT_EQ(image_decode_cache_.num_locked_images(), 2); // Next image is not budgeted, we should unlock all images other than the last // image. image_decode_cache_.set_budget_exceeded(true); - bridge->DrawingCanvas()->drawImage(images[2].paint_image(), 0u, 0u, nullptr); - canvas->drawPicture(bridge->record_for_testing()); + bridge->GetPaintCanvas()->drawImage(images[2].paint_image(), 0u, 0u, nullptr); + bridge->GetOrCreateResourceProvider()->FlushCanvas(); EXPECT_EQ(image_decode_cache_.num_locked_images(), 1); // Ask the provider to release everything, no locked images should remain. @@ -1022,9 +1030,8 @@ TEST_F(Canvas2DLayerBridgeTest, ImagesLockedUntilCacheLimit) { } TEST_F(Canvas2DLayerBridgeTest, QueuesCleanupTaskForLockedImages) { - auto color_params = - CanvasColorParams(CanvasColorSpace::kSRGB, CanvasPixelFormat::kF16, - kOpaque, CanvasForceRGBA::kNotForced); + auto color_params = CanvasColorParams(CanvasColorSpace::kSRGB, + CanvasPixelFormat::kF16, kOpaque); std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration, color_params); @@ -1033,12 +1040,9 @@ TEST_F(Canvas2DLayerBridgeTest, QueuesCleanupTaskForLockedImages) { cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(10, 10)), SkIRect::MakeWH(10, 10), kNone_SkFilterQuality, SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace()); - bridge->DrawingCanvas()->drawImage(image.paint_image(), 0u, 0u, nullptr); + bridge->GetPaintCanvas()->drawImage(image.paint_image(), 0u, 0u, nullptr); - // TODO(jochin): Can just call provider::FlushSkia() once we move recorder_ - // to the resource provider. The following is a temp workaround. - cc::PaintCanvas* canvas = bridge->GetOrCreateResourceProvider()->Canvas(); - canvas->drawPicture(bridge->record_for_testing()); + bridge->GetOrCreateResourceProvider()->FlushCanvas(); EXPECT_EQ(image_decode_cache_.num_locked_images(), 1); base::RunLoop().RunUntilIdle(); @@ -1046,9 +1050,8 @@ TEST_F(Canvas2DLayerBridgeTest, QueuesCleanupTaskForLockedImages) { } TEST_F(Canvas2DLayerBridgeTest, ImageCacheOnContextLost) { - auto color_params = - CanvasColorParams(CanvasColorSpace::kSRGB, CanvasPixelFormat::kF16, - kOpaque, CanvasForceRGBA::kNotForced); + auto color_params = CanvasColorParams(CanvasColorSpace::kSRGB, + CanvasPixelFormat::kF16, kOpaque); std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(IntSize(300, 300), Canvas2DLayerBridge::kEnableAcceleration, color_params); @@ -1060,14 +1063,14 @@ TEST_F(Canvas2DLayerBridgeTest, ImageCacheOnContextLost) { cc::DrawImage(cc::CreateDiscardablePaintImage(gfx::Size(20, 20)), SkIRect::MakeWH(5, 5), kNone_SkFilterQuality, SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace())}; - bridge->DrawingCanvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr); + bridge->GetPaintCanvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr); // Lose the context and ensure that the image provider is not used. bridge->GetOrCreateResourceProvider()->OnContextDestroyed(); // We should unref all images on the cache when the context is destroyed. EXPECT_EQ(image_decode_cache_.num_locked_images(), 0); image_decode_cache_.set_disallow_cache_use(true); - bridge->DrawingCanvas()->drawImage(images[1].paint_image(), 0u, 0u, &flags); + bridge->GetPaintCanvas()->drawImage(images[1].paint_image(), 0u, 0u, &flags); } TEST_F(Canvas2DLayerBridgeTest, @@ -1076,7 +1079,7 @@ TEST_F(Canvas2DLayerBridgeTest, std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge( size, Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams()); - bridge->DrawingCanvas()->clear(SK_ColorRED); + bridge->GetPaintCanvas()->clear(SK_ColorRED); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->layer_for_testing()); @@ -1104,8 +1107,7 @@ class CustomFakeCanvasResourceHost : public FakeCanvasResourceHost { TEST_F(Canvas2DLayerBridgeTest, WritePixelsRestoresClipStack) { CanvasColorParams color_params(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kF16, kOpaque, - CanvasForceRGBA::kNotForced); + CanvasPixelFormat::kF16, kOpaque); IntSize size = IntSize(300, 300); auto host = std::make_unique<CustomFakeCanvasResourceHost>(size); std::unique_ptr<Canvas2DLayerBridge> bridge = @@ -1113,23 +1115,26 @@ TEST_F(Canvas2DLayerBridgeTest, WritePixelsRestoresClipStack) { std::move(host)); PaintFlags flags; - EXPECT_EQ(bridge->DrawingCanvas()->getTotalMatrix().get(SkMatrix::kMTransX), - 0); + // MakeBridge() results in a call to restore the matrix. So we already have 1. + EXPECT_EQ(bridge->GetPaintCanvas()->getTotalMatrix().get(SkMatrix::kMTransX), + 5); + // Drawline so WritePixels has something to flush + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); + bridge->DidDraw(FloatRect(0, 0, 1, 1)); - cc::PaintCanvas* canvas = bridge->GetOrCreateResourceProvider()->Canvas(); + // WritePixels flushes recording. Post flush, a new drawing canvas is created + // that should have the matrix restored onto it. bridge->WritePixels(SkImageInfo::MakeN32Premul(10, 10), nullptr, 10, 0, 0); - // Recording canvas maintain clip stack, while underlying SkCanvas should not. - EXPECT_EQ(canvas->getTotalMatrix().get(SkMatrix::kMTransX), 0); - EXPECT_EQ(bridge->DrawingCanvas()->getTotalMatrix().get(SkMatrix::kMTransX), + EXPECT_EQ(bridge->GetPaintCanvas()->getTotalMatrix().get(SkMatrix::kMTransX), 5); - bridge->DrawingCanvas()->drawLine(0, 0, 2, 2, flags); - // Flush recording. Recording canvas should maintain matrix, while SkCanvas - // should not. + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); + // Standard flush recording. Post flush, a new drawing canvas is created that + // should have the matrix restored onto it. DrawSomething(bridge.get()); - EXPECT_EQ(bridge->DrawingCanvas()->getTotalMatrix().get(SkMatrix::kMTransX), + + EXPECT_EQ(bridge->GetPaintCanvas()->getTotalMatrix().get(SkMatrix::kMTransX), 5); - EXPECT_EQ(canvas->getTotalMatrix().get(SkMatrix::kMTransX), 0); } TEST_F(Canvas2DLayerBridgeTest, DisplayedCanvasIsRateLimited) { diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.cc index a947db5fb56..d8868023915 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.cc @@ -40,12 +40,10 @@ CanvasColorParams::CanvasColorParams() = default; CanvasColorParams::CanvasColorParams(CanvasColorSpace color_space, CanvasPixelFormat pixel_format, - OpacityMode opacity_mode, - CanvasForceRGBA force_rgba) + OpacityMode opacity_mode) : color_space_(color_space), pixel_format_(pixel_format), - opacity_mode_(opacity_mode), - force_rgba_(force_rgba) {} + opacity_mode_(opacity_mode) {} CanvasColorParams::CanvasColorParams(const SkImageInfo& info) : CanvasColorParams(info.refColorSpace(), info.colorType()) {} @@ -63,10 +61,16 @@ bool CanvasColorParams::NeedsColorConversion( } SkColorType CanvasColorParams::GetSkColorType() const { - if (pixel_format_ == CanvasPixelFormat::kF16) - return kRGBA_F16_SkColorType; - return force_rgba_ == CanvasForceRGBA::kForced ? kRGBA_8888_SkColorType - : kN32_SkColorType; + switch (pixel_format_) { + case CanvasPixelFormat::kF16: + return kRGBA_F16_SkColorType; + case CanvasPixelFormat::kRGBA8: + return kRGBA_8888_SkColorType; + case CanvasPixelFormat::kBGRA8: + return kBGRA_8888_SkColorType; + } + NOTREACHED(); + return kN32_SkColorType; } SkAlphaType CanvasColorParams::GetSkAlphaType() const { @@ -111,6 +115,9 @@ gfx::ColorSpace CanvasColorParams::GetStorageGfxColorSpace() const { } sk_sp<SkColorSpace> CanvasColorParams::GetSkColorSpace() const { + static_assert(kN32_SkColorType == kRGBA_8888_SkColorType || + kN32_SkColorType == kBGRA_8888_SkColorType, + "Unexpected kN32_SkColorType value."); skcms_Matrix3x3 gamut = SkNamedGamut::kSRGB; skcms_TransferFunction transferFn = SkNamedTransferFn::kSRGB; switch (color_space_) { @@ -132,10 +139,6 @@ sk_sp<SkColorSpace> CanvasColorParams::GetSkColorSpace() const { } gfx::BufferFormat CanvasColorParams::GetBufferFormat() const { - static_assert(kN32_SkColorType == kRGBA_8888_SkColorType || - kN32_SkColorType == kBGRA_8888_SkColorType, - "Unexpected kN32_SkColorType value."); - switch (GetSkColorType()) { case kRGBA_8888_SkColorType: return gfx::BufferFormat::RGBA_8888; @@ -152,10 +155,6 @@ gfx::BufferFormat CanvasColorParams::GetBufferFormat() const { GLenum CanvasColorParams::GLUnsizedInternalFormat() const { // TODO(junov): try GL_RGB when opacity_mode_ == kOpaque - static_assert(kN32_SkColorType == kRGBA_8888_SkColorType || - kN32_SkColorType == kBGRA_8888_SkColorType, - "Unexpected kN32_SkColorType value."); - switch (GetSkColorType()) { case kRGBA_8888_SkColorType: return GL_RGBA; @@ -171,10 +170,6 @@ GLenum CanvasColorParams::GLUnsizedInternalFormat() const { } GLenum CanvasColorParams::GLSizedInternalFormat() const { - static_assert(kN32_SkColorType == kRGBA_8888_SkColorType || - kN32_SkColorType == kBGRA_8888_SkColorType, - "Unexpected kN32_SkColorType value."); - switch (GetSkColorType()) { case kRGBA_8888_SkColorType: return GL_RGBA8; @@ -210,10 +205,9 @@ viz::ResourceFormat CanvasColorParams::TransferableResourceFormat() const { CanvasColorParams::CanvasColorParams(const sk_sp<SkColorSpace> color_space, SkColorType color_type) { color_space_ = CanvasColorSpace::kSRGB; - pixel_format_ = CanvasPixelFormat::kRGBA8; + pixel_format_ = GetNativeCanvasPixelFormat(); // When there is no color space information, the SkImage is in legacy mode and - // the color type is kN32_SkColorType (which translates to kRGBA8 canvas pixel - // format). + // the color type is kRGBA8 canvas pixel format. if (!color_space) return; @@ -239,7 +233,7 @@ CanvasColorParams::CanvasColorParams(const sk_sp<SkColorSpace> color_space, if (color_type == kRGBA_F16_SkColorType) pixel_format_ = CanvasPixelFormat::kF16; else if (color_type == kRGBA_8888_SkColorType) - force_rgba_ = CanvasForceRGBA::kForced; + pixel_format_ = CanvasPixelFormat::kRGBA8; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.h index 5c1dfde62a9..3920ea5c865 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.h +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params.h @@ -34,28 +34,29 @@ enum class CanvasColorSpace { enum class CanvasPixelFormat { kRGBA8, + kBGRA8, kF16, }; -// todo(crbug/1021986) remove force_rgba in canvasColorParams -enum class CanvasForceRGBA { kForced, kNotForced }; - class PLATFORM_EXPORT CanvasColorParams { DISALLOW_NEW(); public: // The default constructor will create an output-blended 8-bit surface. CanvasColorParams(); - CanvasColorParams(CanvasColorSpace, - CanvasPixelFormat, - OpacityMode, - CanvasForceRGBA); + CanvasColorParams(CanvasColorSpace, CanvasPixelFormat, OpacityMode); explicit CanvasColorParams(const SkImageInfo&); + static CanvasPixelFormat GetNativeCanvasPixelFormat() { + if (kN32_SkColorType == kRGBA_8888_SkColorType) + return CanvasPixelFormat::kRGBA8; + else if (kN32_SkColorType == kBGRA_8888_SkColorType) + return CanvasPixelFormat::kBGRA8; + } + CanvasColorSpace ColorSpace() const { return color_space_; } CanvasPixelFormat PixelFormat() const { return pixel_format_; } OpacityMode GetOpacityMode() const { return opacity_mode_; } - CanvasForceRGBA GetForceRGBA() const { return force_rgba_; } void SetCanvasColorSpace(CanvasColorSpace c) { color_space_ = c; } void SetCanvasPixelFormat(CanvasPixelFormat f) { pixel_format_ = f; } @@ -99,9 +100,8 @@ class PLATFORM_EXPORT CanvasColorParams { SkColorType color_type); CanvasColorSpace color_space_ = CanvasColorSpace::kSRGB; - CanvasPixelFormat pixel_format_ = CanvasPixelFormat::kRGBA8; + CanvasPixelFormat pixel_format_ = GetNativeCanvasPixelFormat(); OpacityMode opacity_mode_ = kNonOpaque; - CanvasForceRGBA force_rgba_ = CanvasForceRGBA::kNotForced; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params_test.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params_test.cc index 352fa6c89a0..b34686a8065 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_color_params_test.cc @@ -25,8 +25,7 @@ TEST(CanvasColorParamsTest, MatchSkColorSpaceWithGfxColorSpace) { }; for (int iter_color_space = 0; iter_color_space < 4; iter_color_space++) { CanvasColorParams color_params(canvas_color_spaces[iter_color_space], - CanvasPixelFormat::kF16, kNonOpaque, - CanvasForceRGBA::kNotForced); + CanvasPixelFormat::kF16, kNonOpaque); sk_sp<SkColorSpace> canvas_drawing_color_space = color_params.GetSkColorSpace(); sk_sp<SkColorSpace> canvas_media_color_space = diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h deleted file mode 100644 index 417d0bf00d4..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h +++ /dev/null @@ -1,67 +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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_HEURISTIC_PARAMETERS_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_HEURISTIC_PARAMETERS_H_ - -#include "build/build_config.h" - -namespace blink { - -namespace canvas_heuristic_parameters { - -enum { - // Disable Deferral overdraw parameters - //====================================== - - // Heuristic: Canvases that are overdrawn beyond this factor in a - // single frame will be disabled deferral. - kExpensiveOverdrawThreshold = 10, - - // GPU readback prevention heuristics - //==================================== - - // When a canvas is used as a source image, if its destination is - // non-accelerated and the source canvas is accelerated, a readback - // from the gpu is necessary. This option causes the source canvas to - // switch to non-accelerated when this situation is encountered to - // prevent future canvas-to-canvas draws from requiring a readback. - kDisableAccelerationToAvoidReadbacks = 0, - - // See description of DisableAccelerationToAvoidReadbacks. This is the - // opposite strategy : accelerate the destination canvas. If both - // EnableAccelerationToAvoidReadbacks and - // DisableAccelerationToAvoidReadbacks are specified, we try to enable - // acceleration on the destination first. If that does not succeed, - // we disable acceleration on the source canvas. Either way, future - // readbacks are prevented. - kEnableAccelerationToAvoidReadbacks = 1, - - // Accelerated rendering heuristics - // ================================= - - // Enables frequent flushing of the GrContext for accelerated canvas. Since - // skia internally batches the GrOp list when flushing the recording onto the - // SkCanvasand may only flush it the command buffer at the end of the frame, - // it can lead to inefficient parallelization with the GPU. This enables - // triggering context flushes at regular intervals, after a fixed number of - // draws. - kEnableGrContextFlushes = 1, - - // The maximum number of draw ops executed on the canvas, after which the - // underlying GrContext is flushed. - kMaxDrawsBeforeContextFlush = 50, - - // Canvas resource provider - // ======================== - - // The maximum number of inflight resources waiting to be used for recycling. - kMaxRecycledCanvasResources = 2, -}; // enum - -} // namespace canvas_heuristic_parameters - -} // namespace blink - -#endif diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc index 429b7c2fab1..c414406d28a 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.cc @@ -15,6 +15,7 @@ #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" #include "gpu/command_buffer/client/raster_interface.h" #include "gpu/command_buffer/client/shared_image_interface.h" +#include "gpu/command_buffer/common/capabilities.h" #include "gpu/command_buffer/common/gpu_memory_buffer_support.h" #include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/command_buffer/common/sync_token.h" @@ -48,7 +49,8 @@ gpu::mojom::blink::MailboxPtr SharedBitmapIdToGpuMailboxPtr( CanvasResource::CanvasResource(base::WeakPtr<CanvasResourceProvider> provider, SkFilterQuality filter_quality, const CanvasColorParams& color_params) - : owning_thread_id_(base::PlatformThread::CurrentId()), + : owning_thread_ref_(base::PlatformThread::CurrentRef()), + owning_thread_task_runner_(Thread::Current()->GetTaskRunner()), provider_(std::move(provider)), filter_quality_(filter_quality), color_params_(color_params) {} @@ -60,12 +62,14 @@ CanvasResource::~CanvasResource() { } void CanvasResource::OnDestroy() { - if (owning_thread_id_ != base::PlatformThread::CurrentId()) { + if (is_cross_thread()) { // Destroyed on wrong thread. This can happen when the thread of origin was // torn down, in which case the GPU context owning any underlying resources // no longer exists. Abandon(); } else { + if (provider_) + provider_->OnDestroyResource(); TearDown(); } #if DCHECK_IS_ON() @@ -111,7 +115,6 @@ static void ReleaseFrameResources( // TODO(khushalsagar): If multiple readers had access to this resource, losing // it once should make sure subsequent releases don't try to recycle this // resource. - // Also what about single buffered canvas? if (lost_resource) resource->NotifyResourceLost(); if (resource_provider && !lost_resource && resource->IsRecycleable()) @@ -286,6 +289,12 @@ void CanvasResourceSharedBitmap::Abandon() { shared_mapping_ = {}; } +void CanvasResourceSharedBitmap::NotifyResourceLost() { + // Release our reference to the shared memory mapping since the resource can + // no longer be safely recycled and this memory is needed for copy-on-write. + shared_mapping_ = {}; +} + const gpu::Mailbox& CanvasResourceSharedBitmap::GetOrCreateGpuMailbox( MailboxSyncMode sync_mode) { return shared_bitmap_id_; @@ -315,16 +324,16 @@ CanvasResourceSharedImage::CanvasResourceSharedImage( base::WeakPtr<CanvasResourceProvider> provider, SkFilterQuality filter_quality, const CanvasColorParams& color_params, - bool is_overlay_candidate, bool is_origin_top_left, - bool allow_concurrent_read_write_access, - bool is_accelerated) + bool is_accelerated, + uint32_t shared_image_usage_flags) : CanvasResource(std::move(provider), filter_quality, color_params), context_provider_wrapper_(std::move(context_provider_wrapper)), - is_overlay_candidate_(is_overlay_candidate), size_(size), is_origin_top_left_(is_origin_top_left), is_accelerated_(is_accelerated), + is_overlay_candidate_(shared_image_usage_flags & + gpu::SHARED_IMAGE_USAGE_SCANOUT), texture_target_( is_overlay_candidate_ ? gpu::GetBufferTextureTarget( @@ -333,14 +342,14 @@ CanvasResourceSharedImage::CanvasResourceSharedImage( context_provider_wrapper_->ContextProvider() ->GetCapabilities()) : GL_TEXTURE_2D), - owning_thread_task_runner_(Thread::Current()->GetTaskRunner()) { - if (!context_provider_wrapper_) - return; - + use_oop_rasterization_(context_provider_wrapper_->ContextProvider() + ->GetCapabilities() + .supports_oop_raster) { auto* gpu_memory_buffer_manager = Platform::Current()->GetGpuMemoryBufferManager(); if (!is_accelerated_) { DCHECK(gpu_memory_buffer_manager); + DCHECK(shared_image_usage_flags & gpu::SHARED_IMAGE_USAGE_DISPLAY); gpu_memory_buffer_ = gpu_memory_buffer_manager->CreateGpuMemoryBuffer( gfx::Size(size), ColorParams().GetBufferFormat(), @@ -355,23 +364,26 @@ CanvasResourceSharedImage::CanvasResourceSharedImage( context_provider_wrapper_->ContextProvider()->SharedImageInterface(); DCHECK(shared_image_interface); - uint32_t flags = gpu::SHARED_IMAGE_USAGE_DISPLAY | - gpu::SHARED_IMAGE_USAGE_GLES2 | - gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT; - if (is_overlay_candidate_) - flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT; - if (allow_concurrent_read_write_access) - flags |= gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE; + // The GLES2 flag is needed for rendering via GL using a GrContext. + if (use_oop_rasterization_) { + shared_image_usage_flags = shared_image_usage_flags | + gpu::SHARED_IMAGE_USAGE_RASTER | + gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION; + } else { + shared_image_usage_flags = shared_image_usage_flags | + gpu::SHARED_IMAGE_USAGE_GLES2 | + gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT; + } gpu::Mailbox shared_image_mailbox; if (gpu_memory_buffer_) { shared_image_mailbox = shared_image_interface->CreateSharedImage( gpu_memory_buffer_.get(), gpu_memory_buffer_manager, - ColorParams().GetStorageGfxColorSpace(), flags); + ColorParams().GetStorageGfxColorSpace(), shared_image_usage_flags); } else { shared_image_mailbox = shared_image_interface->CreateSharedImage( ColorParams().TransferableResourceFormat(), gfx::Size(size), - ColorParams().GetStorageGfxColorSpace(), flags); + ColorParams().GetStorageGfxColorSpace(), shared_image_usage_flags); } // Wait for the mailbox to be ready to be used. @@ -380,6 +392,10 @@ CanvasResourceSharedImage::CanvasResourceSharedImage( auto* raster_interface = RasterInterface(); DCHECK(raster_interface); owning_thread_data().shared_image_mailbox = shared_image_mailbox; + + if (use_oop_rasterization_) + return; + owning_thread_data().texture_id_for_read_access = raster_interface->CreateAndConsumeForGpuRaster(shared_image_mailbox); @@ -387,7 +403,8 @@ CanvasResourceSharedImage::CanvasResourceSharedImage( // a texture for writes. if (!is_accelerated_) return; - if (allow_concurrent_read_write_access) { + if (shared_image_usage_flags & + gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE) { owning_thread_data().texture_id_for_write_access = raster_interface->CreateAndConsumeForGpuRaster(shared_image_mailbox); } else { @@ -402,15 +419,14 @@ scoped_refptr<CanvasResourceSharedImage> CanvasResourceSharedImage::Create( base::WeakPtr<CanvasResourceProvider> provider, SkFilterQuality filter_quality, const CanvasColorParams& color_params, - bool is_overlay_candidate, bool is_origin_top_left, - bool allow_concurrent_read_write_access, - bool is_accelerated) { + bool is_accelerated, + uint32_t shared_image_usage_flags) { TRACE_EVENT0("blink", "CanvasResourceSharedImage::Create"); auto resource = base::AdoptRef(new CanvasResourceSharedImage( size, std::move(context_provider_wrapper), std::move(provider), - filter_quality, color_params, is_overlay_candidate, is_origin_top_left, - allow_concurrent_read_write_access, is_accelerated)); + filter_quality, color_params, is_origin_top_left, is_accelerated, + shared_image_usage_flags)); return resource->IsValid() ? resource : nullptr; } @@ -422,13 +438,11 @@ CanvasResourceSharedImage::~CanvasResourceSharedImage() { OnDestroy(); } -GLenum CanvasResourceSharedImage::TextureTarget() const { - return texture_target_; -} - void CanvasResourceSharedImage::TearDown() { DCHECK(!is_cross_thread()); + // The context deletes all shared images on destruction which means no + // cleanup is needed if the context was lost. if (ContextProviderWrapper()) { auto* raster_interface = RasterInterface(); auto* shared_image_interface = @@ -459,11 +473,8 @@ void CanvasResourceSharedImage::TearDown() { } void CanvasResourceSharedImage::Abandon() { - if (auto context_provider = SharedGpuContext::ContextProviderWrapper()) { - auto* sii = context_provider->ContextProvider()->SharedImageInterface(); - if (sii) - sii->DestroySharedImage(gpu::SyncToken(), mailbox()); - } + // Called when the owning thread has been torn down which will destroy the + // context on which the shared image was created so no cleanup is necessary. } void CanvasResourceSharedImage::WillDraw() { @@ -484,17 +495,10 @@ void CanvasResourceSharedImage::OnBitmapImageDestroyed( bool has_read_ref_on_texture, const gpu::SyncToken& sync_token, bool is_lost) { - if (resource->is_cross_thread()) { - auto& task_runner = *resource->owning_thread_task_runner_; - PostCrossThreadTask( - task_runner, FROM_HERE, - CrossThreadBindOnce(&CanvasResourceSharedImage::OnBitmapImageDestroyed, - std::move(resource), has_read_ref_on_texture, - sync_token, is_lost)); - return; - } + DCHECK(!resource->is_cross_thread()); if (has_read_ref_on_texture) { + DCHECK(!resource->use_oop_rasterization_); DCHECK_GT(resource->owning_thread_data().bitmap_image_read_refs, 0u); resource->owning_thread_data().bitmap_image_read_refs--; @@ -546,7 +550,8 @@ scoped_refptr<StaticBitmapImage> CanvasResourceSharedImage::Bitmap() { // created here, the image will create a new representation from the mailbox // rather than referring to the shared image's texture ID if it was provided // below. - const bool has_read_ref_on_texture = !is_cross_thread(); + const bool has_read_ref_on_texture = + !is_cross_thread() && !use_oop_rasterization_; GLuint texture_id_for_image = 0u; if (has_read_ref_on_texture) { texture_id_for_image = owning_thread_data().texture_id_for_read_access; @@ -574,8 +579,8 @@ scoped_refptr<StaticBitmapImage> CanvasResourceSharedImage::Bitmap() { gpu::SyncToken token = is_cross_thread() ? sync_token() : gpu::SyncToken(); image = AcceleratedStaticBitmapImage::CreateFromCanvasMailbox( mailbox(), token, texture_id_for_image, image_info, texture_target_, - context_provider_wrapper_, owning_thread_id_, is_origin_top_left_, - std::move(release_callback)); + is_origin_top_left_, context_provider_wrapper_, owning_thread_ref_, + owning_thread_task_runner_, std::move(release_callback)); DCHECK(image); return image; @@ -708,8 +713,9 @@ scoped_refptr<StaticBitmapImage> ExternalCanvasResource::Bitmap() { return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox( mailbox_, GetSyncToken(), /*shared_image_texture_id=*/0u, - CreateSkImageInfo(), texture_target_, context_provider_wrapper_, - owning_thread_id_, is_origin_top_left_, std::move(release_callback)); + CreateSkImageInfo(), texture_target_, is_origin_top_left_, + context_provider_wrapper_, owning_thread_ref_, owning_thread_task_runner_, + std::move(release_callback)); } void ExternalCanvasResource::TearDown() { @@ -793,7 +799,7 @@ scoped_refptr<StaticBitmapImage> CanvasResourceSwapChain::Bitmap() { // It's safe to share the front buffer texture id if we're on the same thread // since the |release_callback| ensures this resource will be alive. GLuint shared_texture_id = 0u; - if (base::PlatformThread::CurrentId() == owning_thread_id_) + if (!is_cross_thread()) shared_texture_id = front_buffer_texture_id_; // The |release_callback| keeps a ref on this resource to ensure the backing @@ -806,22 +812,22 @@ scoped_refptr<StaticBitmapImage> CanvasResourceSwapChain::Bitmap() { return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox( front_buffer_mailbox_, sync_token_, shared_texture_id, image_info, - GL_TEXTURE_2D, context_provider_wrapper_, owning_thread_id_, - /*is_origin_top_left=*/true, std::move(release_callback)); + GL_TEXTURE_2D, true /*is_origin_top_left*/, context_provider_wrapper_, + owning_thread_ref_, owning_thread_task_runner_, + std::move(release_callback)); } void CanvasResourceSwapChain::Abandon() { - if (auto context_provider = SharedGpuContext::ContextProviderWrapper()) { - auto* sii = context_provider->ContextProvider()->SharedImageInterface(); - DCHECK(sii); - sii->DestroySharedImage(gpu::SyncToken(), front_buffer_mailbox_); - sii->DestroySharedImage(gpu::SyncToken(), back_buffer_mailbox_); - } + // Called when the owning thread has been torn down which will destroy the + // context on which the shared image was created so no cleanup is necessary. } void CanvasResourceSwapChain::TearDown() { + // The context deletes all shared images on destruction which means no + // cleanup is needed if the context was lost. if (!context_provider_wrapper_) return; + auto* raster_interface = context_provider_wrapper_->ContextProvider()->RasterInterface(); DCHECK(raster_interface); @@ -855,7 +861,7 @@ const gpu::SyncToken CanvasResourceSwapChain::GetSyncToken() { } void CanvasResourceSwapChain::PresentSwapChain() { - DCHECK_EQ(base::PlatformThread::CurrentId(), owning_thread_id_); + DCHECK(!is_cross_thread()); DCHECK(context_provider_wrapper_); TRACE_EVENT0("blink", "CanvasResourceSwapChain::PresentSwapChain"); @@ -882,7 +888,8 @@ void CanvasResourceSwapChain::PresentSwapChain() { // The wait sync token ensure that the present executes before we do the copy. raster_interface->CopySubTexture(front_buffer_mailbox_, back_buffer_mailbox_, GL_TEXTURE_2D, 0, 0, 0, 0, size_.Width(), - size_.Height()); + size_.Height(), false /* unpack_flip_y */, + false /* unpack_premultiply_alpha */); } base::WeakPtr<WebGraphicsContext3DProviderWrapper> diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h index b5f09b1d63d..e68d38e845b 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource.h @@ -133,19 +133,6 @@ class PLATFORM_EXPORT CanvasResource // CanvasResourceProvider and derivatives should call this. virtual void TakeSkImage(sk_sp<SkImage> image) = 0; - // Provides the texture ID that can be used to write to this resource. - // TODO(khushalsagar): Won't work with OOPR. - virtual GLuint GetBackingTextureHandleForOverwrite() { - NOTREACHED(); - return 0; - } - - // Returns the texture target for the ID provided above. - virtual GLenum TextureTarget() const { - NOTREACHED(); - return 0; - } - // Called when the resource is marked lost. Losing a resource does not mean // that the backing memory has been destroyed, since the resource itself keeps // a ref on that memory. @@ -153,12 +140,7 @@ class PLATFORM_EXPORT CanvasResource // token for the resource to be safely recycled and its the GL state may be // inconsistent with when the resource was given to the compositor. So it // should not be recycled for writing again but can be safely read from. - virtual void NotifyResourceLost() { - // TODO(khushalsagar): Some implementations respect the don't write again - // policy but some don't. Fix that once shared images replace all - // accelerated use-cases. - Abandon(); - } + virtual void NotifyResourceLost() = 0; void SetFilterQuality(SkFilterQuality filter) { filter_quality_ = filter; } // The filter quality to use when the resource is drawn by the compositor. @@ -166,6 +148,10 @@ class PLATFORM_EXPORT CanvasResource SkImageInfo CreateSkImageInfo() const; + bool is_cross_thread() const { + return base::PlatformThread::CurrentRef() != owning_thread_ref_; + } + protected: CanvasResource(base::WeakPtr<CanvasResourceProvider>, SkFilterQuality, @@ -212,7 +198,15 @@ class PLATFORM_EXPORT CanvasResource CanvasResourceProvider* Provider() { return provider_.get(); } base::WeakPtr<CanvasResourceProvider> WeakProvider() { return provider_; } - const base::PlatformThreadId owning_thread_id_; + const base::PlatformThreadRef owning_thread_ref_; + const scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner_; + + protected: + // Returns the texture target for the resource. + virtual GLenum TextureTarget() const { + NOTREACHED(); + return 0; + } private: // Sync token that was provided when resource was released @@ -245,10 +239,11 @@ class PLATFORM_EXPORT CanvasResourceSharedBitmap final : public CanvasResource { scoped_refptr<StaticBitmapImage> Bitmap() final; bool OriginClean() const final { return is_origin_clean_; } void SetOriginClean(bool flag) final { is_origin_clean_ = flag; } + const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override; + void NotifyResourceLost() override; private: void TearDown() override; - const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override; bool HasGpuMailbox() const override; CanvasResourceSharedBitmap(const IntSize&, @@ -271,10 +266,9 @@ class PLATFORM_EXPORT CanvasResourceSharedImage final : public CanvasResource { base::WeakPtr<CanvasResourceProvider>, SkFilterQuality, const CanvasColorParams&, - bool is_overlay_candidate, bool is_origin_top_left, - bool allow_concurrent_read_write_access, - bool is_accelerated); + bool is_accelerated, + uint32_t shared_image_usage_flags); ~CanvasResourceSharedImage() override; bool IsRecycleable() const final { return true; } @@ -302,16 +296,15 @@ class PLATFORM_EXPORT CanvasResourceSharedImage final : public CanvasResource { GLuint GetTextureIdForWriteAccess() const { return owning_thread_data().texture_id_for_write_access; } + GLenum TextureTarget() const override { return texture_target_; } void WillDraw(); - bool is_cross_thread() const { - return base::PlatformThread::CurrentId() != owning_thread_id_; - } bool has_read_access() const { return owning_thread_data().bitmap_image_read_refs > 0u; } bool is_lost() const { return owning_thread_data().is_lost; } void CopyRenderingResultsToGpuMemoryBuffer(const sk_sp<SkImage>& image); + const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override; private: // These members are either only accessed on the owning thread, or are only @@ -345,8 +338,6 @@ class PLATFORM_EXPORT CanvasResourceSharedImage final : public CanvasResource { void Abandon() override; base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper() const override; - const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override; - GLenum TextureTarget() const final; bool HasGpuMailbox() const override; const gpu::SyncToken GetSyncToken() override; bool IsOverlayCandidate() const final { return is_overlay_candidate_; } @@ -356,17 +347,16 @@ class PLATFORM_EXPORT CanvasResourceSharedImage final : public CanvasResource { base::WeakPtr<CanvasResourceProvider>, SkFilterQuality, const CanvasColorParams&, - bool is_overlay_candidate, bool is_origin_top_left, - bool allow_concurrent_read_write_access, - bool is_accelerated); + bool is_accelerated, + uint32_t shared_image_usage_flags); OwningThreadData& owning_thread_data() { - DCHECK_EQ(base::PlatformThread::CurrentId(), owning_thread_id_); + DCHECK(!is_cross_thread()); return owning_thread_data_; } const OwningThreadData& owning_thread_data() const { - DCHECK_EQ(base::PlatformThread::CurrentId(), owning_thread_id_); + DCHECK(!is_cross_thread()); return owning_thread_data_; } @@ -394,12 +384,12 @@ class PLATFORM_EXPORT CanvasResourceSharedImage final : public CanvasResource { std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_; // Accessed on any thread. - const bool is_overlay_candidate_; const IntSize size_; const bool is_origin_top_left_; const bool is_accelerated_; + const bool is_overlay_candidate_; const GLenum texture_target_; - const scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner_; + const bool use_oop_rasterization_; OwningThreadData owning_thread_data_; }; @@ -428,14 +418,18 @@ class PLATFORM_EXPORT ExternalCanvasResource final : public CanvasResource { void Abandon() final; IntSize Size() const final { return size_; } void TakeSkImage(sk_sp<SkImage> image) final; + void NotifyResourceLost() override { + // Used for single buffering mode which doesn't need to care about sync + // token synchronization. + } scoped_refptr<StaticBitmapImage> Bitmap() override; + const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override; private: void TearDown() override; GLenum TextureTarget() const final { return texture_target_; } bool IsOverlayCandidate() const final { return true; } - const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override; bool HasGpuMailbox() const override; const gpu::SyncToken GetSyncToken() override; base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper() @@ -481,20 +475,24 @@ class PLATFORM_EXPORT CanvasResourceSwapChain final : public CanvasResource { void Abandon() final; IntSize Size() const final { return size_; } void TakeSkImage(sk_sp<SkImage> image) final; + void NotifyResourceLost() override { + // Used for single buffering mode which doesn't need to care about sync + // token synchronization. + } scoped_refptr<StaticBitmapImage> Bitmap() override; GLenum TextureTarget() const final { return GL_TEXTURE_2D; } - GLuint GetBackingTextureHandleForOverwrite() final { + GLuint GetBackingTextureHandleForOverwrite() { return back_buffer_texture_id_; } void PresentSwapChain(); + const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override; private: void TearDown() override; bool IsOverlayCandidate() const final { return true; } - const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) override; bool HasGpuMailbox() const override; const gpu::SyncToken GetSyncToken() override; base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper() diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc index 48e1f492be5..2e0040edaca 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc @@ -14,8 +14,8 @@ #include "components/viz/common/resources/single_release_callback.h" #include "services/viz/public/mojom/compositing/frame_timing_details.mojom-blink.h" #include "services/viz/public/mojom/hit_test/hit_test_region_list.mojom-blink.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource.h" @@ -69,7 +69,7 @@ CanvasResourceDispatcher::CanvasResourceDispatcher( DCHECK(!sink_.is_bound()); mojo::Remote<mojom::blink::EmbeddedFrameSinkProvider> provider; - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( provider.BindNewPipeAndPassReceiver()); DCHECK(provider); @@ -216,8 +216,7 @@ bool CanvasResourceDispatcher::PrepareFrame( // TODO(crbug.com/652931): update the device_scale_factor frame->metadata.device_scale_factor = 1.0f; - if (current_begin_frame_ack_.sequence_number == - viz::BeginFrameArgs::kInvalidFrameNumber) { + if (!current_begin_frame_ack_.frame_id.IsSequenceValid()) { // TODO(eseckler): This shouldn't be necessary when OffscreenCanvas no // longer submits CompositorFrames without prior BeginFrame. current_begin_frame_ack_ = viz::BeginFrameAck::CreateManualAckWithDamage(); @@ -353,7 +352,7 @@ void CanvasResourceDispatcher::OnBeginFrame( } // TODO(fserb): Update this with the correct value if we are on RAF submit. - current_begin_frame_ack_.sequence_number = + current_begin_frame_ack_.frame_id.sequence_number = viz::BeginFrameArgs::kInvalidFrameNumber; } diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h index 9a6ac41b2fb..b2b6d55dbc0 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h @@ -15,6 +15,8 @@ #include "mojo/public/cpp/bindings/remote.h" #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h" #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h" +#include "third_party/blink/renderer/platform/geometry/int_size.h" +#include "third_party/blink/renderer/platform/platform_export.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc index f7c20310f27..d63606224c1 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc @@ -19,6 +19,18 @@ CanvasResourceHost::ReplaceResourceProvider( std::move(resource_provider_); resource_provider_ = std::move(new_resource_provider); UpdateMemoryUsage(); + if (resource_provider_) { + resource_provider_->Canvas()->restoreToCount(1); + InitializeForRecording(resource_provider_->Canvas()); + // Using unretained here since CanvasResourceHost owns |resource_provider_| + // and is guaranteed to outlive it + resource_provider_->SetRestoreClipStackCallback(base::BindRepeating( + &CanvasResourceHost::InitializeForRecording, base::Unretained(this))); + } + if (old_resource_provider) { + old_resource_provider->SetRestoreClipStackCallback( + CanvasResourceProvider::RestoreMatrixClipStackCb()); + } return old_resource_provider; } @@ -27,4 +39,9 @@ void CanvasResourceHost::DiscardResourceProvider() { UpdateMemoryUsage(); } +void CanvasResourceHost::InitializeForRecording(cc::PaintCanvas* canvas) { + canvas->save(); + RestoreCanvasMatrixClipStack(canvas); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.h index 9c1e69a63b4..16dbe14e169 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.h +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_host.h @@ -43,6 +43,8 @@ class PLATFORM_EXPORT CanvasResourceHost { virtual bool IsPrinting() const { return false; } private: + void InitializeForRecording(cc::PaintCanvas* canvas); + std::unique_ptr<CanvasResourceProvider> resource_provider_; }; diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc index 5ae4615f80f..7955722efa4 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc @@ -9,21 +9,66 @@ #include "base/stl_util.h" #include "build/build_config.h" #include "cc/paint/decode_stashing_image_provider.h" +#include "cc/paint/display_item_list.h" #include "cc/tiles/software_image_decode_cache.h" #include "components/viz/common/resources/resource_format_utils.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/raster_interface.h" #include "gpu/command_buffer/common/capabilities.h" +#include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/config/gpu_driver_bug_workaround_type.h" #include "gpu/config/gpu_feature_info.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h" -#include "third_party/blink/renderer/platform/graphics/canvas_heuristic_parameters.h" #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" +#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h" #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h" #include "third_party/skia/include/core/SkSurface.h" namespace blink { +namespace { + +bool IsGMBAllowed(IntSize size, + const CanvasColorParams& color_params, + const gpu::Capabilities& caps) { + return gpu::IsImageSizeValidForGpuMemoryBufferFormat( + gfx::Size(size), color_params.GetBufferFormat()) && + gpu::IsImageFromGpuMemoryBufferFormatSupported( + color_params.GetBufferFormat(), caps); +} + +} // namespace + +class CanvasResourceProvider::CanvasImageProvider : public cc::ImageProvider { + public: + CanvasImageProvider(cc::ImageDecodeCache* cache_n32, + cc::ImageDecodeCache* cache_f16, + const gfx::ColorSpace& target_color_space, + SkColorType target_color_type, + bool is_hardware_decode_cache); + ~CanvasImageProvider() override = default; + + // cc::ImageProvider implementation. + cc::ImageProvider::ScopedResult GetRasterContent( + const cc::DrawImage&) override; + + void ReleaseLockedImages() { locked_images_.clear(); } + + private: + void CanUnlockImage(ScopedResult); + void CleanupLockedImages(); + + bool is_hardware_decode_cache_; + bool cleanup_task_pending_ = false; + Vector<ScopedResult> locked_images_; + cc::PlaybackImageProvider playback_image_provider_n32_; + base::Optional<cc::PlaybackImageProvider> playback_image_provider_f16_; + + base::WeakPtrFactory<CanvasImageProvider> weak_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(CanvasImageProvider); +}; // * Renders to a Skia RAM-backed bitmap. // * Mailboxing is not supported : cannot be directly composited. @@ -33,16 +78,14 @@ class CanvasResourceProviderBitmap : public CanvasResourceProvider { const IntSize& size, SkFilterQuality filter_quality, const CanvasColorParams& color_params, - base::WeakPtr<WebGraphicsContext3DProviderWrapper> - context_provider_wrapper, base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher) : CanvasResourceProvider(kBitmap, size, - /*msaa_sample_count=*/0, + 0 /*msaa_sample_count*/, filter_quality, color_params, - /*is_origin_top_left=*/true, - std::move(context_provider_wrapper), + true /*is_origin_top_left*/, + nullptr /*context_provider_wrapper*/, std::move(resource_dispatcher)) {} ~CanvasResourceProviderBitmap() override = default; @@ -56,9 +99,10 @@ class CanvasResourceProviderBitmap : public CanvasResourceProvider { return nullptr; // Does not support direct compositing } - scoped_refptr<StaticBitmapImage> Snapshot() override { + scoped_refptr<StaticBitmapImage> Snapshot( + const ImageOrientation& orientation) override { TRACE_EVENT0("blink", "CanvasResourceProviderBitmap::Snapshot"); - return SnapshotInternal(); + return SnapshotInternal(orientation); } sk_sp<SkSurface> CreateSkSurface() const override { @@ -83,7 +127,6 @@ class CanvasResourceProviderSharedBitmap : public CanvasResourceProviderBitmap { : CanvasResourceProviderBitmap(size, filter_quality, color_params, - nullptr, // context_provider_wrapper std::move(resource_dispatcher)) { DCHECK(ResourceDispatcher()); type_ = kSharedBitmap; @@ -97,7 +140,8 @@ class CanvasResourceProviderSharedBitmap : public CanvasResourceProviderBitmap { if (!IsBitmapFormatSupported(color_params.TransferableResourceFormat())) { // If the rendering format is not supported, downgrate to 8-bits. // TODO(junov): Should we try 12-12-12-12 and 10-10-10-2? - color_params.SetCanvasPixelFormat(CanvasPixelFormat::kRGBA8); + color_params.SetCanvasPixelFormat( + CanvasColorParams::GetNativeCanvasPixelFormat()); } return CanvasResourceSharedBitmap::Create(Size(), color_params, @@ -134,9 +178,8 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { context_provider_wrapper, base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher, bool is_origin_top_left, - bool is_overlay_candidate, - bool maybe_single_buffered, - bool is_accelerated) + bool is_accelerated, + uint32_t shared_image_usage_flags) : CanvasResourceProvider( kSharedImage, size, @@ -145,16 +188,20 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { // TODO(khushalsagar): The software path seems to be assuming N32 // somewhere in the later pipeline but for offscreen canvas only. CanvasColorParams(color_params.ColorSpace(), - color_params.PixelFormat(), - color_params.GetOpacityMode(), - is_accelerated ? CanvasForceRGBA::kForced - : CanvasForceRGBA::kNotForced), + is_accelerated && color_params.PixelFormat() != + CanvasPixelFormat::kF16 + ? CanvasPixelFormat::kRGBA8 + : color_params.PixelFormat(), + color_params.GetOpacityMode()), is_origin_top_left, std::move(context_provider_wrapper), std::move(resource_dispatcher)), - is_overlay_candidate_(is_overlay_candidate), - maybe_single_buffered_(maybe_single_buffered), - is_accelerated_(is_accelerated) { + is_accelerated_(is_accelerated), + shared_image_usage_flags_(shared_image_usage_flags), + use_oop_rasterization_(ContextProviderWrapper() + ->ContextProvider() + ->GetCapabilities() + .supports_oop_raster) { resource_ = NewOrRecycledResource(); if (resource_) EnsureWriteAccess(); @@ -162,24 +209,32 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { ~CanvasResourceProviderSharedImage() override {} - bool IsValid() const final { return GetSkSurface() && !IsGpuContextLost(); } bool IsAccelerated() const final { return is_accelerated_; } bool SupportsDirectCompositing() const override { return true; } + bool IsValid() const final { + if (use_oop_rasterization_) + return !IsGpuContextLost(); + else + return GetSkSurface() && !IsGpuContextLost(); + } + bool SupportsSingleBuffering() const override { - return maybe_single_buffered_; + return shared_image_usage_flags_ & + gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE; } - GLuint GetBackingTextureHandleForOverwrite() override { + gpu::Mailbox GetBackingMailboxForOverwrite( + MailboxSyncMode sync_mode) override { DCHECK(is_accelerated_); if (IsGpuContextLost()) - return 0u; + return gpu::Mailbox(); - FlushGrContext(); - WillDraw(); - return resource()->GetTextureIdForWriteAccess(); + WillDrawInternal(false); + return resource_->GetOrCreateGpuMailbox(sync_mode); } + GLenum GetBackingTextureTarget() const override { - return resource_->TextureTarget(); + return resource()->TextureTarget(); } scoped_refptr<CanvasResource> CreateResource() final { @@ -187,15 +242,14 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { if (IsGpuContextLost()) return nullptr; - bool allow_concurrent_read_write_access = maybe_single_buffered_; return CanvasResourceSharedImage::Create( Size(), ContextProviderWrapper(), CreateWeakPtr(), FilterQuality(), - ColorParams(), is_overlay_candidate_, IsOriginTopLeft(), - allow_concurrent_read_write_access, is_accelerated_); + ColorParams(), IsOriginTopLeft(), is_accelerated_, + shared_image_usage_flags_); } void NotifyTexParamsModified(const CanvasResource* resource) override { - if (!is_accelerated_) + if (!is_accelerated_ || use_oop_rasterization_) return; if (resource_.get() == resource) { @@ -215,6 +269,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { if (IsGpuContextLost()) return nullptr; + FlushCanvas(); // Its important to end read access and ref the resource before the WillDraw // call below. Since it relies on resource ref-count to trigger // copy-on-write and asserts that we only have write access when the @@ -237,7 +292,8 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { return resource; } - scoped_refptr<StaticBitmapImage> Snapshot() override { + scoped_refptr<StaticBitmapImage> Snapshot( + const ImageOrientation& orientation) override { TRACE_EVENT0("blink", "CanvasResourceProviderSharedImage::Snapshot"); if (!IsValid()) return nullptr; @@ -246,9 +302,10 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { // rendering results visible on the GpuMemoryBuffer while we return cpu // memory, rendererd to by skia, here. if (!is_accelerated_) - return SnapshotInternal(); + return SnapshotInternal(orientation); if (!cached_snapshot_) { + FlushCanvas(); EndWriteAccess(); cached_snapshot_ = resource_->Bitmap(); } @@ -258,7 +315,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { return cached_snapshot_; } - void WillDraw() override { + void WillDrawInternal(bool write_to_local_texture) { DCHECK(resource_); if (IsGpuContextLost()) @@ -293,9 +350,11 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { old_resource_shared_image->GetTextureIdForReadAccess(), GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); } + UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.ContentChangeMode", + mode_ == SkSurface::kRetain_ContentChangeMode); surface_->replaceBackendTexture(CreateGrTextureForResource(), - GetGrSurfaceOrigin()); - surface_->flush(); + GetGrSurfaceOrigin(), mode_); + mode_ = SkSurface::kRetain_ContentChangeMode; if (!old_resource_shared_image->has_read_access()) { RasterInterface()->EndSharedImageAccessDirectCHROMIUM( old_resource_shared_image->GetTextureIdForReadAccess()); @@ -303,10 +362,55 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { } } - EnsureWriteAccess(); + if (write_to_local_texture) + EnsureWriteAccess(); + else + EndWriteAccess(); + resource()->WillDraw(); } + void WillDraw() override { WillDrawInternal(true); } + + void RasterRecord(sk_sp<cc::PaintRecord> last_recording) override { + if (!use_oop_rasterization_) { + CanvasResourceProvider::RasterRecord(std::move(last_recording)); + return; + } + gpu::raster::RasterInterface* ri = RasterInterface(); + SkColor background_color = ColorParams().GetOpacityMode() == kOpaque + ? SK_ColorBLACK + : SK_ColorTRANSPARENT; + + auto list = base::MakeRefCounted<cc::DisplayItemList>( + cc::DisplayItemList::kTopLevelDisplayItemList); + + list->StartPaint(); + list->push<cc::DrawRecordOp>(std::move(last_recording)); + list->EndPaintOfUnpaired(gfx::Rect(Size().Width(), Size().Height())); + list->Finalize(); + + gfx::Size size(Size().Width(), Size().Height()); + size_t max_op_size_hint = + gpu::raster::RasterInterface::kDefaultMaxOpSizeHint; + bool use_lcd = false; + gfx::Rect full_raster_rect(Size().Width(), Size().Height()); + gfx::Rect playback_rect(Size().Width(), Size().Height()); + gfx::Vector2dF post_translate(0.f, 0.f); + + ri->BeginRasterCHROMIUM( + background_color, GetMSAASampleCount(), use_lcd, + ColorParams().GetStorageGfxColorSpace(), + resource()->GetOrCreateGpuMailbox(kUnverifiedSyncToken).name); + + ri->RasterCHROMIUM(list.get(), GetOrCreateCanvasImageProvider(), size, + full_raster_rect, playback_rect, post_translate, + 1.f /* post_scale */, false /* requires_clear */, + &max_op_size_hint); + + ri->EndRasterCHROMIUM(); + } + bool DoCopyOnWrite() { // If the canvas is single buffered, concurrent read/writes to the resource // are allowed. Note that we ignore the resource lost case as well since @@ -358,7 +462,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { GrGLTextureInfo texture_info = {}; texture_info.fID = resource()->GetTextureIdForWriteAccess(); - texture_info.fTarget = resource_->TextureTarget(); + texture_info.fTarget = resource()->TextureTarget(); texture_info.fFormat = ColorParams().GLSizedInternalFormat(); return GrBackendTexture(Size().Width(), Size().Height(), GrMipMapped::kNo, texture_info); @@ -389,7 +493,8 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { DCHECK(!resource()->is_cross_thread()) << "Write access is only allowed on the owning thread"; - if (current_resource_has_write_access_ || IsGpuContextLost()) + if (current_resource_has_write_access_ || IsGpuContextLost() || + use_oop_rasterization_) return; if (is_accelerated_) { @@ -410,7 +515,13 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { if (!current_resource_has_write_access_ || IsGpuContextLost()) return; + DCHECK(!use_oop_rasterization_); + if (is_accelerated_) { + // We reset |mode_| here since the draw commands which overwrite the + // complete canvas must have been flushed at this point without triggering + // copy-on-write. + mode_ = SkSurface::kRetain_ContentChangeMode; // Issue any skia work using this resource before releasing write access. FlushGrContext(); auto texture_id = resource()->GetTextureIdForWriteAccess(); @@ -432,10 +543,10 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { return static_cast<const CanvasResourceSharedImage*>(resource_.get()); } - const bool is_overlay_candidate_; - const bool maybe_single_buffered_; const bool is_accelerated_; + const uint32_t shared_image_usage_flags_; bool current_resource_has_write_access_ = false; + const bool use_oop_rasterization_; scoped_refptr<CanvasResource> resource_; scoped_refptr<StaticBitmapImage> cached_snapshot_; }; @@ -486,7 +597,7 @@ class CanvasResourceProviderPassThrough final : public CanvasResourceProvider { return nullptr; } - scoped_refptr<StaticBitmapImage> Snapshot() override { + scoped_refptr<StaticBitmapImage> Snapshot(const ImageOrientation&) override { auto resource = GetImportedResource(); if (IsGpuContextLost() || !resource) return nullptr; @@ -513,7 +624,7 @@ class CanvasResourceProviderSwapChain final : public CanvasResourceProvider { msaa_sample_count, filter_quality, color_params, - /*is_origin_top_left=*/true, + true /*is_origin_top_left*/, std::move(context_provider_wrapper), std::move(resource_dispatcher)), msaa_sample_count_(msaa_sample_count) { @@ -542,15 +653,15 @@ class CanvasResourceProviderSwapChain final : public CanvasResourceProvider { "CanvasResourceProviderSwapChain::ProduceCanvasResource"); if (!IsValid()) return nullptr; + FlushCanvas(); if (dirty_) { - FlushSkia(); resource_->PresentSwapChain(); dirty_ = false; } return resource_; } - scoped_refptr<StaticBitmapImage> Snapshot() override { + scoped_refptr<StaticBitmapImage> Snapshot(const ImageOrientation&) override { TRACE_EVENT0("blink", "CanvasResourceProviderSwapChain::Snapshot"); // Use ProduceCanvasResource to ensure any queued commands are flushed and @@ -598,15 +709,6 @@ enum class CanvasResourceType { const Vector<CanvasResourceType>& GetResourceTypeFallbackList( CanvasResourceProvider::ResourceUsage usage) { - static const Vector<CanvasResourceType> kSoftwareFallbackList({ - CanvasResourceType::kBitmap, - }); - - static const Vector<CanvasResourceType> kAcceleratedFallbackList({ - CanvasResourceType::kSharedImage, - // Fallback to software - CanvasResourceType::kBitmap, - }); static const Vector<CanvasResourceType> kCompositedFallbackList({ CanvasResourceType::kSharedImage, @@ -644,14 +746,16 @@ const Vector<CanvasResourceType>& GetResourceTypeFallbackList( kCompositedFallbackList.begin(), kCompositedFallbackList.end())); + static const Vector<CanvasResourceType> kEmptyList; switch (usage) { + case CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage: case CanvasResourceProvider::ResourceUsage::kSoftwareResourceUsage: - return kSoftwareFallbackList; + NOTREACHED(); + return kEmptyList; case CanvasResourceProvider::ResourceUsage:: kSoftwareCompositedResourceUsage: return kCompositedFallbackList; - case CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage: - return kAcceleratedFallbackList; + case CanvasResourceProvider::ResourceUsage:: kAcceleratedCompositedResourceUsage: return kCompositedFallbackList; @@ -663,39 +767,15 @@ const Vector<CanvasResourceType>& GetResourceTypeFallbackList( return kAcceleratedDirect3DFallbackList; case CanvasResourceProvider::ResourceUsage:: kSoftwareCompositedDirect2DResourceUsage: - return kCompositedFallbackList; + NOTREACHED(); + return kEmptyList; } NOTREACHED(); + return kEmptyList; } } // unnamed namespace -std::unique_ptr<CanvasResourceProvider> CanvasResourceProvider::CreateForCanvas( - const IntSize& size, - ResourceUsage usage, - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper, - unsigned msaa_sample_count, - SkFilterQuality filter_quality, - const CanvasColorParams& color_params, - uint8_t presentation_mode, - base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher, - bool is_origin_top_left) { - base::UmaHistogramEnumeration("Blink.Canvas.ResourceProviderUsage", usage); - - std::unique_ptr<CanvasResourceProvider> provider = Create( - size, usage, context_provider_wrapper, msaa_sample_count, filter_quality, - color_params, presentation_mode, resource_dispatcher, is_origin_top_left); - - if (provider && provider->IsValid()) { - base::UmaHistogramBoolean("Blink.Canvas.ResourceProviderIsAccelerated", - provider->IsAccelerated()); - base::UmaHistogramEnumeration("Blink.Canvas.ResourceProviderType", - provider->type_); - } - - return provider; -} - std::unique_ptr<CanvasResourceProvider> CanvasResourceProvider::Create( const IntSize& size, ResourceUsage usage, @@ -706,6 +786,9 @@ std::unique_ptr<CanvasResourceProvider> CanvasResourceProvider::Create( uint8_t presentation_mode, base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher, bool is_origin_top_left) { + DCHECK_EQ(msaa_sample_count, 0u); + DCHECK(usage != ResourceUsage::kSoftwareResourceUsage); + std::unique_ptr<CanvasResourceProvider> provider; bool is_gpu_memory_buffer_image_allowed = false; @@ -718,15 +801,12 @@ std::unique_ptr<CanvasResourceProvider> CanvasResourceProvider::Create( const int max_texture_size = context_capabilities.max_texture_size; if (size.Width() > max_texture_size || size.Height() > max_texture_size) - usage = ResourceUsage::kSoftwareResourceUsage; + return CreateBitmapProvider(size, filter_quality, color_params); is_gpu_memory_buffer_image_allowed = (presentation_mode & kAllowImageChromiumPresentationMode) && Platform::Current()->GetGpuMemoryBufferManager() && - gpu::IsImageSizeValidForGpuMemoryBufferFormat( - gfx::Size(size), color_params.GetBufferFormat()) && - gpu::IsImageFromGpuMemoryBufferFormatSupported( - color_params.GetBufferFormat(), context_capabilities); + IsGMBAllowed(size, color_params, context_capabilities); is_swap_chain_allowed = (presentation_mode & kAllowSwapChainPresentationMode) && @@ -764,54 +844,57 @@ std::unique_ptr<CanvasResourceProvider> CanvasResourceProvider::Create( break; case CanvasResourceType::kBitmap: provider = std::make_unique<CanvasResourceProviderBitmap>( - size, filter_quality, color_params, context_provider_wrapper, - resource_dispatcher); + size, filter_quality, color_params, resource_dispatcher); break; case CanvasResourceType::kSharedImage: { - DCHECK_NE(usage, ResourceUsage::kSoftwareResourceUsage); - if (!context_provider_wrapper) continue; - const bool is_accelerated = - usage == ResourceUsage::kAcceleratedResourceUsage || - usage == ResourceUsage::kAcceleratedCompositedResourceUsage || - usage == ResourceUsage::kAcceleratedDirect2DResourceUsage || - usage == ResourceUsage::kAcceleratedDirect3DResourceUsage; - - // If the rendering will be in software and we don't have GMB support, - // fallback to bitmap provider type. - if (!is_accelerated && !is_gpu_memory_buffer_image_allowed) - continue; - - // texture_storage_image is required to create shared images supporting - // scanout usage. const bool can_use_overlays = is_gpu_memory_buffer_image_allowed && context_provider_wrapper->ContextProvider() ->GetCapabilities() .texture_storage_image; - const bool is_overlay_candidate = - (usage == ResourceUsage::kAcceleratedDirect2DResourceUsage || - usage == ResourceUsage::kAcceleratedDirect3DResourceUsage || - usage == ResourceUsage::kAcceleratedCompositedResourceUsage || - usage == ResourceUsage::kSoftwareCompositedResourceUsage) && - can_use_overlays; - - // Single buffering requires concurrent read/write access to the - // resource which is sub-optimal for software raster since that would - // require concurrent access to the resource on the CPU and GPU, so - // enable it for accelerated low latency canvas only. - const bool maybe_single_buffered = - (usage == ResourceUsage::kAcceleratedDirect2DResourceUsage || - usage == ResourceUsage::kAcceleratedDirect3DResourceUsage) && - is_gpu_memory_buffer_image_allowed; + bool is_accelerated = false; + uint32_t shared_image_usage_flags = 0u; + switch (usage) { + case ResourceUsage::kSoftwareResourceUsage: + NOTREACHED(); + continue; + case ResourceUsage::kSoftwareCompositedResourceUsage: + // Rendering in software with accelerated compositing. + if (!is_gpu_memory_buffer_image_allowed) + continue; + shared_image_usage_flags |= gpu::SHARED_IMAGE_USAGE_DISPLAY; + if (can_use_overlays) + shared_image_usage_flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT; + is_accelerated = false; + break; + case ResourceUsage::kAcceleratedDirect2DResourceUsage: + case ResourceUsage::kAcceleratedDirect3DResourceUsage: + if (can_use_overlays) { + shared_image_usage_flags |= + gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE; + } + FALLTHROUGH; + case ResourceUsage::kAcceleratedCompositedResourceUsage: + shared_image_usage_flags |= gpu::SHARED_IMAGE_USAGE_DISPLAY; + if (can_use_overlays) + shared_image_usage_flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT; + FALLTHROUGH; + case ResourceUsage::kAcceleratedResourceUsage: + is_accelerated = true; + break; + case ResourceUsage::kSoftwareCompositedDirect2DResourceUsage: + NOTREACHED(); + continue; + } provider = std::make_unique<CanvasResourceProviderSharedImage>( size, msaa_sample_count, filter_quality, color_params, context_provider_wrapper, resource_dispatcher, is_origin_top_left, - is_overlay_candidate, maybe_single_buffered, is_accelerated); + is_accelerated, shared_image_usage_flags); } break; } if (!provider->IsValid()) @@ -822,35 +905,53 @@ std::unique_ptr<CanvasResourceProvider> CanvasResourceProvider::Create( return nullptr; } -class CanvasResourceProvider::CanvasImageProvider : public cc::ImageProvider { - public: - CanvasImageProvider(cc::ImageDecodeCache* cache_n32, - cc::ImageDecodeCache* cache_f16, - const gfx::ColorSpace& target_color_space, - SkColorType target_color_type, - bool is_hardware_decode_cache); - ~CanvasImageProvider() override = default; - - // cc::ImageProvider implementation. - cc::ImageProvider::ScopedResult GetRasterContent( - const cc::DrawImage&) override; +std::unique_ptr<CanvasResourceProvider> +CanvasResourceProvider::CreateBitmapProvider( + const IntSize& size, + SkFilterQuality filter_quality, + const CanvasColorParams& color_params) { + auto provider = std::make_unique<CanvasResourceProviderBitmap>( + size, filter_quality, color_params, nullptr /*resource_dispatcher*/); + if (provider->IsValid()) + return provider; - void ReleaseLockedImages() { locked_images_.clear(); } + return nullptr; +} - private: - void CanUnlockImage(ScopedResult); - void CleanupLockedImages(); +std::unique_ptr<CanvasResourceProvider> +CanvasResourceProvider::CreateSharedImageProvider( + const IntSize& size, + base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper, + SkFilterQuality filter_quality, + const CanvasColorParams& color_params, + bool is_origin_top_left, + RasterMode raster_mode, + uint32_t shared_image_usage_flags) { + if (!context_provider_wrapper) + return nullptr; - bool is_hardware_decode_cache_; - bool cleanup_task_pending_ = false; - Vector<ScopedResult> locked_images_; - cc::PlaybackImageProvider playback_image_provider_n32_; - base::Optional<cc::PlaybackImageProvider> playback_image_provider_f16_; + const auto& caps = + context_provider_wrapper->ContextProvider()->GetCapabilities(); + if (size.Width() > caps.max_texture_size || + size.Height() > caps.max_texture_size) { + return nullptr; + } - base::WeakPtrFactory<CanvasImageProvider> weak_factory_{this}; + bool can_use_overlays = + IsGMBAllowed(size, color_params, caps) && caps.texture_storage_image; + if (!can_use_overlays) + shared_image_usage_flags &= ~gpu::SHARED_IMAGE_USAGE_SCANOUT; + + auto provider = std::make_unique<CanvasResourceProviderSharedImage>( + size, 0 /* msaa_sample_count */, filter_quality, color_params, + context_provider_wrapper, nullptr /*resource_dispatcher*/, + is_origin_top_left, raster_mode == RasterMode::kGPU, + shared_image_usage_flags); + if (provider->IsValid()) + return provider; - DISALLOW_COPY_AND_ASSIGN(CanvasImageProvider); -}; + return nullptr; +} CanvasResourceProvider::CanvasImageProvider::CanvasImageProvider( cc::ImageDecodeCache* cache_n32, @@ -962,6 +1063,8 @@ CanvasResourceProvider::CanvasResourceProvider( } CanvasResourceProvider::~CanvasResourceProvider() { + UMA_HISTOGRAM_EXACT_LINEAR("Blink.Canvas.MaximumInflightResources", + max_inflight_resources_, 20); if (context_provider_wrapper_) context_provider_wrapper_->RemoveObserver(this); } @@ -972,52 +1075,57 @@ SkSurface* CanvasResourceProvider::GetSkSurface() const { return surface_.get(); } -void CanvasResourceProvider::InitializePaintCanvas() { - // Since canvas_ has a reference to canvas_image_provider_, canvas must be - // deleted before the image_provider. - canvas_ = nullptr; - canvas_image_provider_ = nullptr; - - // Create an ImageDecodeCache for half float images only if the canvas is - // using half float back storage. - cc::ImageDecodeCache* cache_f16 = nullptr; - if (ColorParams().GetSkColorType() == kRGBA_F16_SkColorType) - cache_f16 = ImageDecodeCacheF16(); - canvas_image_provider_ = std::make_unique<CanvasImageProvider>( - ImageDecodeCacheRGBA8(), cache_f16, gfx::ColorSpace::CreateSRGB(), - color_params_.GetSkColorType(), use_hardware_decode_cache()); +void CanvasResourceProvider::EnsureSkiaCanvas() { + WillDraw(); + + if (skia_canvas_) + return; cc::SkiaPaintCanvas::ContextFlushes context_flushes; - if (IsAccelerated() && + if (IsAccelerated() && ContextProviderWrapper() && !ContextProviderWrapper() ->ContextProvider() ->GetGpuFeatureInfo() .IsWorkaroundEnabled(gpu::DISABLE_2D_CANVAS_AUTO_FLUSH)) { - context_flushes.enable = - canvas_heuristic_parameters::kEnableGrContextFlushes; - context_flushes.max_draws_before_flush = - canvas_heuristic_parameters::kMaxDrawsBeforeContextFlush; + context_flushes.enable = true; + context_flushes.max_draws_before_flush = kMaxDrawsBeforeContextFlush; } - canvas_ = std::make_unique<cc::SkiaPaintCanvas>(GetSkSurface()->getCanvas(), - canvas_image_provider_.get(), - context_flushes); + skia_canvas_ = std::make_unique<cc::SkiaPaintCanvas>( + GetSkSurface()->getCanvas(), GetOrCreateCanvasImageProvider(), + context_flushes); +} + +CanvasResourceProvider::CanvasImageProvider* +CanvasResourceProvider::GetOrCreateCanvasImageProvider() { + if (!canvas_image_provider_) { + // Create an ImageDecodeCache for half float images only if the canvas is + // using half float back storage. + cc::ImageDecodeCache* cache_f16 = nullptr; + if (ColorParams().GetSkColorType() == kRGBA_F16_SkColorType) + cache_f16 = ImageDecodeCacheF16(); + canvas_image_provider_ = std::make_unique<CanvasImageProvider>( + ImageDecodeCacheRGBA8(), cache_f16, gfx::ColorSpace::CreateSRGB(), + color_params_.GetSkColorType(), use_hardware_decode_cache()); + } + return canvas_image_provider_.get(); } cc::PaintCanvas* CanvasResourceProvider::Canvas() { - WillDraw(); + if (!recorder_) { + // A raw pointer is safe here because the callback is only used by the + // |recorder_|. + recorder_ = std::make_unique<MemoryManagedPaintRecorder>(WTF::BindRepeating( + &CanvasResourceProvider::SetNeedsFlush, WTF::Unretained(this))); - if (!canvas_) { - TRACE_EVENT0("blink", "CanvasResourceProvider::Canvas"); - DCHECK(!canvas_image_provider_); - InitializePaintCanvas(); + return recorder_->beginRecording(Size().Width(), Size().Height()); } - return canvas_.get(); + return recorder_->getRecordingCanvas(); } void CanvasResourceProvider::OnContextDestroyed() { if (canvas_image_provider_) { - DCHECK(canvas_); - canvas_->reset_image_provider(); + DCHECK(skia_canvas_); + skia_canvas_->reset_image_provider(); canvas_image_provider_.reset(); } } @@ -1027,16 +1135,19 @@ void CanvasResourceProvider::ReleaseLockedImages() { canvas_image_provider_->ReleaseLockedImages(); } -scoped_refptr<StaticBitmapImage> CanvasResourceProvider::SnapshotInternal() { +scoped_refptr<StaticBitmapImage> CanvasResourceProvider::SnapshotInternal( + const ImageOrientation& orientation) { if (!IsValid()) return nullptr; auto paint_image = MakeImageSnapshot(); DCHECK(!paint_image.GetSkImage()->isTextureBacked()); - return UnacceleratedStaticBitmapImage::Create(std::move(paint_image)); + return UnacceleratedStaticBitmapImage::Create(std::move(paint_image), + orientation); } cc::PaintImage CanvasResourceProvider::MakeImageSnapshot() { + FlushCanvas(); auto sk_image = GetSkSurface()->makeImageSnapshot(); if (!sk_image) return cc::PaintImage(); @@ -1076,7 +1187,23 @@ GrContext* CanvasResourceProvider::GetGrContext() const { return context_provider_wrapper_->ContextProvider()->GetGrContext(); } -void CanvasResourceProvider::FlushSkia() const { +sk_sp<cc::PaintRecord> CanvasResourceProvider::FlushCanvas() { + if (!HasRecordedDrawOps()) + return nullptr; + sk_sp<cc::PaintRecord> last_recording = recorder_->finishRecordingAsPicture(); + RasterRecord(last_recording); + needs_flush_ = false; + cc::PaintCanvas* canvas = + recorder_->beginRecording(Size().Width(), Size().Height()); + if (restore_clip_stack_callback_) + restore_clip_stack_callback_.Run(canvas); + return last_recording; +} + +void CanvasResourceProvider::RasterRecord( + sk_sp<cc::PaintRecord> last_recording) { + EnsureSkiaCanvas(); + skia_canvas_->drawPicture(std::move(last_recording)); GetSkSurface()->flush(); } @@ -1094,6 +1221,18 @@ bool CanvasResourceProvider::WritePixels(const SkImageInfo& orig_info, TRACE_EVENT0("blink", "CanvasResourceProvider::WritePixels"); DCHECK(IsValid()); + DCHECK(!HasRecordedDrawOps()); + + EnsureSkiaCanvas(); + + // Apply clipstack to skia_canvas_ and then restore it to original state once + // we leave this scope. This is needed because each recording initializes and + // resets this state after every flush. restore_clip_stack_callback_ sets the + // initial save required for a restore. + cc::PaintCanvasAutoRestore auto_restore(skia_canvas_.get(), false); + if (restore_clip_stack_callback_) + restore_clip_stack_callback_.Run(skia_canvas_.get()); + return GetSkSurface()->getCanvas()->writePixels(orig_info, pixels, row_bytes, x, y); } @@ -1142,7 +1281,7 @@ void CanvasResourceProvider::RecycleResource( scoped_refptr<CanvasResource> resource) { // We don't want to keep an arbitrary large number of canvases. if (canvas_resources_.size() > - canvas_heuristic_parameters::kMaxRecycledCanvasResources) + static_cast<unsigned int>(kMaxRecycledCanvasResources)) return; // Need to check HasOneRef() because if there are outstanding references to @@ -1163,9 +1302,17 @@ void CanvasResourceProvider::ClearRecycledResources() { canvas_resources_.clear(); } +void CanvasResourceProvider::OnDestroyResource() { + --num_inflight_resources_; +} + scoped_refptr<CanvasResource> CanvasResourceProvider::NewOrRecycledResource() { - if (canvas_resources_.IsEmpty()) + if (canvas_resources_.IsEmpty()) { canvas_resources_.push_back(CreateResource()); + ++num_inflight_resources_; + if (num_inflight_resources_ > max_inflight_resources_) + max_inflight_resources_ = num_inflight_resources_; + } if (IsSingleBuffered()) { DCHECK_EQ(canvas_resources_.size(), 1u); @@ -1203,4 +1350,37 @@ scoped_refptr<CanvasResource> CanvasResourceProvider::GetImportedResource() return canvas_resources_.back(); } +void CanvasResourceProvider::SkipQueuedDrawCommands() { + // Note that this function only gets called when canvas needs a full repaint, + // so always update the |mode_| to discard the old copy of canvas content. + mode_ = SkSurface::kDiscard_ContentChangeMode; + + if (!HasRecordedDrawOps()) + return; + recorder_->finishRecordingAsPicture(); + cc::PaintCanvas* canvas = + recorder_->beginRecording(Size().Width(), Size().Height()); + if (restore_clip_stack_callback_) + restore_clip_stack_callback_.Run(canvas); +} + +void CanvasResourceProvider::SetRestoreClipStackCallback( + RestoreMatrixClipStackCb callback) { + DCHECK(restore_clip_stack_callback_.is_null() || callback.is_null()); + restore_clip_stack_callback_ = std::move(callback); +} + +void CanvasResourceProvider::RestoreBackBuffer(const cc::PaintImage& image) { + DCHECK_EQ(image.height(), Size().Height()); + DCHECK_EQ(image.width(), Size().Width()); + EnsureSkiaCanvas(); + cc::PaintFlags copy_paint; + copy_paint.setBlendMode(SkBlendMode::kSrc); + skia_canvas_->drawImage(image, 0, 0, ©_paint); +} + +bool CanvasResourceProvider::HasRecordedDrawOps() const { + return recorder_ && recorder_->ListHasDrawOps(); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h index 910b0e526c0..e88d3511299 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h @@ -8,6 +8,8 @@ #include "cc/paint/skia_paint_canvas.h" #include "cc/raster/playback_image_provider.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource.h" +#include "third_party/blink/renderer/platform/graphics/image_orientation.h" +#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h" #include "third_party/skia/include/core/SkSurface.h" class GrContext; @@ -56,8 +58,9 @@ class PLATFORM_EXPORT CanvasResourceProvider public: // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. + // todo(juanmihd@) ResourceUsage will be removed soon, try avoiding using this enum class ResourceUsage { - kSoftwareResourceUsage = 0, + kSoftwareResourceUsage = 0, // deprecated kSoftwareCompositedResourceUsage = 1, kAcceleratedResourceUsage = 2, kAcceleratedCompositedResourceUsage = 3, @@ -92,19 +95,33 @@ class PLATFORM_EXPORT CanvasResourceProvider kMaxValue = kSwapChain, }; - void static RecordTypeToUMA(ResourceProviderType type); + using RestoreMatrixClipStackCb = + base::RepeatingCallback<void(cc::PaintCanvas*)>; - static std::unique_ptr<CanvasResourceProvider> CreateForCanvas( + // todo(juanmihd@) Check whether SkFilterQuality is needed in all of this, or + // just call setFilterQuality explicitly + static std::unique_ptr<CanvasResourceProvider> CreateBitmapProvider( + const IntSize&, + SkFilterQuality, + const CanvasColorParams&); + + // Specifies whether the provider should rasterize paint commands on the CPU + // or GPU. This is used to support software raster with GPU compositing + enum class RasterMode { + kGPU, + kCPU, + }; + + static std::unique_ptr<CanvasResourceProvider> CreateSharedImageProvider( const IntSize&, - ResourceUsage, base::WeakPtr<WebGraphicsContext3DProviderWrapper>, - unsigned msaa_sample_count, SkFilterQuality, const CanvasColorParams&, - uint8_t presentation_mode, - base::WeakPtr<CanvasResourceDispatcher>, - bool is_origin_top_left = true); + bool is_origin_top_left, + RasterMode raster_mode, + uint32_t shared_image_usage_flags); + // TODO(juanmihd): Clean up creation methods/usage. See crbug.com/1035589. static std::unique_ptr<CanvasResourceProvider> Create( const IntSize&, ResourceUsage, @@ -119,16 +136,18 @@ class PLATFORM_EXPORT CanvasResourceProvider // Use Snapshot() for capturing a frame that is intended to be displayed via // the compositor. Cases that are destined to be transferred via a // TransferableResource should call ProduceCanvasResource() instead. + // The ImageOrientationEnum conveys the desired orientation of the image, and + // should be derived from the source of the bitmap data. virtual scoped_refptr<CanvasResource> ProduceCanvasResource() = 0; - virtual scoped_refptr<StaticBitmapImage> Snapshot() = 0; + virtual scoped_refptr<StaticBitmapImage> Snapshot( + const ImageOrientation& = kDefaultImageOrientation) = 0; // WebGraphicsContext3DProvider::DestructionObserver implementation. void OnContextDestroyed() override; cc::PaintCanvas* Canvas(); - void InitializePaintCanvas(); void ReleaseLockedImages(); - void FlushSkia() const; + sk_sp<cc::PaintRecord> FlushCanvas(); const CanvasColorParams& ColorParams() const { return color_params_; } void SetFilterQuality(SkFilterQuality quality) { filter_quality_ = quality; } const IntSize& Size() const { return size_; } @@ -172,9 +191,11 @@ class PLATFORM_EXPORT CanvasResourceProvider size_t row_bytes, int x, int y); - virtual GLuint GetBackingTextureHandleForOverwrite() { + + virtual gpu::Mailbox GetBackingMailboxForOverwrite( + MailboxSyncMode sync_mode) { NOTREACHED(); - return 0; + return gpu::Mailbox(); } virtual GLenum GetBackingTextureTarget() const { return GL_TEXTURE_2D; } virtual void* GetPixelBufferAddressForOverwrite() { @@ -196,7 +217,19 @@ class PLATFORM_EXPORT CanvasResourceProvider return canvas_resources_.size(); } + void SkipQueuedDrawCommands(); + void SetRestoreClipStackCallback(RestoreMatrixClipStackCb); + bool needs_flush() const { return needs_flush_; } + void RestoreBackBuffer(const cc::PaintImage&); + + ResourceProviderType GetType() const { return type_; } + bool HasRecordedDrawOps() const; + + void OnDestroyResource(); + protected: + class CanvasImageProvider; + gpu::gles2::GLES2Interface* ContextGL() const; gpu::raster::RasterInterface* RasterInterface() const; GrContext* GetGrContext() const; @@ -209,7 +242,7 @@ class PLATFORM_EXPORT CanvasResourceProvider : kBottomLeft_GrSurfaceOrigin; } SkFilterQuality FilterQuality() const { return filter_quality_; } - scoped_refptr<StaticBitmapImage> SnapshotInternal(); + scoped_refptr<StaticBitmapImage> SnapshotInternal(const ImageOrientation&); scoped_refptr<CanvasResource> GetImportedResource() const; CanvasResourceProvider(const ResourceProviderType&, @@ -227,13 +260,14 @@ class PLATFORM_EXPORT CanvasResourceProvider // decodes/uploads in the cache is invalidated only when the canvas contents // change. cc::PaintImage MakeImageSnapshot(); + virtual void RasterRecord(sk_sp<cc::PaintRecord>); + CanvasImageProvider* GetOrCreateCanvasImageProvider(); ResourceProviderType type_; mutable sk_sp<SkSurface> surface_; // mutable for lazy init + SkSurface::ContentChangeMode mode_ = SkSurface::kRetain_ContentChangeMode; private: - class CanvasImageProvider; - virtual sk_sp<SkSurface> CreateSkSurface() const = 0; virtual scoped_refptr<CanvasResource> CreateResource(); bool use_hardware_decode_cache() const { @@ -245,6 +279,8 @@ class PLATFORM_EXPORT CanvasResourceProvider cc::ImageDecodeCache* ImageDecodeCacheRGBA8(); cc::ImageDecodeCache* ImageDecodeCacheF16(); + void EnsureSkiaCanvas(); + void SetNeedsFlush() { needs_flush_ = true; } base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_; base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher_; @@ -254,7 +290,10 @@ class PLATFORM_EXPORT CanvasResourceProvider const CanvasColorParams color_params_; const bool is_origin_top_left_; std::unique_ptr<CanvasImageProvider> canvas_image_provider_; - std::unique_ptr<cc::SkiaPaintCanvas> canvas_; + std::unique_ptr<cc::SkiaPaintCanvas> skia_canvas_; + std::unique_ptr<PaintRecorder> recorder_; + + bool needs_flush_ = false; const cc::PaintImage::Id snapshot_paint_image_id_; cc::PaintImage::ContentId snapshot_paint_image_content_id_ = @@ -267,6 +306,17 @@ class PLATFORM_EXPORT CanvasResourceProvider bool resource_recycling_enabled_ = true; bool is_single_buffered_ = false; + // The maximum number of in-flight resources waiting to be used for recycling. + static constexpr int kMaxRecycledCanvasResources = 2; + // The maximum number of draw ops executed on the canvas, after which the + // underlying GrContext is flushed. + static constexpr int kMaxDrawsBeforeContextFlush = 50; + + size_t num_inflight_resources_ = 0; + size_t max_inflight_resources_ = 0; + + RestoreMatrixClipStackCb restore_clip_stack_callback_; + base::WeakPtrFactory<CanvasResourceProvider> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(CanvasResourceProvider); diff --git a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc index 85b64c72473..2924293e7bd 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc @@ -74,9 +74,9 @@ class CanvasResourceProviderTest : public Test { TEST_F(CanvasResourceProviderTest, CanvasResourceProviderAcceleratedOverlay) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); auto provider = CanvasResourceProvider::Create( kSize, @@ -92,7 +92,9 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderAcceleratedOverlay) { EXPECT_TRUE(provider->SupportsDirectCompositing()); EXPECT_TRUE(provider->SupportsSingleBuffering()); EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace()); - EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat()); + // As it is an CanvasResourceProviderSharedImage and an accelerated canvas, it + // will internally force it to kRGBA8 + EXPECT_EQ(provider->ColorParams().PixelFormat(), CanvasPixelFormat::kRGBA8); EXPECT_EQ(provider->ColorParams().GetOpacityMode(), kColorParams.GetOpacityMode()); @@ -103,16 +105,14 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderAcceleratedOverlay) { TEST_F(CanvasResourceProviderTest, CanvasResourceProviderTexture) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); - auto provider = CanvasResourceProvider::Create( - kSize, CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage, - context_provider_wrapper_, 0 /* msaa_sample_count */, - kLow_SkFilterQuality, kColorParams, - CanvasResourceProvider::kDefaultPresentationMode, - nullptr /* resource_dispatcher */, true /* is_origin_top_left */); + auto provider = CanvasResourceProvider::CreateSharedImageProvider( + kSize, context_provider_wrapper_, kLow_SkFilterQuality, kColorParams, + true /*is_origin_top_left*/, CanvasResourceProvider::RasterMode::kGPU, + 0u /*shared_image_usage_flags*/); EXPECT_EQ(provider->Size(), kSize); EXPECT_TRUE(provider->IsValid()); @@ -120,7 +120,9 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderTexture) { EXPECT_TRUE(provider->SupportsDirectCompositing()); EXPECT_FALSE(provider->SupportsSingleBuffering()); EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace()); - EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat()); + // As it is an CanvasResourceProviderSharedImage and an accelerated canvas, it + // will internally force it to kRGBA8 + EXPECT_EQ(provider->ColorParams().PixelFormat(), CanvasPixelFormat::kRGBA8); EXPECT_EQ(provider->ColorParams().GetOpacityMode(), kColorParams.GetOpacityMode()); @@ -129,14 +131,13 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderTexture) { TEST_F(CanvasResourceProviderTest, CanvasResourceProviderUnacceleratedOverlay) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); auto provider = CanvasResourceProvider::Create( kSize, - CanvasResourceProvider::ResourceUsage:: - kSoftwareCompositedDirect2DResourceUsage, + CanvasResourceProvider::ResourceUsage::kSoftwareCompositedResourceUsage, context_provider_wrapper_, 0 /* msaa_sample_count */, kLow_SkFilterQuality, kColorParams, CanvasResourceProvider::kAllowImageChromiumPresentationMode, @@ -161,9 +162,9 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderUnacceleratedOverlay) { TEST_F(CanvasResourceProviderTest, CanvasResourceProviderSharedImageResourceRecycling) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); auto provider = CanvasResourceProvider::Create( kSize, @@ -180,7 +181,9 @@ TEST_F(CanvasResourceProviderTest, EXPECT_FALSE(provider->IsSingleBuffered()); EXPECT_FALSE(provider->SupportsSingleBuffering()); EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace()); - EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat()); + // As it is an CanvasResourceProviderSharedImage and an accelerated canvas, it + // will internally force it to kRGBA8 + EXPECT_EQ(provider->ColorParams().PixelFormat(), CanvasPixelFormat::kRGBA8); EXPECT_EQ(provider->ColorParams().GetOpacityMode(), kColorParams.GetOpacityMode()); @@ -215,9 +218,9 @@ TEST_F(CanvasResourceProviderTest, TEST_F(CanvasResourceProviderTest, CanvasResourceProviderSharedImageStaticBitmapImage) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); auto provider = CanvasResourceProvider::Create( kSize, @@ -233,21 +236,25 @@ TEST_F(CanvasResourceProviderTest, auto image = provider->Snapshot(); ASSERT_TRUE(image); auto new_image = provider->Snapshot(); - EXPECT_EQ(image->GetMailbox(), new_image->GetMailbox()); + EXPECT_EQ(image->GetMailboxHolder().mailbox, + new_image->GetMailboxHolder().mailbox); EXPECT_EQ(provider->ProduceCanvasResource()->GetOrCreateGpuMailbox( kOrderingBarrier), - image->GetMailbox()); + image->GetMailboxHolder().mailbox); // Resource updated after draw. provider->Canvas()->clear(SK_ColorWHITE); + provider->FlushCanvas(); new_image = provider->Snapshot(); - EXPECT_NE(new_image->GetMailbox(), image->GetMailbox()); + EXPECT_NE(new_image->GetMailboxHolder().mailbox, + image->GetMailboxHolder().mailbox); // Resource recycled. - auto original_mailbox = image->GetMailbox(); + auto original_mailbox = image->GetMailboxHolder().mailbox; image.reset(); provider->Canvas()->clear(SK_ColorBLACK); - EXPECT_EQ(original_mailbox, provider->Snapshot()->GetMailbox()); + provider->FlushCanvas(); + EXPECT_EQ(original_mailbox, provider->Snapshot()->GetMailboxHolder().mailbox); } TEST_F(CanvasResourceProviderTest, @@ -259,9 +266,9 @@ TEST_F(CanvasResourceProviderTest, fake_context->SetCapabilities(caps); const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); auto provider = CanvasResourceProvider::Create( kSize, @@ -282,16 +289,12 @@ TEST_F(CanvasResourceProviderTest, TEST_F(CanvasResourceProviderTest, CanvasResourceProviderBitmap) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); - auto provider = CanvasResourceProvider::Create( - kSize, CanvasResourceProvider::ResourceUsage::kSoftwareResourceUsage, - context_provider_wrapper_, 0 /* msaa_sample_count */, - kLow_SkFilterQuality, kColorParams, - CanvasResourceProvider::kAllowImageChromiumPresentationMode, - nullptr /* resource_dispatcher */, true /* is_origin_top_left */); + auto provider = CanvasResourceProvider::CreateBitmapProvider( + kSize, kLow_SkFilterQuality, kColorParams); EXPECT_EQ(provider->Size(), kSize); EXPECT_TRUE(provider->IsValid()); @@ -308,9 +311,9 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderBitmap) { TEST_F(CanvasResourceProviderTest, CanvasResourceProviderSharedBitmap) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); MockCanvasResourceDispatcherClient client; CanvasResourceDispatcher resource_dispatcher( @@ -343,9 +346,9 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderSharedBitmap) { TEST_F(CanvasResourceProviderTest, CanvasResourceProviderDirect2DGpuMemoryBuffer) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); auto provider = CanvasResourceProvider::Create( kSize, @@ -361,7 +364,9 @@ TEST_F(CanvasResourceProviderTest, EXPECT_TRUE(provider->SupportsDirectCompositing()); EXPECT_TRUE(provider->SupportsSingleBuffering()); EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace()); - EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat()); + // As it is an CanvasResourceProviderSharedImage and an accelerated canvas, it + // will internally force it to kRGBA8 + EXPECT_EQ(provider->ColorParams().PixelFormat(), CanvasPixelFormat::kRGBA8); EXPECT_EQ(provider->ColorParams().GetOpacityMode(), kColorParams.GetOpacityMode()); @@ -373,9 +378,9 @@ TEST_F(CanvasResourceProviderTest, TEST_F(CanvasResourceProviderTest, CanvasResourceProviderDirect3DGpuMemoryBuffer) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); auto provider = CanvasResourceProvider::Create( kSize, @@ -404,7 +409,7 @@ TEST_F(CanvasResourceProviderTest, ExternalCanvasResource::Create( mailbox, kSize, GL_TEXTURE_2D, kColorParams, SharedGpuContext::ContextProviderWrapper(), provider->CreateWeakPtr(), - kMedium_SkFilterQuality, /*is_origin_top_left=*/true); + kMedium_SkFilterQuality, true /*is_origin_top_left*/); // NewOrRecycledResource() would return nullptr before an ImportResource(). EXPECT_TRUE(provider->ImportResource(resource)); @@ -417,9 +422,9 @@ TEST_F(CanvasResourceProviderTest, // https://crbug.com/985366 TEST_F(CanvasResourceProviderTest, CanvasResourceProviderDirect3DTexture) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); auto provider = CanvasResourceProvider::Create( kSize, @@ -435,7 +440,9 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderDirect3DTexture) { EXPECT_TRUE(provider->SupportsDirectCompositing()); EXPECT_FALSE(provider->SupportsSingleBuffering()); EXPECT_EQ(provider->ColorParams().ColorSpace(), kColorParams.ColorSpace()); - EXPECT_EQ(provider->ColorParams().PixelFormat(), kColorParams.PixelFormat()); + // As it is an CanvasResourceProviderSharedImage and an accelerated canvas, it + // will internally force it to kRGBA8 + EXPECT_EQ(provider->ColorParams().PixelFormat(), CanvasPixelFormat::kRGBA8); EXPECT_EQ(provider->ColorParams().GetOpacityMode(), kColorParams.GetOpacityMode()); @@ -453,10 +460,56 @@ TEST_F(CanvasResourceProviderTest, CanvasResourceProviderDirect3DTexture) { callback->Run(gpu::SyncToken(), true /* is_lost */); } +TEST_F(CanvasResourceProviderTest, DimensionsExceedMaxTextureSize_Bitmap) { + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); + + auto provider = CanvasResourceProvider::CreateBitmapProvider( + IntSize(kMaxTextureSize - 1, kMaxTextureSize), kLow_SkFilterQuality, + kColorParams); + EXPECT_FALSE(provider->SupportsDirectCompositing()); + provider = CanvasResourceProvider::CreateBitmapProvider( + IntSize(kMaxTextureSize, kMaxTextureSize), kLow_SkFilterQuality, + kColorParams); + EXPECT_FALSE(provider->SupportsDirectCompositing()); + provider = CanvasResourceProvider::CreateBitmapProvider( + IntSize(kMaxTextureSize + 1, kMaxTextureSize), kLow_SkFilterQuality, + kColorParams); + EXPECT_FALSE(provider->SupportsDirectCompositing()); +} + +TEST_F(CanvasResourceProviderTest, DimensionsExceedMaxTextureSize_SharedImage) { + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); + + auto provider = CanvasResourceProvider::CreateSharedImageProvider( + IntSize(kMaxTextureSize - 1, kMaxTextureSize), context_provider_wrapper_, + kLow_SkFilterQuality, kColorParams, true /*is_origin_top_left*/, + CanvasResourceProvider::RasterMode::kGPU, + 0u /*shared_image_usage_flags*/); + EXPECT_TRUE(provider->SupportsDirectCompositing()); + provider = CanvasResourceProvider::CreateSharedImageProvider( + IntSize(kMaxTextureSize, kMaxTextureSize), context_provider_wrapper_, + kLow_SkFilterQuality, kColorParams, true /*is_origin_top_left*/, + CanvasResourceProvider::RasterMode::kGPU, + 0u /*shared_image_usage_flags*/); + EXPECT_TRUE(provider->SupportsDirectCompositing()); + provider = CanvasResourceProvider::CreateSharedImageProvider( + IntSize(kMaxTextureSize + 1, kMaxTextureSize), context_provider_wrapper_, + kLow_SkFilterQuality, kColorParams, true /*is_origin_top_left*/, + CanvasResourceProvider::RasterMode::kGPU, + 0u /*shared_image_usage_flags*/); + // The CanvasResourceProvider for SharedImage should not be created or valid + // if the texture size is greater than the maximum value + EXPECT_TRUE(!provider || !provider->IsValid()); +} + TEST_F(CanvasResourceProviderTest, DimensionsExceedMaxTextureSize) { - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); for (int i = 0; i < static_cast<int>(CanvasResourceProvider::ResourceUsage::kMaxValue); @@ -464,18 +517,20 @@ TEST_F(CanvasResourceProviderTest, DimensionsExceedMaxTextureSize) { SCOPED_TRACE(i); auto usage = static_cast<CanvasResourceProvider::ResourceUsage>(i); bool should_support_compositing = false; + std::unique_ptr<CanvasResourceProvider> provider; switch (usage) { + // Skipping ResourceUsages that will be removed after this refactor + // bug(1035589) case CanvasResourceProvider::ResourceUsage::kSoftwareResourceUsage: - should_support_compositing = false; - break; + continue; + case CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage: + continue; case CanvasResourceProvider::ResourceUsage:: kSoftwareCompositedResourceUsage: FALLTHROUGH; case CanvasResourceProvider::ResourceUsage:: kSoftwareCompositedDirect2DResourceUsage: FALLTHROUGH; - case CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage: - FALLTHROUGH; case CanvasResourceProvider::ResourceUsage:: kAcceleratedCompositedResourceUsage: FALLTHROUGH; @@ -488,39 +543,42 @@ TEST_F(CanvasResourceProviderTest, DimensionsExceedMaxTextureSize) { break; } - auto provider = CanvasResourceProvider::Create( - IntSize(kMaxTextureSize - 1, kMaxTextureSize), usage, - context_provider_wrapper_, 0 /* msaa_sample_count */, - kLow_SkFilterQuality, kColorParams, - CanvasResourceProvider::kAllowImageChromiumPresentationMode, - nullptr /* resource_dispatcher */, true /* is_origin_top_left */); + provider = CanvasResourceProvider::Create( + IntSize(kMaxTextureSize - 1, kMaxTextureSize), usage, + context_provider_wrapper_, 0 /* msaa_sample_count */, + kLow_SkFilterQuality, kColorParams, + CanvasResourceProvider::kAllowImageChromiumPresentationMode, + nullptr /* resource_dispatcher */, true /* is_origin_top_left */); + EXPECT_EQ(provider->SupportsDirectCompositing(), should_support_compositing); - provider = CanvasResourceProvider::Create( - IntSize(kMaxTextureSize, kMaxTextureSize), usage, - context_provider_wrapper_, 0 /* msaa_sample_count */, - kLow_SkFilterQuality, kColorParams, - CanvasResourceProvider::kAllowImageChromiumPresentationMode, - nullptr /* resource_dispatcher */, true /* is_origin_top_left */); + provider = CanvasResourceProvider::Create( + IntSize(kMaxTextureSize, kMaxTextureSize), usage, + context_provider_wrapper_, 0 /* msaa_sample_count */, + kLow_SkFilterQuality, kColorParams, + CanvasResourceProvider::kAllowImageChromiumPresentationMode, + nullptr /* resource_dispatcher */, true /* is_origin_top_left */); + EXPECT_EQ(provider->SupportsDirectCompositing(), should_support_compositing); - provider = CanvasResourceProvider::Create( - IntSize(kMaxTextureSize + 1, kMaxTextureSize), usage, - context_provider_wrapper_, 0 /* msaa_sample_count */, - kLow_SkFilterQuality, kColorParams, - CanvasResourceProvider::kAllowImageChromiumPresentationMode, - nullptr /* resource_dispatcher */, true /* is_origin_top_left */); + provider = CanvasResourceProvider::Create( + IntSize(kMaxTextureSize + 1, kMaxTextureSize), usage, + context_provider_wrapper_, 0 /* msaa_sample_count */, + kLow_SkFilterQuality, kColorParams, + CanvasResourceProvider::kAllowImageChromiumPresentationMode, + nullptr /* resource_dispatcher */, true /* is_origin_top_left */); + EXPECT_FALSE(provider->SupportsDirectCompositing()); } } TEST_F(CanvasResourceProviderTest, CanvasResourceProviderDirect2DSwapChain) { const IntSize kSize(10, 10); - const CanvasColorParams kColorParams(CanvasColorSpace::kSRGB, - CanvasPixelFormat::kRGBA8, kNonOpaque, - CanvasForceRGBA::kNotForced); + const CanvasColorParams kColorParams( + CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(), + kNonOpaque); auto provider = CanvasResourceProvider::Create( kSize, diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.cc b/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.cc index 680b810bf83..20929011633 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.cc @@ -194,7 +194,7 @@ void ColorCorrectionTestUtils::CompareColorCorrectedPixels( bool ColorCorrectionTestUtils::ConvertPixelsToColorSpaceAndPixelFormatForTest( void* src_data, - int num_elements, + size_t num_elements, CanvasColorSpace src_color_space, ImageDataStorageFormat src_storage_format, CanvasColorSpace dst_color_space, @@ -221,14 +221,13 @@ bool ColorCorrectionTestUtils::ConvertPixelsToColorSpaceAndPixelFormatForTest( (src_storage_format == kUint8ClampedArrayStorageFormat) ? CanvasPixelFormat::kRGBA8 : CanvasPixelFormat::kF16, - kNonOpaque, CanvasForceRGBA::kNotForced) + kNonOpaque) .GetSkColorSpaceForSkSurfaces(); if (!src_sk_color_space.get()) src_sk_color_space = SkColorSpace::MakeSRGB(); sk_sp<SkColorSpace> dst_sk_color_space = - CanvasColorParams(dst_color_space, dst_canvas_pixel_format, kNonOpaque, - CanvasForceRGBA::kNotForced) + CanvasColorParams(dst_color_space, dst_canvas_pixel_format, kNonOpaque) .GetSkColorSpaceForSkSurfaces(); if (!dst_sk_color_space.get()) dst_sk_color_space = SkColorSpace::MakeSRGB(); diff --git a/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.h b/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.h index 30c6c677174..3323b5d2bf9 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.h +++ b/chromium/third_party/blink/renderer/platform/graphics/color_correction_test_utils.h @@ -65,7 +65,7 @@ class ColorCorrectionTestUtils { static bool ConvertPixelsToColorSpaceAndPixelFormatForTest( void* src_data, - int num_elements, + size_t num_elements, CanvasColorSpace src_color_space, ImageDataStorageFormat src_storage_format, CanvasColorSpace dst_color_space, diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/README.md b/chromium/third_party/blink/renderer/platform/graphics/compositing/README.md index f8334d739ce..d40b6ed2b7a 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/README.md +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/README.md @@ -16,7 +16,7 @@ Inputs: `PaintArtifact` Outputs: List of `cc::Layer` objects and `cc::PropertyTree`'s. The algorithm walks through the list of `PaintChunks` in the `PaintArtifact`, -allocating new `c::Layers` if the `PaintChunk` cannot merge into an existing +allocating new `cc::Layers` if the `PaintChunk` cannot merge into an existing `cc::Layer`. The reasons why it would not be able to do so are: 1. The `PaintChunk` requires a foreign layer (see below) diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc index 70cd474d009..9f8c025c661 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper_test.cc @@ -21,7 +21,7 @@ class ChunkToLayerMapperTest : public testing::Test { DEFINE_STATIC_LOCAL( base::Optional<PaintChunk::Id>, id, (PaintChunk::Id(fake_client, DisplayItem::kDrawingFirst))); - PaintChunk chunk(0, 0, *id, state); + PaintChunk chunk(0, 1, *id, state); return chunk; } diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc index cdaf3e8115e..1e7e0c1f113 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc @@ -24,7 +24,7 @@ namespace blink { ContentLayerClientImpl::ContentLayerClientImpl() : cc_picture_layer_(cc::PictureLayer::Create(this)), - raster_invalidator_( + raster_invalidation_function_( base::BindRepeating(&ContentLayerClientImpl::InvalidateRect, base::Unretained(this))), layer_state_(PropertyTreeState::Uninitialized()) {} @@ -42,9 +42,12 @@ void ContentLayerClientImpl::AppendAdditionalInfoAsJSON( json.SetValue("paintChunkContents", paint_chunk_debug_data_->Clone()); #endif - if ((flags & kLayerTreeIncludesPaintInvalidations) && - raster_invalidator_.GetTracking()) - raster_invalidator_.GetTracking()->AsJSON(&json); + if ((flags & (kLayerTreeIncludesInvalidations | + kLayerTreeIncludesDetailedInvalidations)) && + raster_invalidator_.GetTracking()) { + raster_invalidator_.GetTracking()->AsJSON( + &json, flags & kLayerTreeIncludesDetailedInvalidations); + } #if DCHECK_IS_ON() if (flags & kLayerTreeIncludesPaintRecords) { @@ -73,7 +76,7 @@ scoped_refptr<cc::PictureLayer> ContentLayerClientImpl::UpdateCcPictureLayer( auto json = std::make_unique<JSONObject>(); json->SetString("data", chunk.ToString()); json->SetArray("displayItems", - paint_artifact->GetDisplayItemList().SubsequenceAsJSON( + paint_artifact->GetDisplayItemList().DisplayItemsAsJSON( chunk.begin_index, chunk.end_index, DisplayItemList::kShowOnlyDisplayItemTypes)); paint_chunk_debug_data_->PushObject(std::move(json)); @@ -85,8 +88,8 @@ scoped_refptr<cc::PictureLayer> ContentLayerClientImpl::UpdateCcPictureLayer( if (layer_state != layer_state_) cc_picture_layer_->SetSubtreePropertyChanged(); - raster_invalidator_.Generate(paint_artifact, paint_chunks, layer_bounds, - layer_state); + raster_invalidator_.Generate(raster_invalidation_function_, paint_artifact, + paint_chunks, layer_bounds, layer_state); layer_state_ = layer_state; // Note: cc::Layer API assumes the layer bounds start at (0, 0), but the @@ -97,7 +100,6 @@ scoped_refptr<cc::PictureLayer> ContentLayerClientImpl::UpdateCcPictureLayer( cc_picture_layer_->SetOffsetToTransformParent( layer_bounds.OffsetFromOrigin()); cc_picture_layer_->SetBounds(layer_bounds.size()); - cc_picture_layer_->SetIsDrawable(true); cc_picture_layer_->SetHitTestable(true); base::Optional<RasterUnderInvalidationCheckingParams> params; @@ -111,14 +113,20 @@ scoped_refptr<cc::PictureLayer> ContentLayerClientImpl::UpdateCcPictureLayer( display_item_list, cc::DisplayItemList::kTopLevelDisplayItemList, base::OptionalOrNullptr(params)); - cc_picture_layer_->SetSafeOpaqueBackgroundColor( - paint_chunks[0].safe_opaque_background_color); + cc_picture_layer_->SetIsDrawable( + (!layer_bounds.IsEmpty() && cc_display_item_list_->TotalOpCount()) || + // Backdrop filters require the layer to be drawable even if the layer + // draws nothing. + !layer_state.Effect().BackdropFilter().IsEmpty()); + + auto safe_opaque_background_color = + paint_artifact->SafeOpaqueBackgroundColor(paint_chunks); + cc_picture_layer_->SetSafeOpaqueBackgroundColor(safe_opaque_background_color); // TODO(masonfreed): We don't need to set the background color here; only the // safe opaque background color matters. But making that change would require // rebaselining 787 tests to remove the "background_color" property from the // layer dumps. - cc_picture_layer_->SetBackgroundColor( - paint_chunks[0].safe_opaque_background_color); + cc_picture_layer_->SetBackgroundColor(safe_opaque_background_color); return cc_picture_layer_; } diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h b/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h index 5ce14faec81..aeb201b488f 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h @@ -74,6 +74,8 @@ class PLATFORM_EXPORT ContentLayerClientImpl : public cc::ContentLayerClient, scoped_refptr<cc::PictureLayer> cc_picture_layer_; scoped_refptr<cc::DisplayItemList> cc_display_item_list_; RasterInvalidator raster_invalidator_; + RasterInvalidator::RasterInvalidationFunction raster_invalidation_function_; + PropertyTreeState layer_state_; String debug_name_; diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/layers_as_json.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/layers_as_json.cc index 318662e15cc..3502e78074f 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/layers_as_json.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/layers_as_json.cc @@ -25,10 +25,8 @@ String PointerAsString(const void* ptr) { } // namespace // Create a JSON version of the specified |layer|. -std::unique_ptr<JSONObject> CCLayerAsJSON( - const cc::Layer* layer, - LayerTreeFlags flags, - const FloatPoint& offset_from_transform_node) { +std::unique_ptr<JSONObject> CCLayerAsJSON(const cc::Layer* layer, + LayerTreeFlags flags) { auto json = std::make_unique<JSONObject>(); if (flags & kLayerTreeIncludesDebugInfo) { @@ -38,8 +36,11 @@ std::unique_ptr<JSONObject> CCLayerAsJSON( json->SetString("name", String(layer->DebugName().c_str())); - if (offset_from_transform_node != FloatPoint()) - json->SetArray("position", PointAsJSONArray(offset_from_transform_node)); + if (layer->offset_to_transform_parent() != gfx::Vector2dF()) { + json->SetArray( + "position", + PointAsJSONArray(FloatPoint(layer->offset_to_transform_parent()))); + } // This is testing against gfx::Size(), *not* whether the size is empty. if (layer->bounds() != gfx::Size()) @@ -127,13 +128,15 @@ int LayersAsJSON::AddTransformJSON( } void LayersAsJSON::AddLayer(const cc::Layer& layer, - const FloatPoint& offset, const TransformPaintPropertyNode& transform, const LayerAsJSONClient* json_client) { - if (!(flags_ & kLayerTreeIncludesAllLayers) && !layer.DrawsContent()) + if (!(flags_ & kLayerTreeIncludesAllLayers) && !layer.DrawsContent() && + (layer.DebugName() == "LayoutView #document" || + layer.DebugName() == "Inner Viewport Scroll Layer" || + layer.DebugName() == "Scrolling Contents Layer")) return; - auto layer_json = CCLayerAsJSON(&layer, flags_, offset); + auto layer_json = CCLayerAsJSON(&layer, flags_); if (json_client) { json_client->AppendAdditionalInfoAsJSON(flags_, layer, *(layer_json.get())); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/layers_as_json.h b/chromium/third_party/blink/renderer/platform/graphics/compositing/layers_as_json.h index 0008f97f94a..4be8eed283d 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/layers_as_json.h +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/layers_as_json.h @@ -16,7 +16,6 @@ class Layer; namespace blink { -class FloatPoint; class JSONArray; class JSONObject; class TransformPaintPropertyNode; @@ -27,9 +26,10 @@ enum { kLayerTreeNormal = 0, // Dump extra debugging info like layer addresses. kLayerTreeIncludesDebugInfo = 1 << 0, - kLayerTreeIncludesPaintInvalidations = 1 << 1, - kLayerTreeIncludesPaintingPhases = 1 << 2, - kLayerTreeIncludesAllLayers = 1 << 3, + kLayerTreeIncludesInvalidations = 1 << 1, + kLayerTreeIncludesDetailedInvalidations = 1 << 2, + kLayerTreeIncludesPaintingPhases = 1 << 3, + kLayerTreeIncludesAllLayers = 1 << 4, kLayerTreeIncludesCompositingReasons = 1 << 5, kLayerTreeIncludesPaintRecords = 1 << 6, // Outputs all layers as a layer tree. The default is output children @@ -50,7 +50,6 @@ class PLATFORM_EXPORT LayersAsJSON { LayersAsJSON(LayerTreeFlags); void AddLayer(const cc::Layer& layer, - const FloatPoint& offset, const TransformPaintPropertyNode& transform, const LayerAsJSONClient* json_client); @@ -69,8 +68,7 @@ class PLATFORM_EXPORT LayersAsJSON { PLATFORM_EXPORT std::unique_ptr<JSONObject> CCLayerAsJSON( const cc::Layer* layer, - LayerTreeFlags flags, - const FloatPoint& position); + LayerTreeFlags flags); } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc index 76d426253a5..015e3f9418f 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc @@ -7,24 +7,26 @@ #include <memory> #include <utility> -#include "cc/layers/layer.h" +#include "cc/layers/scrollbar_layer_base.h" #include "cc/paint/display_item_list.h" #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/mutator_host.h" #include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/renderer/platform/geometry/geometry_as_json.h" #include "third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h" #include "third_party/blink/renderer/platform/graphics/graphics_context.h" +#include "third_party/blink/renderer/platform/graphics/graphics_layer.h" #include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h" #include "third_party/blink/renderer/platform/graphics/paint/display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" +#include "third_party/blink/renderer/platform/graphics/paint/graphics_layer_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h" #include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h" #include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h" -#include "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h" #include "third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h" @@ -60,6 +62,14 @@ void PaintArtifactCompositor::WillBeRemovedFromFrame() { root_layer_->RemoveAllChildren(); } +std::unique_ptr<JSONArray> PaintArtifactCompositor::GetPendingLayersAsJSON( + const PaintArtifact* paint_artifact) const { + std::unique_ptr<JSONArray> result = std::make_unique<JSONArray>(); + for (const PendingLayer& pending_layer : pending_layers_) + result->PushObject(pending_layer.ToJSON(paint_artifact)); + return result; +} + // Get a JSON representation of what layers exist for this PAC. Note that // |paint_artifact| is only needed for pre-CAP mode. std::unique_ptr<JSONObject> PaintArtifactCompositor::GetLayersAsJSON( @@ -67,6 +77,13 @@ std::unique_ptr<JSONObject> PaintArtifactCompositor::GetLayersAsJSON( const PaintArtifact* paint_artifact) const { DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled() || paint_artifact); + + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && + !tracks_raster_invalidations_) { + flags &= ~(kLayerTreeIncludesInvalidations | + kLayerTreeIncludesDetailedInvalidations); + } + LayersAsJSON layers_as_json(flags); if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { for (const auto& layer : root_layer_->children()) { @@ -90,28 +107,47 @@ std::unique_ptr<JSONObject> PaintArtifactCompositor::GetLayersAsJSON( } } DCHECK(transform); - layers_as_json.AddLayer(*layer, - FloatPoint(layer->offset_to_transform_parent()), - *transform, json_client); + layers_as_json.AddLayer(*layer, *transform, json_client); } } else { for (const auto& paint_chunk : paint_artifact->PaintChunks()) { const auto& display_item = paint_artifact->GetDisplayItemList()[paint_chunk.begin_index]; - DCHECK(display_item.IsForeignLayer()); - const auto& foreign_layer_display_item = - static_cast<const ForeignLayerDisplayItem&>(display_item); - cc::Layer* layer = foreign_layer_display_item.GetLayer(); - layers_as_json.AddLayer( - *layer, foreign_layer_display_item.Offset(), - paint_chunk.properties.Transform(), - foreign_layer_display_item.GetLayerAsJSONClient()); + cc::Layer* layer = nullptr; + const LayerAsJSONClient* json_client = nullptr; + if (display_item.IsGraphicsLayerWrapper()) { + const GraphicsLayerDisplayItem& graphics_layer_display_item = + static_cast<const GraphicsLayerDisplayItem&>(display_item); + layer = graphics_layer_display_item.GetGraphicsLayer().CcLayer(); + json_client = &graphics_layer_display_item.GetGraphicsLayer(); + } else { + DCHECK(display_item.IsForeignLayer()); + const auto& foreign_layer_display_item = + static_cast<const ForeignLayerDisplayItem&>(display_item); + layer = foreign_layer_display_item.GetLayer(); + json_client = foreign_layer_display_item.GetLayerAsJSONClient(); + } + // Need to retrieve the transform from |pending_layers_| so that + // any decomposition is not double-reported via |layer|'s + // offset_from_transform_parent and |paint_chunk|'s transform inside + // AddLayer. + const TransformPaintPropertyNode* transform = nullptr; + for (const auto& pending_layer : pending_layers_) { + if (pending_layer.property_tree_state.Transform().CcNodeId( + layer->property_tree_sequence_number()) == + layer->transform_tree_index()) { + transform = &pending_layer.property_tree_state.Transform(); + break; + } + } + DCHECK(transform); + layers_as_json.AddLayer(*layer, *transform, json_client); } } return layers_as_json.Finalize(); } -static scoped_refptr<cc::Layer> ForeignLayerForPaintChunk( +static scoped_refptr<cc::Layer> CcLayerForPaintChunk( const PaintArtifact& paint_artifact, const PaintChunk& paint_chunk, const FloatPoint& pending_layer_offset) { @@ -120,33 +156,44 @@ static scoped_refptr<cc::Layer> ForeignLayerForPaintChunk( const auto& display_item = paint_artifact.GetDisplayItemList()[paint_chunk.begin_index]; - if (!display_item.IsForeignLayer()) + if (!display_item.IsForeignLayer() && + !display_item.IsGraphicsLayerWrapper()) { return nullptr; + } - // When a foreign layer's offset_to_transform_parent() changes, we don't - // call PaintArtifaceCompositor::SetNeedsUpdate() because the update won't - // change anything but cause unnecessary commit. Though - // UpdateTouchActionRects() depends on offset_to_transform_parent(), a - // foreign layer chunk doesn't have hit_test_data. + // UpdateTouchActionRects() depends on the layer's offset, but when the + // layer's offset changes, we do not call SetNeedsUpdate() (this is an + // optimization because the update would only cause an extra commit). This is + // only OK if the [Foreign|Graphics]Layer doesn't have hit test data. DCHECK(!paint_chunk.hit_test_data); - const auto& foreign_layer_display_item = - static_cast<const ForeignLayerDisplayItem&>(display_item); - auto* layer = foreign_layer_display_item.GetLayer(); - layer->SetOffsetToTransformParent(gfx::Vector2dF( - foreign_layer_display_item.Offset() + pending_layer_offset)); + cc::Layer* layer = nullptr; + FloatPoint layer_offset; + if (display_item.IsGraphicsLayerWrapper()) { + const auto& graphics_layer_display_item = + static_cast<const GraphicsLayerDisplayItem&>(display_item); + layer = graphics_layer_display_item.GetGraphicsLayer().CcLayer(); + layer_offset = FloatPoint(graphics_layer_display_item.GetGraphicsLayer() + .GetOffsetFromTransformNode()); + } else { + const auto& foreign_layer_display_item = + static_cast<const ForeignLayerDisplayItem&>(display_item); + layer = foreign_layer_display_item.GetLayer(); + layer_offset = foreign_layer_display_item.Offset(); + } + layer->SetOffsetToTransformParent( + gfx::Vector2dF(layer_offset + pending_layer_offset)); return layer; } const TransformPaintPropertyNode& -PaintArtifactCompositor::ScrollOffsetTranslationForLayer( +PaintArtifactCompositor::NearestScrollTranslationForLayer( const PaintArtifact& paint_artifact, const PendingLayer& pending_layer) { - if (const auto* scroll_hit_test = - ScrollHitTestForLayer(paint_artifact, pending_layer)) { - if (scroll_hit_test->scroll_offset) - return *scroll_hit_test->scroll_offset; - } + if (const auto* scroll_translation = + ScrollTranslationForLayer(paint_artifact, pending_layer)) + return *scroll_translation; + const auto& transform = pending_layer.property_tree_state.Transform(); // TODO(pdr): This could be a performance issue because it crawls up the // transform tree for each pending layer. If this is on profiles, we should @@ -154,41 +201,33 @@ PaintArtifactCompositor::ScrollOffsetTranslationForLayer( return transform.NearestScrollTranslationNode(); } -const HitTestData::ScrollHitTest* -PaintArtifactCompositor::ScrollHitTestForLayer( +const TransformPaintPropertyNode* +PaintArtifactCompositor::ScrollTranslationForLayer( const PaintArtifact& paint_artifact, const PendingLayer& pending_layer) { if (pending_layer.paint_chunk_indices.size() != 1) return nullptr; - const auto& paint_chunk = - paint_artifact.PaintChunks()[pending_layer.paint_chunk_indices[0]]; - if (paint_chunk.size() != 1) - return nullptr; - - const HitTestData* hit_test_data = paint_chunk.hit_test_data.get(); - if (!hit_test_data) + const auto& paint_chunk = pending_layer.FirstPaintChunk(paint_artifact); + if (!paint_chunk.hit_test_data) return nullptr; - return base::OptionalOrNullptr(hit_test_data->scroll_hit_test); + return paint_chunk.hit_test_data->scroll_translation; } scoped_refptr<cc::Layer> PaintArtifactCompositor::ScrollHitTestLayerForPendingLayer( const PaintArtifact& paint_artifact, const PendingLayer& pending_layer) { - const auto* scroll_hit_test = - ScrollHitTestForLayer(paint_artifact, pending_layer); - if (!scroll_hit_test) - return nullptr; - const auto* scroll_offset = scroll_hit_test->scroll_offset; - if (!scroll_offset) + const auto* scroll_translation = + ScrollTranslationForLayer(paint_artifact, pending_layer); + if (!scroll_translation) return nullptr; // We shouldn't decomposite scroll transform nodes. DCHECK_EQ(FloatPoint(), pending_layer.offset_of_decomposited_transforms); - const auto& scroll_node = *scroll_offset->ScrollNode(); + const auto& scroll_node = *scroll_translation->ScrollNode(); auto scroll_element_id = scroll_node.GetCompositorElementId(); scoped_refptr<cc::Layer> scroll_layer; @@ -209,17 +248,20 @@ PaintArtifactCompositor::ScrollHitTestLayerForPendingLayer( // match (see: crbug.com/753124). To do this, use // |scroll_hit_test->scroll_container_bounds|. auto bounds = scroll_node.ContainerRect().Size(); - // Mark the layer as scrollable. - // TODO(pdr): When CAP launches this parameter for bounds will not be - // needed. - scroll_layer->SetScrollable(static_cast<gfx::Size>(bounds)); // Set the layer's bounds equal to the container because the scroll layer // does not scroll. scroll_layer->SetBounds(static_cast<gfx::Size>(bounds)); + + if (scroll_node.NodeChanged() != PaintPropertyChangeType::kUnchanged) { + scroll_layer->SetNeedsPushProperties(); + scroll_layer->SetNeedsCommit(); + } + return scroll_layer; } -scoped_refptr<cc::Layer> PaintArtifactCompositor::ScrollbarLayerForPendingLayer( +scoped_refptr<cc::ScrollbarLayerBase> +PaintArtifactCompositor::ScrollbarLayerForPendingLayer( const PaintArtifact& paint_artifact, const PendingLayer& pending_layer) { if (pending_layer.paint_chunk_indices.size() != 1) @@ -235,29 +277,11 @@ scoped_refptr<cc::Layer> PaintArtifactCompositor::ScrollbarLayerForPendingLayer( if (!item.IsScrollbar()) return nullptr; - const auto& scrollbar_item = static_cast<const ScrollbarDisplayItem&>(item); - scoped_refptr<cc::Layer> scrollbar_layer; - for (auto& existing_layer : scrollbar_layers_) { - if (existing_layer->element_id() == scrollbar_item.ElementId()) - scrollbar_layer = existing_layer; - } - if (scrollbar_layer) { - cc::Scrollbar* scrollbar = scrollbar_item.GetScrollbar(); - if (scrollbar->NeedsRepaintPart(cc::THUMB) || - scrollbar->NeedsRepaintPart(cc::TRACK_BUTTONS_TICKMARKS)) - scrollbar_layer->SetNeedsDisplay(); - } else { - scrollbar_layer = scrollbar_item.CreateLayer(); - } - // We should never decomposite scroll translations, so we don't need to adjust // the layer's offset for decomposited transforms. DCHECK_EQ(FloatPoint(), pending_layer.offset_of_decomposited_transforms); - const IntRect& rect = scrollbar_item.GetRect(); - scrollbar_layer->SetOffsetToTransformParent( - gfx::Vector2dF(FloatPoint(rect.Location()))); - scrollbar_layer->SetBounds(gfx::Size(rect.Size())); - return scrollbar_layer; + + return static_cast<const ScrollbarDisplayItem&>(item).GetLayer(); } std::unique_ptr<ContentLayerClientImpl> @@ -281,20 +305,20 @@ PaintArtifactCompositor::CompositedLayerForPendingLayer( scoped_refptr<const PaintArtifact> paint_artifact, const PendingLayer& pending_layer, Vector<std::unique_ptr<ContentLayerClientImpl>>& new_content_layer_clients, - Vector<scoped_refptr<cc::Layer>>& new_scroll_hit_test_layers, - Vector<scoped_refptr<cc::Layer>>& new_scrollbar_layers) { + Vector<scoped_refptr<cc::Layer>>& new_scroll_hit_test_layers) { auto paint_chunks = paint_artifact->GetPaintChunkSubset(pending_layer.paint_chunk_indices); DCHECK(paint_chunks.size()); const PaintChunk& first_paint_chunk = paint_chunks[0]; - DCHECK(first_paint_chunk.size()); - // If the paint chunk is a foreign layer, just return that layer. - if (scoped_refptr<cc::Layer> foreign_layer = ForeignLayerForPaintChunk( - *paint_artifact, first_paint_chunk, - pending_layer.offset_of_decomposited_transforms)) { + scoped_refptr<cc::Layer> cc_layer; + // If the paint chunk is a foreign layer or placeholder for a GraphicsLayer, + // just return its cc::Layer. + if ((cc_layer = CcLayerForPaintChunk( + *paint_artifact, first_paint_chunk, + pending_layer.offset_of_decomposited_transforms))) { DCHECK_EQ(paint_chunks.size(), 1u); - return foreign_layer; + return cc_layer; } // If the paint chunk is a scroll hit test layer, lookup/create the layer. @@ -304,9 +328,8 @@ PaintArtifactCompositor::CompositedLayerForPendingLayer( return scroll_layer; } - if (scoped_refptr<cc::Layer> scrollbar_layer = + if (auto scrollbar_layer = ScrollbarLayerForPendingLayer(*paint_artifact, pending_layer)) { - new_scrollbar_layers.push_back(scrollbar_layer); return scrollbar_layer; } @@ -315,11 +338,9 @@ PaintArtifactCompositor::CompositedLayerForPendingLayer( ClientForPaintChunk(first_paint_chunk); IntRect cc_combined_bounds = EnclosingIntRect(pending_layer.bounds); - auto cc_layer = content_layer_client->UpdateCcPictureLayer( + cc_layer = content_layer_client->UpdateCcPictureLayer( paint_artifact, paint_chunks, cc_combined_bounds, pending_layer.property_tree_state); - if (cc_combined_bounds.IsEmpty()) - cc_layer->SetIsDrawable(false); new_content_layer_clients.push_back(std::move(content_layer_client)); @@ -345,8 +366,7 @@ void PaintArtifactCompositor::UpdateTouchActionRects( const auto& chunk_state = chunk.properties.GetPropertyTreeState(); for (const auto& touch_action_rect : hit_test_data->touch_action_rects) { - auto rect = - FloatClipRect(FloatRect(PixelSnappedIntRect(touch_action_rect.rect))); + auto rect = FloatClipRect(FloatRect(touch_action_rect.rect)); if (!GeometryMapper::LocalToAncestorVisualRect(chunk_state, layer_state, rect)) { continue; @@ -364,12 +384,13 @@ void PaintArtifactCompositor::UpdateNonFastScrollableRegions( cc::Layer* layer, const gfx::Vector2dF& layer_offset, const PropertyTreeState& layer_state, - const PaintChunkSubset& paint_chunks) { + const PaintChunkSubset& paint_chunks, + PropertyTreeManager* property_tree_manager) { cc::Region non_fast_scrollable_regions_in_layer_space; for (const auto& chunk : paint_chunks) { // Add any non-fast scrollable hit test data from the paint chunk. const auto* hit_test_data = chunk.hit_test_data.get(); - if (!hit_test_data || !hit_test_data->scroll_hit_test) + if (!hit_test_data || hit_test_data->scroll_hit_test_rect.IsEmpty()) continue; // Skip the scroll hit test rect if it is for scrolling this cc::Layer. @@ -377,21 +398,21 @@ void PaintArtifactCompositor::UpdateNonFastScrollableRegions( // pre-CompositeAfterPaint does not paint scroll hit test data for // composited scrollers. if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { - if (layer->scrollable()) { - const auto* scroll_offset = - hit_test_data->scroll_hit_test->scroll_offset; - if (scroll_offset) { - const auto& scroll_node = *scroll_offset->ScrollNode(); - auto scroll_element_id = scroll_node.GetCompositorElementId(); - if (layer->element_id() == scroll_element_id) - continue; - } + if (const auto* scroll_translation = hit_test_data->scroll_translation) { + const auto& scroll_node = *scroll_translation->ScrollNode(); + auto scroll_element_id = scroll_node.GetCompositorElementId(); + if (layer->element_id() == scroll_element_id) + continue; + // Ensure the cc scroll node to prepare for possible descendant nodes + // referenced by later composited layers. This can't be done by ensuring + // parent transform node in EnsureCompositorTransformNode() if the + // transform tree and the scroll tree have different topologies. + DCHECK(property_tree_manager); + property_tree_manager->EnsureCompositorScrollNode(*scroll_translation); } } - const auto& bounds = - hit_test_data->scroll_hit_test->scroll_container_bounds; - auto rect = FloatClipRect(FloatRect(bounds)); + FloatClipRect rect(FloatRect(chunk.hit_test_data->scroll_hit_test_rect)); const auto& chunk_state = chunk.properties.GetPropertyTreeState(); if (!GeometryMapper::LocalToAncestorVisualRect(chunk_state, layer_state, rect)) { @@ -399,7 +420,7 @@ void PaintArtifactCompositor::UpdateNonFastScrollableRegions( } rect.MoveBy(FloatPoint(-layer_offset.x(), -layer_offset.y())); non_fast_scrollable_regions_in_layer_space.Union( - (gfx::Rect)EnclosingIntRect(rect.Rect())); + EnclosingIntRect(rect.Rect())); } layer->SetNonFastScrollableRegion(non_fast_scrollable_regions_in_layer_space); } @@ -436,53 +457,111 @@ PaintArtifactCompositor::PendingLayer::PendingLayer( paint_chunk_indices.push_back(chunk_index); } -void PaintArtifactCompositor::PendingLayer::Merge(const PendingLayer& guest) { - DCHECK(compositing_type != kRequiresOwnLayer && - guest.compositing_type != kRequiresOwnLayer); - paint_chunk_indices.AppendVector(guest.paint_chunk_indices); - FloatClipRect guest_bounds_in_home(guest.bounds); - GeometryMapper::LocalToAncestorVisualRect( - guest.property_tree_state, property_tree_state, guest_bounds_in_home); - bounds.Unite(guest_bounds_in_home.Rect()); - // TODO(crbug.com/701991): Upgrade GeometryMapper. - // If we knew the new bounds is enclosed by the mapped opaque region of - // the guest layer, we can deduce the merged layer being opaque too, and - // update rect_known_to_be_opaque accordingly. +FloatRect PaintArtifactCompositor::PendingLayer::UniteRectsKnownToBeOpaque( + const FloatRect& a, + const FloatRect& b) { + // Check a or b by itself. + FloatRect maximum(a); + float maximum_area = a.Size().Area(); + if (b.Size().Area() > maximum_area) { + maximum = b; + maximum_area = b.Size().Area(); + } + // Check the regions that include the intersection of a and b. This can be + // done by taking the intersection and expanding it vertically and + // horizontally. These expanded intersections will both still be fully opaque. + FloatRect intersection = a; + intersection.InclusiveIntersect(b); + if (!intersection.IsZero()) { + FloatRect vert_expanded_intersection(intersection); + vert_expanded_intersection.ShiftYEdgeTo(std::min(a.Y(), b.Y())); + vert_expanded_intersection.ShiftMaxYEdgeTo(std::max(a.MaxY(), b.MaxY())); + if (vert_expanded_intersection.Size().Area() > maximum_area) { + maximum = vert_expanded_intersection; + maximum_area = vert_expanded_intersection.Size().Area(); + } + FloatRect horiz_expanded_intersection(intersection); + horiz_expanded_intersection.ShiftXEdgeTo(std::min(a.X(), b.X())); + horiz_expanded_intersection.ShiftMaxXEdgeTo(std::max(a.MaxX(), b.MaxX())); + if (horiz_expanded_intersection.Size().Area() > maximum_area) { + maximum = horiz_expanded_intersection; + maximum_area = horiz_expanded_intersection.Size().Area(); + } + } + return maximum; } -static bool CanUpcastTo(const PropertyTreeState& guest, - const PropertyTreeState& home); +FloatRect PaintArtifactCompositor::PendingLayer::MapRectKnownToBeOpaque( + const PropertyTreeState& new_state) const { + if (rect_known_to_be_opaque.IsEmpty()) + return FloatRect(); -bool PaintArtifactCompositor::PendingLayer::CanMerge( - const PendingLayer& guest, - const PropertyTreeState& guest_state) const { - if (compositing_type == kRequiresOwnLayer || - guest.compositing_type == kRequiresOwnLayer) { - return false; + FloatClipRect float_clip_rect(rect_known_to_be_opaque); + GeometryMapper::LocalToAncestorVisualRect(property_tree_state, new_state, + float_clip_rect); + return float_clip_rect.IsTight() ? float_clip_rect.Rect() : FloatRect(); +} + +std::unique_ptr<JSONObject> PaintArtifactCompositor::PendingLayer::ToJSON( + const PaintArtifact* paint_artifact) const { + std::unique_ptr<JSONObject> result = std::make_unique<JSONObject>(); + result->SetArray("bounds", RectAsJSONArray(bounds)); + result->SetArray("rect_known_to_be_opaque", + RectAsJSONArray(rect_known_to_be_opaque)); + result->SetObject("property_tree_state", property_tree_state.ToJSON()); + result->SetArray("offset_of_decomposited_transforms", + PointAsJSONArray(offset_of_decomposited_transforms)); + std::unique_ptr<JSONArray> chunks = std::make_unique<JSONArray>(); + for (wtf_size_t chunk_index : paint_chunk_indices) { + if (paint_artifact) { + StringBuilder sb; + sb.AppendFormat("index=%i ", chunk_index); + sb.Append(paint_artifact->PaintChunks()[chunk_index].ToString()); + chunks->PushString(sb.ToString()); + } else { + chunks->PushInteger(chunk_index); + } } - if (&property_tree_state.Effect().Unalias() != - &guest_state.Effect().Unalias()) { + result->SetArray("paint_chunks", std::move(chunks)); + return result; +} + +FloatRect PaintArtifactCompositor::PendingLayer::VisualRectForOverlapTesting() + const { + FloatClipRect visual_rect(bounds); + GeometryMapper::LocalToAncestorVisualRect( + property_tree_state, PropertyTreeState::Root(), visual_rect, + kIgnorePlatformOverlayScrollbarSize, kNonInclusiveIntersect, + kExpandVisualRectForAnimation); + return visual_rect.Rect(); +} + +bool PaintArtifactCompositor::PendingLayer::Merge(const PendingLayer& guest) { + PropertyTreeState new_state = PropertyTreeState::Uninitialized(); + if (!CanMerge(guest, guest.property_tree_state, &new_state, &bounds)) return false; - } - return CanUpcastTo(guest_state, property_tree_state); + + paint_chunk_indices.AppendVector(guest.paint_chunk_indices); + rect_known_to_be_opaque = + UniteRectsKnownToBeOpaque(MapRectKnownToBeOpaque(new_state), + guest.MapRectKnownToBeOpaque(new_state)); + property_tree_state = new_state; + return true; } void PaintArtifactCompositor::PendingLayer::Upcast( const PropertyTreeState& new_state) { DCHECK(compositing_type != kRequiresOwnLayer); + if (property_tree_state == new_state) + return; + FloatClipRect float_clip_rect(bounds); GeometryMapper::LocalToAncestorVisualRect(property_tree_state, new_state, float_clip_rect); bounds = float_clip_rect.Rect(); + rect_known_to_be_opaque = MapRectKnownToBeOpaque(new_state); property_tree_state = new_state; - // TODO(crbug.com/701991): Upgrade GeometryMapper. - // A local visual rect mapped to an ancestor space may become a polygon - // (e.g. consider transformed clip), also effects may affect the opaque - // region. To determine whether the layer is still opaque, we need to - // query conservative opaque rect after mapping to an ancestor space, - // which is not supported by GeometryMapper yet. - rect_known_to_be_opaque = FloatRect(); } const PaintChunk& PaintArtifactCompositor::PendingLayer::FirstPaintChunk( @@ -490,55 +569,127 @@ const PaintChunk& PaintArtifactCompositor::PendingLayer::FirstPaintChunk( return paint_artifact.PaintChunks()[paint_chunk_indices[0]]; } -static bool IsNonCompositingAncestorOf( - const TransformPaintPropertyNode& unaliased_ancestor, - const TransformPaintPropertyNode& node) { - for (const auto* n = &node; n != &unaliased_ancestor; +static bool HasCompositedTransformToAncestor( + const TransformPaintPropertyNode& node, + const TransformPaintPropertyNode& unaliased_ancestor) { + for (const auto* n = &node.Unalias(); n != &unaliased_ancestor; n = SafeUnalias(n->Parent())) { - if (!n || n->HasDirectCompositingReasons()) - return false; + if (n->HasDirectCompositingReasons()) + return true; } - return true; + return false; +} + +// Returns the lowest common ancestor if there is no composited transform +// between the two transforms. +static const TransformPaintPropertyNode* NonCompositedLowestCommonAncestor( + const TransformPaintPropertyNode& transform1, + const TransformPaintPropertyNode& transform2) { + const auto& lca = LowestCommonAncestor(transform1, transform2).Unalias(); + if (HasCompositedTransformToAncestor(transform1, lca) || + HasCompositedTransformToAncestor(transform2, lca)) + return nullptr; + return &lca; +} + +static bool ClipChainHasCompositedTransformTo( + const ClipPaintPropertyNode& node, + const ClipPaintPropertyNode& unaliased_ancestor, + const TransformPaintPropertyNode& transform) { + for (const auto* n = &node.Unalias(); n != &unaliased_ancestor; + n = SafeUnalias(n->Parent())) { + if (!NonCompositedLowestCommonAncestor(n->LocalTransformSpace(), transform)) + return true; + } + return false; } // Determines whether drawings based on the 'guest' state can be painted into -// a layer with the 'home' state. A number of criteria need to be met: +// a layer with the 'home' state, and if yes, returns the common ancestor state +// to which both layer will be upcasted. +// A number of criteria need to be met: // 1. The guest effect must be a descendant of the home effect. However this // check is enforced by the layerization recursion. Here we assume the // guest has already been upcasted to the same effect. // 2. The guest transform and the home transform have compatible backface // visibility. -// 3. The guest clip must be a descendant of the home clip. +// 3. The guest transform space must be within compositing boundary of the home +// transform space. // 4. The local space of each clip and effect node on the ancestor chain must // be within compositing boundary of the home transform space. -// 5. The guest transform space must be within compositing boundary of the -// home -// transform space. -static bool CanUpcastTo(const PropertyTreeState& guest, - const PropertyTreeState& home) { +base::Optional<PropertyTreeState> CanUpcastWith(const PropertyTreeState& guest, + const PropertyTreeState& home) { DCHECK_EQ(&home.Effect().Unalias(), &guest.Effect().Unalias()); if (home.Transform().IsBackfaceHidden() != guest.Transform().IsBackfaceHidden()) + return base::nullopt; + + auto* upcast_transform = + NonCompositedLowestCommonAncestor(home.Transform(), guest.Transform()); + if (!upcast_transform) + return base::nullopt; + + const auto& clip_lca = + LowestCommonAncestor(home.Clip(), guest.Clip()).Unalias(); + if (ClipChainHasCompositedTransformTo(home.Clip(), clip_lca, + *upcast_transform) || + ClipChainHasCompositedTransformTo(guest.Clip(), clip_lca, + *upcast_transform)) + return base::nullopt; + + return PropertyTreeState(*upcast_transform, clip_lca, home.Effect()); +} + +// We will only allow merging if the merged-area:home-area+guest-area doesn't +// exceed the ratio |kMergingSparsityTolerance|:1. +static constexpr float kMergeSparsityTolerance = 6; + +bool PaintArtifactCompositor::PendingLayer::CanMerge( + const PendingLayer& guest, + const PropertyTreeState& guest_state, + PropertyTreeState* out_merged_state, + FloatRect* out_merged_bounds) const { + if (compositing_type == kRequiresOwnLayer || + guest.compositing_type == kRequiresOwnLayer) { return false; + } + if (&property_tree_state.Effect().Unalias() != + &guest_state.Effect().Unalias()) { + return false; + } - const auto& home_clip = home.Clip().Unalias(); - for (const auto* current_clip = &guest.Clip().Unalias(); - current_clip != &home_clip; - current_clip = SafeUnalias(current_clip->Parent())) { - // If we had direct compositing reasons on a clip node, we would want to - // return false here. - if (!current_clip) - return false; - if (!IsNonCompositingAncestorOf( - home.Transform().Unalias(), - current_clip->LocalTransformSpace().Unalias())) { + const base::Optional<PropertyTreeState>& merged_state = + CanUpcastWith(guest_state, property_tree_state); + if (!merged_state) + return false; + + FloatClipRect new_home_bounds(bounds); + GeometryMapper::LocalToAncestorVisualRect(property_tree_state, *merged_state, + new_home_bounds); + FloatClipRect new_guest_bounds(guest.bounds); + GeometryMapper::LocalToAncestorVisualRect(guest_state, *merged_state, + new_guest_bounds); + + FloatRect merged_bounds = + UnionRect(new_home_bounds.Rect(), new_guest_bounds.Rect()); + // Don't check for sparcity if we may further decomposite the effect, so that + // the merged layer may be merged to other layers with the decomposited + // effect, which is often better than not merging even if the merged layer is + // sparse because we may create less composited effects and render surfaces. + if (guest_state.Effect().IsRoot() || + guest_state.Effect().HasDirectCompositingReasons()) { + float sum_area = new_home_bounds.Rect().Size().Area() + + new_guest_bounds.Rect().Size().Area(); + if (merged_bounds.Size().Area() > kMergeSparsityTolerance * sum_area) return false; - } } - return IsNonCompositingAncestorOf(home.Transform().Unalias(), - guest.Transform().Unalias()); + if (out_merged_state) + *out_merged_state = *merged_state; + if (out_merged_bounds) + *out_merged_bounds = merged_bounds; + return true; } // Returns nullptr if 'ancestor' is not a strict ancestor of 'node'. @@ -559,21 +710,15 @@ static const EffectPaintPropertyNode* StrictUnaliasedChildOfAlongPath( bool PaintArtifactCompositor::MightOverlap(const PendingLayer& layer_a, const PendingLayer& layer_b) { - FloatClipRect bounds_a(layer_a.bounds); - GeometryMapper::LocalToAncestorVisualRect( - layer_a.property_tree_state, PropertyTreeState::Root(), bounds_a); - FloatClipRect bounds_b(layer_b.bounds); - GeometryMapper::LocalToAncestorVisualRect( - layer_b.property_tree_state, PropertyTreeState::Root(), bounds_b); - - return bounds_a.Rect().Intersects(bounds_b.Rect()); + return layer_a.VisualRectForOverlapTesting().Intersects( + layer_b.VisualRectForOverlapTesting()); } bool PaintArtifactCompositor::DecompositeEffect( const EffectPaintPropertyNode& unaliased_parent_effect, - size_t first_layer_in_parent_group_index, + wtf_size_t first_layer_in_parent_group_index, const EffectPaintPropertyNode& unaliased_effect, - size_t layer_index) { + wtf_size_t layer_index) { // The layer must be the last layer in pending_layers_. DCHECK_EQ(layer_index, pending_layers_.size() - 1); @@ -593,24 +738,29 @@ bool PaintArtifactCompositor::DecompositeEffect( ? *unaliased_effect.OutputClip() : layer.property_tree_state.Clip(), unaliased_effect); - if (!CanUpcastTo(layer.property_tree_state, group_state)) + base::Optional<PropertyTreeState> upcast_state = + CanUpcastWith(layer.property_tree_state, group_state); + if (!upcast_state) return false; - PropertyTreeState upcast_state = group_state; - upcast_state.SetEffect(unaliased_parent_effect); + upcast_state->SetEffect(unaliased_parent_effect); // Exotic blending layer can be decomposited only if its parent group - // (which defines the scope of the blending) has only one layer before it, + // (which defines the scope of the blending) has zero or one layer before it, // and it can be merged into that layer. if (unaliased_effect.BlendMode() != SkBlendMode::kSrcOver) { - if (layer_index - 1 != first_layer_in_parent_group_index) - return false; - if (!pending_layers_[first_layer_in_parent_group_index].CanMerge( - layer, upcast_state)) - return false; + auto num_previous_siblings = + layer_index - first_layer_in_parent_group_index; + if (num_previous_siblings) { + if (num_previous_siblings > 1) + return false; + if (!pending_layers_[first_layer_in_parent_group_index].CanMerge( + layer, *upcast_state)) + return false; + } } - layer.Upcast(upcast_state); + layer.Upcast(*upcast_state); return true; } @@ -657,13 +807,12 @@ static bool SkipGroupIfEffectivelyInvisible( return true; } -static bool IsCompositedScrollHitTest(const DisplayItem& item) { - if (!item.IsScrollHitTest()) +static bool IsCompositedScrollHitTest(const PaintChunk& chunk) { + if (!chunk.hit_test_data) return false; - const auto* scroll_offset_node = - static_cast<const ScrollHitTestDisplayItem&>(item).scroll_offset_node(); - return scroll_offset_node && - scroll_offset_node->HasDirectCompositingReasons(); + const auto* scroll_translation = chunk.hit_test_data->scroll_translation; + return scroll_translation && + scroll_translation->HasDirectCompositingReasons(); } static bool IsCompositedScrollbar(const DisplayItem& item) { @@ -697,10 +846,10 @@ void PaintArtifactCompositor::LayerizeGroup( // Every paint chunk will be visited by the main loop below for exactly // once, except for chunks that enter or exit groups (case B & C below). For // normal chunk visit (case A), the only cost is determining squash, which - // costs O(qd), where d came from "canUpcastTo" and geometry mapping. + // costs O(qd), where d came from |CanUpcastWith| and geometry mapping. // Subtotal: O(pqd) // For group entering and exiting, it could cost O(d) for each group, for - // searching the shallowest subgroup (strictChildOfAlongPath), thus O(d^2) + // searching the shallowest subgroup (StrictChildOfAlongPath), thus O(d^2) // in total. // Also when exiting group, the group may be decomposited and squashed to a // previous layer. Again finding the host costs O(qd). Merging would cost @@ -714,12 +863,17 @@ void PaintArtifactCompositor::LayerizeGroup( const auto& chunk_effect = chunk_it->properties.Effect().Unalias(); if (&chunk_effect == &unaliased_group) { // Case A: The next chunk belongs to the current group but no subgroup. - const auto& first_display_item = - paint_artifact.GetDisplayItemList()[chunk_it->begin_index]; - bool requires_own_layer = first_display_item.IsForeignLayer() || - IsCompositedScrollHitTest(first_display_item) || - IsCompositedScrollbar(first_display_item); - DCHECK(!requires_own_layer || chunk_it->size() == 1u); + bool requires_own_layer = false; + if (IsCompositedScrollHitTest(*chunk_it)) { + requires_own_layer = true; + } else if (chunk_it->size()) { + const auto& first_display_item = + paint_artifact.GetDisplayItemList()[chunk_it->begin_index]; + requires_own_layer = first_display_item.IsForeignLayer() || + first_display_item.IsGraphicsLayerWrapper() || + IsCompositedScrollbar(first_display_item); + } + DCHECK(!requires_own_layer || chunk_it->size() <= 1u); pending_layers_.emplace_back( *chunk_it, chunk_it - paint_artifact.PaintChunks().begin(), @@ -761,8 +915,7 @@ void PaintArtifactCompositor::LayerizeGroup( for (wtf_size_t candidate_index = pending_layers_.size() - 1; candidate_index-- > first_layer_in_current_group;) { PendingLayer& candidate_layer = pending_layers_[candidate_index]; - if (candidate_layer.CanMerge(new_layer, new_layer.property_tree_state)) { - candidate_layer.Merge(new_layer); + if (candidate_layer.Merge(new_layer)) { pending_layers_.pop_back(); break; } @@ -788,8 +941,8 @@ void PaintArtifactCompositor::CollectPendingLayers( } void SynthesizedClip::UpdateLayer(bool needs_layer, - const FloatRoundedRect& rrect, - scoped_refptr<const RefCountedPath> path) { + const ClipPaintPropertyNode& clip, + const TransformPaintPropertyNode& transform) { if (!needs_layer) { layer_.reset(); return; @@ -799,33 +952,40 @@ void SynthesizedClip::UpdateLayer(bool needs_layer, layer_->SetIsDrawable(true); } - IntRect layer_bounds = EnclosingIntRect(rrect.Rect()); - gfx::Vector2dF new_layer_origin(layer_bounds.X(), layer_bounds.Y()); - - SkRRect new_local_rrect = rrect; - new_local_rrect.offset(-new_layer_origin.x(), -new_layer_origin.y()); - - bool path_in_layer_changed = false; - if (path_ == path) { - path_in_layer_changed = path && layer_origin_ != new_layer_origin; - } else if (!path_ || !path) { - path_in_layer_changed = true; + const RefCountedPath* path = clip.ClipPath(); + SkRRect new_rrect = clip.PixelSnappedClipRect(); + IntRect layer_bounds = EnclosingIntRect(clip.PixelSnappedClipRect().Rect()); + bool needs_display = false; + + auto new_translation_2d_or_matrix = + GeometryMapper::SourceToDestinationProjection(clip.LocalTransformSpace(), + transform); + new_translation_2d_or_matrix.MapRect(layer_bounds); + new_translation_2d_or_matrix.PostTranslate(-layer_bounds.X(), + -layer_bounds.Y()); + + if (!path && new_translation_2d_or_matrix.IsIdentityOr2DTranslation()) { + const auto& translation = new_translation_2d_or_matrix.Translation2D(); + new_rrect.offset(translation.Width(), translation.Height()); + needs_display = !rrect_is_local_ || new_rrect != rrect_; + translation_2d_or_matrix_ = GeometryMapper::Translation2DOrMatrix(); + rrect_is_local_ = true; } else { - SkPath new_path = path->GetSkPath(); - new_path.offset(layer_origin_.x() - new_layer_origin.x(), - layer_origin_.y() - new_layer_origin.y()); - path_in_layer_changed = path_->GetSkPath() != new_path; + needs_display = rrect_is_local_ || new_rrect != rrect_ || + new_translation_2d_or_matrix != translation_2d_or_matrix_ || + (path_ != path && (!path_ || !path || *path_ != *path)); + translation_2d_or_matrix_ = new_translation_2d_or_matrix; + rrect_is_local_ = false; } - if (local_rrect_ != new_local_rrect || path_in_layer_changed) { + if (needs_display) layer_->SetNeedsDisplay(); - } - layer_->SetOffsetToTransformParent(new_layer_origin); - layer_->SetBounds(gfx::Size(layer_bounds.Width(), layer_bounds.Height())); - layer_origin_ = new_layer_origin; - local_rrect_ = new_local_rrect; - path_ = std::move(path); + layer_->SetOffsetToTransformParent( + gfx::Vector2dF(layer_bounds.X(), layer_bounds.Y())); + layer_->SetBounds(gfx::Size(layer_bounds.Size())); + rrect_ = new_rrect; + path_ = path; } scoped_refptr<cc::DisplayItemList> SynthesizedClip::PaintContentsToDisplayList( @@ -835,16 +995,22 @@ scoped_refptr<cc::DisplayItemList> SynthesizedClip::PaintContentsToDisplayList( PaintFlags flags; flags.setAntiAlias(true); cc_list->StartPaint(); - if (!path_) { - cc_list->push<cc::DrawRRectOp>(local_rrect_, flags); + if (rrect_is_local_) { + cc_list->push<cc::DrawRRectOp>(rrect_, flags); } else { cc_list->push<cc::SaveOp>(); - cc_list->push<cc::TranslateOp>(-layer_origin_.x(), -layer_origin_.y()); - cc_list->push<cc::ClipPathOp>(path_->GetSkPath(), SkClipOp::kIntersect, - true); - SkRRect rrect = local_rrect_; - rrect.offset(layer_origin_.x(), layer_origin_.y()); - cc_list->push<cc::DrawRRectOp>(rrect, flags); + if (translation_2d_or_matrix_.IsIdentityOr2DTranslation()) { + const auto& translation = translation_2d_or_matrix_.Translation2D(); + cc_list->push<cc::TranslateOp>(translation.Width(), translation.Height()); + } else { + cc_list->push<cc::ConcatOp>(SkMatrix(TransformationMatrix::ToSkMatrix44( + translation_2d_or_matrix_.Matrix()))); + } + if (path_) { + cc_list->push<cc::ClipPathOp>(path_->GetSkPath(), SkClipOp::kIntersect, + true); + } + cc_list->push<cc::DrawRRectOp>(rrect_, flags); cc_list->push<cc::RestoreOp>(); } cc_list->EndPaintOfUnpaired(gfx::Rect(layer_->bounds())); @@ -853,26 +1019,26 @@ scoped_refptr<cc::DisplayItemList> SynthesizedClip::PaintContentsToDisplayList( } SynthesizedClip& PaintArtifactCompositor::CreateOrReuseSynthesizedClipLayer( - const ClipPaintPropertyNode& node, + const ClipPaintPropertyNode& clip, + const TransformPaintPropertyNode& transform, bool needs_layer, CompositorElementId& mask_isolation_id, CompositorElementId& mask_effect_id) { auto* entry = std::find_if(synthesized_clip_cache_.begin(), - synthesized_clip_cache_.end(), [&node](const auto& entry) { - return entry.key == &node && !entry.in_use; + synthesized_clip_cache_.end(), [&clip](const auto& entry) { + return entry.key == &clip && !entry.in_use; }); if (entry == synthesized_clip_cache_.end()) { - auto clip = std::make_unique<SynthesizedClip>(); - synthesized_clip_cache_.push_back( - SynthesizedClipEntry{&node, std::move(clip), false}); + synthesized_clip_cache_.push_back(SynthesizedClipEntry{ + &clip, std::make_unique<SynthesizedClip>(), false}); entry = synthesized_clip_cache_.end() - 1; } entry->in_use = true; SynthesizedClip& synthesized_clip = *entry->synthesized_clip; if (needs_layer) { - synthesized_clip.UpdateLayer(needs_layer, node.ClipRect(), node.ClipPath()); + synthesized_clip.UpdateLayer(needs_layer, clip, transform); synthesized_clip.Layer()->SetLayerTreeHost(root_layer_->layer_tree_host()); } mask_isolation_id = synthesized_clip.GetMaskIsolationId(); @@ -903,14 +1069,14 @@ static void UpdateCompositorViewportProperties( *properties.page_scale); } if (properties.inner_scroll_translation) { - ids.inner_scroll = property_tree_manager.EnsureCompositorScrollNode( + ids.inner_scroll = property_tree_manager.EnsureCompositorInnerScrollNode( *properties.inner_scroll_translation); if (properties.outer_clip) { ids.outer_clip = property_tree_manager.EnsureCompositorClipNode( *properties.outer_clip); } if (properties.outer_scroll_translation) { - ids.outer_scroll = property_tree_manager.EnsureCompositorScrollNode( + ids.outer_scroll = property_tree_manager.EnsureCompositorOuterScrollNode( *properties.outer_scroll_translation); } } @@ -995,11 +1161,9 @@ void PaintArtifactCompositor::DecompositeTransforms( // The scroll translation node of a scroll hit test layer may not be // referenced by any pending layer's property tree state. Disallow // decomposition of it (and its ancestors). - if (const auto* scroll_hit_test = - ScrollHitTestForLayer(paint_artifact, pending_layer)) { - if (const auto* scroll_offset = scroll_hit_test->scroll_offset) - mark_not_decompositable(scroll_offset); - } + if (const auto* scroll_translation = + ScrollTranslationForLayer(paint_artifact, pending_layer)) + mark_not_decompositable(scroll_translation); } } @@ -1053,7 +1217,6 @@ void PaintArtifactCompositor::Update( Vector<std::unique_ptr<ContentLayerClientImpl>> new_content_layer_clients; new_content_layer_clients.ReserveCapacity(pending_layers_.size()); Vector<scoped_refptr<cc::Layer>> new_scroll_hit_test_layers; - Vector<scoped_refptr<cc::Layer>> new_scrollbar_layers; // Maps from cc effect id to blink effects. Containing only the effects // having composited layers. @@ -1074,7 +1237,7 @@ void PaintArtifactCompositor::Update( if (&clip.LocalTransformSpace() == &transform) { // Limit layer bounds to hide the areas that will be never visible // because of the clip. - pending_layer.bounds.Intersect(clip.ClipRect().Rect()); + pending_layer.bounds.Intersect(clip.PixelSnappedClipRect().Rect()); } else if (const auto* scroll = transform.ScrollNode()) { // Limit layer bounds to the scroll range to hide the areas that will // never be scrolled into the visible area. @@ -1084,7 +1247,7 @@ void PaintArtifactCompositor::Update( scoped_refptr<cc::Layer> layer = CompositedLayerForPendingLayer( paint_artifact, pending_layer, new_content_layer_clients, - new_scroll_hit_test_layers, new_scrollbar_layers); + new_scroll_hit_test_layers); // In Pre-CompositeAfterPaint, touch action rects and non-fast scrollable // regions are updated through ScrollingCoordinator. @@ -1093,9 +1256,9 @@ void PaintArtifactCompositor::Update( pending_layer.paint_chunk_indices); UpdateTouchActionRects(layer.get(), layer->offset_to_transform_parent(), property_state, paint_chunks); - UpdateNonFastScrollableRegions(layer.get(), - layer->offset_to_transform_parent(), - property_state, paint_chunks); + UpdateNonFastScrollableRegions( + layer.get(), layer->offset_to_transform_parent(), property_state, + paint_chunks, &property_tree_manager); } layer->SetLayerTreeHost(root_layer_->layer_tree_host()); @@ -1110,7 +1273,7 @@ void PaintArtifactCompositor::Update( // The compositor scroll node is not directly stored in the property tree // state but can be created via the scroll offset translation node. const auto& scroll_translation = - ScrollOffsetTranslationForLayer(*paint_artifact, pending_layer); + NearestScrollTranslationForLayer(*paint_artifact, pending_layer); int scroll_id = property_tree_manager.EnsureCompositorScrollNode(scroll_translation); @@ -1125,6 +1288,9 @@ void PaintArtifactCompositor::Update( bool backface_hidden = property_state.Transform().IsBackfaceHidden(); layer->SetDoubleSided(!backface_hidden); layer->SetShouldCheckBackfaceVisibility(backface_hidden); + bool has_will_change_transform = + property_state.Transform().RequiresCompositingForWillChangeTransform(); + layer->SetHasWillChangeTransformHint(has_will_change_transform); // If the property tree state has changed between the layer and the root, // we need to inform the compositor so damage can be calculated. Calling @@ -1164,7 +1330,6 @@ void PaintArtifactCompositor::Update( property_tree_manager.Finalize(); content_layer_clients_.swap(new_content_layer_clients); scroll_hit_test_layers_.swap(new_scroll_hit_test_layers); - scrollbar_layers_.swap(new_scrollbar_layers); auto pos = std::remove_if(synthesized_clip_cache_.begin(), synthesized_clip_cache_.end(), @@ -1207,20 +1372,14 @@ void PaintArtifactCompositor::Update( #endif } -cc::PropertyTrees* PaintArtifactCompositor::GetPropertyTreesForDirectUpdate() { +bool PaintArtifactCompositor::CanDirectlyUpdateProperties() const { // Don't try to retrieve property trees if we need an update. The full // update will update all of the nodes, so a direct update doesn't need to // do anything. if (needs_update_) - return nullptr; - - if (!root_layer_) - return nullptr; + return false; - auto* host = root_layer_->layer_tree_host(); - if (!host) - return nullptr; - return host->property_trees(); + return root_layer_ && root_layer_->layer_tree_host(); } bool PaintArtifactCompositor::DirectlyUpdateCompositedOpacityValue( @@ -1228,9 +1387,9 @@ bool PaintArtifactCompositor::DirectlyUpdateCompositedOpacityValue( // We can only directly-update compositor values if all content associated // with the node is known to be composited. DCHECK(effect.HasDirectCompositingReasons()); - if (auto* property_trees = GetPropertyTreesForDirectUpdate()) { + if (CanDirectlyUpdateProperties()) { return PropertyTreeManager::DirectlyUpdateCompositedOpacityValue( - property_trees, *root_layer_->layer_tree_host(), effect); + *root_layer_->layer_tree_host(), effect); } return false; } @@ -1243,9 +1402,9 @@ bool PaintArtifactCompositor::DirectlyUpdateScrollOffsetTransform( // CompositeAfterPaint because we cannot query CompositedLayerMapping here. DCHECK(transform.HasDirectCompositingReasons()); } - if (auto* property_trees = GetPropertyTreesForDirectUpdate()) { + if (CanDirectlyUpdateProperties()) { return PropertyTreeManager::DirectlyUpdateScrollOffsetTransform( - property_trees, *root_layer_->layer_tree_host(), transform); + *root_layer_->layer_tree_host(), transform); } return false; } @@ -1255,9 +1414,9 @@ bool PaintArtifactCompositor::DirectlyUpdateTransform( // We can only directly-update compositor values if all content associated // with the node is known to be composited. DCHECK(transform.HasDirectCompositingReasons()); - if (auto* property_trees = GetPropertyTreesForDirectUpdate()) { + if (CanDirectlyUpdateProperties()) { return PropertyTreeManager::DirectlyUpdateTransform( - property_trees, *root_layer_->layer_tree_host(), transform); + *root_layer_->layer_tree_host(), transform); } return false; } @@ -1267,13 +1426,27 @@ bool PaintArtifactCompositor::DirectlyUpdatePageScaleTransform( // We can only directly-update compositor values if all content associated // with the node is known to be composited. DCHECK(transform.HasDirectCompositingReasons()); - if (auto* property_trees = GetPropertyTreesForDirectUpdate()) { + if (CanDirectlyUpdateProperties()) { return PropertyTreeManager::DirectlyUpdatePageScaleTransform( - property_trees, *root_layer_->layer_tree_host(), transform); + *root_layer_->layer_tree_host(), transform); } return false; } +bool PaintArtifactCompositor::DirectlySetScrollOffset( + CompositorElementId element_id, + const FloatPoint& scroll_offset) { + if (!root_layer_ || !root_layer_->layer_tree_host()) + return false; + auto* property_trees = root_layer_->layer_tree_host()->property_trees(); + if (!property_trees->element_id_to_scroll_node_index.contains(element_id)) + return false; + PropertyTreeManager::DirectlySetScrollOffset( + *root_layer_->layer_tree_host(), element_id, + gfx::ScrollOffset(scroll_offset)); + return true; +} + static cc::RenderSurfaceReason GetRenderSurfaceCandidateReason( const cc::EffectNode& effect, const Vector<const EffectPaintPropertyNode*>& blink_effects) { @@ -1283,7 +1456,7 @@ static cc::RenderSurfaceReason GetRenderSurfaceCandidateReason( return cc::RenderSurfaceReason::kBlendModeDstIn; if (effect.opacity != 1.f) return cc::RenderSurfaceReason::kOpacity; - if (static_cast<size_t>(effect.id) < blink_effects.size() && + if (static_cast<wtf_size_t>(effect.id) < blink_effects.size() && blink_effects[effect.id] && blink_effects[effect.id]->HasActiveOpacityAnimation()) return cc::RenderSurfaceReason::kOpacityAnimation; @@ -1379,6 +1552,8 @@ void PaintArtifactCompositor::UpdateLayerDebugInfo( debug_info.compositing_reasons = CompositingReason::Descriptions(compositing_reasons); + debug_info.compositing_reason_ids = + CompositingReason::ShortNames(compositing_reasons); debug_info.owner_node_id = id.client.OwnerNodeId(); if (RasterInvalidationTracking::IsTracingRasterInvalidations() && @@ -1398,9 +1573,11 @@ CompositingReasons PaintArtifactCompositor::GetCompositingReasons( return CompositingReason::kOverlap; if (layer.compositing_type == PendingLayer::kRequiresOwnLayer) { + const auto& first_chunk = layer.FirstPaintChunk(paint_artifact); + if (IsCompositedScrollHitTest(first_chunk)) + return CompositingReason::kOverflowScrolling; const auto& display_item = - paint_artifact.GetDisplayItemList() - [layer.FirstPaintChunk(paint_artifact).begin_index]; + paint_artifact.GetDisplayItemList()[first_chunk.begin_index]; switch (display_item.GetType()) { case DisplayItem::kForeignLayerCanvas: return CompositingReason::kCanvas; @@ -1408,8 +1585,6 @@ CompositingReasons PaintArtifactCompositor::GetCompositingReasons( return CompositingReason::kPlugin; case DisplayItem::kForeignLayerVideo: return CompositingReason::kVideo; - case DisplayItem::kScrollHitTest: - return CompositingReason::kOverflowScrolling; case DisplayItem::kScrollbarHorizontal: return CompositingReason::kLayerForHorizontalScrollbar; case DisplayItem::kScrollbarVertical: @@ -1442,7 +1617,11 @@ Vector<cc::Layer*> PaintArtifactCompositor::SynthesizedClipLayersForTesting() } void LayerListBuilder::Add(scoped_refptr<cc::Layer> layer) { +#if DCHECK_IS_ON() DCHECK(list_valid_); + DCHECK(!layer_ids_.Contains(layer->id())); + layer_ids_.insert(layer->id()); +#endif list_.push_back(layer); } @@ -1455,7 +1634,7 @@ cc::LayerList LayerListBuilder::Finalize() { #if DCHECK_IS_ON() void PaintArtifactCompositor::ShowDebugData() { LOG(ERROR) << GetLayersAsJSON(kLayerTreeIncludesDebugInfo | - kLayerTreeIncludesPaintInvalidations) + kLayerTreeIncludesDetailedInvalidations) ->ToPrettyJSONString() .Utf8(); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h index 25abeddc235..766de18b6ae 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h @@ -18,10 +18,19 @@ #include "third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h" #include "third_party/blink/renderer/platform/graphics/compositing_reasons.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer_client.h" +#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/vector.h" +#if DCHECK_IS_ON() +#include "third_party/blink/renderer/platform/wtf/hash_set.h" +#endif + +namespace cc { +class ScrollbarLayerBase; +} + namespace gfx { class Vector2dF; } @@ -31,6 +40,7 @@ namespace blink { class ContentLayerClientImpl; class JSONObject; class PaintArtifact; +class PropertyTreeManager; class SynthesizedClip; struct PaintChunk; @@ -45,6 +55,9 @@ class LayerListBuilder { // The list becomes invalid once |Finalize| is called. bool list_valid_ = true; cc::LayerList list_; +#if DCHECK_IS_ON() + HashSet<int> layer_ids_; +#endif }; // This class maintains unique stable cc effect IDs (and optionally a persistent @@ -73,10 +86,10 @@ class SynthesizedClip : private cc::ContentLayerClient { } void UpdateLayer(bool needs_layer, - const FloatRoundedRect& rrect, - scoped_refptr<const RefCountedPath> path); + const ClipPaintPropertyNode&, + const TransformPaintPropertyNode&); - cc::Layer* Layer() { return layer_.get(); } + cc::PictureLayer* Layer() { return layer_.get(); } CompositorElementId GetMaskIsolationId() const { return mask_isolation_id_; } CompositorElementId GetMaskEffectId() const { return mask_effect_id_; } @@ -91,8 +104,9 @@ class SynthesizedClip : private cc::ContentLayerClient { private: scoped_refptr<cc::PictureLayer> layer_; - gfx::Vector2dF layer_origin_; - SkRRect local_rrect_ = SkRRect::MakeEmpty(); + GeometryMapper::Translation2DOrMatrix translation_2d_or_matrix_; + bool rrect_is_local_ = false; + SkRRect rrect_; scoped_refptr<const RefCountedPath> path_; CompositorElementId mask_isolation_id_; CompositorElementId mask_effect_id_; @@ -139,6 +153,12 @@ class PLATFORM_EXPORT PaintArtifactCompositor final bool DirectlyUpdateTransform(const TransformPaintPropertyNode&); bool DirectlyUpdatePageScaleTransform(const TransformPaintPropertyNode&); + // Directly sets cc::ScrollTree::current_scroll_offset. This doesn't affect + // cc::TransformNode::scroll_offset (which will be synched with blink + // transform node in DirectlyUpdateScrollOffsetTransform() or Update()). + bool DirectlySetScrollOffset(CompositorElementId, + const FloatPoint& scroll_offset); + // The root layer of the tree managed by this object. cc::Layer* RootLayer() const { return root_layer_.get(); } @@ -148,6 +168,9 @@ class PLATFORM_EXPORT PaintArtifactCompositor final // going to be removed from its frame. void WillBeRemovedFromFrame(); + std::unique_ptr<JSONArray> GetPendingLayersAsJSON( + const PaintArtifact* = nullptr) const; + std::unique_ptr<JSONObject> GetLayersAsJSON( LayerTreeFlags, const PaintArtifact* = nullptr) const; @@ -174,7 +197,8 @@ class PLATFORM_EXPORT PaintArtifactCompositor final cc::Layer*, const gfx::Vector2dF& layer_offset, const PropertyTreeState& layer_state, - const PaintChunkSubset& paint_chunks); + const PaintChunkSubset& paint_chunks, + PropertyTreeManager* = nullptr); void SetNeedsUpdate() { needs_update_ = true; } bool NeedsUpdate() const { return needs_update_; } @@ -202,15 +226,23 @@ class PLATFORM_EXPORT PaintArtifactCompositor final PendingLayer(const PaintChunk& first_paint_chunk, wtf_size_t first_chunk_index, bool requires_own_layer); - // Merge another pending layer after this one, appending all its paint - // chunks after chunks in this layer, with appropriate space conversion - // applied. The merged layer must have a property tree state that's deeper - // than this layer, i.e. can "upcast" to this layer's state. - void Merge(const PendingLayer& guest); + + // Merges |guest| into |this| if it can, by appending chunks of |guest| + // after chunks of |this|, with appropriate space conversion applied to + // both layers from their original property tree states to |merged_state|. + // Returns whether the merge is successful. + bool Merge(const PendingLayer& guest); + + // Returns true if |guest| can be merged into |this|, and sets the output + // paramsters with the property tree state and bounds of the merged layer. // |guest_state| is for cases that we want to check if we can merge |guest| - // if it has |guest_state| (which may be different from its current state). + // if it has |guest_state| in the future (which may be different from its + // current state). bool CanMerge(const PendingLayer& guest, - const PropertyTreeState& guest_state) const; + const PropertyTreeState& guest_state, + PropertyTreeState* merged_state = nullptr, + FloatRect* merged_bounds = nullptr) const; + // Mutate this layer's property tree state to a more general (shallower) // state, thus the name "upcast". The concrete effect of this is to // "decomposite" some of the properties, so that fewer properties will be @@ -220,6 +252,15 @@ class PLATFORM_EXPORT PaintArtifactCompositor final const PaintChunk& FirstPaintChunk(const PaintArtifact&) const; + // Returns the largest rect known to be opaque given two opaque rects. + static FloatRect UniteRectsKnownToBeOpaque(const FloatRect&, + const FloatRect&); + FloatRect MapRectKnownToBeOpaque(const PropertyTreeState&) const; + + std::unique_ptr<JSONObject> ToJSON(const PaintArtifact* = nullptr) const; + + FloatRect VisualRectForOverlapTesting() const; + // The rects are in the space of property_tree_state. FloatRect bounds; FloatRect rect_known_to_be_opaque; @@ -263,9 +304,9 @@ class PLATFORM_EXPORT PaintArtifactCompositor final Vector<PaintChunk>::const_iterator& chunk_cursor); static bool MightOverlap(const PendingLayer&, const PendingLayer&); bool DecompositeEffect(const EffectPaintPropertyNode& unaliased_parent_effect, - size_t first_layer_in_parent_group_index, + wtf_size_t first_layer_in_parent_group_index, const EffectPaintPropertyNode& unaliased_effect, - size_t layer_index); + wtf_size_t layer_index); // Builds a leaf layer that represents a single paint chunk. scoped_refptr<cc::Layer> CompositedLayerForPendingLayer( @@ -273,19 +314,19 @@ class PLATFORM_EXPORT PaintArtifactCompositor final const PendingLayer&, Vector<std::unique_ptr<ContentLayerClientImpl>>& new_content_layer_clients, - Vector<scoped_refptr<cc::Layer>>& new_scroll_hit_test_layers, - Vector<scoped_refptr<cc::Layer>>& new_scrollbar_layers); + Vector<scoped_refptr<cc::Layer>>& new_scroll_hit_test_layers); bool PropertyTreeStateChanged(const PropertyTreeState&) const; - const TransformPaintPropertyNode& ScrollOffsetTranslationForLayer( + const TransformPaintPropertyNode& NearestScrollTranslationForLayer( const PaintArtifact&, const PendingLayer&); - // If the pending layer is a special scroll hit test layer, return the - // associated hit test information. - const HitTestData::ScrollHitTest* ScrollHitTestForLayer(const PaintArtifact&, - const PendingLayer&); + // If the pending layer has scroll hit test data, return the associated + // scroll translation node. + const TransformPaintPropertyNode* ScrollTranslationForLayer( + const PaintArtifact&, + const PendingLayer&); // Finds an existing or creates a new scroll hit test layer for the pending // layer, returning nullptr if the layer is not a scroll hit test layer. @@ -295,8 +336,9 @@ class PLATFORM_EXPORT PaintArtifactCompositor final // Finds an existing or creates a new scrollbar layer for the pending layer, // returning nullptr if the layer is not a scrollbar layer. - scoped_refptr<cc::Layer> ScrollbarLayerForPendingLayer(const PaintArtifact&, - const PendingLayer&); + scoped_refptr<cc::ScrollbarLayerBase> ScrollbarLayerForPendingLayer( + const PaintArtifact&, + const PendingLayer&); // Finds a client among the current vector of clients that matches the paint // chunk's id, or otherwise allocates a new one. @@ -309,6 +351,7 @@ class PLATFORM_EXPORT PaintArtifactCompositor final // However, |mask_isolation_id| is always set. SynthesizedClip& CreateOrReuseSynthesizedClipLayer( const ClipPaintPropertyNode&, + const TransformPaintPropertyNode&, bool needs_layer, CompositorElementId& mask_isolation_id, CompositorElementId& mask_effect_id) final; @@ -318,7 +361,7 @@ class PLATFORM_EXPORT PaintArtifactCompositor final const cc::LayerList&, const Vector<const EffectPaintPropertyNode*>&); - cc::PropertyTrees* GetPropertyTreesForDirectUpdate(); + bool CanDirectlyUpdateProperties() const; CompositingReasons GetCompositingReasons(const PendingLayer& layer, const PendingLayer* previous_layer, @@ -341,7 +384,6 @@ class PLATFORM_EXPORT PaintArtifactCompositor final Vector<SynthesizedClipEntry> synthesized_clip_cache_; Vector<scoped_refptr<cc::Layer>> scroll_hit_test_layers_; - Vector<scoped_refptr<cc::Layer>> scrollbar_layers_; Vector<PendingLayer, 0> pending_layers_; diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc index e5df1538463..34024cdd908 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc @@ -52,7 +52,7 @@ PaintChunk DefaultChunk() { return PaintChunk(0, 1, DefaultId(), PropertyTreeState::Root()); } -gfx::Transform Translation(SkMScalar x, SkMScalar y) { +gfx::Transform Translation(SkScalar x, SkScalar y) { gfx::Transform transform; transform.Translate(x, y); return transform; @@ -122,7 +122,7 @@ class PaintArtifactCompositorTest : public testing::Test, return *GetPropertyTrees().effect_tree.Node(layer->effect_tree_index()); } - const cc::LayerTreeHost& GetLayerTreeHost() { + cc::LayerTreeHost& GetLayerTreeHost() { return *layer_tree_->layer_tree_host(); } @@ -159,12 +159,11 @@ class PaintArtifactCompositorTest : public testing::Test, cc::Layer* RootLayer() { return paint_artifact_compositor_->RootLayer(); } - // CompositeAfterPaint creates scroll hit test display items (which create - // scroll hit test layers in PaintArtifactCompositor) whereas before - // CompositeAfterPaint, scrollable foreign layers are created in - // ScrollingCoordinator and passed to PaintArtifactCompositor. This function - // is used to create a chunk representing the scrollable layer in either of - // these modes. + // CompositeAfterPaint creates scroll hit test data (which create scroll hit + // test layers in PaintArtifactCompositor) whereas before CompositeAfterPaint, + // scrollable foreign layers are created in ScrollingCoordinator and passed + // to PaintArtifactCompositor. This function is used to create a chunk + // representing the scrollable layer in either of these modes. void CreateScrollableChunk(TestPaintArtifact& artifact, const TransformPaintPropertyNode& scroll_offset, const ClipPaintPropertyNode& clip, @@ -174,7 +173,6 @@ class PaintArtifactCompositorTest : public testing::Test, const auto* scroll_node = scroll_offset.ScrollNode(); scoped_refptr<cc::Layer> layer = cc::Layer::Create(); auto rect = scroll_node->ContainerRect(); - layer->SetScrollable(gfx::Size(rect.Size())); layer->SetBounds(gfx::Size(rect.Size())); layer->SetElementId(scroll_node->GetCompositorElementId()); artifact.Chunk(scroll_offset, clip, effect) @@ -184,8 +182,9 @@ class PaintArtifactCompositorTest : public testing::Test, // Returns the |num|th scrollable layer. In CompositeAfterPaint, this will be // a scroll hit test layer, whereas currently this will be a content layer. cc::Layer* ScrollableLayerAt(size_t num) { + const cc::ScrollTree& scroll_tree = GetPropertyTrees().scroll_tree; for (auto& layer : RootLayer()->children()) { - if (layer->scrollable()) { + if (scroll_tree.FindNodeFromElementId(layer->element_id())) { if (num == 0) return layer.get(); num--; @@ -199,8 +198,9 @@ class PaintArtifactCompositorTest : public testing::Test, // content layers are scrollable and non-scrollable, so this will return the // |num|th content layer that is not scrollable. cc::Layer* NonScrollableLayerAt(size_t num) { + const cc::ScrollTree& scroll_tree = GetPropertyTrees().scroll_tree; for (auto& layer : RootLayer()->children()) { - if (!layer->scrollable()) { + if (!scroll_tree.FindNodeFromElementId(layer->element_id())) { if (num == 0) return layer.get(); num--; @@ -240,7 +240,7 @@ class PaintArtifactCompositorTest : public testing::Test, } void AddSimpleRectChunk(TestPaintArtifact& artifact) { - artifact.Chunk().RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack); + artifact.Chunk().RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack); } void UpdateWithArtifactWithOpacity(float opacity, @@ -251,7 +251,7 @@ class PaintArtifactCompositorTest : public testing::Test, AddSimpleRectChunk(artifact); auto effect = CreateOpacityEffect(e0(), opacity); artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); if (include_subsequent_chunk) AddSimpleRectChunk(artifact); Update(artifact.Build()); @@ -265,6 +265,10 @@ class PaintArtifactCompositorTest : public testing::Test, MockScrollCallbacks& ScrollCallbacks() { return scroll_callbacks_; } + PaintArtifactCompositor& GetPaintArtifactCompositor() { + return *paint_artifact_compositor_; + } + private: MockScrollCallbacks scroll_callbacks_; std::unique_ptr<PaintArtifactCompositor> paint_artifact_compositor_; @@ -274,7 +278,7 @@ class PaintArtifactCompositorTest : public testing::Test, std::unique_ptr<LayerTreeHostEmbedder> layer_tree_; }; -INSTANTIATE_LAYER_LIST_TEST_SUITE_P(PaintArtifactCompositorTest); +INSTANTIATE_PAINT_TEST_SUITE_P(PaintArtifactCompositorTest); const auto kNotScrollingOnMain = cc::MainThreadScrollingReason::kNotScrollingOnMain; @@ -286,7 +290,7 @@ TEST_P(PaintArtifactCompositorTest, EmptyPaintArtifact) { TEST_P(PaintArtifactCompositorTest, OneChunkWithAnOffset) { TestPaintArtifact artifact; - artifact.Chunk().RectDrawing(FloatRect(50, -50, 100, 100), Color::kWhite); + artifact.Chunk().RectDrawing(IntRect(50, -50, 100, 100), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -307,10 +311,10 @@ TEST_P(PaintArtifactCompositorTest, OneTransform) { TestPaintArtifact artifact; artifact.Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); - artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kGray); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); + artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kGray); artifact.Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack); + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(2u, LayerCount()); @@ -349,10 +353,10 @@ TEST_P(PaintArtifactCompositorTest, OneTransformWithAlias) { TestPaintArtifact artifact; artifact.Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); - artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kGray); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); + artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kGray); artifact.Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack); + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(2u, LayerCount()); @@ -393,9 +397,9 @@ TEST_P(PaintArtifactCompositorTest, TransformCombining) { TestPaintArtifact artifact; artifact.Chunk(*transform1, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 300, 200), Color::kWhite); + .RectDrawing(IntRect(0, 0, 300, 200), Color::kWhite); artifact.Chunk(*transform2, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 300, 200), Color::kBlack); + .RectDrawing(IntRect(0, 0, 300, 200), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(2u, LayerCount()); @@ -441,11 +445,11 @@ TEST_P(PaintArtifactCompositorTest, BackfaceVisibility) { TestPaintArtifact artifact; artifact.Chunk(*backface_hidden_transform, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 300, 200), Color::kWhite); + .RectDrawing(IntRect(0, 0, 300, 200), Color::kWhite); artifact.Chunk(*backface_inherited_transform, c0(), e0()) - .RectDrawing(FloatRect(100, 100, 100, 100), Color::kBlack); + .RectDrawing(IntRect(100, 100, 100, 100), Color::kBlack); artifact.Chunk(*backface_visible_transform, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 300, 200), Color::kDarkGray); + .RectDrawing(IntRect(0, 0, 300, 200), Color::kDarkGray); Update(artifact.Build()); ASSERT_EQ(2u, LayerCount()); @@ -479,7 +483,7 @@ TEST_P(PaintArtifactCompositorTest, FlattensInheritedTransform) { TestPaintArtifact artifact; artifact.Chunk(*transform3, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 300, 200), Color::kWhite); + .RectDrawing(IntRect(0, 0, 300, 200), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -534,7 +538,7 @@ TEST_P(PaintArtifactCompositorTest, FlattensInheritedTransformWithAlias) { TestPaintArtifact artifact; artifact.Chunk(*transform3, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 300, 200), Color::kWhite); + .RectDrawing(IntRect(0, 0, 300, 200), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -588,13 +592,13 @@ TEST_P(PaintArtifactCompositorTest, SortingContextID) { TestPaintArtifact artifact; artifact.Chunk(*transform1, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 300, 200), Color::kWhite); + .RectDrawing(IntRect(0, 0, 300, 200), Color::kWhite); artifact.Chunk(*transform2, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 300, 200), Color::kLightGray); + .RectDrawing(IntRect(0, 0, 300, 200), Color::kLightGray); artifact.Chunk(*transform3, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 300, 200), Color::kDarkGray); + .RectDrawing(IntRect(0, 0, 300, 200), Color::kDarkGray); artifact.Chunk(*transform4, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 300, 200), Color::kBlack); + .RectDrawing(IntRect(0, 0, 300, 200), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(4u, LayerCount()); @@ -646,7 +650,7 @@ TEST_P(PaintArtifactCompositorTest, OneClip) { TestPaintArtifact artifact; artifact.Chunk(t0(), *clip, e0()) - .RectDrawing(FloatRect(220, 80, 300, 200), Color::kBlack); + .RectDrawing(IntRect(220, 80, 300, 200), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -671,7 +675,7 @@ TEST_P(PaintArtifactCompositorTest, OneClipWithAlias) { TestPaintArtifact artifact; artifact.Chunk(t0(), *clip, e0()) - .RectDrawing(FloatRect(220, 80, 300, 200), Color::kBlack); + .RectDrawing(IntRect(220, 80, 300, 200), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -705,13 +709,13 @@ TEST_P(PaintArtifactCompositorTest, NestedClips) { TestPaintArtifact artifact; artifact.Chunk(*transform1, *clip1, e0()) - .RectDrawing(FloatRect(300, 350, 100, 100), Color::kWhite); + .RectDrawing(IntRect(300, 350, 100, 100), Color::kWhite); artifact.Chunk(*transform2, *clip2, e0()) - .RectDrawing(FloatRect(300, 350, 100, 100), Color::kLightGray); + .RectDrawing(IntRect(300, 350, 100, 100), Color::kLightGray); artifact.Chunk(*transform1, *clip1, e0()) - .RectDrawing(FloatRect(300, 350, 100, 100), Color::kDarkGray); + .RectDrawing(IntRect(300, 350, 100, 100), Color::kDarkGray); artifact.Chunk(*transform2, *clip2, e0()) - .RectDrawing(FloatRect(300, 350, 100, 100), Color::kBlack); + .RectDrawing(IntRect(300, 350, 100, 100), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(4u, LayerCount()); @@ -771,13 +775,13 @@ TEST_P(PaintArtifactCompositorTest, NestedClipsWithAlias) { TestPaintArtifact artifact; artifact.Chunk(*transform1, *clip1, e0()) - .RectDrawing(FloatRect(300, 350, 100, 100), Color::kWhite); + .RectDrawing(IntRect(300, 350, 100, 100), Color::kWhite); artifact.Chunk(*transform2, *clip2, e0()) - .RectDrawing(FloatRect(300, 350, 100, 100), Color::kLightGray); + .RectDrawing(IntRect(300, 350, 100, 100), Color::kLightGray); artifact.Chunk(*transform1, *clip1, e0()) - .RectDrawing(FloatRect(300, 350, 100, 100), Color::kDarkGray); + .RectDrawing(IntRect(300, 350, 100, 100), Color::kDarkGray); artifact.Chunk(*transform2, *clip2, e0()) - .RectDrawing(FloatRect(300, 350, 100, 100), Color::kBlack); + .RectDrawing(IntRect(300, 350, 100, 100), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(4u, LayerCount()); @@ -830,7 +834,7 @@ TEST_P(PaintArtifactCompositorTest, DeeplyNestedClips) { TestPaintArtifact artifact; artifact.Chunk(t0(), *clips.back(), e0()) - .RectDrawing(FloatRect(0, 0, 200, 200), Color::kWhite); + .RectDrawing(IntRect(0, 0, 200, 200), Color::kWhite); Update(artifact.Build()); // Check the drawing layer. It's clipped. @@ -849,20 +853,122 @@ TEST_P(PaintArtifactCompositorTest, DeeplyNestedClips) { for (auto it = clips.rbegin(); it != clips.rend(); ++it) { const ClipPaintPropertyNode* paint_clip_node = it->get(); EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP, clip_node->clip_type); - EXPECT_EQ(paint_clip_node->ClipRect().Rect(), clip_node->clip); + EXPECT_EQ(paint_clip_node->UnsnappedClipRect().Rect(), clip_node->clip); clip_node = GetPropertyTrees().clip_tree.Node(clip_node->parent_id); } } +TEST_P(PaintArtifactCompositorTest, SiblingClipsWithAlias) { + auto real_common_clip = + CreateClip(c0(), t0(), FloatRoundedRect(0, 0, 800, 600)); + auto common_clip = ClipPaintPropertyNode::CreateAlias(*real_common_clip); + auto real_clip1 = + CreateClip(*common_clip, t0(), FloatRoundedRect(0, 0, 300, 200)); + auto clip1 = ClipPaintPropertyNode::CreateAlias(*real_clip1); + auto real_clip2 = + CreateClip(*common_clip, t0(), FloatRoundedRect(400, 0, 400, 600)); + auto clip2 = ClipPaintPropertyNode::CreateAlias(*real_clip2); + + TestPaintArtifact artifact; + artifact.Chunk(t0(), *clip1, e0()) + .RectDrawing(IntRect(0, 0, 111, 222), Color::kWhite); + artifact.Chunk(t0(), *clip2, e0()) + .RectDrawing(IntRect(333, 444, 555, 666), Color::kBlack); + Update(artifact.Build()); + + // The two chunks are merged together. + ASSERT_EQ(1u, LayerCount()); + const cc::Layer* layer = LayerAt(0); + EXPECT_THAT( + layer->GetPicture(), + Pointee(DrawsRectangles(Vector<RectWithColor>{ + // This is the first RectDrawing with real_clip1 applied. + RectWithColor(FloatRect(0, 0, 111, 200), Color::kWhite), + // This is the second RectDrawing with real_clip2 applied. + RectWithColor(FloatRect(400, 444, 400, 156), Color::kBlack)}))); + EXPECT_EQ(gfx::Transform(), layer->ScreenSpaceTransform()); + const cc::ClipNode* clip_node = + GetPropertyTrees().clip_tree.Node(layer->clip_tree_index()); + EXPECT_EQ(cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP, clip_node->clip_type); + ASSERT_EQ(gfx::RectF(0, 0, 800, 600), clip_node->clip); +} + +TEST_P(PaintArtifactCompositorTest, SiblingClipsWithCompositedTransform) { + auto t1 = CreateTransform(t0(), TransformationMatrix(), FloatPoint3D(), + CompositingReason::kWillChangeTransform); + auto t2 = CreateTransform(*t1, TransformationMatrix().Translate(1, 2)); + auto c1 = CreateClip(c0(), t0(), FloatRoundedRect(0, 0, 400, 600)); + auto c2 = CreateClip(c0(), *t2, FloatRoundedRect(400, 0, 400, 600)); + + TestPaintArtifact artifact; + artifact.Chunk(t0(), *c1, e0()) + .RectDrawing(IntRect(0, 0, 640, 480), Color::kWhite); + artifact.Chunk(t0(), *c2, e0()) + .RectDrawing(IntRect(0, 0, 640, 480), Color::kBlack); + Update(artifact.Build()); + + // We can't merge the two chunks because their clips have unmergeable + // transforms. + ASSERT_EQ(2u, LayerCount()); +} + +TEST_P(PaintArtifactCompositorTest, SiblingTransformsWithAlias) { + auto real_common_transform = + CreateTransform(t0(), TransformationMatrix().Translate(5, 6)); + auto common_transform = + TransformPaintPropertyNode::CreateAlias(*real_common_transform); + auto real_transform1 = + CreateTransform(*common_transform, TransformationMatrix().Scale(2)); + auto transform1 = TransformPaintPropertyNode::CreateAlias(*real_transform1); + auto real_transform2 = + CreateTransform(*common_transform, TransformationMatrix().Scale(0.5)); + auto transform2 = TransformPaintPropertyNode::CreateAlias(*real_transform2); + + TestPaintArtifact artifact; + artifact.Chunk(*transform1, c0(), e0()) + .RectDrawing(IntRect(0, 0, 111, 222), Color::kWhite); + artifact.Chunk(*transform2, c0(), e0()) + .RectDrawing(IntRect(0, 0, 333, 444), Color::kBlack); + Update(artifact.Build()); + + // The two chunks are merged together. + ASSERT_EQ(1u, LayerCount()); + const cc::Layer* layer = LayerAt(0); + EXPECT_THAT(layer->GetPicture(), + Pointee(DrawsRectangles(Vector<RectWithColor>{ + RectWithColor(FloatRect(0, 0, 222, 444), Color::kWhite), + RectWithColor(FloatRect(0, 0, 166.5, 222), Color::kBlack)}))); + gfx::Transform expected_transform; + expected_transform.Translate(5, 6); + EXPECT_EQ(expected_transform, layer->ScreenSpaceTransform()); +} + +TEST_P(PaintArtifactCompositorTest, SiblingTransformsWithComposited) { + auto t1 = CreateTransform(t0(), TransformationMatrix(), FloatPoint3D(), + CompositingReason::kWillChangeTransform); + auto t2 = CreateTransform(*t1, TransformationMatrix().Translate(1, 2)); + auto t3 = CreateTransform(t0(), TransformationMatrix().Translate(3, 4)); + + TestPaintArtifact artifact; + artifact.Chunk(*t2, c0(), e0()) + .RectDrawing(IntRect(0, 0, 640, 480), Color::kWhite); + artifact.Chunk(*t3, c0(), e0()) + .RectDrawing(IntRect(0, 0, 640, 480), Color::kBlack); + Update(artifact.Build()); + + // We can't merge the two chunks because their transforms are not mergeable. + ASSERT_EQ(2u, LayerCount()); +} + TEST_P(PaintArtifactCompositorTest, ForeignLayerPassesThrough) { scoped_refptr<cc::Layer> layer = cc::Layer::Create(); layer->SetIsDrawable(true); layer->SetBounds(gfx::Size(400, 300)); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk().ForeignLayer(layer, FloatPoint(50, 60)); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kGray); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -877,7 +983,7 @@ TEST_P(PaintArtifactCompositorTest, ForeignLayerPassesThrough) { TEST_P(PaintArtifactCompositorTest, EffectTreeConversionWithAlias) { Update(TestPaintArtifact() .Chunk() - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite) + .RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite) .Build()); auto root_stable_id = GetPropertyTrees().effect_tree.Node(1)->stable_id; @@ -892,11 +998,11 @@ TEST_P(PaintArtifactCompositorTest, EffectTreeConversionWithAlias) { TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *effect2) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); artifact.Chunk(t0(), c0(), *effect1) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); artifact.Chunk(t0(), c0(), *effect3) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(3u, LayerCount()); @@ -961,6 +1067,7 @@ static scoped_refptr<ScrollPaintPropertyNode> CreateScroll( static void CheckCcScrollNode(const ScrollPaintPropertyNode& blink_scroll, const cc::ScrollNode& cc_scroll) { + EXPECT_TRUE(cc_scroll.scrollable); EXPECT_EQ(static_cast<gfx::Size>(blink_scroll.ContainerRect().Size()), cc_scroll.container_bounds); EXPECT_EQ(static_cast<gfx::Size>(blink_scroll.ContentsSize()), @@ -983,7 +1090,7 @@ TEST_P(PaintArtifactCompositorTest, OneScrollNode) { TestPaintArtifact artifact; CreateScrollableChunk(artifact, *scroll_translation, c0(), e0()); artifact.Chunk(*scroll_translation, c0(), e0()) - .RectDrawing(FloatRect(-110, 12, 170, 19), Color::kWhite); + .RectDrawing(IntRect(-110, 12, 170, 19), Color::kWhite); // Scroll node ElementIds are referenced by scroll animations. Update(artifact.Build()); @@ -1017,7 +1124,6 @@ TEST_P(PaintArtifactCompositorTest, OneScrollNode) { Pointee(DrawsRectangle(FloatRect(0, 0, 57, 19), Color::kWhite))); auto* scroll_layer = ScrollableLayerAt(0); - EXPECT_TRUE(scroll_layer->scrollable()); // The scroll layer should be sized to the container bounds. // TODO(pdr): The container bounds will not include scrollbars but the scroll // layer should extend below scrollbars. @@ -1047,9 +1153,9 @@ TEST_P(PaintArtifactCompositorTest, TransformUnderScrollNode) { TestPaintArtifact artifact; artifact.Chunk(*scroll_translation, c0(), e0()) - .RectDrawing(FloatRect(-20, 4, 60, 8), Color::kBlack) + .RectDrawing(IntRect(-20, 4, 60, 8), Color::kBlack) .Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(1, -30, 5, 70), Color::kWhite); + .RectDrawing(IntRect(1, -30, 5, 70), Color::kWhite); Update(artifact.Build()); const cc::ScrollTree& scroll_tree = GetPropertyTrees().scroll_tree; @@ -1101,10 +1207,10 @@ TEST_P(PaintArtifactCompositorTest, NestedScrollNodes) { CreateScrollTranslation(*scroll_translation_a, 37, 41, *scroll_b); TestPaintArtifact artifact; artifact.Chunk(*scroll_translation_a, c0(), *effect) - .RectDrawing(FloatRect(7, 11, 13, 17), Color::kWhite); + .RectDrawing(IntRect(7, 11, 13, 17), Color::kWhite); CreateScrollableChunk(artifact, *scroll_translation_a, c0(), *effect); artifact.Chunk(*scroll_translation_b, c0(), *effect) - .RectDrawing(FloatRect(1, 2, 3, 5), Color::kWhite); + .RectDrawing(IntRect(1, 2, 3, 5), Color::kWhite); CreateScrollableChunk(artifact, *scroll_translation_b, c0(), *effect); Update(artifact.Build()); @@ -1150,10 +1256,10 @@ TEST_P(PaintArtifactCompositorTest, ScrollHitTestLayerOrder) { TestPaintArtifact artifact; artifact.Chunk(*scroll_translation, *clip, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); CreateScrollableChunk(artifact, *scroll_translation, *clip, e0()); artifact.Chunk(*transform, *clip, e0()) - .RectDrawing(FloatRect(0, 0, 50, 50), Color::kBlack); + .RectDrawing(IntRect(0, 0, 50, 50), Color::kBlack); Update(artifact.Build()); // The first content layer (background) should not have the scrolling element @@ -1197,7 +1303,7 @@ TEST_P(PaintArtifactCompositorTest, NestedScrollableLayerOrder) { CreateScrollableChunk(artifact, *scroll_translation_2, *clip_2->Parent(), e0()); artifact.Chunk(*scroll_translation_2, *clip_2, e0()) - .RectDrawing(FloatRect(0, 0, 50, 50), Color::kWhite); + .RectDrawing(IntRect(0, 0, 50, 50), Color::kWhite); Update(artifact.Build()); // Two scroll layers should be created for each scroll translation node. @@ -1281,8 +1387,8 @@ TEST_P(PaintArtifactCompositorTest, AncestorScrollNodes) { TEST_P(PaintArtifactCompositorTest, MergeSimpleChunks) { TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(2u, artifact->PaintChunks().size()); @@ -1306,10 +1412,10 @@ TEST_P(PaintArtifactCompositorTest, MergeClip) { auto clip = CreateClip(c0(), t0(), FloatRoundedRect(10, 20, 50, 60)); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(t0(), *clip, e0()) - .RectDrawing(FloatRect(0, 0, 200, 300), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 300, 400), Color::kGray); + .RectDrawing(IntRect(0, 0, 200, 300), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 300, 400), Color::kGray); auto artifact = test_artifact.Build(); @@ -1338,10 +1444,10 @@ TEST_P(PaintArtifactCompositorTest, Merge2DTransform) { FloatPoint3D(100, 100, 0)); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1373,11 +1479,11 @@ TEST_P(PaintArtifactCompositorTest, Merge2DTransformDirectAncestor) { TestPaintArtifact test_artifact; test_artifact.Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); // The second chunk can merge into the first because it has a descendant // state of the first's transform and no direct compositing reason. test_artifact.Chunk(*transform2, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); auto artifact = test_artifact.Build(); ASSERT_EQ(2u, artifact->PaintChunks().size()); @@ -1402,10 +1508,10 @@ TEST_P(PaintArtifactCompositorTest, MergeTransformOrigin) { FloatPoint3D(100, 100, 0)); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1432,10 +1538,10 @@ TEST_P(PaintArtifactCompositorTest, MergeOpacity) { auto effect = CreateOpacityEffect(e0(), opacity); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1464,10 +1570,10 @@ TEST_P(PaintArtifactCompositorTest, MergeOpacityWithAlias) { auto effect = EffectPaintPropertyNode::CreateAlias(*real_effect); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1505,10 +1611,10 @@ TEST_P(PaintArtifactCompositorTest, MergeNestedWithAlias) { auto effect = EffectPaintPropertyNode::CreateAlias(*real_effect); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(*transform, *clip, *effect) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1544,10 +1650,10 @@ TEST_P(PaintArtifactCompositorTest, ClipPushedUp) { auto clip = CreateClip(c0(), *transform2, FloatRoundedRect(10, 20, 50, 60)); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(t0(), *clip, e0()) - .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 300, 400), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1570,10 +1676,7 @@ TEST_P(PaintArtifactCompositorTest, ClipPushedUp) { } } -// TODO(crbug.com/696842): The effect refuses to "decomposite" because it's in -// a deeper transform space than its chunk. We should allow decomposite if -// the two transform nodes share the same direct compositing ancestor. -TEST_P(PaintArtifactCompositorTest, DISABLED_EffectPushedUp) { +TEST_P(PaintArtifactCompositorTest, EffectPushedUp) { // Tests merging of an element which has an effect applied to it, // but has an ancestor transform of them. This can happen for fixed- // or absolute-position elements which escape scroll transforms. @@ -1590,10 +1693,10 @@ TEST_P(PaintArtifactCompositorTest, DISABLED_EffectPushedUp) { auto effect = CreateOpacityEffect(e0(), *transform2, &c0(), opacity); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 300, 400), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1615,10 +1718,7 @@ TEST_P(PaintArtifactCompositorTest, DISABLED_EffectPushedUp) { } } -// TODO(crbug.com/696842): The effect refuses to "decomposite" because it's in -// a deeper transform space than its chunk. We should allow decomposite if -// the two transform nodes share the same direct compositing ancestor. -TEST_P(PaintArtifactCompositorTest, DISABLED_EffectAndClipPushedUp) { +TEST_P(PaintArtifactCompositorTest, EffectAndClipPushedUp) { // Tests merging of an element which has an effect applied to it, // but has an ancestor transform of them. This can happen for fixed- // or absolute-position elements which escape scroll transforms. @@ -1634,10 +1734,10 @@ TEST_P(PaintArtifactCompositorTest, DISABLED_EffectAndClipPushedUp) { auto effect = CreateOpacityEffect(e0(), *transform2, clip.get(), opacity); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(t0(), *clip, *effect) - .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 300, 400), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1669,10 +1769,10 @@ TEST_P(PaintArtifactCompositorTest, ClipAndEffectNoTransform) { auto effect = CreateOpacityEffect(e0(), t0(), clip.get(), opacity); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(t0(), *clip, *effect) - .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 300, 400), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1701,10 +1801,10 @@ TEST_P(PaintArtifactCompositorTest, TwoClips) { auto clip2 = CreateClip(*clip, t0(), FloatRoundedRect(10, 20, 50, 60)); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(t0(), *clip2, e0()) - .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 300, 400), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1735,10 +1835,10 @@ TEST_P(PaintArtifactCompositorTest, TwoTransformsClipBetween) { CreateTransform(*transform, TransformationMatrix().Translate(20, 25), FloatPoint3D(100, 100, 0)); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(*transform2, *clip, e0()) - .RectDrawing(FloatRect(0, 0, 300, 400), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 300, 400), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1764,10 +1864,10 @@ TEST_P(PaintArtifactCompositorTest, OverlapTransform) { CompositingReason::k3DTransform); TestPaintArtifact test_artifact; - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); test_artifact.Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 200, 300), Color::kGray); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 200, 300), Color::kGray); auto artifact = test_artifact.Build(); ASSERT_EQ(3u, artifact->PaintChunks().size()); @@ -1807,6 +1907,59 @@ TEST_P(PaintArtifactCompositorTest, MightOverlap) { PendingLayer pending_layer2(paint_chunk2, 1, false); EXPECT_FALSE(MightOverlap(pending_layer, pending_layer2)); } + + auto transform3 = + CreateAnimatingTransform(t0(), TransformationMatrix().Translate(100, 0), + FloatPoint3D(100, 100, 0)); + { + SetTransform(paint_chunk2, *transform3); + PendingLayer pending_layer2(paint_chunk2, 1, false); + EXPECT_TRUE(MightOverlap(pending_layer, pending_layer2)); + } +} + +TEST_P(PaintArtifactCompositorTest, UniteRectsKnownToBeOpaque) { + // X aligned and intersect: unite. + EXPECT_EQ(FloatRect(10, 20, 30, 60), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(10, 20, 30, 40), + FloatRect(10, 30, 30, 50))); + // X aligned and adjacent: unite. + EXPECT_EQ(FloatRect(10, 20, 30, 90), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(10, 20, 30, 40), + FloatRect(10, 60, 30, 50))); + // X aligned and separate: choose the bigger one. + EXPECT_EQ(FloatRect(10, 61, 30, 50), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(10, 20, 30, 40), + FloatRect(10, 61, 30, 50))); + // Y aligned and intersect: unite. + EXPECT_EQ(FloatRect(10, 20, 60, 40), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(10, 20, 30, 40), + FloatRect(30, 20, 40, 40))); + // Y aligned and adjacent: unite. + EXPECT_EQ(FloatRect(10, 20, 70, 40), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(10, 20, 30, 40), + FloatRect(40, 20, 40, 40))); + // Y aligned and separate: choose the bigger one. + EXPECT_EQ(FloatRect(41, 20, 40, 40), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(10, 20, 30, 40), + FloatRect(41, 20, 40, 40))); + // Get the biggest expanded intersection. + EXPECT_EQ(FloatRect(0, 0, 9, 19), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(0, 0, 10, 10), + FloatRect(0, 9, 9, 10))); + EXPECT_EQ(FloatRect(0, 0, 19, 9), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(0, 0, 10, 10), + FloatRect(9, 0, 10, 9))); + // Otherwise choose the bigger one. + EXPECT_EQ(FloatRect(20, 30, 40, 50), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(10, 20, 30, 40), + FloatRect(20, 30, 40, 50))); + EXPECT_EQ(FloatRect(10, 20, 40, 50), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(10, 20, 40, 50), + FloatRect(20, 30, 30, 40))); + EXPECT_EQ(FloatRect(10, 20, 40, 50), + PendingLayer::UniteRectsKnownToBeOpaque(FloatRect(10, 20, 40, 50), + FloatRect(20, 30, 40, 50))); } TEST_P(PaintArtifactCompositorTest, PendingLayer) { @@ -1825,49 +1978,164 @@ TEST_P(PaintArtifactCompositorTest, PendingLayer) { chunk2.properties = chunk1.properties; chunk2.known_to_be_opaque = true; chunk2.bounds = IntRect(10, 20, 30, 40); - pending_layer.Merge(PendingLayer(chunk2, 1, false)); + ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunk2, 1, false))); // Bounds not equal to one PaintChunk. EXPECT_EQ(FloatRect(0, 0, 40, 60), pending_layer.bounds); EXPECT_EQ((Vector<wtf_size_t>{0, 1}), pending_layer.paint_chunk_indices); - EXPECT_NE(pending_layer.bounds, pending_layer.rect_known_to_be_opaque); + EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.rect_known_to_be_opaque); PaintChunk chunk3 = DefaultChunk(); chunk3.properties = chunk1.properties; chunk3.known_to_be_opaque = true; chunk3.bounds = IntRect(-5, -25, 20, 20); - pending_layer.Merge(PendingLayer(chunk3, 2, false)); + ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunk3, 2, false))); EXPECT_EQ(FloatRect(-5, -25, 45, 85), pending_layer.bounds); EXPECT_EQ((Vector<wtf_size_t>{0, 1, 2}), pending_layer.paint_chunk_indices); - EXPECT_NE(pending_layer.bounds, pending_layer.rect_known_to_be_opaque); + EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.rect_known_to_be_opaque); } -TEST_P(PaintArtifactCompositorTest, PendingLayerWithGeometry) { - auto transform = - CreateTransform(t0(), TransformationMatrix().Translate(20, 25), - FloatPoint3D(100, 100, 0)); +TEST_P(PaintArtifactCompositorTest, PendingLayerMergeWithGuestTransform) { + auto transform = Create2DTranslation(t0(), 20, 25); PaintChunk chunk1 = DefaultChunk(); chunk1.properties = PropertyTreeState::Root(); chunk1.bounds = IntRect(0, 0, 30, 40); + PaintChunk chunk2 = DefaultChunk(); + chunk2.properties = chunk1.properties; + SetTransform(chunk2, *transform); + chunk2.bounds = IntRect(0, 0, 50, 60); + PendingLayer pending_layer(chunk1, 0, false); + ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunk2, 1, false))); + EXPECT_EQ(FloatRect(0, 0, 70, 85), pending_layer.bounds); + EXPECT_EQ(PropertyTreeState::Root(), pending_layer.property_tree_state); +} - EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.bounds); +TEST_P(PaintArtifactCompositorTest, PendingLayerMergeWithHomeTransform) { + auto transform = Create2DTranslation(t0(), 20, 25); + + PaintChunk chunk1 = DefaultChunk(); + chunk1.properties = PropertyTreeState::Root(); + SetTransform(chunk1, *transform); + chunk1.bounds = IntRect(0, 0, 30, 40); + + PaintChunk chunk2 = DefaultChunk(); + chunk2.properties = PropertyTreeState::Root(); + chunk2.bounds = IntRect(0, 0, 50, 60); + + PendingLayer pending_layer(chunk1, 0, false); + ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunk2, 1, false))); + EXPECT_EQ(FloatRect(0, 0, 50, 65), pending_layer.bounds); + EXPECT_EQ(PropertyTreeState::Root(), pending_layer.property_tree_state); +} + +TEST_P(PaintArtifactCompositorTest, PendingLayerMergeWithBothTransforms) { + auto t1 = Create2DTranslation(t0(), 20, 25); + auto t2 = Create2DTranslation(t0(), -20, -25); + + PaintChunk chunk1 = DefaultChunk(); + chunk1.properties = PropertyTreeState::Root(); + SetTransform(chunk1, *t1); + chunk1.bounds = IntRect(0, 0, 30, 40); + + PaintChunk chunk2 = DefaultChunk(); + chunk2.properties = PropertyTreeState::Root(); + SetTransform(chunk2, *t2); + chunk2.bounds = IntRect(0, 0, 50, 60); + + PendingLayer pending_layer(chunk1, 0, false); + ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunk2, 1, false))); + EXPECT_EQ(FloatRect(-20, -25, 70, 90), pending_layer.bounds); + EXPECT_EQ(PropertyTreeState::Root(), pending_layer.property_tree_state); +} + +TEST_P(PaintArtifactCompositorTest, PendingLayerDontMergeSparse) { + PaintChunk chunk1 = DefaultChunk(); + chunk1.properties = PropertyTreeState::Root(); // (t0(), c0(), *e1); + chunk1.known_to_be_opaque = true; + chunk1.bounds = IntRect(0, 0, 30, 40); PaintChunk chunk2 = DefaultChunk(); chunk2.properties = chunk1.properties; - SetTransform(chunk2, *transform); + chunk2.known_to_be_opaque = true; + chunk2.bounds = IntRect(200, 200, 30, 40); + + PendingLayer pending_layer(chunk1, 0, false); + ASSERT_FALSE(pending_layer.Merge(PendingLayer(chunk2, 1, false))); + EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.bounds); + EXPECT_EQ(chunk1.properties, pending_layer.property_tree_state); + EXPECT_EQ(Vector<wtf_size_t>{0}, pending_layer.paint_chunk_indices); +} + +TEST_P(PaintArtifactCompositorTest, PendingLayerDontMergeSparseWithTransforms) { + auto t1 = Create2DTranslation(t0(), 20, 25); + auto t2 = Create2DTranslation(t0(), 1000, 1000); + + PaintChunk chunk1 = DefaultChunk(); + chunk1.properties = PropertyTreeState::Root(); + SetTransform(chunk1, *t1); + chunk1.bounds = IntRect(0, 0, 30, 40); + + PaintChunk chunk2 = DefaultChunk(); + chunk2.properties = PropertyTreeState::Root(); + SetTransform(chunk2, *t2); chunk2.bounds = IntRect(0, 0, 50, 60); - pending_layer.Merge(PendingLayer(chunk2, 1, false)); - EXPECT_EQ(FloatRect(0, 0, 70, 85), pending_layer.bounds); + PendingLayer pending_layer(chunk1, 0, false); + ASSERT_FALSE(pending_layer.Merge(PendingLayer(chunk2, 1, false))); + EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.bounds); + EXPECT_EQ(chunk1.properties, pending_layer.property_tree_state); + EXPECT_EQ(Vector<wtf_size_t>{0}, pending_layer.paint_chunk_indices); +} + +TEST_P(PaintArtifactCompositorTest, + PendingLayerDontMergeSparseInCompositedEffect) { + auto t1 = Create2DTranslation(t0(), 20, 25); + auto t2 = Create2DTranslation(t0(), 1000, 1000); + auto e1 = + CreateOpacityEffect(e0(), 1.0f, CompositingReason::kWillChangeOpacity); + + PaintChunk chunk1 = DefaultChunk(); + chunk1.properties = PropertyTreeState(*t1, c0(), *e1); + chunk1.bounds = IntRect(0, 0, 30, 40); + + PaintChunk chunk2 = DefaultChunk(); + chunk2.properties = PropertyTreeState(*t2, c0(), *e1); + chunk2.bounds = IntRect(0, 0, 50, 60); + + PendingLayer pending_layer(chunk1, 0, false); + ASSERT_FALSE(pending_layer.Merge(PendingLayer(chunk2, 1, false))); + EXPECT_EQ(FloatRect(0, 0, 30, 40), pending_layer.bounds); + EXPECT_EQ(chunk1.properties, pending_layer.property_tree_state); + EXPECT_EQ(Vector<wtf_size_t>{0}, pending_layer.paint_chunk_indices); +} + +TEST_P(PaintArtifactCompositorTest, + PendingLayerMergeSparseInNonCompositedEffect) { + auto t1 = Create2DTranslation(t0(), 20, 25); + auto t2 = Create2DTranslation(t0(), 1000, 1000); + auto e1 = CreateOpacityEffect(e0(), 1.0f, CompositingReason::kNone); + + PaintChunk chunk1 = DefaultChunk(); + chunk1.properties = PropertyTreeState(*t1, c0(), *e1); + chunk1.bounds = IntRect(0, 0, 30, 40); + + PaintChunk chunk2 = DefaultChunk(); + chunk2.properties = PropertyTreeState(*t2, c0(), *e1); + chunk2.bounds = IntRect(0, 0, 50, 60); + + PendingLayer pending_layer(chunk1, 0, false); + ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunk2, 1, false))); + EXPECT_EQ(FloatRect(20, 25, 1030, 1035), pending_layer.bounds); + EXPECT_EQ(PropertyTreeState(t0(), c0(), *e1), + pending_layer.property_tree_state); + EXPECT_EQ((Vector<wtf_size_t>{0, 1}), pending_layer.paint_chunk_indices); } -// TODO(crbug.com/701991): -// The test is disabled because opaque rect mapping is not implemented yet. -TEST_P(PaintArtifactCompositorTest, DISABLED_PendingLayerKnownOpaque) { +TEST_P(PaintArtifactCompositorTest, PendingLayerKnownOpaque) { PaintChunk chunk1 = DefaultChunk(); chunk1.properties = PropertyTreeState::Root(); chunk1.bounds = IntRect(0, 0, 30, 40); @@ -1880,7 +2148,7 @@ TEST_P(PaintArtifactCompositorTest, DISABLED_PendingLayerKnownOpaque) { chunk2.properties = chunk1.properties; chunk2.bounds = IntRect(0, 0, 25, 35); chunk2.known_to_be_opaque = true; - pending_layer.Merge(PendingLayer(chunk2, 1, false)); + ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunk2, 1, false))); // Chunk 2 doesn't cover the entire layer, so not opaque. EXPECT_EQ(FloatRect(chunk2.bounds), pending_layer.rect_known_to_be_opaque); @@ -1890,7 +2158,7 @@ TEST_P(PaintArtifactCompositorTest, DISABLED_PendingLayerKnownOpaque) { chunk3.properties = chunk1.properties; chunk3.bounds = IntRect(0, 0, 50, 60); chunk3.known_to_be_opaque = true; - pending_layer.Merge(PendingLayer(chunk3, 2, false)); + ASSERT_TRUE(pending_layer.Merge(PendingLayer(chunk3, 2, false))); // Chunk 3 covers the entire layer, so now it's opaque. EXPECT_EQ(FloatRect(chunk3.bounds), pending_layer.bounds); @@ -1921,7 +2189,7 @@ TEST_P(PaintArtifactCompositorTest, TransformWithElementId) { auto transform = CreateSampleTransformNodeWithElementId(); TestPaintArtifact artifact; artifact.Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack); + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack); Update(artifact.Build()); EXPECT_EQ(2, @@ -1932,7 +2200,7 @@ TEST_P(PaintArtifactCompositorTest, EffectWithElementId) { auto effect = CreateSampleEffectNodeWithElementId(); TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack); + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack); Update(artifact.Build()); EXPECT_EQ(2, ElementIdToEffectNodeIndex(effect->GetCompositorElementId())); @@ -1943,7 +2211,7 @@ TEST_P(PaintArtifactCompositorTest, EffectWithElementIdWithAlias) { auto effect = EffectPaintPropertyNode::CreateAlias(*real_effect); TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack); + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack); Update(artifact.Build()); EXPECT_EQ(2, @@ -1963,9 +2231,9 @@ TEST_P(PaintArtifactCompositorTest, NonCompositedSimpleLuminanceMask) { TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *masked) - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kGray); + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); artifact.Chunk(t0(), c0(), *masking) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite); + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -1979,6 +2247,47 @@ TEST_P(PaintArtifactCompositorTest, NonCompositedSimpleLuminanceMask) { const cc::EffectNode* masked_group = GetPropertyTrees().effect_tree.Node(layer->effect_tree_index()); EXPECT_FALSE(masked_group->HasRenderSurface()); + EXPECT_EQ(SkBlendMode::kSrcOver, masked_group->blend_mode); + EXPECT_TRUE(masked_group->filters.IsEmpty()); + // It's the last effect node. |masking| has been decomposited. + EXPECT_EQ(masked_group, GetPropertyTrees().effect_tree.back()); +} + +TEST_P(PaintArtifactCompositorTest, CompositedLuminanceMaskOneChild) { + auto masked = CreateOpacityEffect( + e0(), 1.0, CompositingReason::kIsolateCompositedDescendants); + EffectPaintPropertyNode::State masking_state; + masking_state.local_transform_space = &t0(); + masking_state.output_clip = &c0(); + masking_state.color_filter = kColorFilterLuminanceToAlpha; + masking_state.blend_mode = SkBlendMode::kDstIn; + masking_state.direct_compositing_reasons = CompositingReason::kLayerForMask; + auto masking = + EffectPaintPropertyNode::Create(*masked, std::move(masking_state)); + + TestPaintArtifact artifact; + artifact.Chunk(t0(), c0(), *masked) + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); + artifact.Chunk(t0(), c0(), *masking) + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); + Update(artifact.Build()); + ASSERT_EQ(2u, LayerCount()); + + const cc::Layer* masking_layer = LayerAt(1); + const cc::EffectNode* masking_group = + GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index()); + + // Render surface is not needed for one child. + EXPECT_FALSE(masking_group->HasRenderSurface()); + ASSERT_EQ(1u, masking_group->filters.size()); + EXPECT_EQ(cc::FilterOperation::REFERENCE, + masking_group->filters.at(0).type()); + EXPECT_EQ(SkBlendMode::kDstIn, masking_group->blend_mode); + + // The parent also has a render surface to define the scope of the backdrop + // of the kDstIn blend mode. + EXPECT_TRUE( + GetPropertyTrees().effect_tree.parent(masking_group)->HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, CompositedLuminanceMaskTwoChildren) { @@ -1997,11 +2306,11 @@ TEST_P(PaintArtifactCompositorTest, CompositedLuminanceMaskTwoChildren) { TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *masked) - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kGray); + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); artifact.Chunk(t0(), c0(), *child_of_masked) - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kGray); + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); artifact.Chunk(t0(), c0(), *masking) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite); + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(3u, LayerCount()); @@ -2014,6 +2323,82 @@ TEST_P(PaintArtifactCompositorTest, CompositedLuminanceMaskTwoChildren) { ASSERT_EQ(1u, masking_group->filters.size()); EXPECT_EQ(cc::FilterOperation::REFERENCE, masking_group->filters.at(0).type()); + EXPECT_EQ(SkBlendMode::kDstIn, masking_group->blend_mode); + + // The parent also has a render surface to define the scope of the backdrop + // of the kDstIn blend mode. + EXPECT_TRUE( + GetPropertyTrees().effect_tree.parent(masking_group)->HasRenderSurface()); +} + +TEST_P(PaintArtifactCompositorTest, CompositedMaskOneChild) { + auto masked = CreateOpacityEffect( + e0(), 1.0, CompositingReason::kIsolateCompositedDescendants); + EffectPaintPropertyNode::State masking_state; + masking_state.local_transform_space = &t0(); + masking_state.output_clip = &c0(); + masking_state.blend_mode = SkBlendMode::kDstIn; + masking_state.direct_compositing_reasons = CompositingReason::kLayerForMask; + auto masking = + EffectPaintPropertyNode::Create(*masked, std::move(masking_state)); + + TestPaintArtifact artifact; + artifact.Chunk(t0(), c0(), *masked) + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); + artifact.Chunk(t0(), c0(), *masking) + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); + Update(artifact.Build()); + ASSERT_EQ(2u, LayerCount()); + + const cc::Layer* masking_layer = LayerAt(1); + const cc::EffectNode* masking_group = + GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index()); + + // Render surface is not needed for one child. + EXPECT_FALSE(masking_group->HasRenderSurface()); + EXPECT_EQ(SkBlendMode::kDstIn, masking_group->blend_mode); + + // The parent also has a render surface to define the scope of the backdrop + // of the kDstIn blend mode. + EXPECT_TRUE( + GetPropertyTrees().effect_tree.parent(masking_group)->HasRenderSurface()); +} + +TEST_P(PaintArtifactCompositorTest, CompositedMaskTwoChildren) { + auto masked = CreateOpacityEffect( + e0(), 1.0, CompositingReason::kIsolateCompositedDescendants); + EffectPaintPropertyNode::State masking_state; + masking_state.local_transform_space = &t0(); + masking_state.output_clip = &c0(); + masking_state.blend_mode = SkBlendMode::kDstIn; + auto masking = + EffectPaintPropertyNode::Create(*masked, std::move(masking_state)); + + auto child_of_masked = CreateOpacityEffect( + *masking, 1.0, CompositingReason::kIsolateCompositedDescendants); + + TestPaintArtifact artifact; + artifact.Chunk(t0(), c0(), *masked) + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); + artifact.Chunk(t0(), c0(), *child_of_masked) + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); + artifact.Chunk(t0(), c0(), *masking) + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); + Update(artifact.Build()); + ASSERT_EQ(3u, LayerCount()); + + const cc::Layer* masking_layer = LayerAt(2); + const cc::EffectNode* masking_group = + GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index()); + + // There is a render surface because there are two children. + EXPECT_TRUE(masking_group->HasRenderSurface()); + EXPECT_EQ(SkBlendMode::kDstIn, masking_group->blend_mode); + + // The parent also has a render surface to define the scope of the backdrop + // of the kDstIn blend mode. + EXPECT_TRUE( + GetPropertyTrees().effect_tree.parent(masking_group)->HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, NonCompositedSimpleExoticBlendMode) { @@ -2028,9 +2413,9 @@ TEST_P(PaintArtifactCompositorTest, NonCompositedSimpleExoticBlendMode) { TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *masked) - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kGray); + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); artifact.Chunk(t0(), c0(), *masking) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite); + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -2038,6 +2423,9 @@ TEST_P(PaintArtifactCompositorTest, NonCompositedSimpleExoticBlendMode) { const cc::EffectNode* group = GetPropertyTrees().effect_tree.Node(layer->effect_tree_index()); EXPECT_FALSE(group->HasRenderSurface()); + EXPECT_EQ(SkBlendMode::kSrcOver, group->blend_mode); + // It's the last effect node. |masking| has been decomposited. + EXPECT_EQ(group, GetPropertyTrees().effect_tree.back()); } TEST_P(PaintArtifactCompositorTest, ForcedCompositedExoticBlendMode) { @@ -2054,9 +2442,9 @@ TEST_P(PaintArtifactCompositorTest, ForcedCompositedExoticBlendMode) { TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *masked) - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kGray); + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); artifact.Chunk(t0(), c0(), *masking) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite); + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(2u, LayerCount()); @@ -2065,8 +2453,12 @@ TEST_P(PaintArtifactCompositorTest, ForcedCompositedExoticBlendMode) { GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index()); EXPECT_EQ(SkBlendMode::kXor, masking_group->blend_mode); - /// This requires a render surface. + // This requires a render surface. EXPECT_TRUE(masking_group->HasRenderSurface()); + // The parent also requires a render surface to define the backdrop scope of + // the blend mode. + EXPECT_TRUE( + GetPropertyTrees().effect_tree.parent(masking_group)->HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, @@ -2086,11 +2478,11 @@ TEST_P(PaintArtifactCompositorTest, TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *masked_child1) - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kGray); + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); artifact.Chunk(t0(), c0(), *masked_child2) - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack); + .RectDrawing(IntRect(100, 100, 200, 200), Color::kBlack); artifact.Chunk(t0(), c0(), *masking) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite); + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(3u, LayerCount()); @@ -2099,8 +2491,12 @@ TEST_P(PaintArtifactCompositorTest, GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index()); EXPECT_EQ(SkBlendMode::kXor, masking_group->blend_mode); - /// This requires a render surface. + // This requires a render surface. EXPECT_TRUE(masking_group->HasRenderSurface()); + // The parent also requires a render surface to define the backdrop scope of + // the blend mode. + EXPECT_TRUE( + GetPropertyTrees().effect_tree.parent(masking_group)->HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, @@ -2122,11 +2518,11 @@ TEST_P(PaintArtifactCompositorTest, TestPaintArtifact artifact; artifact.Chunk(*transform1, c0(), *masked) - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kGray); + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray); artifact.Chunk(*transform2, c0(), *masked) - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack); + .RectDrawing(IntRect(100, 100, 200, 200), Color::kBlack); artifact.Chunk(t0(), c0(), *masking) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite); + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(3u, LayerCount()); @@ -2135,8 +2531,42 @@ TEST_P(PaintArtifactCompositorTest, GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index()); EXPECT_EQ(SkBlendMode::kXor, masking_group->blend_mode); - /// This requires a render surface. + // This requires a render surface. EXPECT_TRUE(masking_group->HasRenderSurface()); + // The parent also requires a render surface to define the backdrop scope of + // the blend mode. + EXPECT_TRUE( + GetPropertyTrees().effect_tree.parent(masking_group)->HasRenderSurface()); +} + +TEST_P(PaintArtifactCompositorTest, DecompositeExoticBlendModeWithoutBackdrop) { + auto parent_effect = CreateOpacityEffect( + e0(), 1.0, CompositingReason::kIsolateCompositedDescendants); + EffectPaintPropertyNode::State blend_state1; + blend_state1.local_transform_space = &t0(); + blend_state1.blend_mode = SkBlendMode::kScreen; + auto blend_effect1 = + EffectPaintPropertyNode::Create(*parent_effect, std::move(blend_state1)); + EffectPaintPropertyNode::State blend_state2; + blend_state2.local_transform_space = &t0(); + blend_state2.blend_mode = SkBlendMode::kScreen; + auto blend_effect2 = + EffectPaintPropertyNode::Create(*parent_effect, std::move(blend_state2)); + + Update(TestPaintArtifact() + .Chunk(t0(), c0(), *blend_effect1) + .RectDrawing(IntRect(100, 100, 200, 200), Color::kGray) + .Chunk(t0(), c0(), *blend_effect2) + .RectDrawing(IntRect(100, 100, 200, 200), Color::kBlack) + .Build()); + + ASSERT_EQ(1u, LayerCount()); + const auto* effect = + GetPropertyTrees().effect_tree.Node(LayerAt(0)->effect_tree_index()); + EXPECT_EQ(1.0f, effect->opacity); + EXPECT_EQ(SkBlendMode::kSrcOver, effect->blend_mode); + // Don't need a render surface because all blend effects are decomposited. + EXPECT_FALSE(effect->HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, UpdateProducesNewSequenceNumber) { @@ -2149,8 +2579,8 @@ TEST_P(PaintArtifactCompositorTest, UpdateProducesNewSequenceNumber) { TestPaintArtifact test_artifact; test_artifact.Chunk(*transform, *clip, *effect) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kWhite); - test_artifact.Chunk().RectDrawing(FloatRect(0, 0, 100, 100), Color::kGray); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kWhite); + test_artifact.Chunk().RectDrawing(IntRect(0, 0, 100, 100), Color::kGray); auto artifact = test_artifact.Build(); Update(artifact); @@ -2189,9 +2619,9 @@ TEST_P(PaintArtifactCompositorTest, DecompositeClip) { auto clip = CreateClip(c0(), t0(), FloatRoundedRect(75, 75, 100, 100)); TestPaintArtifact artifact; - artifact.Chunk().RectDrawing(FloatRect(50, 50, 100, 100), Color::kGray); + artifact.Chunk().RectDrawing(IntRect(50, 50, 100, 100), Color::kGray); artifact.Chunk(t0(), *clip, e0()) - .RectDrawing(FloatRect(100, 100, 100, 100), Color::kGray); + .RectDrawing(IntRect(100, 100, 100, 100), Color::kGray); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -2208,10 +2638,10 @@ TEST_P(PaintArtifactCompositorTest, DecompositeEffect) { auto effect = CreateOpacityEffect(e0(), 0.5); TestPaintArtifact artifact; - artifact.Chunk().RectDrawing(FloatRect(50, 25, 100, 100), Color::kGray); + artifact.Chunk().RectDrawing(IntRect(50, 25, 100, 100), Color::kGray); artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(25, 75, 100, 100), Color::kGray); - artifact.Chunk().RectDrawing(FloatRect(75, 75, 100, 100), Color::kGray); + .RectDrawing(IntRect(25, 75, 100, 100), Color::kGray); + artifact.Chunk().RectDrawing(IntRect(75, 75, 100, 100), Color::kGray); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -2226,10 +2656,10 @@ TEST_P(PaintArtifactCompositorTest, DirectlyCompositedEffect) { auto effect = CreateOpacityEffect(e0(), 0.5f, CompositingReason::kAll); TestPaintArtifact artifact; - artifact.Chunk().RectDrawing(FloatRect(50, 25, 100, 100), Color::kGray); + artifact.Chunk().RectDrawing(IntRect(50, 25, 100, 100), Color::kGray); artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(25, 75, 100, 100), Color::kGray); - artifact.Chunk().RectDrawing(FloatRect(75, 75, 100, 100), Color::kGray); + .RectDrawing(IntRect(25, 75, 100, 100), Color::kGray); + artifact.Chunk().RectDrawing(IntRect(75, 75, 100, 100), Color::kGray); Update(artifact.Build()); ASSERT_EQ(3u, LayerCount()); @@ -2261,10 +2691,10 @@ TEST_P(PaintArtifactCompositorTest, DecompositeDeepEffect) { auto effect3 = CreateOpacityEffect(*effect2, 0.3f); TestPaintArtifact artifact; - artifact.Chunk().RectDrawing(FloatRect(50, 25, 100, 100), Color::kGray); + artifact.Chunk().RectDrawing(IntRect(50, 25, 100, 100), Color::kGray); artifact.Chunk(t0(), c0(), *effect3) - .RectDrawing(FloatRect(25, 75, 100, 100), Color::kGray); - artifact.Chunk().RectDrawing(FloatRect(75, 75, 100, 100), Color::kGray); + .RectDrawing(IntRect(25, 75, 100, 100), Color::kGray); + artifact.Chunk().RectDrawing(IntRect(75, 75, 100, 100), Color::kGray); Update(artifact.Build()); ASSERT_EQ(3u, LayerCount()); @@ -2298,11 +2728,11 @@ TEST_P(PaintArtifactCompositorTest, IndirectlyCompositedEffect) { CompositingReason::k3DTransform); TestPaintArtifact artifact; - artifact.Chunk().RectDrawing(FloatRect(50, 25, 100, 100), Color::kGray); + artifact.Chunk().RectDrawing(IntRect(50, 25, 100, 100), Color::kGray); artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(25, 75, 100, 100), Color::kGray); + .RectDrawing(IntRect(25, 75, 100, 100), Color::kGray); artifact.Chunk(*transform, c0(), *effect) - .RectDrawing(FloatRect(75, 75, 100, 100), Color::kGray); + .RectDrawing(IntRect(75, 75, 100, 100), Color::kGray); Update(artifact.Build()); ASSERT_EQ(3u, LayerCount()); @@ -2333,20 +2763,20 @@ TEST_P(PaintArtifactCompositorTest, DecompositedEffectNotMergingDueToOverlap) { auto transform = CreateTransform(t0(), TransformationMatrix(), FloatPoint3D(), CompositingReason::k3DTransform); TestPaintArtifact artifact; - artifact.Chunk().RectDrawing(FloatRect(0, 0, 50, 50), Color::kGray); + artifact.Chunk().RectDrawing(IntRect(0, 0, 50, 50), Color::kGray); artifact.Chunk(t0(), c0(), *effect1) - .RectDrawing(FloatRect(100, 0, 50, 50), Color::kGray); + .RectDrawing(IntRect(100, 0, 50, 50), Color::kGray); // This chunk has a transform that must be composited, thus causing effect1 // to be composited too. artifact.Chunk(*transform, c0(), *effect1) - .RectDrawing(FloatRect(200, 0, 50, 50), Color::kGray); + .RectDrawing(IntRect(200, 0, 50, 50), Color::kGray); artifact.Chunk(t0(), c0(), *effect2) - .RectDrawing(FloatRect(200, 100, 50, 50), Color::kGray); + .RectDrawing(IntRect(200, 100, 50, 50), Color::kGray); // This chunk overlaps with the 2nd chunk, but is seemingly safe to merge. // However because effect1 gets composited due to a composited transform, // we can't merge with effect1 nor skip it to merge with the first chunk. artifact.Chunk(t0(), c0(), *effect2) - .RectDrawing(FloatRect(100, 0, 50, 50), Color::kGray); + .RectDrawing(IntRect(100, 0, 50, 50), Color::kGray); Update(artifact.Build()); ASSERT_EQ(4u, LayerCount()); @@ -2476,7 +2906,7 @@ TEST_P(PaintArtifactCompositorTest, auto effect = CreateOpacityEffect(e0(), 0.0001f, CompositingReason::kCanvas); TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); } @@ -2489,7 +2919,7 @@ TEST_P(PaintArtifactCompositorTest, CreateOpacityEffect(*tiny_effect, 0.5f, CompositingReason::kNone); TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *visible_effect) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) EXPECT_EQ(0u, LayerCount()); @@ -2505,7 +2935,7 @@ TEST_P( auto visible_effect = CreateOpacityEffect(*tiny_effect, 0.5f); TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *visible_effect) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); } @@ -2517,7 +2947,7 @@ TEST_P(PaintArtifactCompositorTest, CreateOpacityEffect(*tiny_effect, 0.5f, CompositingReason::kCanvas); TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *visible_effect) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) EXPECT_EQ(0u, LayerCount()); @@ -2532,7 +2962,7 @@ TEST_P(PaintArtifactCompositorTest, UpdateManagesLayerElementIds) { { TestPaintArtifact artifact; artifact.Chunk(*transform, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -2552,7 +2982,7 @@ TEST_P(PaintArtifactCompositorTest, UpdateManagesLayerElementIds) { } TEST_P(PaintArtifactCompositorTest, SynthesizedClipSimple) { - // This tests the simplist case that a single layer needs to be clipped + // This tests the simplest case that a single layer needs to be clipped // by a single composited rounded clip. FloatSize corner(5, 5); FloatRoundedRect rrect(FloatRect(50, 50, 300, 200), corner, corner, corner, @@ -2561,55 +2991,20 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipSimple) { TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); - if (RuntimeEnabledFeatures::FastBorderRadiusEnabled()) { - // Expectation in effect stack diagram: - // l0 - // [ mask_isolation_0 ] - // [ e0 ] - // One content layer. - ASSERT_EQ(1u, LayerCount()); - // There is still a "synthesized layer" but it's null. - ASSERT_EQ(1u, SynthesizedClipLayerCount()); - EXPECT_FALSE(SynthesizedClipLayerAt(0)); - - const cc::Layer* content0 = LayerAt(0); - - constexpr int c0_id = 1; - constexpr int e0_id = 1; - - int c1_id = content0->clip_tree_index(); - const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); - EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); - ASSERT_EQ(c0_id, cc_c1.parent_id); - int mask_isolation_0_id = content0->effect_tree_index(); - const cc::EffectNode& mask_isolation_0 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); - ASSERT_EQ(e0_id, mask_isolation_0.parent_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_0.rounded_corner_bounds); - EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); - return; - } - // Expectation in effect stack diagram: - // l1 - // l0 [ mask_effect_0 ] + // content0 // [ mask_isolation_0 ] // [ e0 ] // One content layer. - ASSERT_EQ(2u, LayerCount()); + ASSERT_EQ(1u, LayerCount()); + // There is still a "synthesized layer" but it's null. ASSERT_EQ(1u, SynthesizedClipLayerCount()); + EXPECT_FALSE(SynthesizedClipLayerAt(0)); const cc::Layer* content0 = LayerAt(0); - const cc::Layer* clip_mask0 = LayerAt(1); - - constexpr int c0_id = 1; - constexpr int e0_id = 1; int c1_id = content0->clip_tree_index(); const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); @@ -2620,20 +3015,10 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipSimple) { *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); ASSERT_EQ(e0_id, mask_isolation_0.parent_id); EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_TRUE(mask_isolation_0.HasRenderSurface()); - - EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0); - EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds()); - EXPECT_EQ(c1_id, clip_mask0->clip_tree_index()); - int mask_effect_0_id = clip_mask0->effect_tree_index(); - const cc::EffectNode& mask_effect_0 = - *GetPropertyTrees().effect_tree.Node(mask_effect_0_id); - ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode); - - // The masks DrawsContent because it has content that it masks which also - // DrawsContent. - EXPECT_TRUE(clip_mask0->DrawsContent()); + EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_0.rounded_corner_bounds); + EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, SynthesizedClipRotatedNotSupported) { @@ -2650,14 +3035,14 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipRotatedNotSupported) { TestPaintArtifact artifact; artifact.Chunk(*transform, *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); // Expectation in effect stack diagram: - // l1 - // l0 [ mask_effect_0 ] - // [ mask_isolation_0 ] - // [ e0 ] + // clip_mask0 + // content0 [ mask_effect_0 ] + // [ mask_isolation_0 ] + // [ e0 ] // One content layer. ASSERT_EQ(2u, LayerCount()); ASSERT_EQ(1u, SynthesizedClipLayerCount()); @@ -2665,9 +3050,6 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipRotatedNotSupported) { const cc::Layer* content0 = LayerAt(0); const cc::Layer* clip_mask0 = LayerAt(1); - constexpr int c0_id = 1; - constexpr int e0_id = 1; - int c1_id = content0->clip_tree_index(); const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); @@ -2687,6 +3069,8 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipRotatedNotSupported) { *GetPropertyTrees().effect_tree.Node(mask_effect_0_id); ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id); EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode); + // Render surface is not needed for DstIn controlling only one layer. + EXPECT_FALSE(mask_effect_0.HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, SynthesizedClip90DegRotationSupported) { @@ -2703,55 +3087,20 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClip90DegRotationSupported) { TestPaintArtifact artifact; artifact.Chunk(*transform, *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); - if (RuntimeEnabledFeatures::FastBorderRadiusEnabled()) { - // Expectation in effect stack diagram: - // l0 - // [ mask_isolation_0 ] - // [ e0 ] - // One content layer. - ASSERT_EQ(1u, LayerCount()); - // There is still a "synthesized layer" but it's null. - ASSERT_EQ(1u, SynthesizedClipLayerCount()); - EXPECT_FALSE(SynthesizedClipLayerAt(0)); - - const cc::Layer* content0 = LayerAt(0); - - constexpr int c0_id = 1; - constexpr int e0_id = 1; - - int c1_id = content0->clip_tree_index(); - const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); - EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); - ASSERT_EQ(c0_id, cc_c1.parent_id); - int mask_isolation_0_id = content0->effect_tree_index(); - const cc::EffectNode& mask_isolation_0 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); - ASSERT_EQ(e0_id, mask_isolation_0.parent_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_0.rounded_corner_bounds); - EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); - return; - } - // Expectation in effect stack diagram: - // l1 - // l0 [ mask_effect_0 ] + // content0 // [ mask_isolation_0 ] // [ e0 ] // One content layer. - ASSERT_EQ(2u, LayerCount()); + ASSERT_EQ(1u, LayerCount()); + // There is still a "synthesized layer" but it's null. ASSERT_EQ(1u, SynthesizedClipLayerCount()); + EXPECT_FALSE(SynthesizedClipLayerAt(0)); const cc::Layer* content0 = LayerAt(0); - const cc::Layer* clip_mask0 = LayerAt(1); - - constexpr int c0_id = 1; - constexpr int e0_id = 1; int c1_id = content0->clip_tree_index(); const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); @@ -2762,21 +3111,15 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClip90DegRotationSupported) { *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); ASSERT_EQ(e0_id, mask_isolation_0.parent_id); EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_TRUE(mask_isolation_0.HasRenderSurface()); - - EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0); - EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds()); - EXPECT_EQ(c1_id, clip_mask0->clip_tree_index()); - int mask_effect_0_id = clip_mask0->effect_tree_index(); - const cc::EffectNode& mask_effect_0 = - *GetPropertyTrees().effect_tree.Node(mask_effect_0_id); - ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode); + EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_0.rounded_corner_bounds); + EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, - SynthesizedClipSimpleFastBorderNotSupported2) { - // This tests the simplist case that a single layer needs to be clipped + SynthesizedClipShaderBasedBorderRadiusNotSupported2) { + // This tests the simplest case that a single layer needs to be clipped // by a single composited rounded clip. Because the radius is unsymmetric, // it falls back to a mask layer. FloatSize corner(30, 40); @@ -2786,14 +3129,14 @@ TEST_P(PaintArtifactCompositorTest, TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); // Expectation in effect stack diagram: - // l1 - // l0 [ mask_effect_0 ] - // [ mask_isolation_0 ] - // [ e0 ] + // clip_mask0 + // content0 [ mask_effect_0 ] + // [ mask_isolation_0 ] + // [ e0 ] // One content layer. ASSERT_EQ(2u, LayerCount()); ASSERT_EQ(1u, SynthesizedClipLayerCount()); @@ -2801,9 +3144,6 @@ TEST_P(PaintArtifactCompositorTest, const cc::Layer* content0 = LayerAt(0); const cc::Layer* clip_mask0 = LayerAt(1); - constexpr int c0_id = 1; - constexpr int e0_id = 1; - int c1_id = content0->clip_tree_index(); const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); @@ -2829,8 +3169,9 @@ TEST_P(PaintArtifactCompositorTest, EXPECT_TRUE(clip_mask0->DrawsContent()); } -TEST_P(PaintArtifactCompositorTest, - SynthesizedClipSimpleFastBorderNotSupportedMacNonEqualCorners) { +TEST_P( + PaintArtifactCompositorTest, + SynthesizedClipSimpleShaderBasedBorderRadiusNotSupportedMacNonEqualCorners) { // Tests that on Mac, we fall back to a mask layer if the corners are not all // the same radii. FloatSize corner(30, 30); @@ -2840,25 +3181,19 @@ TEST_P(PaintArtifactCompositorTest, TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); #if defined(OS_MACOSX) ASSERT_EQ(2u, LayerCount()); #else - if (RuntimeEnabledFeatures::FastBorderRadiusEnabled()) - ASSERT_EQ(1u, LayerCount()); - else - ASSERT_EQ(2u, LayerCount()); + ASSERT_EQ(1u, LayerCount()); #endif } TEST_P(PaintArtifactCompositorTest, SynthesizedClipNested) { - // This tests the simplist case that a single layer needs to be clipped + // This tests the simplest case that a single layer needs to be clipped // by a single composited rounded clip. - if (!RuntimeEnabledFeatures::FastBorderRadiusEnabled()) - return; - FloatSize corner(5, 5); FloatRoundedRect rrect(FloatRect(50, 50, 300, 200), corner, corner, corner, corner); @@ -2873,18 +3208,18 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipNested) { TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, *filter) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); artifact.Chunk(*t1, *c3, *filter) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); // Expectation in effect stack diagram: - // l0 - // [ mask_isolation_2 ] - // l1 [ mask_isolation_1 ] - // [ filter ] - // [ mask_isolation_0 ] - // [ e0 ] + // content1 + // [ mask_isolation_2 ] + // content0 [ mask_isolation_1 ] + // [ filter ] + // [ mask_isolation_0 ] + // [ e0 ] // Two content layers. /// // mask_isolation_1 will have a render surface. mask_isolation_2 will not @@ -2902,9 +3237,7 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipNested) { const cc::Layer* content0 = LayerAt(0); const cc::Layer* content1 = LayerAt(1); - constexpr int c0_id = 1; constexpr int c1_id = 2; - constexpr int e0_id = 1; constexpr int e1_id = 2; int c3_id = content1->clip_tree_index(); @@ -2965,11 +3298,11 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipIsNotDrawable) { TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 0, 0), Color::kBlack); + .RectDrawing(IntRect(0, 0, 0, 0), Color::kBlack); Update(artifact.Build()); // Expectation in effect stack diagram: - // l1 + // content0 // [ mask_isolation_0 ] // [ e0 ] // One content layer, no clip mask (because layer doesn't draw content). @@ -2980,9 +3313,6 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipIsNotDrawable) { const cc::Layer* content0 = LayerAt(0); - constexpr int c0_id = 1; - constexpr int e0_id = 1; - int c1_id = content0->clip_tree_index(); const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); @@ -3005,7 +3335,7 @@ TEST_P(PaintArtifactCompositorTest, ReuseSyntheticClip) { TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 0, 0), Color::kBlack); + .RectDrawing(IntRect(0, 0, 0, 0), Color::kBlack); Update(artifact.Build()); const cc::Layer* content0 = LayerAt(0); @@ -3016,7 +3346,7 @@ TEST_P(PaintArtifactCompositorTest, ReuseSyntheticClip) { TestPaintArtifact repeated_artifact; repeated_artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 0, 0), Color::kBlack); + .RectDrawing(IntRect(0, 0, 0, 0), Color::kBlack); Update(repeated_artifact.Build()); const cc::Layer* content1 = LayerAt(0); @@ -3028,7 +3358,7 @@ TEST_P(PaintArtifactCompositorTest, ReuseSyntheticClip) { TestPaintArtifact changed_artifact; changed_artifact.Chunk(t0(), *c2, e0()) - .RectDrawing(FloatRect(0, 0, 0, 0), Color::kBlack); + .RectDrawing(IntRect(0, 0, 0, 0), Color::kBlack); Update(changed_artifact.Build()); const cc::Layer* content2 = LayerAt(0); @@ -3050,14 +3380,14 @@ TEST_P(PaintArtifactCompositorTest, TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, *e1) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); // Expectation in effect stack diagram: - // l0 l1 - // [ e1 ][ mask_effect_0 ] - // [ mask_isolation_0 ] - // [ e0 ] + // content0 clip_mask0 + // [ e1 ][ mask_effect_0 ] + // [ mask_isolation_0 ] + // [ e0 ] // One content layer, one clip mask. ASSERT_EQ(2u, LayerCount()); ASSERT_EQ(1u, SynthesizedClipLayerCount()); @@ -3065,9 +3395,6 @@ TEST_P(PaintArtifactCompositorTest, const cc::Layer* content0 = LayerAt(0); const cc::Layer* clip_mask0 = LayerAt(1); - constexpr int c0_id = 1; - constexpr int e0_id = 1; - int c1_id = content0->clip_tree_index(); const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); @@ -3106,71 +3433,23 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipContiguous) { TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); artifact.Chunk(*t1, *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); - if (RuntimeEnabledFeatures::FastBorderRadiusEnabled()) { - // Expectation in effect stack diagram: - // l2 - // l0 l1 - // [ mask_isolation_0 ] - // [ e0 ] - // Two content layers, one clip mask. - ASSERT_EQ(2u, LayerCount()); - // There is still a "synthesized layer" but it's null. - ASSERT_EQ(1u, SynthesizedClipLayerCount()); - EXPECT_FALSE(SynthesizedClipLayerAt(0)); - - const cc::Layer* content0 = LayerAt(0); - const cc::Layer* content1 = LayerAt(1); - - constexpr int t0_id = 1; - constexpr int c0_id = 1; - constexpr int e0_id = 1; - - EXPECT_EQ(t0_id, content0->transform_tree_index()); - int c1_id = content0->clip_tree_index(); - const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); - EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); - ASSERT_EQ(c0_id, cc_c1.parent_id); - int mask_isolation_0_id = content0->effect_tree_index(); - const cc::EffectNode& mask_isolation_0 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); - ASSERT_EQ(e0_id, mask_isolation_0.parent_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - - int t1_id = content1->transform_tree_index(); - const cc::TransformNode& cc_t1 = - *GetPropertyTrees().transform_tree.Node(t1_id); - ASSERT_EQ(t0_id, cc_t1.parent_id); - EXPECT_EQ(c1_id, content1->clip_tree_index()); - EXPECT_EQ(mask_isolation_0_id, content1->effect_tree_index()); - - EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_0.rounded_corner_bounds); - EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); - return; - } - // Expectation in effect stack diagram: - // l2 - // l0 l1 [ mask_effect_0 ] - // [ mask_isolation_0 ] - // [ e0 ] + // content0 content1 + // [ mask_isolation_0 ] + // [ e0 ] // Two content layers, one clip mask. - ASSERT_EQ(3u, LayerCount()); + ASSERT_EQ(2u, LayerCount()); + // There is still a "synthesized layer" but it's null. ASSERT_EQ(1u, SynthesizedClipLayerCount()); + EXPECT_FALSE(SynthesizedClipLayerAt(0)); const cc::Layer* content0 = LayerAt(0); const cc::Layer* content1 = LayerAt(1); - const cc::Layer* clip_mask0 = LayerAt(2); - - constexpr int t0_id = 1; - constexpr int c0_id = 1; - constexpr int e0_id = 1; EXPECT_EQ(t0_id, content0->transform_tree_index()); int c1_id = content0->clip_tree_index(); @@ -3190,15 +3469,10 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipContiguous) { EXPECT_EQ(c1_id, content1->clip_tree_index()); EXPECT_EQ(mask_isolation_0_id, content1->effect_tree_index()); - EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0); - EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds()); - EXPECT_EQ(t0_id, clip_mask0->transform_tree_index()); - EXPECT_EQ(c1_id, clip_mask0->clip_tree_index()); - int mask_effect_0_id = clip_mask0->effect_tree_index(); - const cc::EffectNode& mask_effect_0 = - *GetPropertyTrees().effect_tree.Node(mask_effect_0_id); - ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode); + EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_0.rounded_corner_bounds); + EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, SynthesizedClipDiscontiguous) { @@ -3215,89 +3489,28 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDiscontiguous) { TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); artifact.Chunk(*t1, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); - if (RuntimeEnabledFeatures::FastBorderRadiusEnabled()) { - // Expectation in effect stack diagram: - // l1 l4 - // l0 l3 - // [ mask_isolation_0 ] l2 [ mask_isolation_1 ] - // [ e0 ] - // Three content layers. - ASSERT_EQ(3u, LayerCount()); - // There are still "synthesized layers" but they're null. - ASSERT_EQ(2u, SynthesizedClipLayerCount()); - EXPECT_FALSE(SynthesizedClipLayerAt(0)); - EXPECT_FALSE(SynthesizedClipLayerAt(1)); - - const cc::Layer* content0 = LayerAt(0); - const cc::Layer* content1 = LayerAt(1); - const cc::Layer* content2 = LayerAt(2); - - constexpr int t0_id = 1; - constexpr int c0_id = 1; - constexpr int e0_id = 1; - - EXPECT_EQ(t0_id, content0->transform_tree_index()); - int c1_id = content0->clip_tree_index(); - const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); - EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); - ASSERT_EQ(c0_id, cc_c1.parent_id); - int mask_isolation_0_id = content0->effect_tree_index(); - const cc::EffectNode& mask_isolation_0 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); - ASSERT_EQ(e0_id, mask_isolation_0.parent_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_0.rounded_corner_bounds); - EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); - - int t1_id = content1->transform_tree_index(); - const cc::TransformNode& cc_t1 = - *GetPropertyTrees().transform_tree.Node(t1_id); - ASSERT_EQ(t0_id, cc_t1.parent_id); - EXPECT_EQ(c0_id, content1->clip_tree_index()); - EXPECT_EQ(e0_id, content1->effect_tree_index()); - - EXPECT_EQ(t0_id, content2->transform_tree_index()); - EXPECT_EQ(c1_id, content2->clip_tree_index()); - int mask_isolation_1_id = content2->effect_tree_index(); - const cc::EffectNode& mask_isolation_1 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_1_id); - EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id); - ASSERT_EQ(e0_id, mask_isolation_1.parent_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_1.blend_mode); - EXPECT_TRUE(mask_isolation_1.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_1.rounded_corner_bounds); - EXPECT_FALSE(mask_isolation_1.HasRenderSurface()); - return; - } - // Expectation in effect stack diagram: - // l1 l4 - // l0 [ mask_effect_0 ] l3 [ mask_effect_1 ] - // [ mask_isolation_0 ] l2 [ mask_isolation_1 ] - // [ e0 ] - // Three content layers, two clip mask. - ASSERT_EQ(5u, LayerCount()); + // content0 content2 + // [ mask_isolation_0 ] content1 [ mask_isolation_1 ] + // [ e0 ] + // Three content layers. + ASSERT_EQ(3u, LayerCount()); + // There are still "synthesized layers" but they're null because they use + // fast rounded corners. ASSERT_EQ(2u, SynthesizedClipLayerCount()); + EXPECT_FALSE(SynthesizedClipLayerAt(0)); + EXPECT_FALSE(SynthesizedClipLayerAt(1)); const cc::Layer* content0 = LayerAt(0); - const cc::Layer* clip_mask0 = LayerAt(1); - const cc::Layer* content1 = LayerAt(2); - const cc::Layer* content2 = LayerAt(3); - const cc::Layer* clip_mask1 = LayerAt(4); - - constexpr int t0_id = 1; - constexpr int c0_id = 1; - constexpr int e0_id = 1; + const cc::Layer* content1 = LayerAt(1); + const cc::Layer* content2 = LayerAt(2); EXPECT_EQ(t0_id, content0->transform_tree_index()); int c1_id = content0->clip_tree_index(); @@ -3309,17 +3522,10 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDiscontiguous) { *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); ASSERT_EQ(e0_id, mask_isolation_0.parent_id); EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_TRUE(mask_isolation_0.HasRenderSurface()); - - EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0); - EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds()); - EXPECT_EQ(t0_id, clip_mask0->transform_tree_index()); - EXPECT_EQ(c1_id, clip_mask0->clip_tree_index()); - int mask_effect_0_id = clip_mask0->effect_tree_index(); - const cc::EffectNode& mask_effect_0 = - *GetPropertyTrees().effect_tree.Node(mask_effect_0_id); - ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode); + EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_0.rounded_corner_bounds); + EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); int t1_id = content1->transform_tree_index(); const cc::TransformNode& cc_t1 = @@ -3336,17 +3542,10 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDiscontiguous) { EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id); ASSERT_EQ(e0_id, mask_isolation_1.parent_id); EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_1.blend_mode); - EXPECT_TRUE(mask_isolation_1.HasRenderSurface()); - - EXPECT_EQ(SynthesizedClipLayerAt(1), clip_mask1); - EXPECT_EQ(gfx::Size(300, 200), clip_mask1->bounds()); - EXPECT_EQ(t0_id, clip_mask1->transform_tree_index()); - EXPECT_EQ(c1_id, clip_mask1->clip_tree_index()); - int mask_effect_1_id = clip_mask1->effect_tree_index(); - const cc::EffectNode& mask_effect_1 = - *GetPropertyTrees().effect_tree.Node(mask_effect_1_id); - ASSERT_EQ(mask_isolation_1_id, mask_effect_1.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_1.blend_mode); + EXPECT_TRUE(mask_isolation_1.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_1.rounded_corner_bounds); + EXPECT_FALSE(mask_isolation_1.HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, SynthesizedClipAcrossChildEffect) { @@ -3361,75 +3560,27 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipAcrossChildEffect) { TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); artifact.Chunk(t0(), *c1, *e1) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); - if (RuntimeEnabledFeatures::FastBorderRadiusEnabled()) { - // Expectation in effect stack diagram: - // l1 l3 - // l0 [ e1 ] l2 - // [ mask_isolation_0 ] - // [ e0 ] - // Three content layers. - ASSERT_EQ(3u, LayerCount()); - // There is still a "synthesized layer" but it's null. - ASSERT_EQ(1u, SynthesizedClipLayerCount()); - EXPECT_FALSE(SynthesizedClipLayerAt(0)); - - const cc::Layer* content0 = LayerAt(0); - const cc::Layer* content1 = LayerAt(1); - const cc::Layer* content2 = LayerAt(2); - - constexpr int c0_id = 1; - constexpr int e0_id = 1; - - int c1_id = content0->clip_tree_index(); - const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); - EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); - ASSERT_EQ(c0_id, cc_c1.parent_id); - int mask_isolation_0_id = content0->effect_tree_index(); - const cc::EffectNode& mask_isolation_0 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); - ASSERT_EQ(e0_id, mask_isolation_0.parent_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); - - EXPECT_EQ(c1_id, content1->clip_tree_index()); - int e1_id = content1->effect_tree_index(); - const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id); - ASSERT_EQ(mask_isolation_0_id, cc_e1.parent_id); - - EXPECT_EQ(c1_id, content2->clip_tree_index()); - EXPECT_EQ(mask_isolation_0_id, content2->effect_tree_index()); - - int e2_id = content2->effect_tree_index(); - const cc::EffectNode& cc_e2 = *GetPropertyTrees().effect_tree.Node(e2_id); - EXPECT_TRUE(cc_e2.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_0.rounded_corner_bounds); - return; - } - // Expectation in effect stack diagram: - // l1 l3 - // l0 [ e1 ] l2 [ mask_effect_0 ] - // [ mask_isolation_0 ] - // [ e0 ] - // Three content layers, one clip mask. - ASSERT_EQ(4u, LayerCount()); + // content1 + // content0 [ e1 ] content2 + // [ mask_isolation_0 ] + // [ e0 ] + // Three content layers. + ASSERT_EQ(3u, LayerCount()); + // There is still a "synthesized layer" but it's null. ASSERT_EQ(1u, SynthesizedClipLayerCount()); + EXPECT_FALSE(SynthesizedClipLayerAt(0)); const cc::Layer* content0 = LayerAt(0); const cc::Layer* content1 = LayerAt(1); const cc::Layer* content2 = LayerAt(2); - const cc::Layer* clip_mask0 = LayerAt(3); - - constexpr int c0_id = 1; - constexpr int e0_id = 1; int c1_id = content0->clip_tree_index(); const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); @@ -3440,7 +3591,7 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipAcrossChildEffect) { *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); ASSERT_EQ(e0_id, mask_isolation_0.parent_id); EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_TRUE(mask_isolation_0.HasRenderSurface()); + EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); EXPECT_EQ(c1_id, content1->clip_tree_index()); int e1_id = content1->effect_tree_index(); @@ -3450,14 +3601,11 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipAcrossChildEffect) { EXPECT_EQ(c1_id, content2->clip_tree_index()); EXPECT_EQ(mask_isolation_0_id, content2->effect_tree_index()); - EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0); - EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds()); - EXPECT_EQ(c1_id, clip_mask0->clip_tree_index()); - int mask_effect_0_id = clip_mask0->effect_tree_index(); - const cc::EffectNode& mask_effect_0 = - *GetPropertyTrees().effect_tree.Node(mask_effect_0_id); - ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode); + int e2_id = content2->effect_tree_index(); + const cc::EffectNode& cc_e2 = *GetPropertyTrees().effect_tree.Node(e2_id); + EXPECT_TRUE(cc_e2.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_0.rounded_corner_bounds); } TEST_P(PaintArtifactCompositorTest, SynthesizedClipRespectOutputClip) { @@ -3476,97 +3624,30 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipRespectOutputClip) { TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); artifact.Chunk(t0(), *c1, *e1) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); - if (RuntimeEnabledFeatures::FastBorderRadiusEnabled()) { - // Expectation in effect stack diagram: - // l3 - // l1 l2 l5 - // l0 [ mask_isolation_1 ] l4 - // [ mask_isolation_0 ][ e1 ][ mask_isolation_2 ] - // [ e0 ] - // Three content layers. - ASSERT_EQ(3u, LayerCount()); - // There are still "synthesized layers" but they're null. - ASSERT_EQ(3u, SynthesizedClipLayerCount()); - EXPECT_FALSE(SynthesizedClipLayerAt(0)); - EXPECT_FALSE(SynthesizedClipLayerAt(1)); - EXPECT_FALSE(SynthesizedClipLayerAt(2)); - - const cc::Layer* content0 = LayerAt(0); - const cc::Layer* content1 = LayerAt(1); - const cc::Layer* content2 = LayerAt(2); - - constexpr int c0_id = 1; - constexpr int e0_id = 1; - - int c1_id = content0->clip_tree_index(); - const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); - EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); - ASSERT_EQ(c0_id, cc_c1.parent_id); - int mask_isolation_0_id = content0->effect_tree_index(); - const cc::EffectNode& mask_isolation_0 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); - ASSERT_EQ(e0_id, mask_isolation_0.parent_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_0.rounded_corner_bounds); - EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); - - EXPECT_EQ(c1_id, content1->clip_tree_index()); - int mask_isolation_1_id = content1->effect_tree_index(); - const cc::EffectNode& mask_isolation_1 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_1_id); - EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_1.blend_mode); - int e1_id = mask_isolation_1.parent_id; - const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id); - ASSERT_EQ(e0_id, cc_e1.parent_id); - EXPECT_TRUE(mask_isolation_1.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_1.rounded_corner_bounds); - EXPECT_FALSE(mask_isolation_1.HasRenderSurface()); - - EXPECT_EQ(c1_id, content2->clip_tree_index()); - int mask_isolation_2_id = content2->effect_tree_index(); - const cc::EffectNode& mask_isolation_2 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_2_id); - EXPECT_NE(mask_isolation_0_id, mask_isolation_2_id); - EXPECT_NE(mask_isolation_1_id, mask_isolation_2_id); - ASSERT_EQ(e0_id, mask_isolation_2.parent_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_2.blend_mode); - EXPECT_TRUE(mask_isolation_2.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_2.rounded_corner_bounds); - EXPECT_FALSE(mask_isolation_2.HasRenderSurface()); - return; - } - // Expectation in effect stack diagram: - // l3 - // l1 l2 [ mask_effect_1 ] l5 - // l0 [ mask_effect_0 ][ mask_isolation_1 ] l4 [ mask_effect_2 ] + // content1 + // content0 [ mask_isolation_1 ] content2 // [ mask_isolation_0 ][ e1 ][ mask_isolation_2 ] // [ e0 ] - // Three content layers, three clip mask. - ASSERT_EQ(6u, LayerCount()); + // Three content layers. + ASSERT_EQ(3u, LayerCount()); + // There are still "synthesized layers" but they're null because they use + // fast rounded corners. ASSERT_EQ(3u, SynthesizedClipLayerCount()); + EXPECT_FALSE(SynthesizedClipLayerAt(0)); + EXPECT_FALSE(SynthesizedClipLayerAt(1)); + EXPECT_FALSE(SynthesizedClipLayerAt(2)); const cc::Layer* content0 = LayerAt(0); - const cc::Layer* clip_mask0 = LayerAt(1); - const cc::Layer* content1 = LayerAt(2); - const cc::Layer* clip_mask1 = LayerAt(3); - const cc::Layer* content2 = LayerAt(4); - const cc::Layer* clip_mask2 = LayerAt(5); - - constexpr int c0_id = 1; - constexpr int e0_id = 1; + const cc::Layer* content1 = LayerAt(1); + const cc::Layer* content2 = LayerAt(2); int c1_id = content0->clip_tree_index(); const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); @@ -3577,15 +3658,10 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipRespectOutputClip) { *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); ASSERT_EQ(e0_id, mask_isolation_0.parent_id); EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - - EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0); - EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds()); - EXPECT_EQ(c1_id, clip_mask0->clip_tree_index()); - int mask_effect_0_id = clip_mask0->effect_tree_index(); - const cc::EffectNode& mask_effect_0 = - *GetPropertyTrees().effect_tree.Node(mask_effect_0_id); - ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode); + EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_0.rounded_corner_bounds); + EXPECT_FALSE(mask_isolation_0.HasRenderSurface()); EXPECT_EQ(c1_id, content1->clip_tree_index()); int mask_isolation_1_id = content1->effect_tree_index(); @@ -3596,15 +3672,10 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipRespectOutputClip) { int e1_id = mask_isolation_1.parent_id; const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id); ASSERT_EQ(e0_id, cc_e1.parent_id); - - EXPECT_EQ(SynthesizedClipLayerAt(1), clip_mask1); - EXPECT_EQ(gfx::Size(300, 200), clip_mask1->bounds()); - EXPECT_EQ(c1_id, clip_mask1->clip_tree_index()); - int mask_effect_1_id = clip_mask1->effect_tree_index(); - const cc::EffectNode& mask_effect_1 = - *GetPropertyTrees().effect_tree.Node(mask_effect_1_id); - ASSERT_EQ(mask_isolation_1_id, mask_effect_1.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_1.blend_mode); + EXPECT_TRUE(mask_isolation_1.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_1.rounded_corner_bounds); + EXPECT_FALSE(mask_isolation_1.HasRenderSurface()); EXPECT_EQ(c1_id, content2->clip_tree_index()); int mask_isolation_2_id = content2->effect_tree_index(); @@ -3614,15 +3685,10 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipRespectOutputClip) { EXPECT_NE(mask_isolation_1_id, mask_isolation_2_id); ASSERT_EQ(e0_id, mask_isolation_2.parent_id); EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_2.blend_mode); - - EXPECT_EQ(SynthesizedClipLayerAt(2), clip_mask2); - EXPECT_EQ(gfx::Size(300, 200), clip_mask2->bounds()); - EXPECT_EQ(c1_id, clip_mask2->clip_tree_index()); - int mask_effect_2_id = clip_mask2->effect_tree_index(); - const cc::EffectNode& mask_effect_2 = - *GetPropertyTrees().effect_tree.Node(mask_effect_2_id); - ASSERT_EQ(mask_isolation_2_id, mask_effect_2.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_2.blend_mode); + EXPECT_TRUE(mask_isolation_2.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_2.rounded_corner_bounds); + EXPECT_FALSE(mask_isolation_2.HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBlending) { @@ -3643,94 +3709,30 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBlending) { TestPaintArtifact artifact; artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); artifact.Chunk(t0(), *c1, *e1) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); artifact.Chunk(t0(), *c1, e0()) - .RectDrawing(FloatRect(0, 0, 100, 100), Color::kBlack); + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); Update(artifact.Build()); - if (RuntimeEnabledFeatures::FastBorderRadiusEnabled()) { - // Expectation in effect stack diagram: - // l1 l2 l3 l5 - // l0 [ e1 ] l4 - // [ mask_isolation_0 ][ mask_isolation_1 ][ mask_isolation_2 ] - // [ e0 ] - // Three content layers. - ASSERT_EQ(3u, LayerCount()); - // There are still "synthesized layers" but they're null. - ASSERT_EQ(3u, SynthesizedClipLayerCount()); - EXPECT_FALSE(SynthesizedClipLayerAt(0)); - EXPECT_FALSE(SynthesizedClipLayerAt(1)); - EXPECT_FALSE(SynthesizedClipLayerAt(2)); - - const cc::Layer* content0 = LayerAt(0); - const cc::Layer* content1 = LayerAt(1); - const cc::Layer* content2 = LayerAt(2); - - constexpr int c0_id = 1; - constexpr int e0_id = 1; - - int c1_id = content0->clip_tree_index(); - const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); - EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); - ASSERT_EQ(c0_id, cc_c1.parent_id); - int mask_isolation_0_id = content0->effect_tree_index(); - const cc::EffectNode& mask_isolation_0 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); - ASSERT_EQ(e0_id, mask_isolation_0.parent_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_0.rounded_corner_bounds); - - EXPECT_EQ(c1_id, content1->clip_tree_index()); - int e1_id = content1->effect_tree_index(); - const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id); - EXPECT_EQ(SkBlendMode::kSrcOver, cc_e1.blend_mode); - int mask_isolation_1_id = cc_e1.parent_id; - const cc::EffectNode& mask_isolation_1 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_1_id); - EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id); - ASSERT_EQ(e0_id, mask_isolation_1.parent_id); - EXPECT_EQ(SkBlendMode::kMultiply, mask_isolation_1.blend_mode); - EXPECT_TRUE(mask_isolation_1.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_1.rounded_corner_bounds); - - EXPECT_EQ(c1_id, content2->clip_tree_index()); - int mask_isolation_2_id = content2->effect_tree_index(); - const cc::EffectNode& mask_isolation_2 = - *GetPropertyTrees().effect_tree.Node(mask_isolation_2_id); - EXPECT_NE(mask_isolation_0_id, mask_isolation_2_id); - EXPECT_NE(mask_isolation_1_id, mask_isolation_2_id); - ASSERT_EQ(e0_id, mask_isolation_2.parent_id); - EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - EXPECT_TRUE(mask_isolation_2.is_fast_rounded_corner); - EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), - mask_isolation_2.rounded_corner_bounds); - - return; - } - // Expectation in effect stack diagram: - // l1 l2 l3 l5 - // l0 [ mask_effect_0 ][ e1 ][ mask_effect_1 ] l4 [ mask_effect_2 ] + // content1 + // content0 [ e1 ] content2 // [ mask_isolation_0 ][ mask_isolation_1 ][ mask_isolation_2 ] // [ e0 ] - // Three content layers, three clip mask. - ASSERT_EQ(6u, LayerCount()); + // Three content layers. + ASSERT_EQ(3u, LayerCount()); + // There are still "synthesized layers" but they're null because they use + // fast rounded corners. ASSERT_EQ(3u, SynthesizedClipLayerCount()); + EXPECT_FALSE(SynthesizedClipLayerAt(0)); + EXPECT_FALSE(SynthesizedClipLayerAt(1)); + EXPECT_FALSE(SynthesizedClipLayerAt(2)); const cc::Layer* content0 = LayerAt(0); - const cc::Layer* clip_mask0 = LayerAt(1); - const cc::Layer* content1 = LayerAt(2); - const cc::Layer* clip_mask1 = LayerAt(3); - const cc::Layer* content2 = LayerAt(4); - const cc::Layer* clip_mask2 = LayerAt(5); - - constexpr int c0_id = 1; - constexpr int e0_id = 1; + const cc::Layer* content1 = LayerAt(1); + const cc::Layer* content2 = LayerAt(2); int c1_id = content0->clip_tree_index(); const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); @@ -3741,15 +3743,9 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBlending) { *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); ASSERT_EQ(e0_id, mask_isolation_0.parent_id); EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); - - EXPECT_EQ(SynthesizedClipLayerAt(0), clip_mask0); - EXPECT_EQ(gfx::Size(300, 200), clip_mask0->bounds()); - EXPECT_EQ(c1_id, clip_mask0->clip_tree_index()); - int mask_effect_0_id = clip_mask0->effect_tree_index(); - const cc::EffectNode& mask_effect_0 = - *GetPropertyTrees().effect_tree.Node(mask_effect_0_id); - ASSERT_EQ(mask_isolation_0_id, mask_effect_0.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_0.blend_mode); + EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_0.rounded_corner_bounds); EXPECT_EQ(c1_id, content1->clip_tree_index()); int e1_id = content1->effect_tree_index(); @@ -3761,15 +3757,9 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBlending) { EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id); ASSERT_EQ(e0_id, mask_isolation_1.parent_id); EXPECT_EQ(SkBlendMode::kMultiply, mask_isolation_1.blend_mode); - - EXPECT_EQ(SynthesizedClipLayerAt(1), clip_mask1); - EXPECT_EQ(gfx::Size(300, 200), clip_mask1->bounds()); - EXPECT_EQ(c1_id, clip_mask1->clip_tree_index()); - int mask_effect_1_id = clip_mask1->effect_tree_index(); - const cc::EffectNode& mask_effect_1 = - *GetPropertyTrees().effect_tree.Node(mask_effect_1_id); - ASSERT_EQ(mask_isolation_1_id, mask_effect_1.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_1.blend_mode); + EXPECT_TRUE(mask_isolation_1.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_1.rounded_corner_bounds); EXPECT_EQ(c1_id, content2->clip_tree_index()); int mask_isolation_2_id = content2->effect_tree_index(); @@ -3779,22 +3769,123 @@ TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBlending) { EXPECT_NE(mask_isolation_1_id, mask_isolation_2_id); ASSERT_EQ(e0_id, mask_isolation_2.parent_id); EXPECT_EQ(SkBlendMode::kSrcOver, mask_isolation_0.blend_mode); + EXPECT_TRUE(mask_isolation_2.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_2.rounded_corner_bounds); +} + +TEST_P(PaintArtifactCompositorTest, SynthesizedClipDelegateBackdropFilter) { + // This tests the case that an effect with backdrop filter cannot share + // the synthesized mask with its siblings because its backdrop filter has to + // be applied by the outermost mask in the correct transform space. + FloatSize corner(5, 5); + FloatRoundedRect rrect(FloatRect(50, 50, 300, 200), corner, corner, corner, + corner); + auto c1 = CreateClip(c0(), t0(), rrect); + + auto t1 = Create2DTranslation(t0(), 10, 20); + CompositorFilterOperations blur_filter; + blur_filter.AppendBlurFilter(5); + auto e1 = CreateBackdropFilterEffect(e0(), *t1, c1.get(), blur_filter, + FloatPoint()); + + TestPaintArtifact artifact; + artifact.Chunk(*t1, *c1, e0()) + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); + artifact.Chunk(*t1, *c1, *e1) + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); + artifact.Chunk(*t1, *c1, e0()) + .RectDrawing(IntRect(0, 0, 100, 100), Color::kBlack); + Update(artifact.Build()); + + // Expectation in effect stack diagram: + // content1 + // content0 [ e1 ] clip_mask1 content2 + // [ mask_isolation_0 ][ mask_isolation_1 ][ mask_isolation_2 ] + // [ e0 ] + // Three content layers. + ASSERT_EQ(4u, LayerCount()); + const cc::Layer* content0 = LayerAt(0); + const cc::Layer* content1 = LayerAt(1); + const cc::Layer* clip_mask1 = LayerAt(2); + const cc::Layer* content2 = LayerAt(3); + + // Three synthesized layers, two of which are null because because they use + // fast rounded corners. One real synthesized layer is needed because the + // rounded clip and the backdrop filter are in different transform spaces. + EXPECT_FALSE(SynthesizedClipLayerAt(0)); + EXPECT_EQ(clip_mask1, SynthesizedClipLayerAt(1)); + EXPECT_FALSE(SynthesizedClipLayerAt(2)); + + int t1_id = content0->transform_tree_index(); + EXPECT_EQ(t0_id, GetPropertyTrees().transform_tree.Node(t1_id)->parent_id); + int c1_id = content0->clip_tree_index(); + const cc::ClipNode& cc_c1 = *GetPropertyTrees().clip_tree.Node(c1_id); + EXPECT_EQ(gfx::RectF(50, 50, 300, 200), cc_c1.clip); + ASSERT_EQ(c0_id, cc_c1.parent_id); + EXPECT_EQ(t0_id, cc_c1.transform_id); + int mask_isolation_0_id = content0->effect_tree_index(); + const cc::EffectNode& mask_isolation_0 = + *GetPropertyTrees().effect_tree.Node(mask_isolation_0_id); + ASSERT_EQ(e0_id, mask_isolation_0.parent_id); + EXPECT_EQ(t0_id, mask_isolation_0.transform_id); + EXPECT_TRUE(mask_isolation_0.backdrop_filters.IsEmpty()); + EXPECT_TRUE(mask_isolation_0.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_0.rounded_corner_bounds); + + EXPECT_EQ(t1_id, content1->transform_tree_index()); + EXPECT_EQ(c1_id, content1->clip_tree_index()); + int e1_id = content1->effect_tree_index(); + const cc::EffectNode& cc_e1 = *GetPropertyTrees().effect_tree.Node(e1_id); + EXPECT_TRUE(cc_e1.backdrop_filters.IsEmpty()); + EXPECT_EQ(t1_id, cc_e1.transform_id); + EXPECT_EQ(c1_id, cc_e1.clip_id); + EXPECT_FALSE(cc_e1.backdrop_mask_element_id); + + int mask_isolation_1_id = cc_e1.parent_id; + const cc::EffectNode& mask_isolation_1 = + *GetPropertyTrees().effect_tree.Node(mask_isolation_1_id); + EXPECT_NE(mask_isolation_0_id, mask_isolation_1_id); + ASSERT_EQ(e0_id, mask_isolation_1.parent_id); + EXPECT_EQ(t1_id, mask_isolation_1.transform_id); + EXPECT_EQ(c1_id, mask_isolation_1.clip_id); + EXPECT_FALSE(mask_isolation_1.backdrop_filters.IsEmpty()); + EXPECT_FALSE(mask_isolation_1.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(), mask_isolation_1.rounded_corner_bounds); - EXPECT_EQ(SynthesizedClipLayerAt(2), clip_mask2); - EXPECT_EQ(gfx::Size(300, 200), clip_mask2->bounds()); - EXPECT_EQ(c1_id, clip_mask2->clip_tree_index()); - int mask_effect_2_id = clip_mask2->effect_tree_index(); - const cc::EffectNode& mask_effect_2 = - *GetPropertyTrees().effect_tree.Node(mask_effect_2_id); - ASSERT_EQ(mask_isolation_2_id, mask_effect_2.parent_id); - EXPECT_EQ(SkBlendMode::kDstIn, mask_effect_2.blend_mode); + EXPECT_EQ(t0_id, clip_mask1->transform_tree_index()); + EXPECT_EQ(c1_id, clip_mask1->clip_tree_index()); + const cc::EffectNode& mask = + *GetPropertyTrees().effect_tree.Node(clip_mask1->effect_tree_index()); + ASSERT_EQ(mask_isolation_1_id, mask.parent_id); + EXPECT_EQ(SkBlendMode::kDstIn, mask.blend_mode); + EXPECT_TRUE(static_cast<const cc::PictureLayer*>(clip_mask1) + ->is_backdrop_filter_mask()); + EXPECT_TRUE(clip_mask1->element_id()); + EXPECT_EQ(clip_mask1->element_id(), + mask_isolation_1.backdrop_mask_element_id); + + EXPECT_EQ(t1_id, content2->transform_tree_index()); + EXPECT_EQ(c1_id, content2->clip_tree_index()); + int mask_isolation_2_id = content2->effect_tree_index(); + const cc::EffectNode& mask_isolation_2 = + *GetPropertyTrees().effect_tree.Node(mask_isolation_2_id); + EXPECT_NE(mask_isolation_0_id, mask_isolation_2_id); + EXPECT_NE(mask_isolation_1_id, mask_isolation_2_id); + ASSERT_EQ(e0_id, mask_isolation_2.parent_id); + EXPECT_EQ(t0_id, mask_isolation_2.transform_id); + EXPECT_TRUE(mask_isolation_2.backdrop_filters.IsEmpty()); + EXPECT_TRUE(mask_isolation_2.is_fast_rounded_corner); + EXPECT_EQ(gfx::RRectF(50, 50, 300, 200, 5), + mask_isolation_2.rounded_corner_bounds); } TEST_P(PaintArtifactCompositorTest, WillBeRemovedFromFrame) { auto effect = CreateSampleEffectNodeWithElementId(); TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *effect) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack); + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -3806,7 +3897,7 @@ TEST_P(PaintArtifactCompositorTest, WillBeRemovedFromFrame) { TEST_P(PaintArtifactCompositorTest, ContentsNonOpaque) { TestPaintArtifact artifact; - artifact.Chunk().RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack); + artifact.Chunk().RectDrawing(IntRect(100, 100, 200, 200), Color::kBlack); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); EXPECT_FALSE(LayerAt(0)->contents_opaque()); @@ -3815,7 +3906,7 @@ TEST_P(PaintArtifactCompositorTest, ContentsNonOpaque) { TEST_P(PaintArtifactCompositorTest, ContentsOpaque) { TestPaintArtifact artifact; artifact.Chunk() - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack) + .RectDrawing(IntRect(100, 100, 200, 200), Color::kBlack) .KnownToBeOpaque(); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -3825,10 +3916,10 @@ TEST_P(PaintArtifactCompositorTest, ContentsOpaque) { TEST_P(PaintArtifactCompositorTest, ContentsOpaqueUnitedNonOpaque) { TestPaintArtifact artifact; artifact.Chunk() - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack) + .RectDrawing(IntRect(100, 100, 210, 210), Color::kBlack) .KnownToBeOpaque() .Chunk() - .RectDrawing(FloatRect(200, 200, 200, 200), Color::kBlack) + .RectDrawing(IntRect(200, 200, 200, 200), Color::kBlack) .KnownToBeOpaque(); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -3836,13 +3927,30 @@ TEST_P(PaintArtifactCompositorTest, ContentsOpaqueUnitedNonOpaque) { EXPECT_FALSE(LayerAt(0)->contents_opaque()); } +TEST_P(PaintArtifactCompositorTest, ContentsOpaqueUnitedClippedToOpaque) { + // Almost the same as ContentsOpaqueUnitedNonOpaque, but with a clip which + // removes the non-opaque part of the layer, making the layer opaque. + auto clip1 = CreateClip(c0(), t0(), FloatRoundedRect(175, 175, 100, 100)); + TestPaintArtifact artifact; + artifact.Chunk(t0(), *clip1, e0()) + .RectDrawing(IntRect(100, 100, 210, 210), Color::kBlack) + .KnownToBeOpaque() + .Chunk(t0(), *clip1, e0()) + .RectDrawing(IntRect(200, 200, 200, 200), Color::kBlack) + .KnownToBeOpaque(); + Update(artifact.Build()); + ASSERT_EQ(1u, LayerCount()); + EXPECT_EQ(gfx::Size(100, 100), LayerAt(0)->bounds()); + EXPECT_TRUE(LayerAt(0)->contents_opaque()); +} + TEST_P(PaintArtifactCompositorTest, ContentsOpaqueUnitedOpaque1) { TestPaintArtifact artifact; artifact.Chunk() - .RectDrawing(FloatRect(100, 100, 300, 300), Color::kBlack) + .RectDrawing(IntRect(100, 100, 300, 300), Color::kBlack) .KnownToBeOpaque() .Chunk() - .RectDrawing(FloatRect(200, 200, 200, 200), Color::kBlack) + .RectDrawing(IntRect(200, 200, 200, 200), Color::kBlack) .KnownToBeOpaque(); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -3850,20 +3958,38 @@ TEST_P(PaintArtifactCompositorTest, ContentsOpaqueUnitedOpaque1) { EXPECT_TRUE(LayerAt(0)->contents_opaque()); } +TEST_P(PaintArtifactCompositorTest, ContentsOpaqueUnitedWithRoundedClip) { + // Almost the same as ContentsOpaqueUnitedOpaque1, but the first layer has a + // rounded clip. + FloatSize corner(5, 5); + auto clip1 = CreateClip(c0(), t0(), + FloatRoundedRect(FloatRect(175, 175, 100, 100), + corner, corner, corner, corner)); + TestPaintArtifact artifact; + artifact.Chunk(t0(), *clip1, e0()) + .RectDrawing(IntRect(100, 100, 210, 210), Color::kBlack) + .KnownToBeOpaque() + .Chunk(t0(), c0(), e0()) + .RectDrawing(IntRect(200, 200, 100, 100), Color::kBlack) + .KnownToBeOpaque(); + Update(artifact.Build()); + ASSERT_EQ(1u, LayerCount()); + EXPECT_EQ(gfx::Size(125, 125), LayerAt(0)->bounds()); + EXPECT_FALSE(LayerAt(0)->contents_opaque()); +} + TEST_P(PaintArtifactCompositorTest, ContentsOpaqueUnitedOpaque2) { TestPaintArtifact artifact; artifact.Chunk() - .RectDrawing(FloatRect(100, 100, 200, 200), Color::kBlack) + .RectDrawing(IntRect(100, 100, 200, 200), Color::kBlack) .KnownToBeOpaque() .Chunk() - .RectDrawing(FloatRect(100, 100, 300, 300), Color::kBlack) + .RectDrawing(IntRect(100, 100, 300, 300), Color::kBlack) .KnownToBeOpaque(); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); EXPECT_EQ(gfx::Size(300, 300), LayerAt(0)->bounds()); - // TODO(crbug.com/701991): Upgrade GeometryMapper to make this test pass with - // the following EXPECT_FALSE changed to EXPECT_TRUE. - EXPECT_FALSE(LayerAt(0)->contents_opaque()); + EXPECT_TRUE(LayerAt(0)->contents_opaque()); } TEST_P(PaintArtifactCompositorTest, DecompositeEffectWithNoOutputClip) { @@ -3873,9 +3999,9 @@ TEST_P(PaintArtifactCompositorTest, DecompositeEffectWithNoOutputClip) { auto effect1 = CreateOpacityEffect(e0(), t0(), nullptr, 0.5); TestPaintArtifact artifact; - artifact.Chunk().RectDrawing(FloatRect(50, 50, 100, 100), Color::kGray); + artifact.Chunk().RectDrawing(IntRect(50, 50, 100, 100), Color::kGray); artifact.Chunk(t0(), *clip1, *effect1) - .RectDrawing(FloatRect(100, 100, 100, 100), Color::kGray); + .RectDrawing(IntRect(100, 100, 100, 100), Color::kGray); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -3895,9 +4021,9 @@ TEST_P(PaintArtifactCompositorTest, CompositedEffectWithNoOutputClip) { TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *effect1) - .RectDrawing(FloatRect(50, 50, 100, 100), Color::kGray); + .RectDrawing(IntRect(50, 50, 100, 100), Color::kGray); artifact.Chunk(t0(), *clip1, *effect1) - .RectDrawing(FloatRect(100, 100, 100, 100), Color::kGray); + .RectDrawing(IntRect(100, 100, 100, 100), Color::kGray); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -3918,7 +4044,7 @@ TEST_P(PaintArtifactCompositorTest, LayerRasterInvalidationWithClip) { auto clip = CreateClip(c0(), t0(), FloatRoundedRect(10, 20, 300, 400)); TestPaintArtifact artifact1; artifact1.Chunk(t0(), *clip, e0()) - .RectDrawing(FloatRect(50, 50, 200, 200), Color::kBlack); + .RectDrawing(IntRect(50, 50, 200, 200), Color::kBlack); artifact1.Client(0).Validate(); artifact1.Client(1).Validate(); Update(artifact1.Build()); @@ -3936,7 +4062,7 @@ TEST_P(PaintArtifactCompositorTest, LayerRasterInvalidationWithClip) { .Chunk(artifact1.Client(0)) .Properties(t0(), *clip, e0()) .RectDrawing(artifact1.Client(1), - FloatRect(0, 0, 400, 200), Color::kBlack) + IntRect(0, 0, 400, 200), Color::kBlack) .Build(); // Simluate commit to the compositor thread. layer->PushPropertiesTo( @@ -3958,7 +4084,7 @@ TEST_P(PaintArtifactCompositorTest, LayerRasterInvalidationWithClip) { TestPaintArtifact() .Chunk(artifact1.Client(0)) .Properties(t0(), *clip, e0()) - .RectDrawing(artifact1.Client(1), FloatRect(-100, -200, 500, 800), + .RectDrawing(artifact1.Client(1), IntRect(-100, -200, 500, 800), Color::kBlack) .Build(); // Simluate commit to the compositor thread. @@ -4031,7 +4157,7 @@ TEST_P(PaintArtifactCompositorTest, InSubtreeOfPageScale) { TestPaintArtifact artifact; artifact.Chunk(*descendant_transform, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 10, 10), Color::kBlack); + .RectDrawing(IntRect(0, 0, 10, 10), Color::kBlack); ViewportProperties viewport_properties; viewport_properties.page_scale = page_scale_transform.get(); Update(artifact.Build(), viewport_properties); @@ -4085,7 +4211,7 @@ TEST_P(PaintArtifactCompositorTest, ViewportPageScale) { TestPaintArtifact artifact; artifact.Chunk(*scroll_translation, c0(), e0()) - .RectDrawing(FloatRect(0, 0, 10, 10), Color::kBlack); + .RectDrawing(IntRect(0, 0, 10, 10), Color::kBlack); ViewportProperties viewport_properties; viewport_properties.page_scale = scale_transform_node.get(); Update(artifact.Build(), viewport_properties); @@ -4135,7 +4261,7 @@ TEST_P(PaintArtifactCompositorTest, OpacityRenderSurfaces) { FloatPoint3D(), CompositingReason::k3DTransform); TestPaintArtifact artifact; - FloatRect r(150, 150, 100, 100); + IntRect r(150, 150, 100, 100); artifact.Chunk(t0(), c0(), *aa).RectDrawing(r, Color::kWhite); artifact.Chunk(t0(), c0(), *ab).RectDrawing(r, Color::kWhite); artifact.Chunk(t0(), c0(), *b).RectDrawing(r, Color::kWhite); @@ -4184,7 +4310,7 @@ TEST_P(PaintArtifactCompositorTest, OpacityRenderSurfacesWithFilterChildren) { auto filter2 = CreateFilterEffect(*opacity, filter, FloatPoint(), CompositingReason::kActiveFilterAnimation); - FloatRect r(150, 150, 100, 100); + IntRect r(150, 150, 100, 100); Update(TestPaintArtifact() .Chunk(t0(), c0(), *filter1) .RectDrawing(r, Color::kWhite) @@ -4222,7 +4348,7 @@ TEST_P(PaintArtifactCompositorTest, OpacityAnimationRenderSurfaces) { FloatPoint3D(), CompositingReason::k3DTransform); TestPaintArtifact artifact; - FloatRect r(150, 150, 100, 100); + IntRect r(150, 150, 100, 100); artifact.Chunk(t0(), c0(), *aa).RectDrawing(r, Color::kWhite); artifact.Chunk(t0(), c0(), *ab).RectDrawing(r, Color::kWhite); artifact.Chunk(t0(), c0(), *b).RectDrawing(r, Color::kWhite); @@ -4273,12 +4399,10 @@ TEST_P(PaintArtifactCompositorTest, OpacityRenderSurfacesWithBackdropChildren) { auto a = CreateOpacityEffect(*e, 0.5f); CompositorFilterOperations blur_filter; blur_filter.AppendBlurFilter(5); - auto bd = CreateBackdropFilterEffect( - *a, blur_filter, FloatPoint(), - CompositingReason::kActiveBackdropFilterAnimation); + auto bd = CreateBackdropFilterEffect(*a, blur_filter, FloatPoint()); TestPaintArtifact artifact; - FloatRect r(150, 150, 100, 100); + IntRect r(150, 150, 100, 100); artifact.Chunk(t0(), c0(), *a).RectDrawing(r, Color::kWhite); artifact.Chunk(t0(), c0(), *bd).RectDrawing(r, Color::kWhite); Update(artifact.Build()); @@ -4292,12 +4416,11 @@ TEST_P(PaintArtifactCompositorTest, DirectTransformAnimationCausesRenderSurfaceFor2dAxisMisalignedClip) { // When a clip is affected by an animated transform, we should get a render // surface for the effect node. - auto t1 = CreateTransform(t0(), TransformationMatrix(), FloatPoint3D(), - CompositingReason::kActiveTransformAnimation); + auto t1 = CreateAnimatingTransform(t0()); auto e1 = CreateOpacityEffect(e0(), *t1, nullptr, 1.f); auto c1 = CreateClip(c0(), t0(), FloatRoundedRect(50, 50, 50, 50)); TestPaintArtifact artifact; - FloatRect r(150, 150, 100, 100); + IntRect r(150, 150, 100, 100); artifact.Chunk(t0(), c0(), e0()).RectDrawing(r, Color::kWhite); artifact.Chunk(t0(), *c1, *e1).RectDrawing(r, Color::kWhite); Update(artifact.Build()); @@ -4312,13 +4435,12 @@ TEST_P(PaintArtifactCompositorTest, IndirectTransformAnimationCausesRenderSurfaceFor2dAxisMisalignedClip) { // When a clip is affected by an animated transform, we should get a render // surface for the effect node. - auto t1 = CreateTransform(t0(), TransformationMatrix(), FloatPoint3D(), - CompositingReason::kActiveTransformAnimation); + auto t1 = CreateAnimatingTransform(t0()); auto t2 = Create2DTranslation(*t1, 10, 20); auto e1 = CreateOpacityEffect(e0(), *t2, nullptr, 1.f); auto c1 = CreateClip(c0(), t0(), FloatRoundedRect(50, 50, 50, 50)); TestPaintArtifact artifact; - FloatRect r(150, 150, 100, 100); + IntRect r(150, 150, 100, 100); artifact.Chunk(t0(), c0(), e0()).RectDrawing(r, Color::kWhite); artifact.Chunk(t0(), *c1, *e1).RectDrawing(r, Color::kWhite); Update(artifact.Build()); @@ -4338,9 +4460,9 @@ TEST_P(PaintArtifactCompositorTest, OpacityIndirectlyAffectingTwoLayers) { TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *child_composited_effect) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite); + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); artifact.Chunk(t0(), c0(), *grandchild_composited_effect) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kGray); + .RectDrawing(IntRect(150, 150, 100, 100), Color::kGray); Update(artifact.Build()); ASSERT_EQ(2u, LayerCount()); @@ -4364,9 +4486,9 @@ TEST_P(PaintArtifactCompositorTest, TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *child_composited_effect) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite); + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite); artifact.Chunk(t0(), c0(), *grandchild_composited_effect) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kGray); + .RectDrawing(IntRect(150, 150, 100, 100), Color::kGray); Update(artifact.Build()); ASSERT_EQ(2u, LayerCount()); @@ -4399,7 +4521,7 @@ TEST_P(PaintArtifactCompositorTest, Update(TestPaintArtifact() .Chunk(t0(), c0(), *e1) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite) + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite) .Build()); ASSERT_EQ(1u, LayerCount()); EXPECT_OPACITY(LayerAt(0)->effect_tree_index(), 1.f, kNoRenderSurface); @@ -4412,7 +4534,7 @@ TEST_P(PaintArtifactCompositorTest, FilterCreatesRenderSurface) { CompositingReason::kActiveFilterAnimation); Update(TestPaintArtifact() .Chunk(t0(), c0(), *e1) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite) + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite) .Build()); ASSERT_EQ(1u, LayerCount()); EXPECT_OPACITY(LayerAt(0)->effect_tree_index(), 1.f, kHasRenderSurface); @@ -4422,7 +4544,7 @@ TEST_P(PaintArtifactCompositorTest, FilterAnimationCreatesRenderSurface) { auto e1 = CreateAnimatingFilterEffect(e0()); Update(TestPaintArtifact() .Chunk(t0(), c0(), *e1) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite) + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite) .Build()); ASSERT_EQ(1u, LayerCount()); EXPECT_OPACITY(LayerAt(0)->effect_tree_index(), 1.f, kHasRenderSurface); @@ -4431,11 +4553,10 @@ TEST_P(PaintArtifactCompositorTest, FilterAnimationCreatesRenderSurface) { TEST_P(PaintArtifactCompositorTest, BackdropFilterCreatesRenderSurface) { CompositorFilterOperations filter; filter.AppendBlurFilter(5); - auto e1 = CreateBackdropFilterEffect(e0(), filter, FloatPoint(), - CompositingReason::kBackdropFilter); + auto e1 = CreateBackdropFilterEffect(e0(), filter, FloatPoint()); Update(TestPaintArtifact() .Chunk(t0(), c0(), *e1) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite) + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite) .Build()); ASSERT_EQ(1u, LayerCount()); EXPECT_OPACITY(LayerAt(0)->effect_tree_index(), 1.f, kHasRenderSurface); @@ -4446,7 +4567,7 @@ TEST_P(PaintArtifactCompositorTest, auto e1 = CreateAnimatingBackdropFilterEffect(e0()); Update(TestPaintArtifact() .Chunk(t0(), c0(), *e1) - .RectDrawing(FloatRect(150, 150, 100, 100), Color::kWhite) + .RectDrawing(IntRect(150, 150, 100, 100), Color::kWhite) .Build()); ASSERT_EQ(1u, LayerCount()); EXPECT_OPACITY(LayerAt(0)->effect_tree_index(), 1.f, kHasRenderSurface); @@ -4460,7 +4581,7 @@ TEST_P(PaintArtifactCompositorTest, Non2dAxisAlignedClip) { TestPaintArtifact artifact; artifact.Chunk(t0(), *clip, *opacity) - .RectDrawing(FloatRect(50, 50, 50, 50), Color::kWhite); + .RectDrawing(IntRect(50, 50, 50, 50), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -4485,7 +4606,7 @@ TEST_P(PaintArtifactCompositorTest, Non2dAxisAlignedRoundedRectClip) { TestPaintArtifact artifact; artifact.Chunk(t0(), *clip, *opacity) - .RectDrawing(FloatRect(50, 50, 50, 50), Color::kWhite); + .RectDrawing(IntRect(50, 50, 50, 50), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(1u, LayerCount()); @@ -4520,11 +4641,11 @@ TEST_P(PaintArtifactCompositorTest, TestPaintArtifact artifact; artifact.Chunk(t0(), c0(), *opacity) - .RectDrawing(FloatRect(50, 50, 50, 50), Color::kWhite); + .RectDrawing(IntRect(50, 50, 50, 50), Color::kWhite); artifact.Chunk(*rotate1, c0(), *opacity) - .RectDrawing(FloatRect(50, 50, 50, 50), Color::kWhite); + .RectDrawing(IntRect(50, 50, 50, 50), Color::kWhite); artifact.Chunk(*rotate2, *clip, *opacity) - .RectDrawing(FloatRect(50, 50, 50, 50), Color::kWhite); + .RectDrawing(IntRect(50, 50, 50, 50), Color::kWhite); Update(artifact.Build()); ASSERT_EQ(3u, LayerCount()); @@ -4549,7 +4670,7 @@ TEST_P(PaintArtifactCompositorTest, TransformChange) { Update(TestPaintArtifact() .Chunk(1) .Properties(*t2, c0(), e0()) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack) + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack) .Build()); ASSERT_EQ(1u, LayerCount()); cc::Layer* layer = LayerAt(0); @@ -4564,7 +4685,7 @@ TEST_P(PaintArtifactCompositorTest, TransformChange) { Update(TestPaintArtifact() .Chunk(1) .Properties(*t2, c0(), e0()) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack) + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack) .Build()); ASSERT_EQ(1u, LayerCount()); @@ -4589,7 +4710,7 @@ TEST_P(PaintArtifactCompositorTest, TransformChange) { Update(TestPaintArtifact() .Chunk(1) .Properties(*t2, c0(), e0()) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack) + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack) .Build()); ASSERT_EQ(1u, LayerCount()); @@ -4611,7 +4732,7 @@ TEST_P(PaintArtifactCompositorTest, TransformChange) { Update(TestPaintArtifact() .Chunk(1) .Properties(*t2, c0(), e0()) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack) + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack) .Build()); ASSERT_EQ(1u, LayerCount()); @@ -4631,7 +4752,7 @@ TEST_P(PaintArtifactCompositorTest, EffectChange) { Update(TestPaintArtifact() .Chunk(1) .Properties(t0(), c0(), *e2) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack) + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack) .Build()); ASSERT_EQ(1u, LayerCount()); cc::Layer* layer = LayerAt(0); @@ -4650,7 +4771,7 @@ TEST_P(PaintArtifactCompositorTest, EffectChange) { Update(TestPaintArtifact() .Chunk(1) .Properties(t0(), c0(), *e2) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack) + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack) .Build()); ASSERT_EQ(1u, LayerCount()); @@ -4678,7 +4799,7 @@ TEST_P(PaintArtifactCompositorTest, EffectChange) { Update(TestPaintArtifact() .Chunk(1) .Properties(t0(), c0(), *e2) - .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack) + .RectDrawing(IntRect(100, 100, 200, 100), Color::kBlack) .Build()); ASSERT_EQ(1u, LayerCount()); @@ -4691,4 +4812,44 @@ TEST_P(PaintArtifactCompositorTest, EffectChange) { ->effect_changed); } +TEST_P(PaintArtifactCompositorTest, DirectlySetScrollOffset) { + CompositorElementId scroll_element_id = ScrollElementId(123); + auto scroll = CreateScroll(ScrollPaintPropertyNode::Root(), ScrollState1(), + kNotScrollingOnMain, scroll_element_id); + auto scroll_translation = CreateScrollTranslation( + t0(), 7, 9, *scroll, CompositingReason::kWillChangeTransform); + + TestPaintArtifact artifact; + CreateScrollableChunk(artifact, *scroll_translation, c0(), e0()); + Update(artifact.Build()); + + auto& scroll_tree = GetPropertyTrees().scroll_tree; + auto* scroll_layer = ScrollableLayerAt(0); + auto* scroll_node = scroll_tree.FindNodeFromElementId(scroll_element_id); + auto& transform_tree = GetPropertyTrees().transform_tree; + auto* transform_node = transform_tree.Node(scroll_node->transform_id); + EXPECT_EQ(scroll_element_id, scroll_node->element_id); + EXPECT_EQ(scroll_element_id, scroll_layer->element_id()); + EXPECT_EQ(scroll_node->id, scroll_layer->scroll_tree_index()); + EXPECT_EQ(gfx::ScrollOffset(-7, -9), + scroll_tree.current_scroll_offset(scroll_element_id)); + EXPECT_EQ(gfx::ScrollOffset(-7, -9), transform_node->scroll_offset); + + auto& host = GetLayerTreeHost(); + host.Composite(base::TimeTicks::Now(), true); + ASSERT_FALSE(host.LayersThatShouldPushProperties().contains(scroll_layer)); + ASSERT_FALSE(host.proxy()->CommitRequested()); + ASSERT_FALSE(transform_tree.needs_update()); + + ASSERT_TRUE(GetPaintArtifactCompositor().DirectlySetScrollOffset( + scroll_element_id, FloatPoint(-10, -20))); + EXPECT_TRUE(host.LayersThatShouldPushProperties().contains(scroll_layer)); + EXPECT_TRUE(host.proxy()->CommitRequested()); + EXPECT_EQ(gfx::ScrollOffset(-10, -20), + scroll_tree.current_scroll_offset(scroll_element_id)); + // DirectlySetScrollOffset doesn't update transform node. + EXPECT_EQ(gfx::ScrollOffset(-7, -9), transform_node->scroll_offset); + EXPECT_FALSE(transform_tree.needs_update()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc index 1bc279443be..7ea7c8ab662 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc @@ -158,7 +158,7 @@ class ConversionContext { *current_transform_); } - void AppendRestore(size_t n) { + void AppendRestore(wtf_size_t n) { cc_list_.StartPaint(); while (n--) cc_list_.push<cc::RestoreOp>(); @@ -172,8 +172,7 @@ class ConversionContext { // Ends the effect on the top of the state stack if the stack is not empty, // and update the bounds of the SaveLayer[Alpha]Op of the effect. void EndEffect(); - void UpdateEffectBounds(const base::Optional<FloatRect>&, - const TransformPaintPropertyNode&); + void UpdateEffectBounds(const FloatRect&, const TransformPaintPropertyNode&); // Starts a clip state by adjusting the transform state, applying // |combined_clip_rect| which is combined from one or more consecutive clips, @@ -244,7 +243,7 @@ class ConversionContext { // Records the bounds of the effect which initiated the entry. Note that // the effect is not |this->effect| (which is the previous effect), but the // |current_effect_| when this entry is the top of the stack. - base::Optional<FloatRect> bounds; + FloatRect bounds; }; Vector<EffectBoundsInfo> effect_bounds_stack_; @@ -278,6 +277,7 @@ void ConversionContext::TranslateForLayerOffsetOnce() { } void ConversionContext::SwitchToChunkState(const PaintChunk& chunk) { + TranslateForLayerOffsetOnce(); chunk_to_layer_mapper_.SwitchToChunk(chunk); const auto& chunk_state = chunk.properties; @@ -304,18 +304,21 @@ static bool CombineClip(const ClipPaintPropertyNode& clip, return false; // Don't combine two rounded clip rects. - bool clip_is_rounded = clip.ClipRect().IsRounded(); + bool clip_is_rounded = clip.PixelSnappedClipRect().IsRounded(); bool combined_is_rounded = combined_clip_rect.IsRounded(); if (clip_is_rounded && combined_is_rounded) return false; // If one is rounded and the other contains the rounded bounds, use the // rounded as the combined. - if (combined_is_rounded) - return clip.ClipRect().Rect().Contains(combined_clip_rect.Rect()); + if (combined_is_rounded) { + return clip.PixelSnappedClipRect().Rect().Contains( + combined_clip_rect.Rect()); + } if (clip_is_rounded) { - if (combined_clip_rect.Rect().Contains(clip.ClipRect().Rect())) { - combined_clip_rect = clip.ClipRect(); + if (combined_clip_rect.Rect().Contains( + clip.PixelSnappedClipRect().Rect())) { + combined_clip_rect = clip.PixelSnappedClipRect(); return true; } return false; @@ -323,8 +326,8 @@ static bool CombineClip(const ClipPaintPropertyNode& clip, // The combined is the intersection if both are rectangular. DCHECK(!combined_is_rounded && !clip_is_rounded); - combined_clip_rect = FloatRoundedRect( - Intersection(combined_clip_rect.Rect(), clip.ClipRect().Rect())); + combined_clip_rect = FloatRoundedRect(Intersection( + combined_clip_rect.Rect(), clip.PixelSnappedClipRect().Rect())); return true; } @@ -380,9 +383,10 @@ void ConversionContext::SwitchToClip( // Step 3: Now apply the list of clips in top-down order. DCHECK(pending_clips.size()); - auto pending_combined_clip_rect = pending_clips.back()->ClipRect(); + auto pending_combined_clip_rect = + pending_clips.back()->PixelSnappedClipRect(); const auto* lowest_combined_clip_node = pending_clips.back(); - for (size_t i = pending_clips.size() - 1; i--;) { + for (auto i = pending_clips.size() - 1; i--;) { const auto* sub_clip = pending_clips[i]; if (CombineClip(*sub_clip, pending_combined_clip_rect)) { // Continue to combine. @@ -391,7 +395,7 @@ void ConversionContext::SwitchToClip( // |sub_clip| can't be combined to previous clips. Output the current // combined clip, and start new combination. StartClip(pending_combined_clip_rect, *lowest_combined_clip_node); - pending_combined_clip_rect = sub_clip->ClipRect(); + pending_combined_clip_rect = sub_clip->PixelSnappedClipRect(); lowest_combined_clip_node = sub_clip; } } @@ -493,7 +497,7 @@ void ConversionContext::SwitchToEffect( } // Step 3: Now apply the list of effects in top-down order. - for (size_t i = pending_effects.size(); i--;) { + for (auto i = pending_effects.size(); i--;) { const EffectPaintPropertyNode* sub_effect = pending_effects[i]; #if DCHECK_IS_ON() if (!has_pre_cap_effect_hierarchy_issue) @@ -538,9 +542,8 @@ void ConversionContext::StartEffect(const EffectPaintPropertyNode& effect) { bool has_other_effects = effect.BlendMode() != SkBlendMode::kSrcOver || effect.GetColorFilter() != kColorFilterNone; DCHECK(!has_filter || !(has_opacity || has_other_effects)); - - // TODO(crbug.com/904592): Add support for non-composited backdrop-filter - // here. + // We always composite backdrop filters. + DCHECK(effect.BackdropFilter().IsEmpty()); // Apply effects. cc_list_.StartPaint(); @@ -590,25 +593,35 @@ void ConversionContext::StartEffect(const EffectPaintPropertyNode& effect) { const ClipPaintPropertyNode* input_clip = current_clip_; PushState(StateEntry::kEffect, saved_count); effect_bounds_stack_.emplace_back( - EffectBoundsInfo{save_layer_id, current_transform_, base::nullopt}); + EffectBoundsInfo{save_layer_id, current_transform_}); current_clip_ = input_clip; current_effect_ = &effect; + + if (effect.Filter().HasReferenceFilter()) { + auto reference_box = effect.Filter().ReferenceBox(); + reference_box.MoveBy(effect.FiltersOrigin()); + effect_bounds_stack_.back().bounds = reference_box; + if (current_effect_->Filter().HasReferenceFilter()) { + // Emit an empty paint operation to add the filter's source bounds (mapped + // to layer space) to the visual rect of the filter's SaveLayerOp. + cc_list_.StartPaint(); + cc_list_.EndPaintOfUnpaired(chunk_to_layer_mapper_.MapVisualRect( + EnclosingIntRect(reference_box))); + } + } } void ConversionContext::UpdateEffectBounds( - const base::Optional<FloatRect>& bounds, + const FloatRect& bounds, const TransformPaintPropertyNode& transform) { - if (effect_bounds_stack_.IsEmpty() || !bounds) + if (effect_bounds_stack_.IsEmpty() || bounds.IsEmpty()) return; auto& effect_bounds_info = effect_bounds_stack_.back(); - FloatRect mapped_bounds = *bounds; + FloatRect mapped_bounds = bounds; GeometryMapper::SourceToDestinationRect( transform, *effect_bounds_info.transform, mapped_bounds); - if (effect_bounds_info.bounds) - effect_bounds_info.bounds->Unite(mapped_bounds); - else - effect_bounds_info.bounds = mapped_bounds; + effect_bounds_info.bounds.Unite(mapped_bounds); } void ConversionContext::EndEffect() { @@ -622,21 +635,21 @@ void ConversionContext::EndEffect() { DCHECK(effect_bounds_stack_.size()); const auto& bounds_info = effect_bounds_stack_.back(); - base::Optional<FloatRect> bounds = bounds_info.bounds; - if (bounds) { - if (current_effect_->Filter().IsEmpty()) { - cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id, *bounds); - } else { - // The bounds for the SaveLayer[Alpha]Op should be the source bounds - // before the filter is applied, in the space of the TranslateOp which was - // emitted before the SaveLayer[Alpha]Op. - auto save_layer_bounds = *bounds; + FloatRect bounds = bounds_info.bounds; + if (current_effect_->Filter().IsEmpty()) { + if (!bounds.IsEmpty()) + cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id, bounds); + } else { + // The bounds for the SaveLayer[Alpha]Op should be the source bounds + // before the filter is applied, in the space of the TranslateOp which was + // emitted before the SaveLayer[Alpha]Op. + auto save_layer_bounds = bounds; + if (!save_layer_bounds.IsEmpty()) save_layer_bounds.MoveBy(-current_effect_->FiltersOrigin()); - cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id, - save_layer_bounds); - // We need to propagate the filtered bounds to the parent. - bounds = current_effect_->MapRect(*bounds); - } + cc_list_.UpdateSaveLayerBounds(bounds_info.save_layer_id, + save_layer_bounds); + // We need to propagate the filtered bounds to the parent. + bounds = current_effect_->MapRect(bounds); } effect_bounds_stack_.pop_back(); @@ -732,17 +745,16 @@ void ConversionContext::Convert(const PaintChunkSubset& paint_chunks, else continue; - // If we have an empty paint record, then we would prefer not to draw it. - // However, if we also have a non-root effect, it means that the filter - // applied might draw something even if the record is empty. We need to - // "draw" this record in order to ensure that the effect has correct - // visual rects. + // If we have an empty paint record, then we would prefer ignoring it. + // However, if we also have a non-root effect, the empty paint record + // might be for a mask with empty content which should make the masked + // content fully invisible. We need to "draw" this record to ensure that + // the effect has correct visual rect. if ((!record || record->size() == 0) && &chunk_state.Effect() == &EffectPaintPropertyNode::Root()) { continue; } - TranslateForLayerOffsetOnce(); if (!switched_to_chunk_state) { SwitchToChunkState(chunk); switched_to_chunk_state = true; @@ -755,16 +767,17 @@ void ConversionContext::Convert(const PaintChunkSubset& paint_chunks, chunk_to_layer_mapper_.MapVisualRect(item.VisualRect())); } - // Chunk bounds are only important when we are actually drawing. There may - // also be cases when we only generate a paint record and do not draw, - // for example, to implement an SVG clip. In such cases, we can safely - // ignore effect bounds. - base::Optional<FloatRect> chunk_bounds; - if (cc_list_.GetUsageHint() == - cc::DisplayItemList::kTopLevelDisplayItemList) { - chunk_bounds = FloatRect(chunk.bounds); - } - UpdateEffectBounds(chunk_bounds, chunk_state.Transform()); + // If we have an empty paint chunk, then we would prefer ignoring it. + // However, a reference filter can generate visible effect from invisible + // source, and we need to emit paint operations for it. + if (!switched_to_chunk_state && &chunk_state.Effect() != current_effect_) + SwitchToChunkState(chunk); + + // Most effects apply to drawable contents only. Reference filters are + // exceptions, for which we have already added the reference box to the + // bounds of the effect in StartEffect(). + UpdateEffectBounds(FloatRect(chunk.drawable_bounds), + chunk_state.Transform()); } } diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc index 7953f485235..1291b765dde 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc @@ -7,6 +7,7 @@ #include <initializer_list> #include "cc/paint/display_item_list.h" +#include "cc/paint/paint_filter.h" #include "cc/paint/paint_flags.h" #include "cc/paint/paint_op_buffer.h" #include "testing/gmock/include/gmock/gmock.h" @@ -101,7 +102,7 @@ class PaintRecordMatcher Vector<cc::PaintOpType> expected_ops_; }; -#define EXPECT_EFFECT_BOUNDS(rect, op_buffer, index) \ +#define EXPECT_EFFECT_BOUNDS(x, y, width, height, op_buffer, index) \ do { \ FloatRect bounds; \ if (const auto* save_layer_alpha = \ @@ -113,7 +114,7 @@ class PaintRecordMatcher } else { \ FAIL() << "No SaveLayer[Alpha]Op at " << index; \ } \ - EXPECT_EQ(rect, bounds); \ + EXPECT_EQ(FloatRect(x, y, width, height), bounds); \ } while (false) #define EXPECT_TRANSFORM_MATRIX(transform, op_buffer, index) \ @@ -160,32 +161,45 @@ struct TestChunks { DisplayItemList items = DisplayItemList(0); // Add a paint chunk with a non-empty paint record and given property nodes. - void AddChunk(const TransformPaintPropertyNode& t, - const ClipPaintPropertyNode& c, - const EffectPaintPropertyNode& e, - const IntRect& bounds = IntRect(0, 0, 100, 100)) { + void AddChunk( + const TransformPaintPropertyNode& t, + const ClipPaintPropertyNode& c, + const EffectPaintPropertyNode& e, + const IntRect& bounds = IntRect(0, 0, 100, 100), + const base::Optional<IntRect>& drawable_bounds = base::nullopt) { auto record = sk_make_sp<PaintRecord>(); - record->push<cc::DrawRectOp>(bounds, cc::PaintFlags()); - AddChunk(std::move(record), t, c, e, bounds); + record->push<cc::DrawRectOp>(drawable_bounds ? *drawable_bounds : bounds, + cc::PaintFlags()); + AddChunk(std::move(record), t, c, e, bounds, drawable_bounds); } // Add a paint chunk with a given paint record and property nodes. - void AddChunk(sk_sp<PaintRecord> record, - const TransformPaintPropertyNode& t, - const ClipPaintPropertyNode& c, - const EffectPaintPropertyNode& e, - const IntRect& bounds = IntRect(0, 0, 100, 100)) { - size_t i = items.size(); + void AddChunk( + sk_sp<PaintRecord> record, + const TransformPaintPropertyNode& t, + const ClipPaintPropertyNode& c, + const EffectPaintPropertyNode& e, + const IntRect& bounds = IntRect(0, 0, 100, 100), + const base::Optional<IntRect>& drawable_bounds = base::nullopt) { + auto i = items.size(); items.AllocateAndConstruct<DrawingDisplayItem>( DefaultId().client, DefaultId().type, std::move(record)); + if (drawable_bounds) + items.Last().SetVisualRectForTesting(*drawable_bounds); chunks.emplace_back(i, i + 1, DefaultId(), PropertyTreeState(t, c, e)); chunks.back().bounds = bounds; + chunks.back().drawable_bounds = drawable_bounds ? *drawable_bounds : bounds; } -}; -const cc::DisplayItemList::UsageHint kUsageHints[] = { - cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer, - cc::DisplayItemList::kTopLevelDisplayItemList}; + void AddEmptyChunk(const TransformPaintPropertyNode& t, + const ClipPaintPropertyNode& c, + const EffectPaintPropertyNode& e, + const IntRect& bounds = IntRect(0, 0, 100, 100)) { + auto i = items.size(); + chunks.emplace_back(i, i, DefaultId(), PropertyTreeState(t, c, e)); + chunks.back().bounds = bounds; + } +}; TEST_P(PaintChunksToCcLayerTest, EffectGroupingSimple) { // This test verifies effects are applied as a group. @@ -194,23 +208,18 @@ TEST_P(PaintChunksToCcLayerTest, EffectGroupingSimple) { chunks.AddChunk(t0(), c0(), *e1, IntRect(0, 0, 50, 50)); chunks.AddChunk(t0(), c0(), *e1, IntRect(20, 20, 70, 70)); - const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 90, 90)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - sk_sp<PaintRecord> output = - PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(), - gfx::Vector2dF(), chunks.items, - kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT( - *output, - PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1> - cc::PaintOpType::DrawRecord, // <p0/> - cc::PaintOpType::DrawRecord, // <p1/> - cc::PaintOpType::Restore})); // </e1> - EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0); - } + sk_sp<PaintRecord> output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT( + *output, + PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1> + cc::PaintOpType::DrawRecord, // <p0/> + cc::PaintOpType::DrawRecord, // <p1/> + cc::PaintOpType::Restore})); // </e1> + EXPECT_EFFECT_BOUNDS(0, 0, 90, 90, *output, 0); } TEST_P(PaintChunksToCcLayerTest, EffectGroupingNested) { @@ -222,39 +231,30 @@ TEST_P(PaintChunksToCcLayerTest, EffectGroupingNested) { chunks.AddChunk(t0(), c0(), *e2); chunks.AddChunk(t0(), c0(), *e3, IntRect(111, 222, 333, 444)); - const FloatRect kExpectedBounds1[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 444, 666)}; - const FloatRect kExpectedBounds2[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 100, 100)}; - const FloatRect kExpectedBounds3[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(111, 222, 333, 444)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - sk_sp<PaintRecord> output = - PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(), - gfx::Vector2dF(), chunks.items, - kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT( - *output, - PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1> - cc::PaintOpType::SaveLayerAlpha, // <e2> - cc::PaintOpType::DrawRecord, // <p0/> - cc::PaintOpType::Restore, // </e2> - cc::PaintOpType::SaveLayerAlpha, // <e3> - cc::PaintOpType::DrawRecord, // <p1/> - cc::PaintOpType::Restore, // </e3> - cc::PaintOpType::Restore})); // </e1> - EXPECT_EFFECT_BOUNDS(kExpectedBounds1[hint], *output, 0); - EXPECT_EFFECT_BOUNDS(kExpectedBounds2[hint], *output, 1); - EXPECT_EFFECT_BOUNDS(kExpectedBounds3[hint], *output, 4); - } + sk_sp<PaintRecord> output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT( + *output, + PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1> + cc::PaintOpType::SaveLayerAlpha, // <e2> + cc::PaintOpType::DrawRecord, // <p0/> + cc::PaintOpType::Restore, // </e2> + cc::PaintOpType::SaveLayerAlpha, // <e3> + cc::PaintOpType::DrawRecord, // <p1/> + cc::PaintOpType::Restore, // </e3> + cc::PaintOpType::Restore})); // </e1> + EXPECT_EFFECT_BOUNDS(0, 0, 444, 666, *output, 0); + EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 1); + EXPECT_EFFECT_BOUNDS(111, 222, 333, 444, *output, 4); } TEST_P(PaintChunksToCcLayerTest, EffectFilterGroupingNestedWithTransforms) { // This test verifies nested effects with transforms are grouped properly. auto t1 = CreateTransform(t0(), TransformationMatrix().Scale(2.f)); - auto t2 = CreateTransform(*t1, TransformationMatrix().Translate(-50, -50)); + auto t2 = Create2DTranslation(*t1, -50, -50); auto e1 = CreateOpacityEffect(e0(), *t2, &c0(), 0.5); CompositorFilterOperations filter; @@ -264,47 +264,40 @@ TEST_P(PaintChunksToCcLayerTest, EffectFilterGroupingNestedWithTransforms) { chunks.AddChunk(*t2, c0(), *e1, IntRect(0, 0, 50, 50)); chunks.AddChunk(*t1, c0(), *e2, IntRect(20, 20, 70, 70)); - const FloatRect kExpectedBounds1[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 155, 155)}; - const FloatRect kExpectedBounds2[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(10, 10, 70, 70)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - sk_sp<PaintRecord> output = - PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(), - gfx::Vector2dF(), chunks.items, - kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT( - *output, - PaintRecordMatcher::Make( - {cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1*t2> - cc::PaintOpType::SaveLayerAlpha, // <e1> - cc::PaintOpType::DrawRecord, // <p1/> - cc::PaintOpType::Save, cc::PaintOpType::Translate, // <e2_offset> - cc::PaintOpType::SaveLayer, // <e2> - cc::PaintOpType::Translate, // <e2_offset^-1/> - cc::PaintOpType::Save, cc::PaintOpType::Translate, // <t2^-1> - cc::PaintOpType::DrawRecord, // <p2/> - cc::PaintOpType::Restore, // </t2^-1> - cc::PaintOpType::Restore, // </e2> - cc::PaintOpType::Restore, // </e2_offset> - cc::PaintOpType::Restore, // </e1> - cc::PaintOpType::Restore})); // </t1*t2> - EXPECT_TRANSFORM_MATRIX(t1->Matrix() * t2->SlowMatrix(), *output, 1); - // chunk1.bounds + e2(t2^-1(chunk2.bounds)) - EXPECT_EFFECT_BOUNDS(kExpectedBounds1[hint], *output, 2); - // e2_offset - EXPECT_TRANSLATE(60, 60, *output, 5); - // t2^-1(chunk2.bounds) - e2_offset - EXPECT_EFFECT_BOUNDS(kExpectedBounds2[hint], *output, 6); - // -e2_offset - EXPECT_TRANSLATE(-e2->FiltersOrigin().X(), -e2->FiltersOrigin().Y(), - *output, 7); - // t2^1 - EXPECT_TRANSLATE(-t2->Translation2D().Width(), - -t2->Translation2D().Height(), *output, 9); - } + sk_sp<PaintRecord> output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT( + *output, + PaintRecordMatcher::Make( + {cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1*t2> + cc::PaintOpType::SaveLayerAlpha, // <e1> + cc::PaintOpType::DrawRecord, // <p1/> + cc::PaintOpType::Save, cc::PaintOpType::Translate, // <e2_offset> + cc::PaintOpType::SaveLayer, // <e2> + cc::PaintOpType::Translate, // <e2_offset^-1/> + cc::PaintOpType::Save, cc::PaintOpType::Translate, // <t2^-1> + cc::PaintOpType::DrawRecord, // <p2/> + cc::PaintOpType::Restore, // </t2^-1> + cc::PaintOpType::Restore, // </e2> + cc::PaintOpType::Restore, // </e2_offset> + cc::PaintOpType::Restore, // </e1> + cc::PaintOpType::Restore})); // </t1*t2> + EXPECT_TRANSFORM_MATRIX(t1->Matrix() * t2->SlowMatrix(), *output, 1); + // chunk1.bounds + e2(t2^-1(chunk2.bounds)) + EXPECT_EFFECT_BOUNDS(0, 0, 155, 155, *output, 2); + // e2_offset + EXPECT_TRANSLATE(60, 60, *output, 5); + // t2^-1(chunk2.bounds) - e2_offset + EXPECT_EFFECT_BOUNDS(10, 10, 70, 70, *output, 6); + // -e2_offset + EXPECT_TRANSLATE(-e2->FiltersOrigin().X(), -e2->FiltersOrigin().Y(), *output, + 7); + // t2^1 + EXPECT_TRANSLATE(-t2->Translation2D().Width(), -t2->Translation2D().Height(), + *output, 9); } TEST_P(PaintChunksToCcLayerTest, InterleavedClipEffect) { @@ -326,45 +319,38 @@ TEST_P(PaintChunksToCcLayerTest, InterleavedClipEffect) { chunks.AddChunk(t0(), *c3, *e1, IntRect(20, 20, 70, 70)); chunks.AddChunk(t0(), *c4, e0()); - const FloatRect kExpectedBounds1[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 90, 90)}; - const FloatRect kExpectedBounds2[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 50, 50)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - sk_sp<PaintRecord> output = - PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(), - gfx::Vector2dF(), chunks.items, - kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT(*output, PaintRecordMatcher::Make( - {cc::PaintOpType::Save, - cc::PaintOpType::ClipRect, // <c1+c2> - cc::PaintOpType::DrawRecord, // <p0/> - cc::PaintOpType::Save, - cc::PaintOpType::ClipRect, // <c3> - cc::PaintOpType::DrawRecord, // <p1/> - cc::PaintOpType::Restore, // </c3> - cc::PaintOpType::SaveLayerAlpha, // <e1> - cc::PaintOpType::Save, - cc::PaintOpType::ClipRect, // <c3+c4> - cc::PaintOpType::SaveLayerAlpha, // <e2> - cc::PaintOpType::DrawRecord, // <p2/> - cc::PaintOpType::Restore, // </e2> - cc::PaintOpType::Restore, // </c3+c4> - cc::PaintOpType::Save, - cc::PaintOpType::ClipRect, // <c3> - cc::PaintOpType::DrawRecord, // <p3/> - cc::PaintOpType::Restore, // </c3> - cc::PaintOpType::Restore, // </e1> - cc::PaintOpType::Save, - cc::PaintOpType::ClipRect, // <c3+c4> - cc::PaintOpType::DrawRecord, // <p4/> - cc::PaintOpType::Restore, // </c3+c4> - cc::PaintOpType::Restore})); // </c1+c2> - EXPECT_EFFECT_BOUNDS(kExpectedBounds1[hint], *output, 7); - EXPECT_EFFECT_BOUNDS(kExpectedBounds2[hint], *output, 10); - } + sk_sp<PaintRecord> output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT(*output, PaintRecordMatcher::Make( + {cc::PaintOpType::Save, + cc::PaintOpType::ClipRect, // <c1+c2> + cc::PaintOpType::DrawRecord, // <p0/> + cc::PaintOpType::Save, + cc::PaintOpType::ClipRect, // <c3> + cc::PaintOpType::DrawRecord, // <p1/> + cc::PaintOpType::Restore, // </c3> + cc::PaintOpType::SaveLayerAlpha, // <e1> + cc::PaintOpType::Save, + cc::PaintOpType::ClipRect, // <c3+c4> + cc::PaintOpType::SaveLayerAlpha, // <e2> + cc::PaintOpType::DrawRecord, // <p2/> + cc::PaintOpType::Restore, // </e2> + cc::PaintOpType::Restore, // </c3+c4> + cc::PaintOpType::Save, + cc::PaintOpType::ClipRect, // <c3> + cc::PaintOpType::DrawRecord, // <p3/> + cc::PaintOpType::Restore, // </c3> + cc::PaintOpType::Restore, // </e1> + cc::PaintOpType::Save, + cc::PaintOpType::ClipRect, // <c3+c4> + cc::PaintOpType::DrawRecord, // <p4/> + cc::PaintOpType::Restore, // </c3+c4> + cc::PaintOpType::Restore})); // </c1+c2> + EXPECT_EFFECT_BOUNDS(0, 0, 90, 90, *output, 7); + EXPECT_EFFECT_BOUNDS(0, 0, 50, 50, *output, 10); } TEST_P(PaintChunksToCcLayerTest, ClipSpaceInversion) { @@ -407,29 +393,24 @@ TEST_P(PaintChunksToCcLayerTest, OpacityEffectSpaceInversion) { chunks.AddChunk(t0(), c0(), *e1); chunks.AddChunk(*t1, c0(), *e1); - const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 100, 100)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - sk_sp<PaintRecord> output = - PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(), - gfx::Vector2dF(), chunks.items, - kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT(*output, - PaintRecordMatcher::Make( - {cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1> - cc::PaintOpType::SaveLayerAlpha, // <e1> - cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1^-1> - cc::PaintOpType::DrawRecord, // <p0/> - cc::PaintOpType::Restore, // </t1^-1> - cc::PaintOpType::DrawRecord, // <p1/> - cc::PaintOpType::Restore, // </e1> - cc::PaintOpType::Restore})); // </t1> - EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 2); - EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 1); - EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 4); - } + sk_sp<PaintRecord> output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT(*output, + PaintRecordMatcher::Make( + {cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1> + cc::PaintOpType::SaveLayerAlpha, // <e1> + cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1^-1> + cc::PaintOpType::DrawRecord, // <p0/> + cc::PaintOpType::Restore, // </t1^-1> + cc::PaintOpType::DrawRecord, // <p1/> + cc::PaintOpType::Restore, // </e1> + cc::PaintOpType::Restore})); // </t1> + EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 2); + EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 1); + EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 4); } TEST_P(PaintChunksToCcLayerTest, FilterEffectSpaceInversion) { @@ -447,33 +428,29 @@ TEST_P(PaintChunksToCcLayerTest, FilterEffectSpaceInversion) { TestChunks chunks; chunks.AddChunk(t0(), c0(), *e1); - const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(-66, -88, 50, 50)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - auto output = PaintChunksToCcLayer::Convert( - chunks.chunks, PropertyTreeState::Root(), - gfx::Vector2dF(), chunks.items, kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT( - *output, - PaintRecordMatcher::Make( - {cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1> - cc::PaintOpType::Save, cc::PaintOpType::Translate, // <e1_offset> - cc::PaintOpType::SaveLayer, // <e1> - cc::PaintOpType::Translate, // <e1_offset^-1/> - cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1^-1> - cc::PaintOpType::DrawRecord, // <p0/> - cc::PaintOpType::Restore, // </t1^-1> - cc::PaintOpType::Restore, // </e1> - cc::PaintOpType::Restore, // </e1_offset> - cc::PaintOpType::Restore})); // </t1> - EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 1); - EXPECT_TRANSLATE(66, 88, *output, 3); - EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 4); - EXPECT_TRANSLATE(-66, -88, *output, 5); - EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 7); - } + auto output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT( + *output, + PaintRecordMatcher::Make( + {cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1> + cc::PaintOpType::Save, cc::PaintOpType::Translate, // <e1_offset> + cc::PaintOpType::SaveLayer, // <e1> + cc::PaintOpType::Translate, // <e1_offset^-1/> + cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1^-1> + cc::PaintOpType::DrawRecord, // <p0/> + cc::PaintOpType::Restore, // </t1^-1> + cc::PaintOpType::Restore, // </e1> + cc::PaintOpType::Restore, // </e1_offset> + cc::PaintOpType::Restore})); // </t1> + EXPECT_TRANSFORM_MATRIX(t1->Matrix(), *output, 1); + EXPECT_TRANSLATE(66, 88, *output, 3); + EXPECT_EFFECT_BOUNDS(-66, -88, 50, 50, *output, 4); + EXPECT_TRANSLATE(-66, -88, *output, 5); + EXPECT_TRANSFORM_MATRIX(t1->Matrix().Inverse(), *output, 7); } TEST_P(PaintChunksToCcLayerTest, NonRootLayerSimple) { @@ -523,25 +500,20 @@ TEST_P(PaintChunksToCcLayerTest, EffectWithNoOutputClip) { TestChunks chunks; chunks.AddChunk(t0(), *c2, *e1); - const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 100, 100)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - sk_sp<PaintRecord> output = - PaintChunksToCcLayer::Convert( - chunks.chunks, PropertyTreeState(t0(), *c1, e0()), gfx::Vector2dF(), - chunks.items, kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT( - *output, - PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1> - cc::PaintOpType::Save, - cc::PaintOpType::ClipRect, // <c2> - cc::PaintOpType::DrawRecord, // <p0/> - cc::PaintOpType::Restore, // </c2> - cc::PaintOpType::Restore})); // </e1> - EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0); - } + sk_sp<PaintRecord> output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState(t0(), *c1, e0()), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT( + *output, + PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1> + cc::PaintOpType::Save, + cc::PaintOpType::ClipRect, // <c2> + cc::PaintOpType::DrawRecord, // <p0/> + cc::PaintOpType::Restore, // </c2> + cc::PaintOpType::Restore})); // </e1> + EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0); } TEST_P(PaintChunksToCcLayerTest, @@ -553,30 +525,23 @@ TEST_P(PaintChunksToCcLayerTest, TestChunks chunks; chunks.AddChunk(t0(), *c1, *e2); - const FloatRect kExpectedBounds1[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 100, 100)}; - const FloatRect kExpectedBounds2[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 100, 100)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - sk_sp<PaintRecord> output = - PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(), - gfx::Vector2dF(), chunks.items, - kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT( - *output, - PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1> - cc::PaintOpType::SaveLayerAlpha, // <e2> - cc::PaintOpType::Save, - cc::PaintOpType::ClipRect, // <c1> - cc::PaintOpType::DrawRecord, // <p0/> - cc::PaintOpType::Restore, // </c1> - cc::PaintOpType::Restore, // </e2> - cc::PaintOpType::Restore})); // </e1> - EXPECT_EFFECT_BOUNDS(kExpectedBounds1[hint], *output, 0); - EXPECT_EFFECT_BOUNDS(kExpectedBounds2[hint], *output, 1); - } + sk_sp<PaintRecord> output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT( + *output, + PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e1> + cc::PaintOpType::SaveLayerAlpha, // <e2> + cc::PaintOpType::Save, + cc::PaintOpType::ClipRect, // <c1> + cc::PaintOpType::DrawRecord, // <p0/> + cc::PaintOpType::Restore, // </c1> + cc::PaintOpType::Restore, // </e2> + cc::PaintOpType::Restore})); // </e1> + EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0); + EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 1); } TEST_P(PaintChunksToCcLayerTest, @@ -588,25 +553,20 @@ TEST_P(PaintChunksToCcLayerTest, TestChunks chunks; chunks.AddChunk(t0(), *c1, *e2); - const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 100, 100)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - sk_sp<PaintRecord> output = - PaintChunksToCcLayer::Convert( - chunks.chunks, PropertyTreeState(t0(), c0(), *e1), gfx::Vector2dF(), - chunks.items, kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT( - *output, - PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e2> - cc::PaintOpType::Save, - cc::PaintOpType::ClipRect, // <c1> - cc::PaintOpType::DrawRecord, // <p0/> - cc::PaintOpType::Restore, // </c1> - cc::PaintOpType::Restore})); // </e2> - EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0); - } + sk_sp<PaintRecord> output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState(t0(), c0(), *e1), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT( + *output, + PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e2> + cc::PaintOpType::Save, + cc::PaintOpType::ClipRect, // <c1> + cc::PaintOpType::DrawRecord, // <p0/> + cc::PaintOpType::Restore, // </c1> + cc::PaintOpType::Restore})); // </e2> + EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0); } TEST_P(PaintChunksToCcLayerTest, @@ -618,29 +578,23 @@ TEST_P(PaintChunksToCcLayerTest, TestChunks chunks; chunks.AddChunk(t0(), *c1, *e2); - const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 100, 100)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - sk_sp<PaintRecord> output = - PaintChunksToCcLayer::Convert( - chunks.chunks, PropertyTreeState(t0(), *c1, *e1), gfx::Vector2dF(), - chunks.items, kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT( - *output, - PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e2> - cc::PaintOpType::DrawRecord, // <p0/> - cc::PaintOpType::Restore})); // </e2> - EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0); - } + sk_sp<PaintRecord> output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState(t0(), *c1, *e1), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT( + *output, + PaintRecordMatcher::Make({cc::PaintOpType::SaveLayerAlpha, // <e2> + cc::PaintOpType::DrawRecord, // <p0/> + cc::PaintOpType::Restore})); // </e2> + EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0); } TEST_P(PaintChunksToCcLayerTest, VisualRect) { auto layer_transform = CreateTransform(t0(), TransformationMatrix().Scale(20)); - auto chunk_transform = CreateTransform( - *layer_transform, TransformationMatrix().Translate(50, 100)); + auto chunk_transform = Create2DTranslation(*layer_transform, 50, 100); TestChunks chunks; chunks.AddChunk(*chunk_transform, c0(), e0()); @@ -751,22 +705,17 @@ TEST_P(PaintChunksToCcLayerTest, EmptyEffectsAreStored) { chunks.AddChunk(nullptr, t0(), c0(), e0()); chunks.AddChunk(nullptr, t0(), c0(), *e1); - const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 100, 100)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - sk_sp<PaintRecord> output = - PaintChunksToCcLayer::Convert(chunks.chunks, PropertyTreeState::Root(), - gfx::Vector2dF(), chunks.items, - kUsageHints[hint]) - ->ReleaseAsRecord(); - - EXPECT_THAT(*output, PaintRecordMatcher::Make({ - cc::PaintOpType::SaveLayerAlpha, // <e1> - cc::PaintOpType::Restore, // </e1> - })); - EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0); - } + sk_sp<PaintRecord> output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + + EXPECT_THAT(*output, PaintRecordMatcher::Make({ + cc::PaintOpType::SaveLayerAlpha, // <e1> + cc::PaintOpType::Restore, // </e1> + })); + EXPECT_EFFECT_BOUNDS(0, 0, 100, 100, *output, 0); } TEST_P(PaintChunksToCcLayerTest, CombineClips) { @@ -808,7 +757,7 @@ TEST_P(PaintChunksToCcLayerTest, CombineClips) { TEST_P(PaintChunksToCcLayerTest, CombineClipsAcrossTransform) { FloatRoundedRect clip_rect(0, 0, 100, 100); - auto identity = CreateTransform(t0(), TransformationMatrix()); + auto identity = Create2DTranslation(t0(), 0, 0); auto non_identity = CreateTransform(*identity, TransformationMatrix().Scale(2)); auto non_invertible = @@ -935,9 +884,9 @@ TEST_P(PaintChunksToCcLayerTest, ChunksSamePropertyTreeState) { } TEST_P(PaintChunksToCcLayerTest, NoOpForIdentityTransforms) { - auto t1 = CreateTransform(t0(), TransformationMatrix()); - auto t2 = CreateTransform(*t1, TransformationMatrix()); - auto t3 = CreateTransform(*t2, TransformationMatrix()); + auto t1 = Create2DTranslation(t0(), 0, 0); + auto t2 = Create2DTranslation(*t1, 0, 0); + auto t3 = Create2DTranslation(*t2, 0, 0); auto c1 = CreateClip(c0(), *t2, FloatRoundedRect(0, 0, 100, 100)); auto c2 = CreateClip(*c1, *t3, FloatRoundedRect(0, 0, 200, 50)); @@ -1381,27 +1330,105 @@ TEST_P(PaintChunksToCcLayerTest, AllowChunkEscapeLayerNoopEffects) { })); } -// https://crbug.com/918240 -TEST_P(PaintChunksToCcLayerTest, EmptyChunkRectDoesntTurnToUnsetOne) { +TEST_P(PaintChunksToCcLayerTest, EmptyChunkRect) { CompositorFilterOperations filter; filter.AppendBlurFilter(5); auto e1 = CreateFilterEffect(e0(), t0(), &c0(), filter, FloatPoint(0, 0)); TestChunks chunks; chunks.AddChunk(nullptr, t0(), c0(), *e1, {0, 0, 0, 0}); - const FloatRect kExpectedBounds[] = {FloatRect(cc::PaintOp::kUnsetRect), - FloatRect(0, 0, 0, 0)}; - - for (size_t hint = 0; hint < base::size(kUsageHints); ++hint) { - auto output = PaintChunksToCcLayer::Convert( - chunks.chunks, PropertyTreeState::Root(), - gfx::Vector2dF(), chunks.items, kUsageHints[hint]) - ->ReleaseAsRecord(); - EXPECT_THAT(*output, - PaintRecordMatcher::Make({cc::PaintOpType::SaveLayer, // <e1> - cc::PaintOpType::Restore})); // </e1> - EXPECT_EFFECT_BOUNDS(kExpectedBounds[hint], *output, 0); + auto output = + PaintChunksToCcLayer::Convert( + chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(), + chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + ->ReleaseAsRecord(); + EXPECT_THAT(*output, + PaintRecordMatcher::Make({cc::PaintOpType::SaveLayer, // <e1> + cc::PaintOpType::Restore})); // </e1> + EXPECT_EFFECT_BOUNDS(0, 0, 0, 0, *output, 0); +} + +TEST_P(PaintChunksToCcLayerTest, ReferenceFilterOnEmptyChunk) { + CompositorFilterOperations filter; + filter.AppendReferenceFilter(sk_make_sp<cc::RecordPaintFilter>( + sk_make_sp<cc::PaintOpBuffer>(), SkRect::MakeIWH(100, 100))); + filter.SetReferenceBox(FloatRect(11, 22, 33, 44)); + ASSERT_TRUE(filter.HasReferenceFilter()); + auto e1 = CreateFilterEffect(e0(), t0(), &c0(), filter, FloatPoint(10, 20)); + TestChunks chunks; + chunks.AddEmptyChunk(t0(), c0(), *e1, IntRect(0, 0, 200, 300)); + + auto cc_list = base::MakeRefCounted<cc::DisplayItemList>( + cc::DisplayItemList::kTopLevelDisplayItemList); + PaintChunksToCcLayer::ConvertInto(chunks.chunks, PropertyTreeState::Root(), + gfx::Vector2dF(5, 10), FloatSize(), + chunks.items, *cc_list); + ASSERT_EQ(9u, cc_list->TotalOpCount()); + // (16 32) is (11, 22) + filter_offset - layer_offset. + gfx::Rect expected_visual_rect(16, 32, 33, 44); + for (size_t i = 0; i < cc_list->TotalOpCount(); i++) { + SCOPED_TRACE(testing::Message() << "Visual rect of op " << i); + EXPECT_EQ(expected_visual_rect, cc_list->VisualRectForTesting(i)); + } + + auto output = cc_list->ReleaseAsRecord(); + EXPECT_THAT(*output, + PaintRecordMatcher::Make( + {cc::PaintOpType::Save, + cc::PaintOpType::Translate, // layer offset + cc::PaintOpType::Save, // <e1> + cc::PaintOpType::Translate, cc::PaintOpType::SaveLayer, + cc::PaintOpType::Translate, cc::PaintOpType::Restore, + cc::PaintOpType::Restore, // </e1> + cc::PaintOpType::Restore})); + EXPECT_EFFECT_BOUNDS(11, 22, 33, 44, *output, 4); +} + +TEST_P(PaintChunksToCcLayerTest, ReferenceFilterOnChunkWithDrawingDisplayItem) { + CompositorFilterOperations filter; + filter.AppendReferenceFilter(sk_make_sp<cc::RecordPaintFilter>( + sk_make_sp<cc::PaintOpBuffer>(), SkRect::MakeIWH(100, 100))); + filter.SetReferenceBox(FloatRect(11, 22, 33, 44)); + ASSERT_TRUE(filter.HasReferenceFilter()); + auto e1 = CreateFilterEffect(e0(), t0(), &c0(), filter, FloatPoint(10, 20)); + TestChunks chunks; + chunks.AddChunk(t0(), c0(), *e1, IntRect(5, 10, 200, 300), + IntRect(10, 15, 20, 30)); + + auto cc_list = base::MakeRefCounted<cc::DisplayItemList>( + cc::DisplayItemList::kTopLevelDisplayItemList); + PaintChunksToCcLayer::ConvertInto(chunks.chunks, PropertyTreeState::Root(), + gfx::Vector2dF(5, 10), FloatSize(), + chunks.items, *cc_list); + ASSERT_EQ(11u, cc_list->TotalOpCount()); + // This is the visual rect for all filter related paint operations, which is + // the union of the draw record and reference box of the filter in the layer's + // space. + gfx::Rect expected_filter_visual_rect(5, 5, 44, 71); + // This is the visual rect of the DrawingDisplayItem in the layer's space. + gfx::Rect expected_draw_visual_rect(5, 5, 20, 30); + // TotalOpCount() - 1 because the DrawRecord op has a sub operation. + for (size_t i = 0; i < cc_list->TotalOpCount() - 1; i++) { + SCOPED_TRACE(testing::Message() << "Visual rect of op " << i); + EXPECT_EQ(i == 6 ? expected_draw_visual_rect : expected_filter_visual_rect, + cc_list->VisualRectForTesting(i)); } + + auto output = cc_list->ReleaseAsRecord(); + EXPECT_THAT(*output, + PaintRecordMatcher::Make( + {cc::PaintOpType::Save, + cc::PaintOpType::Translate, // layer offset + cc::PaintOpType::Save, // + cc::PaintOpType::Translate, // e1->FilterOrigin() + cc::PaintOpType::SaveLayer, // <e1> + cc::PaintOpType::Translate, // -e1->FilterOrigin() + cc::PaintOpType::DrawRecord, // the DrawingDisplayItem + cc::PaintOpType::Restore, // </e1> + cc::PaintOpType::Restore, cc::PaintOpType::Restore})); + // The effect bounds are the union of the chunk's drawable_bounds and the + // reference box in the filter's space. + EXPECT_EFFECT_BOUNDS(0, -5, 44, 71, *output, 4); } } // namespace diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc index 757e737fa3b..07ded512b38 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc @@ -34,12 +34,6 @@ static constexpr int kSecondaryRootNodeId = 1; } // namespace -inline const TransformPaintPropertyNode& -PropertyTreeManager::EffectState::Transform() const { - return effect_type == CcEffectType::kEffect ? effect->LocalTransformSpace() - : clip->LocalTransformSpace(); -} - PropertyTreeManager::PropertyTreeManager( PropertyTreeManagerClient& client, cc::PropertyTrees& property_trees, @@ -113,9 +107,9 @@ static void SetTransformTreePageScaleFactor( } bool PropertyTreeManager::DirectlyUpdateCompositedOpacityValue( - cc::PropertyTrees* property_trees, cc::LayerTreeHost& host, const EffectPaintPropertyNode& effect) { + auto* property_trees = host.property_trees(); auto* cc_effect = property_trees->effect_tree.Node( effect.CcNodeId(property_trees->sequence_number)); if (!cc_effect) @@ -134,7 +128,6 @@ bool PropertyTreeManager::DirectlyUpdateCompositedOpacityValue( } bool PropertyTreeManager::DirectlyUpdateScrollOffsetTransform( - cc::PropertyTrees* property_trees, cc::LayerTreeHost& host, const TransformPaintPropertyNode& transform) { auto* scroll_node = transform.ScrollNode(); @@ -142,6 +135,7 @@ bool PropertyTreeManager::DirectlyUpdateScrollOffsetTransform( if (!scroll_node) return false; + auto* property_trees = host.property_trees(); auto* cc_scroll_node = property_trees->scroll_tree.Node( scroll_node->CcNodeId(property_trees->sequence_number)); if (!cc_scroll_node) @@ -155,24 +149,22 @@ bool PropertyTreeManager::DirectlyUpdateScrollOffsetTransform( DCHECK(!cc_transform->is_currently_animating); UpdateCcTransformLocalMatrix(*cc_transform, transform); - property_trees->scroll_tree.SetScrollOffset( - scroll_node->GetCompositorElementId(), cc_transform->scroll_offset); - + DirectlySetScrollOffset(host, scroll_node->GetCompositorElementId(), + cc_transform->scroll_offset); cc_transform->transform_changed = true; property_trees->transform_tree.set_needs_update(true); - property_trees->scroll_tree.set_needs_update(true); host.SetNeedsCommit(); return true; } bool PropertyTreeManager::DirectlyUpdateTransform( - cc::PropertyTrees* property_trees, cc::LayerTreeHost& host, const TransformPaintPropertyNode& transform) { // If we have a ScrollNode, we should be using // DirectlyUpdateScrollOffsetTransform(). DCHECK(!transform.ScrollNode()); + auto* property_trees = host.property_trees(); auto* cc_transform = property_trees->transform_tree.Node( transform.CcNodeId(property_trees->sequence_number)); if (!cc_transform) @@ -192,11 +184,11 @@ bool PropertyTreeManager::DirectlyUpdateTransform( } bool PropertyTreeManager::DirectlyUpdatePageScaleTransform( - cc::PropertyTrees* property_trees, cc::LayerTreeHost& host, const TransformPaintPropertyNode& transform) { DCHECK(!transform.ScrollNode()); + auto* property_trees = host.property_trees(); auto* cc_transform = property_trees->transform_tree.Node( transform.CcNodeId(property_trees->sequence_number)); if (!cc_transform) @@ -211,6 +203,19 @@ bool PropertyTreeManager::DirectlyUpdatePageScaleTransform( return true; } +// static +void PropertyTreeManager::DirectlySetScrollOffset( + cc::LayerTreeHost& host, + CompositorElementId element_id, + const gfx::ScrollOffset& scroll_offset) { + auto* property_trees = host.property_trees(); + if (property_trees->scroll_tree.SetScrollOffset(element_id, scroll_offset)) { + if (auto* layer = host.LayerByElementId(element_id)) + layer->SetNeedsPushProperties(); + host.SetNeedsCommit(); + } +} + cc::TransformTree& PropertyTreeManager::GetTransformTree() { return property_trees_.transform_tree; } @@ -298,9 +303,9 @@ void PropertyTreeManager::SetupRootEffectNode() { EffectPaintPropertyNode::Root().SetCcNodeId(new_sequence_number_, effect_node.id); - SetCurrentEffectState(effect_node, CcEffectType::kEffect, - EffectPaintPropertyNode::Root(), - ClipPaintPropertyNode::Root()); + SetCurrentEffectState( + effect_node, CcEffectType::kEffect, EffectPaintPropertyNode::Root(), + ClipPaintPropertyNode::Root(), TransformPaintPropertyNode::Root()); } void PropertyTreeManager::SetupRootScrollNode() { @@ -351,15 +356,17 @@ void PropertyTreeManager::SetCurrentEffectState( const cc::EffectNode& cc_effect_node, CcEffectType effect_type, const EffectPaintPropertyNode& effect, - const ClipPaintPropertyNode& clip) { + const ClipPaintPropertyNode& clip, + const TransformPaintPropertyNode& transform) { const auto* previous_transform = - effect.IsRoot() ? nullptr : ¤t_.Transform(); + effect.IsRoot() ? nullptr : current_.transform; current_.effect_id = cc_effect_node.id; current_.effect_type = effect_type; DCHECK(!effect.IsParentAlias() || !effect.Parent()); current_.effect = &effect; DCHECK(!clip.IsParentAlias() || !clip.Parent()); current_.clip = &clip; + current_.transform = &transform; if (cc_effect_node.HasRenderSurface()) { current_.may_be_2d_axis_misaligned_to_render_surface = @@ -368,7 +375,7 @@ void PropertyTreeManager::SetCurrentEffectState( } else { if (current_.may_be_2d_axis_misaligned_to_render_surface == EffectState::kAligned && - previous_transform != ¤t_.Transform()) { + previous_transform != current_.transform) { current_.may_be_2d_axis_misaligned_to_render_surface = EffectState::kUnknown; } @@ -422,7 +429,9 @@ int PropertyTreeManager::EnsureCompositorTransformNode( // cache a lookup of transform node to scroll translation transform node. const auto& scroll_ancestor = transform_node.NearestScrollTranslationNode(); sticky_data.scroll_ancestor = EnsureCompositorScrollNode(scroll_ancestor); - if (scroll_ancestor.ScrollNode()->ScrollsOuterViewport()) + const auto& scroll_ancestor_compositor_node = + *GetScrollTree().Node(sticky_data.scroll_ancestor); + if (scroll_ancestor_compositor_node.scrolls_outer_viewport) GetTransformTree().AddNodeAffectedByOuterViewportBoundsDelta(id); if (auto shifting_sticky_box_element_id = sticky_data.constraints.nearest_element_shifting_sticky_box) { @@ -466,8 +475,8 @@ int PropertyTreeManager::EnsureCompositorTransformNode( transform_node.FlattensInheritedTransform() && transform_node.Parent() && transform_node.Parent()->RenderingContextId() && !transform_node.Parent()->FlattensInheritedTransform()) { - SetCurrentEffectRenderSurfaceReason( - cc::RenderSurfaceReason::k3dTransformFlattening); + current_cc_effect->render_surface_reason = + cc::RenderSurfaceReason::k3dTransformFlattening; } transform_node.SetCcNodeId(new_sequence_number_, id); @@ -502,7 +511,7 @@ int PropertyTreeManager::EnsureCompositorClipNode( cc::ClipNode& compositor_node = *GetClipTree().Node(id); - compositor_node.clip = clip_node.ClipRect().Rect(); + compositor_node.clip = clip_node.PixelSnappedClipRect().Rect(); compositor_node.transform_id = EnsureCompositorTransformNode(clip_node.LocalTransformSpace()); compositor_node.clip_type = cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP; @@ -534,15 +543,9 @@ void PropertyTreeManager::CreateCompositorScrollNode( scroll_node.UserScrollableHorizontal(); compositor_node.user_scrollable_vertical = scroll_node.UserScrollableVertical(); - compositor_node.scrolls_inner_viewport = scroll_node.ScrollsInnerViewport(); - compositor_node.scrolls_outer_viewport = scroll_node.ScrollsOuterViewport(); compositor_node.prevent_viewport_scrolling_from_inner = scroll_node.PreventViewportScrollingFromInner(); - // |scrolls_using_viewport| should only ever be set on the inner scroll node. - DCHECK(!compositor_node.prevent_viewport_scrolling_from_inner || - compositor_node.scrolls_inner_viewport); - compositor_node.max_scroll_offset_affected_by_page_scale = scroll_node.MaxScrollOffsetAffectedByPageScale(); compositor_node.main_thread_scrolling_reasons = @@ -562,13 +565,10 @@ void PropertyTreeManager::CreateCompositorScrollNode( compositor_node.transform_id = scroll_offset_translation.id; - // TODO(pdr): Set the scroll node's non_fast_scrolling_region value. - scroll_node.SetCcNodeId(new_sequence_number_, id); GetScrollTree().SetScrollOffset(compositor_element_id, scroll_offset_translation.scroll_offset); - GetScrollTree().set_needs_update(true); } int PropertyTreeManager::EnsureCompositorScrollNode( @@ -581,24 +581,38 @@ int PropertyTreeManager::EnsureCompositorScrollNode( return id; } -void PropertyTreeManager::EmitClipMaskLayer() { - cc::EffectNode& mask_isolation = *GetEffectTree().Node(current_.effect_id); +int PropertyTreeManager::EnsureCompositorInnerScrollNode( + const TransformPaintPropertyNode& scroll_offset_translation) { + int node_id = EnsureCompositorScrollNode(scroll_offset_translation); + GetScrollTree().Node(node_id)->scrolls_inner_viewport = true; + return node_id; +} +int PropertyTreeManager::EnsureCompositorOuterScrollNode( + const TransformPaintPropertyNode& scroll_offset_translation) { + int node_id = EnsureCompositorScrollNode(scroll_offset_translation); + GetScrollTree().Node(node_id)->scrolls_outer_viewport = true; + return node_id; +} + +void PropertyTreeManager::EmitClipMaskLayer() { + cc::EffectNode* mask_isolation = GetEffectTree().Node(current_.effect_id); + DCHECK(mask_isolation); bool needs_layer = - !pending_synthetic_mask_layers_.Contains(mask_isolation.id) && - mask_isolation.rounded_corner_bounds.IsEmpty(); + !pending_synthetic_mask_layers_.Contains(mask_isolation->id) && + mask_isolation->rounded_corner_bounds.IsEmpty(); int clip_id = EnsureCompositorClipNode(*current_.clip); CompositorElementId mask_isolation_id, mask_effect_id; SynthesizedClip& clip = client_.CreateOrReuseSynthesizedClipLayer( - *current_.clip, needs_layer, mask_isolation_id, mask_effect_id); + *current_.clip, *current_.transform, needs_layer, mask_isolation_id, + mask_effect_id); // Assignment of mask_isolation.stable_id was delayed until now. // See PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(). DCHECK_EQ(static_cast<uint64_t>(cc::EffectNode::INVALID_STABLE_ID), - mask_isolation.stable_id); - - mask_isolation.stable_id = mask_isolation_id.GetStableId(); + mask_isolation->stable_id); + mask_isolation->stable_id = mask_isolation_id.GetStableId(); if (!needs_layer) return; @@ -609,7 +623,11 @@ void PropertyTreeManager::EmitClipMaskLayer() { mask_effect.clip_id = clip_id; mask_effect.blend_mode = SkBlendMode::kDstIn; - cc::Layer* mask_layer = clip.Layer(); + // The address of mask_isolation may have changed when we insert + // |mask_effect| into the tree. + mask_isolation = GetEffectTree().Node(current_.effect_id); + + cc::PictureLayer* mask_layer = clip.Layer(); const auto& clip_space = current_.clip->LocalTransformSpace(); layer_list_builder_.Add(mask_layer); @@ -624,21 +642,29 @@ void PropertyTreeManager::EmitClipMaskLayer() { mask_layer->SetScrollTreeIndex(scroll_id); mask_layer->SetClipTreeIndex(clip_id); mask_layer->SetEffectTreeIndex(mask_effect.id); + + if (!mask_isolation->backdrop_filters.IsEmpty()) { + mask_layer->SetIsBackdropFilterMask(true); + auto element_id = CompositorElementIdFromUniqueObjectId( + mask_effect.stable_id, CompositorElementIdNamespace::kEffectMask); + mask_layer->SetElementId(element_id); + mask_isolation->backdrop_mask_element_id = element_id; + } } void PropertyTreeManager::CloseCcEffect() { DCHECK(effect_stack_.size()); const auto& previous_state = effect_stack_.back(); - // An effect with exotic blending or backdrop-filter that is masked by a - // synthesized clip must have its blending to the outermost synthesized clip. + // A backdrop effect (exotic blending or backdrop filter) that is masked by a + // synthesized clip must have its effect to the outermost synthesized clip. // These operations need access to the backdrop of the enclosing effect. With // the isolation for a synthesized clip, a blank backdrop will be seen. - // Therefore the blending is delegated to the outermost synthesized clip, thus - // the clip can't be shared with sibling layers, and must be closed now. + // Therefore the backdrop effect is delegated to the outermost synthesized + // clip, thus the clip can't be shared with sibling layers, and must be + // closed now. bool clear_synthetic_effects = - !IsCurrentCcEffectSynthetic() && - current_.effect->BlendMode() != SkBlendMode::kSrcOver; + !IsCurrentCcEffectSynthetic() && current_.effect->HasBackdropEffect(); // We are about to close an effect that was synthesized for isolating // a clip mask. Now emit the actual clip mask that will be composited on @@ -715,7 +741,7 @@ int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip( CloseCcEffect(); BuildEffectNodesRecursively(next_effect); - SynthesizeCcEffectsForClipsIfNeeded(next_clip, SkBlendMode::kSrcOver); + SynthesizeCcEffectsForClipsIfNeeded(next_clip, /*next_effect*/ nullptr); if (layer_draws_content) pending_synthetic_mask_layers_.clear(); @@ -741,7 +767,7 @@ static bool IsNodeOnAncestorChain(const ClipPaintPropertyNode& find, bool PropertyTreeManager::EffectStateMayBe2dAxisMisalignedToRenderSurface( EffectState& state, - size_t index) { + wtf_size_t index) { if (state.may_be_2d_axis_misaligned_to_render_surface == EffectState::kUnknown) { // The root effect has render surface, so it's always kAligned. @@ -752,8 +778,8 @@ bool PropertyTreeManager::EffectStateMayBe2dAxisMisalignedToRenderSurface( EffectState::kMisaligned; } else { state.may_be_2d_axis_misaligned_to_render_surface = - TransformsMayBe2dAxisMisaligned(effect_stack_[index - 1].Transform(), - current_.Transform()) + TransformsMayBe2dAxisMisaligned(*effect_stack_[index - 1].transform, + *current_.transform) ? EffectState::kMisaligned : EffectState::kAligned; } @@ -771,14 +797,14 @@ bool PropertyTreeManager::CurrentEffectMayBe2dAxisMisalignedToRenderSurface() { PropertyTreeManager::CcEffectType PropertyTreeManager::SyntheticEffectType( const ClipPaintPropertyNode& clip) { unsigned effect_type = CcEffectType::kEffect; - if (clip.ClipRect().IsRounded() || clip.ClipPath()) + if (clip.PixelSnappedClipRect().IsRounded() || clip.ClipPath()) effect_type |= CcEffectType::kSyntheticForNonTrivialClip; // Cc requires that a rectangluar clip is 2d-axis-aligned with the render // surface to correctly apply the clip. if (CurrentEffectMayBe2dAxisMisalignedToRenderSurface() || TransformsMayBe2dAxisMisaligned(clip.LocalTransformSpace(), - current_.Transform())) + *current_.transform)) effect_type |= CcEffectType::kSyntheticFor2dAxisAlignment; return static_cast<CcEffectType>(effect_type); } @@ -793,21 +819,26 @@ void PropertyTreeManager::ForceRenderSurfaceIfSyntheticRoundedCornerClip( bool PropertyTreeManager::SupportsShaderBasedRoundedCorner( const ClipPaintPropertyNode& clip, - PropertyTreeManager::CcEffectType type) { - if (!RuntimeEnabledFeatures::FastBorderRadiusEnabled()) - return false; - + PropertyTreeManager::CcEffectType type, + const EffectPaintPropertyNode* next_effect) { if (type & CcEffectType::kSyntheticFor2dAxisAlignment) return false; if (clip.ClipPath()) return false; + // Don't use shader based rounded corner if the next effect has backdrop + // filter and the clip is in different transform space, because we will use + // the effect's transform space for the mask isolation effect node. + if (next_effect && !next_effect->BackdropFilter().IsEmpty() && + &next_effect->LocalTransformSpace() != &clip.LocalTransformSpace()) + return false; + auto WidthAndHeightAreTheSame = [](const FloatSize& size) { return size.Width() == size.Height(); }; - const FloatRoundedRect::Radii& radii = clip.ClipRect().GetRadii(); + const FloatRoundedRect::Radii& radii = clip.PixelSnappedClipRect().GetRadii(); if (!WidthAndHeightAreTheSame(radii.TopLeft()) || !WidthAndHeightAreTheSame(radii.TopRight()) || !WidthAndHeightAreTheSame(radii.BottomRight()) || @@ -815,11 +846,10 @@ bool PropertyTreeManager::SupportsShaderBasedRoundedCorner( return false; } - // Rounded corners that differ are not supported by the - // CALayerOverlay system on Mac. Instead of letting it fall back - // to the (worse for memory and battery) non-CALayerOverlay system - // for such cases, fall back to a non-fast border-radius mask for - // the effect node. + // Rounded corners that differ are not supported by the CALayerOverlay system + // on Mac. Instead of letting it fall back to the (worse for memory and + // battery) non-CALayerOverlay system for such cases, fall back to a + // non-shader border-radius mask for the effect node. #if defined(OS_MACOSX) if (radii.TopLeft() != radii.TopRight() || radii.TopLeft() != radii.BottomRight() || @@ -831,20 +861,55 @@ bool PropertyTreeManager::SupportsShaderBasedRoundedCorner( return true; } -SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded( +static cc::RenderSurfaceReason RenderSurfaceReasonForBackdropEffect( + const EffectPaintPropertyNode& backdrop_effect) { + DCHECK(backdrop_effect.HasBackdropEffect()); + if (!backdrop_effect.BackdropFilter().IsEmpty()) + return cc::RenderSurfaceReason::kBackdropFilter; + if (backdrop_effect.HasActiveBackdropFilterAnimation()) + return cc::RenderSurfaceReason::kBackdropFilterAnimation; + DCHECK_NE(backdrop_effect.BlendMode(), SkBlendMode::kSrcOver); + // For optimization, we will set render surface reason for DstIn later in + // PaintArtifactCompositor::UpdateRenderSurfaceForEffects() only if needed. + if (backdrop_effect.BlendMode() == SkBlendMode::kDstIn) + return cc::RenderSurfaceReason::kNone; + return cc::RenderSurfaceReason::kBlendMode; +} + +void PropertyTreeManager::PopulateCcEffectNodeBackdropEffect( + cc::EffectNode& effect_node, + const EffectPaintPropertyNode& backdrop_effect) { + DCHECK(backdrop_effect.HasBackdropEffect()); + + effect_node.backdrop_filters = + backdrop_effect.BackdropFilter().AsCcFilterOperations(); + effect_node.backdrop_filter_bounds = backdrop_effect.BackdropFilterBounds(); + effect_node.filters_origin = backdrop_effect.FiltersOrigin(); + effect_node.blend_mode = backdrop_effect.BlendMode(); + if (effect_node.render_surface_reason == cc::RenderSurfaceReason::kNone) { + effect_node.render_surface_reason = + RenderSurfaceReasonForBackdropEffect(backdrop_effect); + } +} + +PropertyTreeManager::BackdropEffectState +PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded( const ClipPaintPropertyNode& target_clip_arg, - SkBlendMode delegated_blend) { + const EffectPaintPropertyNode* next_effect) { const auto* target_clip = &target_clip_arg.Unalias(); - if (delegated_blend != SkBlendMode::kSrcOver) { - // Exit all synthetic effect node if the next child has exotic blending mode - // because it has to access the backdrop of enclosing effect. + auto backdrop_effect_state = kNoBackdropEffect; + if (next_effect && next_effect->HasBackdropEffect()) { + // Exit all synthetic effect node if the next child has backdrop effect + // (exotic blending mode or backdrop filter) because it has to access the + // backdrop of enclosing effect. while (IsCurrentCcEffectSynthetic()) CloseCcEffect(); - // An effect node can't omit render surface if it has child with exotic - // blending mode. See comments below for more detail. - // TODO(crbug.com/504464): Remove premature optimization here. - SetCurrentEffectRenderSurfaceReason(cc::RenderSurfaceReason::kBlendMode); + // An effect node can't omit render surface if it has child with backdrop + // effect, in order to define the scope of the backdrop. + SetCurrentEffectRenderSurfaceReason( + cc::RenderSurfaceReason::kBackdropScope); + backdrop_effect_state = kBackdropEffectToBeSetOnCcEffectNode; } else { // Exit synthetic effects until there are no more synthesized clips below // our lowest common ancestor. @@ -857,7 +922,7 @@ SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded( // In CompositeAfterPaint this should never happen. if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) NOTREACHED(); - return delegated_blend; + return backdrop_effect_state; } const auto* pre_exit_clip = current_.clip; CloseCcEffect(); @@ -886,10 +951,13 @@ SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded( // In CompositeAfterPaint this should never happen. if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) NOTREACHED(); - return delegated_blend; + return backdrop_effect_state; } - for (size_t i = pending_clips.size(); i--;) { + if (pending_clips.IsEmpty()) + return backdrop_effect_state; + + for (auto i = pending_clips.size(); i--;) { const auto& pending_clip = pending_clips[i]; // For a non-trivial clip, the synthetic effect is an isolation to enclose @@ -898,29 +966,16 @@ SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded( // surface which is axis-aligned with the clip. cc::EffectNode& synthetic_effect = *GetEffectTree().Node( GetEffectTree().Insert(cc::EffectNode(), current_.effect_id)); + if (pending_clip.type & CcEffectType::kSyntheticForNonTrivialClip) { synthetic_effect.clip_id = EnsureCompositorClipNode(*pending_clip.clip); // For non-trivial clip, isolation_effect.stable_id will be assigned later // when the effect is closed. For now the default value INVALID_STABLE_ID // is used. See PropertyTreeManager::EmitClipMaskLayer(). - } else { - synthetic_effect.stable_id = - CompositorElementIdFromUniqueObjectId(NewUniqueObjectId()) - .GetStableId(); - // The clip of the synthetic effect is the parent of the clip, so that - // the clip itself will be applied in the render surface. - DCHECK(pending_clip.clip->Parent()); - synthetic_effect.clip_id = - EnsureCompositorClipNode(*pending_clip.clip->Parent()); - } - const auto& transform = pending_clip.clip->LocalTransformSpace(); - synthetic_effect.transform_id = EnsureCompositorTransformNode(transform); - synthetic_effect.double_sided = !transform.IsBackfaceHidden(); - if (pending_clip.type & CcEffectType::kSyntheticForNonTrivialClip) { if (SupportsShaderBasedRoundedCorner(*pending_clip.clip, - pending_clip.type)) { + pending_clip.type, next_effect)) { synthetic_effect.rounded_corner_bounds = - gfx::RRectF(pending_clip.clip->ClipRect()); + gfx::RRectF(pending_clip.clip->PixelSnappedClipRect()); synthetic_effect.is_fast_rounded_corner = true; // Nested rounded corner clips need to force render surfaces for @@ -938,31 +993,50 @@ SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded( } } else { synthetic_effect.render_surface_reason = - pending_clip.clip->ClipRect().IsRounded() + pending_clip.clip->PixelSnappedClipRect().IsRounded() ? cc::RenderSurfaceReason::kRoundedCorner : cc::RenderSurfaceReason::kClipPath; } pending_synthetic_mask_layers_.insert(synthetic_effect.id); + } else { + DCHECK(pending_clip.type & CcEffectType::kSyntheticFor2dAxisAlignment); + synthetic_effect.stable_id = + CompositorElementIdFromUniqueObjectId(NewUniqueObjectId()) + .GetStableId(); + // The clip of the synthetic effect is the parent of the clip, so that + // the clip itself will be applied in the render surface. + DCHECK(pending_clip.clip->Parent()); + synthetic_effect.clip_id = + EnsureCompositorClipNode(*pending_clip.clip->Parent()); } + if (pending_clip.type & CcEffectType::kSyntheticFor2dAxisAlignment) { synthetic_effect.render_surface_reason = cc::RenderSurfaceReason::kClipAxisAlignment; } - // Clip and kDstIn do not commute. This shall never be reached because - // kDstIn is only used internally to implement CSS clip-path and mask, - // and there is never a difference between the output clip of the effect - // and the mask content. - DCHECK(delegated_blend != SkBlendMode::kDstIn); - synthetic_effect.blend_mode = delegated_blend; - delegated_blend = SkBlendMode::kSrcOver; + const TransformPaintPropertyNode* transform = nullptr; + if (backdrop_effect_state == kBackdropEffectToBeSetOnCcEffectNode) { + // Move the backdrop effect from the original effect up to the outermost + // synthetic effect to ensure the backdrop effect can access the correct + // backdrop. + DCHECK(next_effect); + transform = &next_effect->LocalTransformSpace(); + PopulateCcEffectNodeBackdropEffect(synthetic_effect, *next_effect); + backdrop_effect_state = kBackdropEffectHasSetOnSyntheticEffect; + } else { + transform = &pending_clip.clip->LocalTransformSpace(); + } + + synthetic_effect.transform_id = EnsureCompositorTransformNode(*transform); + synthetic_effect.double_sided = !transform->IsBackfaceHidden(); effect_stack_.emplace_back(current_); SetCurrentEffectState(synthetic_effect, pending_clip.type, *current_.effect, - *pending_clip.clip); + *pending_clip.clip, *transform); } - return delegated_blend; + return backdrop_effect_state; } void PropertyTreeManager::BuildEffectNodesRecursively( @@ -981,26 +1055,35 @@ void PropertyTreeManager::BuildEffectNodesRecursively( "be contiguous."; #endif - // If we don't have an output clip, then we'll use the clip of the last - // non-synthetic effect. This means we should close all synthetic effects on - // the stack first. - if (!next_effect.OutputClip()) { + auto backdrop_effect_state = kNoBackdropEffect; + int output_clip_id = 0; + const auto* output_clip = SafeUnalias(next_effect.OutputClip()); + if (output_clip) { + backdrop_effect_state = + SynthesizeCcEffectsForClipsIfNeeded(*output_clip, &next_effect); + output_clip_id = EnsureCompositorClipNode(*output_clip); + } else { + // If we don't have an output clip, then we'll use the clip of the last + // non-synthetic effect. This means we should close all synthetic effects + // on the stack first. while (IsCurrentCcEffectSynthetic()) CloseCcEffect(); - } - SkBlendMode blend_mode; - const ClipPaintPropertyNode* output_clip; - int output_clip_id; - std::tie(blend_mode, output_clip, output_clip_id) = - GetBlendModeAndOutputClipForEffect(next_effect); + if (next_effect.HasBackdropEffect()) + backdrop_effect_state = kBackdropEffectToBeSetOnCcEffectNode; + output_clip = current_.clip; + DCHECK(output_clip); + output_clip_id = GetEffectTree().Node(current_.effect_id)->clip_id; + DCHECK_EQ(output_clip_id, EnsureCompositorClipNode(*output_clip)); + } int effect_node_id = GetEffectTree().Insert(cc::EffectNode(), current_.effect_id); auto& effect_node = *GetEffectTree().Node(effect_node_id); next_effect.SetCcNodeId(new_sequence_number_, effect_node_id); - PopulateCcEffectNode(effect_node, next_effect, output_clip_id, blend_mode); + PopulateCcEffectNode(effect_node, next_effect, output_clip_id, + backdrop_effect_state); CompositorElementId compositor_element_id = next_effect.GetCompositorElementId(); @@ -1013,45 +1096,18 @@ void PropertyTreeManager::BuildEffectNodesRecursively( effect_stack_.emplace_back(current_); SetCurrentEffectState(effect_node, CcEffectType::kEffect, next_effect, - *output_clip); -} - -std::tuple<SkBlendMode, const ClipPaintPropertyNode*, int> -PropertyTreeManager::GetBlendModeAndOutputClipForEffect( - const EffectPaintPropertyNode& effect) { - SkBlendMode blend_mode; - int output_clip_id; - const auto* output_clip = SafeUnalias(effect.OutputClip()); - if (output_clip) { - blend_mode = - SynthesizeCcEffectsForClipsIfNeeded(*output_clip, effect.BlendMode()); - output_clip_id = EnsureCompositorClipNode(*output_clip); - } else { - DCHECK(!IsCurrentCcEffectSynthetic()); - // An effect node can't omit render surface if it has child with exotic - // blending mode. - // TODO(crbug.com/504464): Remove premature optimization here. - if (effect.BlendMode() != SkBlendMode::kSrcOver) - SetCurrentEffectRenderSurfaceReason(cc::RenderSurfaceReason::kBlendMode); - - blend_mode = effect.BlendMode(); - output_clip = current_.clip; - DCHECK(output_clip); - output_clip_id = GetEffectTree().Node(current_.effect_id)->clip_id; - DCHECK_EQ(output_clip_id, EnsureCompositorClipNode(*output_clip)); - } - return std::make_tuple(blend_mode, output_clip, output_clip_id); + *output_clip, next_effect.LocalTransformSpace()); } void PropertyTreeManager::PopulateCcEffectNode( cc::EffectNode& effect_node, const EffectPaintPropertyNode& effect, int output_clip_id, - SkBlendMode blend_mode) { + BackdropEffectState backdrop_effect_state) { effect_node.stable_id = effect.GetCompositorElementId().GetStableId(); effect_node.clip_id = output_clip_id; - // An effect with filters or backdrop filters needs a render surface. + // An effect with filters or backdrop effect needs a render surface. // Also, kDstIn and kSrcOver blend modes have fast paths if only one layer // is under the blend mode. This value is adjusted in PaintArtifactCompositor // ::UpdateRenderSurfaceForEffects() to account for more than one layer. @@ -1060,38 +1116,34 @@ void PropertyTreeManager::PopulateCcEffectNode( } else if (effect.HasActiveFilterAnimation()) { effect_node.render_surface_reason = cc::RenderSurfaceReason::kFilterAnimation; - } else if (!effect.BackdropFilter().IsEmpty()) { - effect_node.render_surface_reason = - cc::RenderSurfaceReason::kBackdropFilter; - } else if (effect.HasActiveBackdropFilterAnimation()) { - effect_node.render_surface_reason = - cc::RenderSurfaceReason::kBackdropFilterAnimation; - } else if (blend_mode != SkBlendMode::kSrcOver && - blend_mode != SkBlendMode::kDstIn) { - effect_node.render_surface_reason = cc::RenderSurfaceReason::kBlendMode; } + // If needed, render surface reason for backdrop effect will be set in + // PopuluateCcEffectNodeBackdropEffect() below. effect_node.opacity = effect.Opacity(); if (effect.GetColorFilter() != kColorFilterNone) { // Currently color filter is only used by SVG masks. // We are cutting corner here by support only specific configuration. - DCHECK(effect.GetColorFilter() == kColorFilterLuminanceToAlpha); - DCHECK(blend_mode == SkBlendMode::kDstIn); + DCHECK_EQ(effect.GetColorFilter(), kColorFilterLuminanceToAlpha); + DCHECK_EQ(effect.BlendMode(), SkBlendMode::kDstIn); DCHECK(effect.Filter().IsEmpty()); effect_node.filters.Append(cc::FilterOperation::CreateReferenceFilter( sk_make_sp<ColorFilterPaintFilter>(SkLumaColorFilter::Make(), nullptr))); + effect_node.blend_mode = SkBlendMode::kDstIn; } else { - effect_node.filters = effect.Filter().AsCcFilterOperations(); - effect_node.backdrop_filters = - effect.BackdropFilter().AsCcFilterOperations(); - effect_node.backdrop_filter_bounds = effect.BackdropFilterBounds(); - effect_node.backdrop_mask_element_id = effect.BackdropMaskElementId(); - effect_node.filters_origin = effect.FiltersOrigin(); effect_node.transform_id = EnsureCompositorTransformNode(effect.LocalTransformSpace()); + if (backdrop_effect_state == kBackdropEffectToBeSetOnCcEffectNode) { + // We never have backdrop effect and filter on the same effect node. + DCHECK(effect.Filter().IsEmpty()); + PopulateCcEffectNodeBackdropEffect(effect_node, effect); + effect_node.backdrop_mask_element_id = effect.BackdropMaskElementId(); + } else { + effect_node.filters = effect.Filter().AsCcFilterOperations(); + effect_node.filters_origin = effect.FiltersOrigin(); + } } - effect_node.blend_mode = blend_mode; effect_node.double_sided = !effect.LocalTransformSpace().IsBackfaceHidden(); effect_node.effect_changed = effect.NodeChangeAffectsRaster(); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h b/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h index 09b99c7d1f2..4c807b5c0f6 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h @@ -11,7 +11,6 @@ #include "third_party/blink/renderer/platform/wtf/hash_map.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" #include "third_party/blink/renderer/platform/wtf/vector.h" -#include "third_party/skia/include/core/SkBlendMode.h" namespace cc { class ClipTree; @@ -26,6 +25,10 @@ struct TransformNode; enum class RenderSurfaceReason : uint8_t; } +namespace gfx { +class ScrollOffset; +} + namespace blink { class ClipPaintPropertyNode; @@ -39,6 +42,7 @@ class PropertyTreeManagerClient { public: virtual SynthesizedClip& CreateOrReuseSynthesizedClipLayer( const ClipPaintPropertyNode&, + const TransformPaintPropertyNode&, bool needs_layer, CompositorElementId& mask_isolation_id, CompositorElementId& mask_effect_id) = 0; @@ -99,6 +103,12 @@ class PropertyTreeManager { int EnsureCompositorScrollNode( const TransformPaintPropertyNode& scroll_offset_translation); + // Same as above but marks the scroll nodes as being the viewport. + int EnsureCompositorInnerScrollNode( + const TransformPaintPropertyNode& scroll_offset_translation); + int EnsureCompositorOuterScrollNode( + const TransformPaintPropertyNode& scroll_offset_translation); + int EnsureCompositorPageScaleTransformNode(const TransformPaintPropertyNode&); // This function is expected to be invoked right before emitting each layer. @@ -119,21 +129,21 @@ class PropertyTreeManager { void Finalize(); static bool DirectlyUpdateCompositedOpacityValue( - cc::PropertyTrees*, cc::LayerTreeHost&, const EffectPaintPropertyNode&); static bool DirectlyUpdateScrollOffsetTransform( - cc::PropertyTrees*, cc::LayerTreeHost&, const TransformPaintPropertyNode&); - static bool DirectlyUpdateTransform(cc::PropertyTrees*, - cc::LayerTreeHost&, + static bool DirectlyUpdateTransform(cc::LayerTreeHost&, const TransformPaintPropertyNode&); static bool DirectlyUpdatePageScaleTransform( - cc::PropertyTrees*, cc::LayerTreeHost&, const TransformPaintPropertyNode&); + static void DirectlySetScrollOffset(cc::LayerTreeHost&, + CompositorElementId, + const gfx::ScrollOffset&); + private: void SetupRootTransformNode(); void SetupRootClipNode(); @@ -156,8 +166,10 @@ class PropertyTreeManager { kSyntheticFor2dAxisAlignment = 1 << 1 }; - static bool SupportsShaderBasedRoundedCorner(const ClipPaintPropertyNode&, - CcEffectType type); + static bool SupportsShaderBasedRoundedCorner( + const ClipPaintPropertyNode&, + CcEffectType type, + const EffectPaintPropertyNode* next_effect); struct EffectState { // The cc effect node that has the corresponding drawing state to the @@ -177,6 +189,12 @@ class PropertyTreeManager { // It's never nullptr. const ClipPaintPropertyNode* clip; + // The transform space of this state. It's |&effect->LocalTransformSpace()| + // if this state is of kEffect type or synthetic with backdrop filters + // moved up from the original effect. + // Otherwise it's |&clip->LocalTransformSpace()|. + const TransformPaintPropertyNode* transform; + // Whether the transform space of this state may be 2d axis misaligned to // the containing render surface. As there may be new render surfaces // created between this state and the current known ancestor render surface @@ -201,28 +219,35 @@ class PropertyTreeManager { // self and the next render surface. This is used to force a render surface // for all ancestor synthetic rounded clips if a descendant is found. bool contained_by_non_render_surface_synthetic_rounded_clip; - - // The transform space of the state. - const TransformPaintPropertyNode& Transform() const; }; void CollectAnimationElementId(CompositorElementId); void BuildEffectNodesRecursively(const EffectPaintPropertyNode& next_effect); void ForceRenderSurfaceIfSyntheticRoundedCornerClip(EffectState& state); - SkBlendMode SynthesizeCcEffectsForClipsIfNeeded( + + // When we create a synthetic clip, if the next effect has backdrop effect + // (exotic blending or backdrop filter), the backdrop effect should be set + // on the synthetic mask isolation effect node instead of the cc effect node + // that is created for the original blink effect node, to ensure the backdrop + // effect will see the correct backdrop input. + enum BackdropEffectState { + kNoBackdropEffect, + kBackdropEffectHasSetOnSyntheticEffect, + kBackdropEffectToBeSetOnCcEffectNode, + }; + BackdropEffectState SynthesizeCcEffectsForClipsIfNeeded( const ClipPaintPropertyNode& target_clip, - SkBlendMode delegated_blend); + const EffectPaintPropertyNode* next_effect); + void EmitClipMaskLayer(); void CloseCcEffect(); - - // For a given effect node, this returns the blend mode, clip property node, - // and an int indicating cc clip node's id. - std::tuple<SkBlendMode, const ClipPaintPropertyNode*, int> - GetBlendModeAndOutputClipForEffect(const EffectPaintPropertyNode&); void PopulateCcEffectNode(cc::EffectNode&, - const EffectPaintPropertyNode&, + const EffectPaintPropertyNode& effect, int output_clip_id, - SkBlendMode); + BackdropEffectState); + void PopulateCcEffectNodeBackdropEffect( + cc::EffectNode& effect_node, + const EffectPaintPropertyNode& backdrop_effect); bool IsCurrentCcEffectSynthetic() const { return current_.effect_type; } bool IsCurrentCcEffectSyntheticForNonTrivialClip() const { @@ -230,14 +255,15 @@ class PropertyTreeManager { } bool EffectStateMayBe2dAxisMisalignedToRenderSurface(EffectState&, - size_t index); + wtf_size_t index); bool CurrentEffectMayBe2dAxisMisalignedToRenderSurface(); CcEffectType SyntheticEffectType(const ClipPaintPropertyNode&); void SetCurrentEffectState(const cc::EffectNode&, CcEffectType, const EffectPaintPropertyNode&, - const ClipPaintPropertyNode&); + const ClipPaintPropertyNode&, + const TransformPaintPropertyNode&); void SetCurrentEffectRenderSurfaceReason(cc::RenderSurfaceReason); cc::TransformTree& GetTransformTree(); diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc index 86bd4d15537..dfa0ae804e9 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.cc @@ -36,7 +36,7 @@ constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = { {CompositingReason::kActiveBackdropFilterAnimation, "activeBackdropFilterAnimation", "Has an active accelerated backdrop filter animation or transition"}, - {CompositingReason::kImmersiveArOverlay, "immersiveArOverlay", + {CompositingReason::kXrOverlay, "xrOverlay", "Is DOM overlay for WebXR immersive-ar mode"}, {CompositingReason::kScrollDependentPosition, "scrollDependentPosition", "Is fixed or sticky position"}, @@ -58,8 +58,6 @@ constexpr CompositingReasonStringMap kCompositingReasonsStringMap[] = { "Has a backdrop filter"}, {CompositingReason::kRootScroller, "rootScroller", "Is the document.rootScroller"}, - {CompositingReason::kCrossOriginIframe, "crossOriginIframe", - "Is a cross-origin iframe"}, {CompositingReason::kAssumedOverlap, "assumedOverlap", "Might overlap other composited content"}, {CompositingReason::kOverlap, "overlap", diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h b/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h index 5df7a4e83d1..144640a6ddc 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing_reasons.h @@ -27,7 +27,6 @@ using CompositingReasons = uint64_t; V(ActiveOpacityAnimation) \ V(ActiveFilterAnimation) \ V(ActiveBackdropFilterAnimation) \ - V(ImmersiveArOverlay) \ V(ScrollDependentPosition) \ V(OverflowScrolling) \ V(OverflowScrollingParent) \ @@ -40,7 +39,7 @@ using CompositingReasons = uint64_t; V(WillChangeOther) \ V(BackdropFilter) \ V(RootScroller) \ - V(CrossOriginIframe) \ + V(XrOverlay) \ \ /* Overlap reasons that require knowing what's behind you in paint-order \ before knowing the answer. */ \ @@ -124,8 +123,8 @@ class PLATFORM_EXPORT CompositingReason { kComboAllDirectNonStyleDeterminedReasons = kVideo | kCanvas | kPlugin | kIFrame | kOverflowScrollingParent | - kOutOfFlowClipping | kVideoOverlay | kImmersiveArOverlay | kRoot | - kRootScroller | kScrollDependentPosition | kCrossOriginIframe, + kOutOfFlowClipping | kVideoOverlay | kXrOverlay | kRoot | + kRootScroller | kScrollDependentPosition, kComboAllDirectReasons = kComboAllDirectStyleDeterminedReasons | kComboAllDirectNonStyleDeterminedReasons, @@ -149,6 +148,9 @@ class PLATFORM_EXPORT CompositingReason { kComboSquashableReasons = kOverlap | kAssumedOverlap | kOverflowScrollingParent, + kDirectReasonsForPaintOffsetTranslationProperty = + kScrollDependentPosition | kVideo | kCanvas | kPlugin | kIFrame, + kDirectReasonsForTransformProperty = k3DTransform | kWillChangeTransform | kWillChangeOther | kPerspectiveWith3DDescendants | kPreserve3DWith3DDescendants | diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.h b/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.h index 957fbf2b7fe..25b0f8227d6 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.h +++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_element_id.h @@ -25,8 +25,9 @@ enum class CompositorElementIdNamespace { kEffectClipPath, kVerticalScrollbar, kHorizontalScrollbar, + kDOMNodeId, // The following values are for internal usage only. - kMax = kHorizontalScrollbar, + kMax = kDOMNodeId, // A sentinel to indicate the maximum representable namespace id // (the maximum is one less than this value). kMaxRepresentable = 1 << kCompositorNamespaceBitCount diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc b/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc index 243b9a1c0e0..9eec39764c9 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.cc @@ -110,6 +110,10 @@ bool CompositorFilterOperations::HasFilterThatMovesPixels() const { return filter_operations_.HasFilterThatMovesPixels(); } +bool CompositorFilterOperations::HasReferenceFilter() const { + return filter_operations_.HasReferenceFilter(); +} + bool CompositorFilterOperations::operator==( const CompositorFilterOperations& o) const { return reference_box_ == o.reference_box_ && diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h b/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h index c943a2619f9..4c3a334f655 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h +++ b/chromium/third_party/blink/renderer/platform/graphics/compositor_filter_operations.h @@ -49,9 +49,10 @@ class PLATFORM_EXPORT CompositorFilterOperations { FloatRect MapRect(const FloatRect& input_rect) const; bool HasFilterThatMovesPixels() const; + bool HasReferenceFilter() const; void SetReferenceBox(const FloatRect& r) { reference_box_ = r; } - FloatRect ReferenceBox() const { return reference_box_; } + const FloatRect& ReferenceBox() const { return reference_box_; } // For reference filters, this equality operator compares pointers of the // image_filter fields instead of their values. diff --git a/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.cc b/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.cc index 19cb6f21032..9ea0c88d694 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.cc @@ -22,7 +22,7 @@ class ContiguousContainerBase::Buffer { USING_FAST_MALLOC(Buffer); public: - Buffer(size_t buffer_size, const char* type_name) { + Buffer(wtf_size_t buffer_size, const char* type_name) { capacity_ = WTF::Partitions::BufferActualSize(buffer_size); begin_ = end_ = static_cast<char*>(WTF::Partitions::BufferMalloc(capacity_, type_name)); @@ -34,12 +34,12 @@ class ContiguousContainerBase::Buffer { WTF::Partitions::BufferFree(begin_); } - size_t Capacity() const { return capacity_; } - size_t UsedCapacity() const { return end_ - begin_; } - size_t UnusedCapacity() const { return Capacity() - UsedCapacity(); } + wtf_size_t Capacity() const { return capacity_; } + wtf_size_t UsedCapacity() const { return end_ - begin_; } + wtf_size_t UnusedCapacity() const { return Capacity() - UsedCapacity(); } bool IsEmpty() const { return UsedCapacity() == 0; } - void* Allocate(size_t object_size) { + void* Allocate(wtf_size_t object_size) { DCHECK_GE(UnusedCapacity(), object_size); ANNOTATE_CHANGE_SIZE(begin_, capacity_, UsedCapacity(), UsedCapacity() + object_size); @@ -60,12 +60,12 @@ class ContiguousContainerBase::Buffer { // m_begin <= m_end <= m_begin + m_capacity char* begin_; char* end_; - size_t capacity_; + wtf_size_t capacity_; DISALLOW_COPY_AND_ASSIGN(Buffer); }; -ContiguousContainerBase::ContiguousContainerBase(size_t max_object_size) +ContiguousContainerBase::ContiguousContainerBase(wtf_size_t max_object_size) : end_index_(0), max_object_size_(max_object_size) {} ContiguousContainerBase::ContiguousContainerBase( @@ -82,31 +82,31 @@ ContiguousContainerBase& ContiguousContainerBase::operator=( return *this; } -size_t ContiguousContainerBase::CapacityInBytes() const { - size_t capacity = 0; +wtf_size_t ContiguousContainerBase::CapacityInBytes() const { + wtf_size_t capacity = 0; for (const auto& buffer : buffers_) capacity += buffer->Capacity(); return capacity; } -size_t ContiguousContainerBase::UsedCapacityInBytes() const { - size_t used_capacity = 0; +wtf_size_t ContiguousContainerBase::UsedCapacityInBytes() const { + wtf_size_t used_capacity = 0; for (const auto& buffer : buffers_) used_capacity += buffer->UsedCapacity(); return used_capacity; } -size_t ContiguousContainerBase::MemoryUsageInBytes() const { +wtf_size_t ContiguousContainerBase::MemoryUsageInBytes() const { return sizeof(*this) + CapacityInBytes() + elements_.capacity() * sizeof(elements_[0]); } -void ContiguousContainerBase::ReserveInitialCapacity(size_t buffer_size, +void ContiguousContainerBase::ReserveInitialCapacity(wtf_size_t buffer_size, const char* type_name) { AllocateNewBufferForNextAllocation(buffer_size, type_name); } -void* ContiguousContainerBase::Allocate(size_t object_size, +void* ContiguousContainerBase::Allocate(wtf_size_t object_size, const char* type_name) { DCHECK_LE(object_size, max_object_size_); @@ -120,9 +120,9 @@ void* ContiguousContainerBase::Allocate(size_t object_size, } if (!buffer_for_alloc) { - size_t new_buffer_size = buffers_.IsEmpty() - ? kDefaultInitialBufferSize * max_object_size_ - : 2 * buffers_.back()->Capacity(); + wtf_size_t new_buffer_size = + buffers_.IsEmpty() ? kDefaultInitialBufferSize * max_object_size_ + : 2 * buffers_.back()->Capacity(); buffer_for_alloc = AllocateNewBufferForNextAllocation(new_buffer_size, type_name); } @@ -169,7 +169,7 @@ void ContiguousContainerBase::ShrinkToFit() { ContiguousContainerBase::Buffer* ContiguousContainerBase::AllocateNewBufferForNextAllocation( - size_t buffer_size, + wtf_size_t buffer_size, const char* type_name) { DCHECK(buffers_.IsEmpty() || end_index_ == buffers_.size() - 1); std::unique_ptr<Buffer> new_buffer = diff --git a/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.h b/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.h index 3eee5589124..c81241eb708 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.h +++ b/chromium/third_party/blink/renderer/platform/graphics/contiguous_container.h @@ -41,21 +41,21 @@ class PLATFORM_EXPORT ContiguousContainerBase { DISALLOW_NEW(); protected: - explicit ContiguousContainerBase(size_t max_object_size); + explicit ContiguousContainerBase(wtf_size_t max_object_size); ContiguousContainerBase(ContiguousContainerBase&&); ~ContiguousContainerBase(); ContiguousContainerBase& operator=(ContiguousContainerBase&&); - size_t size() const { return elements_.size(); } + wtf_size_t size() const { return elements_.size(); } bool IsEmpty() const { return !size(); } - size_t CapacityInBytes() const; - size_t UsedCapacityInBytes() const; - size_t MemoryUsageInBytes() const; + wtf_size_t CapacityInBytes() const; + wtf_size_t UsedCapacityInBytes() const; + wtf_size_t MemoryUsageInBytes() const; // These do not invoke constructors or destructors. - void ReserveInitialCapacity(size_t, const char* type_name); - void* Allocate(size_t object_size, const char* type_name); + void ReserveInitialCapacity(wtf_size_t, const char* type_name); + void* Allocate(wtf_size_t object_size, const char* type_name); void RemoveLast(); void Clear(); void Swap(ContiguousContainerBase&); @@ -69,11 +69,11 @@ class PLATFORM_EXPORT ContiguousContainerBase { private: class Buffer; - Buffer* AllocateNewBufferForNextAllocation(size_t, const char* type_name); + Buffer* AllocateNewBufferForNextAllocation(wtf_size_t, const char* type_name); Vector<std::unique_ptr<Buffer>> buffers_; unsigned end_index_; - size_t max_object_size_; + wtf_size_t max_object_size_; DISALLOW_COPY_AND_ASSIGN(ContiguousContainerBase); }; @@ -138,10 +138,10 @@ class ContiguousContainer : public ContiguousContainerBase { using value_type = BaseElementType; - explicit ContiguousContainer(size_t max_object_size) + explicit ContiguousContainer(wtf_size_t max_object_size) : ContiguousContainerBase(Align(max_object_size)) {} - ContiguousContainer(size_t max_object_size, size_t initial_size_bytes) + ContiguousContainer(wtf_size_t max_object_size, wtf_size_t initial_size_bytes) : ContiguousContainer(max_object_size) { ReserveInitialCapacity(std::max(max_object_size, initial_size_bytes), WTF_HEAP_PROFILER_TYPE_NAME(BaseElementType)); @@ -190,8 +190,8 @@ class ContiguousContainer : public ContiguousContainerBase { const BaseElementType& First() const { return *begin(); } BaseElementType& Last() { return *rbegin(); } const BaseElementType& Last() const { return *rbegin(); } - BaseElementType& operator[](size_t index) { return *(begin() + index); } - const BaseElementType& operator[](size_t index) const { + BaseElementType& operator[](wtf_size_t index) { return *(begin() + index); } + const BaseElementType& operator[](wtf_size_t index) const { return *(begin() + index); } @@ -226,7 +226,7 @@ class ContiguousContainer : public ContiguousContainerBase { // Appends a new element using memcpy, then default-constructs a base // element in its place. Use with care. - BaseElementType& AppendByMoving(BaseElementType& item, size_t size) { + BaseElementType& AppendByMoving(BaseElementType& item, wtf_size_t size) { DCHECK_GE(size, sizeof(BaseElementType)); void* new_item = AlignedAllocate(size); memcpy(new_item, static_cast<void*>(&item), size); @@ -235,15 +235,15 @@ class ContiguousContainer : public ContiguousContainerBase { } private: - void* AlignedAllocate(size_t size) { + void* AlignedAllocate(wtf_size_t size) { void* result = ContiguousContainerBase::Allocate( Align(size), WTF_HEAP_PROFILER_TYPE_NAME(BaseElementType)); DCHECK_EQ(reinterpret_cast<intptr_t>(result) & (alignment - 1), 0u); return result; } - static size_t Align(size_t size) { - size_t aligned_size = alignment * ((size + alignment - 1) / alignment); + static wtf_size_t Align(wtf_size_t size) { + wtf_size_t aligned_size = alignment * ((size + alignment - 1) / alignment); DCHECK_EQ(aligned_size % alignment, 0u); DCHECK_GE(aligned_size, size); DCHECK_LT(aligned_size, size + alignment); diff --git a/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc b/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc index 59c68f69182..c1a01c9a1a3 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.cc @@ -43,10 +43,12 @@ CrossfadeGeneratedImage::CrossfadeGeneratedImage( percentage_(percentage), crossfade_size_(crossfade_size) {} -void CrossfadeGeneratedImage::DrawCrossfade(cc::PaintCanvas* canvas, - const PaintFlags& flags, - ImageClampingMode clamp_mode, - ImageDecodingMode decode_mode) { +void CrossfadeGeneratedImage::DrawCrossfade( + cc::PaintCanvas* canvas, + const PaintFlags& flags, + RespectImageOrientationEnum respect_orientation, + ImageClampingMode clamp_mode, + ImageDecodingMode decode_mode) { FloatRect from_image_rect(FloatPoint(), FloatSize(from_image_->Size())); FloatRect to_image_rect(FloatPoint(), FloatSize(to_image_->Size())); FloatRect dest_rect((FloatPoint()), crossfade_size_); @@ -72,33 +74,32 @@ void CrossfadeGeneratedImage::DrawCrossfade(cc::PaintCanvas* canvas, image_flags.setBlendMode(SkBlendMode::kPlus); image_flags.setColor(ScaleAlpha(flags.getColor(), percentage_)); to_image_->Draw(canvas, image_flags, dest_rect, to_image_rect, - kDoNotRespectImageOrientation, clamp_mode, decode_mode); + respect_orientation, clamp_mode, decode_mode); } -void CrossfadeGeneratedImage::Draw(cc::PaintCanvas* canvas, - const PaintFlags& flags, - const FloatRect& dst_rect, - const FloatRect& src_rect, - RespectImageOrientationEnum, - ImageClampingMode clamp_mode, - ImageDecodingMode decode_mode) { +void CrossfadeGeneratedImage::Draw( + cc::PaintCanvas* canvas, + const PaintFlags& flags, + const FloatRect& dst_rect, + const FloatRect& src_rect, + RespectImageOrientationEnum respect_orientation, + ImageClampingMode clamp_mode, + ImageDecodingMode decode_mode) { // Draw nothing if either of the images hasn't loaded yet. if (from_image_ == Image::NullImage() || to_image_ == Image::NullImage()) return; PaintCanvasAutoRestore ar(canvas, true); canvas->clipRect(dst_rect); - canvas->translate(dst_rect.X(), dst_rect.Y()); - if (dst_rect.Size() != src_rect.Size()) - canvas->scale(dst_rect.Width() / src_rect.Width(), - dst_rect.Height() / src_rect.Height()); - canvas->translate(-src_rect.X(), -src_rect.Y()); - - DrawCrossfade(canvas, flags, clamp_mode, decode_mode); + canvas->concat( + SkMatrix::MakeRectToRect(src_rect, dst_rect, SkMatrix::kFill_ScaleToFit)); + DrawCrossfade(canvas, flags, respect_orientation, clamp_mode, decode_mode); } -void CrossfadeGeneratedImage::DrawTile(GraphicsContext& context, - const FloatRect& src_rect) { +void CrossfadeGeneratedImage::DrawTile( + GraphicsContext& context, + const FloatRect& src_rect, + RespectImageOrientationEnum respect_orientation) { // Draw nothing if either of the images hasn't loaded yet. if (from_image_ == Image::NullImage() || to_image_ == Image::NullImage()) return; @@ -108,7 +109,8 @@ void CrossfadeGeneratedImage::DrawTile(GraphicsContext& context, FloatRect dest_rect((FloatPoint()), crossfade_size_); flags.setFilterQuality( context.ComputeFilterQuality(this, dest_rect, src_rect)); - DrawCrossfade(context.Canvas(), flags, kClampImageToSourceRect, kSyncDecode); + DrawCrossfade(context.Canvas(), flags, respect_orientation, + kClampImageToSourceRect, kSyncDecode); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.h b/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.h index 55c37030884..17fb61c5692 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.h +++ b/chromium/third_party/blink/renderer/platform/graphics/crossfade_generated_image.h @@ -59,7 +59,9 @@ class PLATFORM_EXPORT CrossfadeGeneratedImage final : public GeneratedImage { RespectImageOrientationEnum, ImageClampingMode, ImageDecodingMode) override; - void DrawTile(GraphicsContext&, const FloatRect&) final; + void DrawTile(GraphicsContext&, + const FloatRect&, + RespectImageOrientationEnum) final; CrossfadeGeneratedImage(scoped_refptr<Image> from_image, scoped_refptr<Image> to_image, @@ -70,6 +72,7 @@ class PLATFORM_EXPORT CrossfadeGeneratedImage final : public GeneratedImage { private: void DrawCrossfade(cc::PaintCanvas*, const cc::PaintFlags&, + RespectImageOrientationEnum, ImageClampingMode, ImageDecodingMode); diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc index 0c2a478ca67..8b05ce0ff90 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc @@ -13,6 +13,7 @@ #include "third_party/blink/renderer/platform/graphics/dark_mode_generic_classifier.h" #include "third_party/blink/renderer/platform/graphics/dark_mode_icon_classifier.h" #include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h" +#include "third_party/blink/renderer/platform/graphics/graphics_context.h" #include "third_party/blink/renderer/platform/graphics/graphics_types.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/effects/SkColorMatrix.h" @@ -129,7 +130,13 @@ void DarkModeFilter::UpdateSettings(const DarkModeSettings& new_settings) { Color DarkModeFilter::InvertColorIfNeeded(const Color& color, ElementRole role) { - if (IsDarkModeActive() && ShouldApplyToColor(color, role)) + if (!IsDarkModeActive()) + return color; + + if (role_override_.has_value()) + role = role_override_.value(); + + if (ShouldApplyToColor(color, role)) return color_filter_->InvertColor(color); return color; } @@ -150,6 +157,9 @@ base::Optional<cc::PaintFlags> DarkModeFilter::ApplyToFlagsIfNeeded( if (!IsDarkModeActive()) return base::nullopt; + if (role_override_.has_value()) + role = role_override_.value(); + cc::PaintFlags dark_mode_flags = flags; if (flags.HasShader()) { dark_mode_flags.setColorFilter(color_filter_->ToSkColorFilter()); @@ -177,6 +187,13 @@ bool DarkModeFilter::ShouldApplyToColor(const Color& color, ElementRole role) { DCHECK(text_classifier_); return text_classifier_->ShouldInvertColor(color) == DarkModeClassification::kApplyFilter; + case ElementRole::kListSymbol: + // TODO(prashant.n): Rename text_classifier_ to foreground_classifier_, + // so that same classifier can be used for all roles which are supposed + // to be at foreground. + DCHECK(text_classifier_); + return text_classifier_->ShouldInvertColor(color) == + DarkModeClassification::kApplyFilter; case ElementRole::kBackground: DCHECK(background_classifier_); return background_classifier_->ShouldInvertColor(color) == @@ -195,4 +212,18 @@ bool DarkModeFilter::ShouldApplyToColor(const Color& color, ElementRole role) { NOTREACHED(); } +ScopedDarkModeElementRoleOverride::ScopedDarkModeElementRoleOverride( + GraphicsContext* graphics_context, + DarkModeFilter::ElementRole role) + : graphics_context_(graphics_context) { + DarkModeFilter& dark_mode_filter = graphics_context->dark_mode_filter_; + previous_role_override_ = dark_mode_filter.role_override_; + dark_mode_filter.role_override_ = role; +} + +ScopedDarkModeElementRoleOverride::~ScopedDarkModeElementRoleOverride() { + DarkModeFilter& dark_mode_filter = graphics_context_->dark_mode_filter_; + dark_mode_filter.role_override_ = previous_role_override_; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter.h b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter.h index b46d2e35b75..56a926f5f0d 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter.h +++ b/chromium/third_party/blink/renderer/platform/graphics/dark_mode_filter.h @@ -21,6 +21,7 @@ namespace blink { class DarkModeColorClassifier; class DarkModeColorFilter; +class ScopedDarkModeElementRoleOverride; class PLATFORM_EXPORT DarkModeFilter { public: @@ -37,7 +38,7 @@ class PLATFORM_EXPORT DarkModeFilter { // TODO(gilmanmh): Add a role for shadows. In general, we don't want to // invert shadows, but we may need to do some other kind of processing for // them. - enum class ElementRole { kText, kBackground, kSVG }; + enum class ElementRole { kText, kListSymbol, kBackground, kSVG }; Color InvertColorIfNeeded(const Color& color, ElementRole element_role); base::Optional<cc::PaintFlags> ApplyToFlagsIfNeeded( const cc::PaintFlags& flags, @@ -52,6 +53,8 @@ class PLATFORM_EXPORT DarkModeFilter { SkColorFilter* GetImageFilterForTesting() { return image_filter_.get(); } private: + friend class ScopedDarkModeElementRoleOverride; + DarkModeSettings settings_; bool ShouldApplyToColor(const Color& color, ElementRole role); @@ -60,6 +63,20 @@ class PLATFORM_EXPORT DarkModeFilter { std::unique_ptr<DarkModeColorClassifier> background_classifier_; std::unique_ptr<DarkModeColorFilter> color_filter_; sk_sp<SkColorFilter> image_filter_; + base::Optional<ElementRole> role_override_; +}; + +// Temporarily override the element role for the scope of this object's +// lifetime - for example when drawing symbols that play the role of text. +class PLATFORM_EXPORT ScopedDarkModeElementRoleOverride { + public: + ScopedDarkModeElementRoleOverride(GraphicsContext* graphics_context, + DarkModeFilter::ElementRole role); + ~ScopedDarkModeElementRoleOverride(); + + private: + GraphicsContext* graphics_context_; + base::Optional<DarkModeFilter::ElementRole> previous_role_override_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc index da6f2259314..b99ac19e445 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.cc @@ -175,15 +175,11 @@ String DeferredImageDecoder::FilenameExtension() const { : filename_extension_; } -sk_sp<PaintImageGenerator> DeferredImageDecoder::CreateGenerator(size_t index) { +sk_sp<PaintImageGenerator> DeferredImageDecoder::CreateGenerator() { if (frame_generator_ && frame_generator_->DecodeFailed()) return nullptr; - PrepareLazyDecodedFrames(); - - // PrepareLazyDecodedFrames should populate the metadata for each frame in - // this image and create the |frame_generator_|, if enough data is available. - if (index >= frame_data_.size()) + if (invalid_image_ || frame_data_.IsEmpty()) return nullptr; DCHECK(frame_generator_); @@ -196,10 +192,16 @@ sk_sp<PaintImageGenerator> DeferredImageDecoder::CreateGenerator(size_t index) { SegmentReader::CreateFromSkROBuffer(std::move(ro_buffer)); // ImageFrameGenerator has the latest known alpha state. There will be a - // performance boost if this frame is opaque. - SkAlphaType alpha_type = frame_generator_->HasAlpha(index) - ? kPremul_SkAlphaType - : kOpaque_SkAlphaType; + // performance boost if the image is opaque since we can avoid painting + // the background in this case. + // For multi-frame images, these maybe animated on the compositor thread. + // So we can not mark them as opaque unless all frames are opaque. + // TODO(khushalsagar): Check whether all frames being added to the + // generator are opaque when populating FrameMetadata below. + SkAlphaType alpha_type = kPremul_SkAlphaType; + if (frame_data_.size() == 1u && !frame_generator_->HasAlpha(0u)) + alpha_type = kOpaque_SkAlphaType; + SkImageInfo info = SkImageInfo::MakeN32(decoded_size.width(), decoded_size.height(), alpha_type, color_space_for_sk_images_); @@ -396,14 +398,19 @@ void DeferredImageDecoder::PrepareLazyDecodedFrames() { if (!metadata_decoder_ || !metadata_decoder_->IsSizeAvailable()) return; + if (invalid_image_) + return; + if (!image_metadata_) image_metadata_ = metadata_decoder_->MakeMetadataForDecodeAcceleration(); // If the image contains a coded size with zero in either or both size // dimensions, the image is invalid. if (image_metadata_->coded_size.has_value() && - image_metadata_->coded_size.value().IsEmpty()) + image_metadata_->coded_size.value().IsEmpty()) { + invalid_image_ = true; return; + } ActivateLazyDecoding(); @@ -411,12 +418,16 @@ void DeferredImageDecoder::PrepareLazyDecodedFrames() { frame_data_.resize(metadata_decoder_->FrameCount()); // The decoder may be invalidated during a FrameCount(). Simply bail if so. - if (metadata_decoder_->Failed()) + if (metadata_decoder_->Failed()) { + invalid_image_ = true; return; + } // We have encountered a broken image file. Simply bail. - if (frame_data_.size() < previous_size) + if (frame_data_.size() < previous_size) { + invalid_image_ = true; return; + } for (size_t i = previous_size; i < frame_data_.size(); ++i) { frame_data_[i].duration_ = metadata_decoder_->FrameDurationAtIndex(i); diff --git a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h index d7a069b0590..76a5fa15af1 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h +++ b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder.h @@ -62,7 +62,7 @@ class PLATFORM_EXPORT DeferredImageDecoder final { String FilenameExtension() const; - sk_sp<PaintImageGenerator> CreateGenerator(size_t index); + sk_sp<PaintImageGenerator> CreateGenerator(); scoped_refptr<SharedBuffer> Data(); void SetData(scoped_refptr<SharedBuffer> data, bool all_data_received); @@ -120,6 +120,10 @@ class PLATFORM_EXPORT DeferredImageDecoder final { const PaintImage::ContentId complete_frame_content_id_; base::Optional<bool> incremental_decode_needed_; + // Set to true if the image is detected to be invalid after parsing the + // metadata. + bool invalid_image_ = false; + // Caches an image's metadata so it can outlive |metadata_decoder_| after all // data is received in cases where multiple generators are created. base::Optional<cc::ImageHeaderMetadata> image_metadata_; diff --git a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc index d515c956a31..7e81cb95bc4 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test.cc @@ -131,8 +131,7 @@ class DeferredImageDecoderTest : public testing::Test, .set_id(paint_image_id_) .set_animation_type(type) .set_completion_state(state) - .set_paint_image_generator( - decoder->CreateGenerator(PaintImage::kDefaultFrameIndex)) + .set_paint_image_generator(decoder->CreateGenerator()) .TakePaintImage(); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test_wo_platform.cc b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test_wo_platform.cc index 2283a6378e3..a8cc839fa98 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test_wo_platform.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/deferred_image_decoder_test_wo_platform.cc @@ -19,7 +19,7 @@ namespace { sk_sp<SkImage> CreateFrameAtIndex(DeferredImageDecoder* decoder, size_t index) { return SkImage::MakeFromGenerator(std::make_unique<SkiaPaintImageGenerator>( - decoder->CreateGenerator(index), index, + decoder->CreateGenerator(), index, cc::PaintImage::kDefaultGeneratorClientId)); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc index ce5709eb94f..aa24b6a28ab 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.cc @@ -34,8 +34,8 @@ static const unsigned kColorMatrixSize = 20; FEColorMatrix::FEColorMatrix(Filter* filter, ColorMatrixType type, - const Vector<float>& values) - : FilterEffect(filter), type_(type), values_(values) {} + Vector<float> values) + : FilterEffect(filter), type_(type), values_(std::move(values)) {} ColorMatrixType FEColorMatrix::GetType() const { return type_; @@ -52,10 +52,10 @@ const Vector<float>& FEColorMatrix::Values() const { return values_; } -bool FEColorMatrix::SetValues(const Vector<float>& values) { +bool FEColorMatrix::SetValues(Vector<float> values) { if (values_ == values) return false; - values_ = values; + values_ = std::move(values); return true; } diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.h index 1621b60ea8c..76c9ffc155b 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.h +++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_color_matrix.h @@ -38,22 +38,17 @@ enum ColorMatrixType { class PLATFORM_EXPORT FEColorMatrix final : public FilterEffect { public: - FEColorMatrix(Filter*, ColorMatrixType, const Vector<float>&); + FEColorMatrix(Filter*, ColorMatrixType, Vector<float>); ColorMatrixType GetType() const; bool SetType(ColorMatrixType); const Vector<float>& Values() const; - bool SetValues(const Vector<float>&); + bool SetValues(Vector<float>); WTF::TextStream& ExternalRepresentation(WTF::TextStream&, int indention) const override; - static inline void CalculateSaturateComponents(float* components, - float value); - static inline void CalculateHueRotateComponents(float* components, - float value); - private: sk_sp<PaintFilter> CreateImageFilter() override; diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.cc index 8ea49549dc5..b2bff044da0 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.cc @@ -34,18 +34,21 @@ FloatRect FETile::MapInputs(const FloatRect& rect) const { return AbsoluteBounds(); } +FloatRect FETile::GetSourceRect() const { + const FilterEffect* input = InputEffect(0); + if (input->GetFilterEffectType() == kFilterEffectTypeSourceInput) + return GetFilter()->FilterRegion(); + return input->FilterPrimitiveSubregion(); +} + sk_sp<PaintFilter> FETile::CreateImageFilter() { sk_sp<PaintFilter> input(paint_filter_builder::Build( InputEffect(0), OperatingInterpolationSpace())); if (!input) return nullptr; - - FloatRect src_rect; - if (InputEffect(0)->GetFilterEffectType() == kFilterEffectTypeSourceInput) - src_rect = GetFilter()->FilterRegion(); - else - src_rect = InputEffect(0)->FilterPrimitiveSubregion(); - FloatRect dst_rect = FilterPrimitiveSubregion(); + FloatRect src_rect = GetFilter()->MapLocalRectToAbsoluteRect(GetSourceRect()); + FloatRect dst_rect = + GetFilter()->MapLocalRectToAbsoluteRect(FilterPrimitiveSubregion()); return sk_make_sp<TilePaintFilter>(src_rect, dst_rect, std::move(input)); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.h b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.h index b20b4bfb99e..95732d35988 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.h +++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_tile.h @@ -40,6 +40,7 @@ class PLATFORM_EXPORT FETile final : public FilterEffect { } FloatRect MapInputs(const FloatRect&) const final; + FloatRect GetSourceRect() const; sk_sp<PaintFilter> CreateImageFilter() override; }; diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.cc index df7735c1182..488aa17c8bb 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/filters/fe_turbulence.cc @@ -131,10 +131,14 @@ sk_sp<PaintFilter> FETurbulence::CreateImageFilter() { // a frequency, not a period. float base_frequency_x = base_frequency_x_ / GetFilter()->Scale(); float base_frequency_y = base_frequency_y_ / GetFilter()->Scale(); + + // Cap the number of octaves to the maximum detectable when rendered with + // 8 bits per pixel, plus one for higher bit depth. + int capped_num_octaves = std::min(NumOctaves(), 9); return sk_make_sp<TurbulencePaintFilter>( type, SkFloatToScalar(base_frequency_x), - SkFloatToScalar(base_frequency_y), NumOctaves(), SkFloatToScalar(Seed()), - StitchTiles() ? &size : nullptr, &rect); + SkFloatToScalar(base_frequency_y), capped_num_octaves, + SkFloatToScalar(Seed()), StitchTiles() ? &size : nullptr, &rect); } static WTF::TextStream& operator<<(WTF::TextStream& ts, diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/filter.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/filter.cc index 515d6d72cb2..c1ae00d3ba2 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/filters/filter.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/filters/filter.cc @@ -49,7 +49,7 @@ Filter::Filter(const FloatRect& reference_box, unit_scaling_(unit_scaling), source_graphic_(MakeGarbageCollected<SourceGraphic>(this)) {} -void Filter::Trace(blink::Visitor* visitor) { +void Filter::Trace(Visitor* visitor) { visitor->Trace(source_graphic_); visitor->Trace(last_effect_); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/filter.h b/chromium/third_party/blink/renderer/platform/graphics/filters/filter.h index d5073d5c854..efd30aa6721 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/filters/filter.h +++ b/chromium/third_party/blink/renderer/platform/graphics/filters/filter.h @@ -44,7 +44,7 @@ class PLATFORM_EXPORT Filter final : public GarbageCollected<Filter> { float scale, UnitScaling); - void Trace(blink::Visitor*); + void Trace(Visitor*); float Scale() const { return scale_; } FloatRect MapLocalRectToAbsoluteRect(const FloatRect&) const; diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.cc b/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.cc index 513efef32de..1b8dd7a802e 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.cc @@ -39,7 +39,7 @@ FilterEffect::FilterEffect(Filter* filter) FilterEffect::~FilterEffect() = default; -void FilterEffect::Trace(blink::Visitor* visitor) { +void FilterEffect::Trace(Visitor* visitor) { visitor->Trace(input_effects_); visitor->Trace(filter_); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.h b/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.h index cec2f5c1208..1f3e99d19fe 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.h +++ b/chromium/third_party/blink/renderer/platform/graphics/filters/filter_effect.h @@ -51,7 +51,7 @@ enum FilterEffectType { class PLATFORM_EXPORT FilterEffect : public GarbageCollected<FilterEffect> { public: virtual ~FilterEffect(); - virtual void Trace(blink::Visitor*); + virtual void Trace(Visitor*); void DisposeImageFilters(); void DisposeImageFiltersRecursive(); diff --git a/chromium/third_party/blink/renderer/platform/graphics/generated_image.cc b/chromium/third_party/blink/renderer/platform/graphics/generated_image.cc index 6efb7a02915..666cc1d5925 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/generated_image.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/generated_image.cc @@ -38,13 +38,15 @@ namespace blink { -void GeneratedImage::DrawPattern(GraphicsContext& dest_context, - const FloatRect& src_rect, - const FloatSize& scale, - const FloatPoint& phase, - SkBlendMode composite_op, - const FloatRect& dest_rect, - const FloatSize& repeat_spacing) { +void GeneratedImage::DrawPattern( + GraphicsContext& dest_context, + const FloatRect& src_rect, + const FloatSize& scale, + const FloatPoint& phase, + SkBlendMode composite_op, + const FloatRect& dest_rect, + const FloatSize& repeat_spacing, + RespectImageOrientationEnum respect_orientation) { FloatRect tile_rect = src_rect; tile_rect.Expand(repeat_spacing); @@ -53,7 +55,7 @@ void GeneratedImage::DrawPattern(GraphicsContext& dest_context, pattern_matrix.preTranslate(tile_rect.X(), tile_rect.Y()); sk_sp<PaintShader> tile_shader = - CreateShader(tile_rect, &pattern_matrix, src_rect); + CreateShader(tile_rect, &pattern_matrix, src_rect, respect_orientation); PaintFlags fill_flags = dest_context.FillFlags(); fill_flags.setShader(std::move(tile_shader)); @@ -63,13 +65,15 @@ void GeneratedImage::DrawPattern(GraphicsContext& dest_context, dest_context.DrawRect(dest_rect, fill_flags); } -sk_sp<PaintShader> GeneratedImage::CreateShader(const FloatRect& tile_rect, - const SkMatrix* pattern_matrix, - const FloatRect& src_rect) { +sk_sp<PaintShader> GeneratedImage::CreateShader( + const FloatRect& tile_rect, + const SkMatrix* pattern_matrix, + const FloatRect& src_rect, + RespectImageOrientationEnum respect_orientation) { auto paint_controller = std::make_unique<PaintController>(); GraphicsContext context(*paint_controller); context.BeginRecording(tile_rect); - DrawTile(context, src_rect); + DrawTile(context, src_rect, respect_orientation); sk_sp<PaintRecord> record = context.EndRecording(); return PaintShader::MakePaintRecord(std::move(record), tile_rect, diff --git a/chromium/third_party/blink/renderer/platform/graphics/generated_image.h b/chromium/third_party/blink/renderer/platform/graphics/generated_image.h index 21d225cf0cc..fd645ff5e07 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/generated_image.h +++ b/chromium/third_party/blink/renderer/platform/graphics/generated_image.h @@ -39,6 +39,9 @@ class PLATFORM_EXPORT GeneratedImage : public Image { bool HasIntrinsicSize() const override { return false; } IntSize Size() const override { return RoundedIntSize(size_); } + FloatSize SizeAsFloat(RespectImageOrientationEnum) const override { + return size_; + } // Assume that generated content has no decoded data we need to worry about void DestroyDecodedData() override {} @@ -52,17 +55,21 @@ class PLATFORM_EXPORT GeneratedImage : public Image { const FloatPoint&, SkBlendMode, const FloatRect&, - const FloatSize& repeat_spacing) final; + const FloatSize& repeat_spacing, + RespectImageOrientationEnum) final; virtual sk_sp<cc::PaintShader> CreateShader(const FloatRect& tile_rect, const SkMatrix* pattern_matrix, - const FloatRect& src_rect); + const FloatRect& src_rect, + RespectImageOrientationEnum); // FIXME: Implement this to be less conservative. bool CurrentFrameKnownToBeOpaque() override { return false; } GeneratedImage(const FloatSize& size) : size_(size) {} - virtual void DrawTile(GraphicsContext&, const FloatRect&) = 0; + virtual void DrawTile(GraphicsContext&, + const FloatRect&, + RespectImageOrientationEnum) = 0; FloatSize size_; }; diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS b/chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS index aa252c3bb21..3238ca1d6d7 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/DEPS @@ -1,6 +1,5 @@ include_rules = [ "+components/viz/test/test_context_provider.h", - "+mojo/public/cpp/bindings/binding.h", "+mojo/public/cpp/system/platform_handle.h", "+device/vr/public/mojom/vr_service.mojom-blink.h", "+gpu/command_buffer/client/gles2_interface.h", diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc index 261c1b0512d..ee8aba554c6 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc @@ -41,8 +41,6 @@ #include "components/viz/common/resources/bitmap_allocation.h" #include "components/viz/common/resources/shared_bitmap.h" #include "components/viz/common/resources/transferable_resource.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" #include "gpu/command_buffer/client/shared_image_interface.h" #include "gpu/command_buffer/common/capabilities.h" @@ -54,6 +52,7 @@ #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource.h" #include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h" +#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h" #include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h" @@ -64,7 +63,6 @@ #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrContext.h" #include "third_party/skia/include/gpu/gl/GrGLTypes.h" -#include "ui/gl/gpu_preference.h" #include "v8/include/v8.h" namespace blink { @@ -193,7 +191,8 @@ DrawingBuffer::DrawingBuffer( opengl_flip_y_extension_( ContextProvider()->GetCapabilities().mesa_framebuffer_flip_y), initial_gpu_(gpu_preference), - current_active_gpu_(gpu_preference) { + current_active_gpu_(gpu_preference), + weak_factory_(this) { // Used by browser tests to detect the use of a DrawingBuffer. TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation", TRACE_EVENT_SCOPE_GLOBAL); @@ -393,11 +392,12 @@ bool DrawingBuffer::FinishPrepareTransferableResourceSoftware( // This holds a ref on the DrawingBuffer that will keep it alive until the // mailbox is released (and while the release callback is running). It also // owns the SharedBitmap. - auto func = WTF::Bind(&DrawingBuffer::MailboxReleasedSoftware, - scoped_refptr<DrawingBuffer>(this), - WTF::Passed(std::move(registered))); + auto func = base::BindOnce(&DrawingBuffer::MailboxReleasedSoftware, + weak_factory_.GetWeakPtr(), + WTF::Passed(std::move(registered))); *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func)); + contents_changed_ = false; ResetBuffersToAutoClear(); return true; } @@ -504,9 +504,8 @@ bool DrawingBuffer::FinishPrepareTransferableResourceGpu( // This holds a ref on the DrawingBuffer that will keep it alive until the // mailbox is released (and while the release callback is running). - auto func = - WTF::Bind(&DrawingBuffer::MailboxReleasedGpu, - scoped_refptr<DrawingBuffer>(this), color_buffer_for_mailbox); + auto func = base::BindOnce(&DrawingBuffer::NotifyMailboxReleasedGpu, + color_buffer_for_mailbox); *out_release_callback = viz::SingleReleaseCallback::Create(std::move(func)); } @@ -518,18 +517,29 @@ bool DrawingBuffer::FinishPrepareTransferableResourceGpu( return true; } +// static +void DrawingBuffer::NotifyMailboxReleasedGpu( + scoped_refptr<ColorBuffer> color_buffer, + const gpu::SyncToken& sync_token, + bool lost_resource) { + DCHECK(color_buffer->owning_thread_ref == base::PlatformThread::CurrentRef()); + + // Update the SyncToken to ensure that we will wait for it even if we + // immediately destroy this buffer. + color_buffer->receive_sync_token = sync_token; + if (color_buffer->drawing_buffer) { + color_buffer->drawing_buffer->MailboxReleasedGpu(color_buffer, + lost_resource); + } +} + void DrawingBuffer::MailboxReleasedGpu(scoped_refptr<ColorBuffer> color_buffer, - const gpu::SyncToken& sync_token, bool lost_resource) { // If the mailbox has been returned by the compositor then it is no // longer being presented, and so is no longer the front buffer. if (color_buffer == front_color_buffer_) front_color_buffer_ = nullptr; - // Update the SyncToken to ensure that we will wait for it even if we - // immediately destroy this buffer. - color_buffer->receive_sync_token = sync_token; - if (destruction_in_progress_ || color_buffer->size != size_ || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR || lost_resource || is_hidden_) { @@ -561,8 +571,7 @@ void DrawingBuffer::MailboxReleasedSoftware(RegisteredBitmap registered, recycled_bitmaps_.push_back(std::move(registered)); } -scoped_refptr<StaticBitmapImage> DrawingBuffer::TransferToStaticBitmapImage( - std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) { +scoped_refptr<StaticBitmapImage> DrawingBuffer::TransferToStaticBitmapImage() { ScopedStateRestorer scoped_state_restorer(this); viz::TransferableResource transferable_resource; @@ -583,35 +592,10 @@ scoped_refptr<StaticBitmapImage> DrawingBuffer::TransferToStaticBitmapImage( SkImage::MakeFromBitmap(black_bitmap)); } + DCHECK(release_callback); DCHECK_EQ(size_.Width(), transferable_resource.size.width()); DCHECK_EQ(size_.Height(), transferable_resource.size.height()); - // Make our own textureId that is a reference on the same texture backing - // being used as the front buffer (which was returned from - // PrepareTransferableResourceInternal()). We do not need to wait on the sync - // token in |transferable_resource| since the mailbox was produced on the same - // |m_gl| context that we are using here. Similarly, the |release_callback| - // will run on the same context so we don't need to send a sync token for this - // consume action back to it. - // TODO(danakj): Instead of using PrepareTransferableResourceInternal(), we - // could just use the actual texture id and avoid needing to produce/consume a - // mailbox. - GLuint texture_id = gl_->CreateAndTexStorage2DSharedImageCHROMIUM( - transferable_resource.mailbox_holder.mailbox.name); - - if (out_release_callback) { - // Allow the consumer to release the resource when done using it, so it can - // be recycled. - *out_release_callback = std::move(release_callback); - } else { - // Return the mailbox but report that the resource is lost to prevent trying - // to use the backing for future frames. We keep it alive with our own - // reference to the backing via our |textureId|. - gpu::SyncToken sync_token; - gl_->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); - release_callback->Run(sync_token, true /* lost_resource */); - } - // We reuse the same mailbox name from above since our texture id was consumed // from it. const auto& sk_image_mailbox = transferable_resource.mailbox_holder.mailbox; @@ -623,12 +607,18 @@ scoped_refptr<StaticBitmapImage> DrawingBuffer::TransferToStaticBitmapImage( const auto& sk_image_sync_token = transferable_resource.mailbox_holder.sync_token; + const SkImageInfo sk_image_info = + SkImageInfo::MakeN32Premul(size_.Width(), size_.Height()); + // TODO(xidachen): Create a small pool of recycled textures from // ImageBitmapRenderingContext's transferFromImageBitmap, and try to use them // in DrawingBuffer. - return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage( - sk_image_mailbox, sk_image_sync_token, texture_id, - context_provider_->GetWeakPtr(), size_, opengl_flip_y_extension_); + return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox( + sk_image_mailbox, sk_image_sync_token, /* shared_image_texture_id = */ 0, + sk_image_info, transferable_resource.mailbox_holder.texture_target, + /* is_origin_top_left = */ opengl_flip_y_extension_, + context_provider_->GetWeakPtr(), base::PlatformThread::CurrentRef(), + Thread::Current()->GetTaskRunner(), std::move(release_callback)); } scoped_refptr<DrawingBuffer::ColorBuffer> @@ -678,18 +668,29 @@ scoped_refptr<CanvasResource> DrawingBuffer::AsCanvasResource( } DrawingBuffer::ColorBuffer::ColorBuffer( - DrawingBuffer* drawing_buffer, + base::WeakPtr<DrawingBuffer> drawing_buffer, const IntSize& size, GLuint texture_id, std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer, gpu::Mailbox mailbox) - : drawing_buffer(drawing_buffer), + : owning_thread_ref(base::PlatformThread::CurrentRef()), + drawing_buffer(std::move(drawing_buffer)), size(size), texture_id(texture_id), gpu_memory_buffer(std::move(gpu_memory_buffer)), mailbox(mailbox) {} DrawingBuffer::ColorBuffer::~ColorBuffer() { + if (base::PlatformThread::CurrentRef() != owning_thread_ref || + !drawing_buffer) { + // If the context has been destroyed no cleanup is necessary since all + // resources below are automatically destroyed. Note that if a ColorBuffer + // is being destroyed on a different thread, it implies that the owning + // thread was destroyed which means the associated context was also + // destroyed. + return; + } + gpu::gles2::GLES2Interface* gl = drawing_buffer->gl_; gpu::SharedImageInterface* sii = drawing_buffer->ContextProvider()->SharedImageInterface(); @@ -777,7 +778,7 @@ bool DrawingBuffer::Initialize(const IntSize& size, bool use_multisampling) { if (ShouldUseChromiumImage()) { // A CHROMIUM_image backed texture requires a specialized set of parameters // on OSX. - texture_target_ = GC3D_TEXTURE_RECTANGLE_ARB; + texture_target_ = gpu::GetPlatformSpecificTextureTarget(); } #endif @@ -850,15 +851,10 @@ bool DrawingBuffer::Initialize(const IntSize& size, bool use_multisampling) { return true; } -bool DrawingBuffer::CopyToPlatformTexture(gpu::gles2::GLES2Interface* dst_gl, - GLenum dst_texture_target, - GLuint dst_texture, - GLint dst_level, - bool premultiply_alpha, - bool flip_y, - const IntPoint& dst_texture_offset, - const IntRect& src_sub_rectangle, - SourceDrawingBuffer src_buffer) { +template <typename CopyFunction> +bool DrawingBuffer::CopyToPlatformInternal(gpu::InterfaceBase* dst_interface, + SourceDrawingBuffer src_buffer, + const CopyFunction& copy_function) { ScopedStateRestorer scoped_state_restorer(this); gpu::gles2::GLES2Interface* src_gl = gl_; @@ -868,9 +864,6 @@ bool DrawingBuffer::CopyToPlatformTexture(gpu::gles2::GLES2Interface* dst_gl, src_gl->Flush(); } - if (!Extensions3DUtil::CanUseCopyTextureCHROMIUM(dst_texture_target)) - return false; - // Contexts may be in a different share group. We must transfer the texture // through a mailbox first. gpu::Mailbox mailbox; @@ -899,31 +892,12 @@ bool DrawingBuffer::CopyToPlatformTexture(gpu::gles2::GLES2Interface* dst_gl, return false; } - dst_gl->WaitSyncTokenCHROMIUM(produce_sync_token.GetConstData()); - - GLuint src_texture = - dst_gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name); - - GLboolean unpack_premultiply_alpha_needed = GL_FALSE; - GLboolean unpack_unpremultiply_alpha_needed = GL_FALSE; - if (want_alpha_channel_ && premultiplied_alpha_ && !premultiply_alpha) - unpack_unpremultiply_alpha_needed = GL_TRUE; - else if (want_alpha_channel_ && !premultiplied_alpha_ && premultiply_alpha) - unpack_premultiply_alpha_needed = GL_TRUE; + dst_interface->WaitSyncTokenCHROMIUM(produce_sync_token.GetConstData()); - dst_gl->BeginSharedImageAccessDirectCHROMIUM( - src_texture, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); - dst_gl->CopySubTextureCHROMIUM( - src_texture, 0, dst_texture_target, dst_texture, dst_level, - dst_texture_offset.X(), dst_texture_offset.Y(), src_sub_rectangle.X(), - src_sub_rectangle.Y(), src_sub_rectangle.Width(), - src_sub_rectangle.Height(), flip_y, unpack_premultiply_alpha_needed, - unpack_unpremultiply_alpha_needed); - dst_gl->EndSharedImageAccessDirectCHROMIUM(src_texture); - dst_gl->DeleteTextures(1, &src_texture); + copy_function(mailbox); gpu::SyncToken sync_token; - dst_gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); + dst_interface->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); src_gl->WaitSyncTokenCHROMIUM(sync_token.GetData()); if (texture_id_to_restore_access) { src_gl->BeginSharedImageAccessDirectCHROMIUM( @@ -933,6 +907,66 @@ bool DrawingBuffer::CopyToPlatformTexture(gpu::gles2::GLES2Interface* dst_gl, return true; } +bool DrawingBuffer::CopyToPlatformTexture(gpu::gles2::GLES2Interface* dst_gl, + GLenum dst_texture_target, + GLuint dst_texture, + GLint dst_level, + bool premultiply_alpha, + bool flip_y, + const IntPoint& dst_texture_offset, + const IntRect& src_sub_rectangle, + SourceDrawingBuffer src_buffer) { + if (!Extensions3DUtil::CanUseCopyTextureCHROMIUM(dst_texture_target)) + return false; + + GLboolean unpack_premultiply_alpha_needed = GL_FALSE; + GLboolean unpack_unpremultiply_alpha_needed = GL_FALSE; + if (want_alpha_channel_ && premultiplied_alpha_ && !premultiply_alpha) + unpack_unpremultiply_alpha_needed = GL_TRUE; + else if (want_alpha_channel_ && !premultiplied_alpha_ && premultiply_alpha) + unpack_premultiply_alpha_needed = GL_TRUE; + + auto copy_function = [&](gpu::Mailbox src_mailbox) { + GLuint src_texture = + dst_gl->CreateAndTexStorage2DSharedImageCHROMIUM(src_mailbox.name); + dst_gl->BeginSharedImageAccessDirectCHROMIUM( + src_texture, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); + dst_gl->CopySubTextureCHROMIUM( + src_texture, 0, dst_texture_target, dst_texture, dst_level, + dst_texture_offset.X(), dst_texture_offset.Y(), src_sub_rectangle.X(), + src_sub_rectangle.Y(), src_sub_rectangle.Width(), + src_sub_rectangle.Height(), flip_y, unpack_premultiply_alpha_needed, + unpack_unpremultiply_alpha_needed); + dst_gl->EndSharedImageAccessDirectCHROMIUM(src_texture); + dst_gl->DeleteTextures(1, &src_texture); + }; + return CopyToPlatformInternal(dst_gl, src_buffer, copy_function); +} + +bool DrawingBuffer::CopyToPlatformMailbox( + gpu::raster::RasterInterface* dst_raster_interface, + gpu::Mailbox dst_mailbox, + GLenum dst_texture_target, + bool flip_y, + const IntPoint& dst_texture_offset, + const IntRect& src_sub_rectangle, + SourceDrawingBuffer src_buffer) { + GLboolean unpack_premultiply_alpha_needed = GL_FALSE; + if (want_alpha_channel_ && !premultiplied_alpha_) + unpack_premultiply_alpha_needed = GL_TRUE; + + auto copy_function = [&](gpu::Mailbox src_mailbox) { + dst_raster_interface->CopySubTexture( + src_mailbox, dst_mailbox, dst_texture_target, dst_texture_offset.X(), + dst_texture_offset.Y(), src_sub_rectangle.X(), src_sub_rectangle.Y(), + src_sub_rectangle.Width(), src_sub_rectangle.Height(), flip_y, + unpack_premultiply_alpha_needed); + }; + + return CopyToPlatformInternal(dst_raster_interface, src_buffer, + copy_function); +} + cc::Layer* DrawingBuffer::CcLayer() { if (!layer_) { layer_ = cc::TextureLayer::CreateForMailbox(this); @@ -957,8 +991,6 @@ cc::Layer* DrawingBuffer::CcLayer() { if (opengl_flip_y_extension_) layer_->SetFlipped(false); - - GraphicsLayer::RegisterContentsLayer(layer_.get()); } return layer_.get(); @@ -1012,9 +1044,6 @@ void DrawingBuffer::BeginDestruction() { multisample_fbo_ = 0; fbo_ = 0; - if (layer_) - GraphicsLayer::UnregisterContentsLayer(layer_.get()); - client_ = nullptr; } @@ -1052,7 +1081,8 @@ bool DrawingBuffer::ResizeDefaultFramebuffer(const IntSize& size) { premultiplied_alpha_false_mailbox_ = sii->CreateSharedImage( format, static_cast<gfx::Size>(size), storage_color_space_, gpu::SHARED_IMAGE_USAGE_GLES2 | - gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT); + gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT | + gpu::SHARED_IMAGE_USAGE_RASTER); gpu::SyncToken sync_token = sii->GenUnverifiedSyncToken(); gl_->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); premultiplied_alpha_false_texture_ = @@ -1618,8 +1648,9 @@ scoped_refptr<DrawingBuffer::ColorBuffer> DrawingBuffer::CreateColorBuffer( // Import frontbuffer of swap chain into GL. texture_id = gl_->CreateAndTexStorage2DSharedImageCHROMIUM( front_buffer_mailbox.name); - front_color_buffer_ = base::AdoptRef( - new ColorBuffer(this, size, texture_id, nullptr, front_buffer_mailbox)); + front_color_buffer_ = base::MakeRefCounted<ColorBuffer>( + weak_factory_.GetWeakPtr(), size, texture_id, nullptr, + front_buffer_mailbox); } // Import the backbuffer of swap chain or allocated SharedImage into GL. texture_id = @@ -1646,9 +1677,9 @@ scoped_refptr<DrawingBuffer::ColorBuffer> DrawingBuffer::CreateColorBuffer( gl_->DeleteFramebuffers(1, &fbo); } - return base::AdoptRef(new ColorBuffer(this, size, texture_id, - std::move(gpu_memory_buffer), - back_buffer_mailbox)); + return base::MakeRefCounted<ColorBuffer>( + weak_factory_.GetWeakPtr(), size, texture_id, + std::move(gpu_memory_buffer), back_buffer_mailbox); } void DrawingBuffer::AttachColorBufferToReadFramebuffer() { diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h index c4f0e99a8a9..9ac03f51f68 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h @@ -37,6 +37,9 @@ #include "cc/layers/texture_layer_client.h" #include "cc/resources/cross_thread_shared_bitmap.h" #include "cc/resources/shared_bitmap_id_registrar.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/client/raster_interface.h" #include "gpu/command_buffer/common/mailbox.h" #include "gpu/command_buffer/common/sync_token.h" #include "gpu/config/gpu_feature_info.h" @@ -225,10 +228,7 @@ class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient, // contents of the front buffer. This is done without any pixel copies. The // texture in the ImageBitmap is from the active ContextProvider on the // DrawingBuffer. - // If out_release_callback is null, the image is discarded. If it is non-null - // the image must be recycled or discarded by calling *out_release_callback. - scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage( - std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback); + scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage(); bool CopyToPlatformTexture(gpu::gles2::GLES2Interface*, GLenum dst_target, @@ -240,6 +240,14 @@ class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient, const IntRect& src_sub_rectangle, SourceDrawingBuffer); + bool CopyToPlatformMailbox(gpu::raster::RasterInterface*, + gpu::Mailbox dst_mailbox, + GLenum dst_texture_target, + bool flip_y, + const IntPoint& dst_texture_offset, + const IntRect& src_sub_rectangle, + SourceDrawingBuffer src_buffer); + sk_sp<SkData> PaintRenderingResultsToDataArray(SourceDrawingBuffer); int SampleCount() const { return sample_count_; } @@ -345,18 +353,22 @@ class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient, bool pixel_pack_buffer_binding_dirty_ = false; }; - struct ColorBuffer : public RefCounted<ColorBuffer> { - ColorBuffer(DrawingBuffer*, + struct ColorBuffer : public base::RefCountedThreadSafe<ColorBuffer> { + ColorBuffer(base::WeakPtr<DrawingBuffer> drawing_buffer, const IntSize&, GLuint texture_id, std::unique_ptr<gfx::GpuMemoryBuffer>, gpu::Mailbox mailbox); ~ColorBuffer(); + // The thread on which the ColorBuffer is created and the DrawingBuffer is + // bound to. + const base::PlatformThreadRef owning_thread_ref; + // The owning DrawingBuffer. Note that DrawingBuffer is explicitly destroyed // by the beginDestruction method, which will eventually drain all of its // ColorBuffers. - scoped_refptr<DrawingBuffer> drawing_buffer; + base::WeakPtr<DrawingBuffer> drawing_buffer; const IntSize size; const GLuint texture_id = 0; std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer; @@ -385,6 +397,11 @@ class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient, DISALLOW_COPY_AND_ASSIGN(ColorBuffer); }; + template <typename CopyFunction> + bool CopyToPlatformInternal(gpu::InterfaceBase* dst_interface, + SourceDrawingBuffer src_buffer, + const CopyFunction& copy_function); + enum ClearOption { ClearOnlyMultisampledFBO, ClearAllFBOs }; // Clears out newly-allocated framebuffers (really, renderbuffers / textures). @@ -420,8 +437,10 @@ class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient, // Callbacks for mailboxes given to the compositor from // FinishPrepareTransferableResource{Gpu,Software}. + static void NotifyMailboxReleasedGpu(scoped_refptr<ColorBuffer>, + const gpu::SyncToken&, + bool lost_resource); void MailboxReleasedGpu(scoped_refptr<ColorBuffer>, - const gpu::SyncToken&, bool lost_resource); void MailboxReleasedSoftware(RegisteredBitmap, const gpu::SyncToken&, @@ -617,9 +636,11 @@ class PLATFORM_EXPORT DrawingBuffer : public cc::TextureLayerClient, bool opengl_flip_y_extension_; - gl::GpuPreference initial_gpu_; + const gl::GpuPreference initial_gpu_; gl::GpuPreference current_active_gpu_; + base::WeakPtrFactory<DrawingBuffer> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(DrawingBuffer); }; diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc index 19d15ce32c4..537c1af0001 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test.cc @@ -203,9 +203,8 @@ TEST_F(DrawingBufferTest, VerifyResizingProperlyAffectsResources) { drawing_buffer_->BeginDestruction(); } -TEST_F(DrawingBufferTest, VerifyDestructionCompleteAfterAllResourceReleased) { - bool live = true; - drawing_buffer_->live_ = &live; +TEST_F(DrawingBufferTest, VerifySharedImagesReleasedAfterReleaseCallback) { + auto* sii = drawing_buffer_->SharedImageInterfaceForTests(); viz::TransferableResource resource1; std::unique_ptr<viz::SingleReleaseCallback> release_callback1; @@ -231,67 +230,20 @@ TEST_F(DrawingBufferTest, VerifyDestructionCompleteAfterAllResourceReleased) { EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3, &release_callback3)); - EXPECT_TRUE(drawing_buffer_->MarkContentsChanged()); - release_callback1->Run(gpu::SyncToken(), false /* lostResource */); - - drawing_buffer_->BeginDestruction(); - ASSERT_EQ(live, true); - - DrawingBufferForTests* raw_pointer = drawing_buffer_.get(); - drawing_buffer_ = nullptr; - ASSERT_EQ(live, true); - - EXPECT_FALSE(raw_pointer->MarkContentsChanged()); - release_callback2->Run(gpu::SyncToken(), false /* lostResource */); - ASSERT_EQ(live, true); - - EXPECT_FALSE(raw_pointer->MarkContentsChanged()); - release_callback3->Run(gpu::SyncToken(), false /* lostResource */); - ASSERT_EQ(live, false); -} - -TEST_F(DrawingBufferTest, verifyDrawingBufferStaysAliveIfResourcesAreLost) { - bool live = true; - drawing_buffer_->live_ = &live; - - viz::TransferableResource resource1; - std::unique_ptr<viz::SingleReleaseCallback> release_callback1; - viz::TransferableResource resource2; - std::unique_ptr<viz::SingleReleaseCallback> release_callback2; - viz::TransferableResource resource3; - std::unique_ptr<viz::SingleReleaseCallback> release_callback3; - - EXPECT_FALSE(drawing_buffer_->MarkContentsChanged()); - EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource1, - &release_callback1)); - VerifyStateWasRestored(); - EXPECT_TRUE(drawing_buffer_->MarkContentsChanged()); - EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource2, - &release_callback2)); - VerifyStateWasRestored(); - EXPECT_TRUE(drawing_buffer_->MarkContentsChanged()); - EXPECT_TRUE(drawing_buffer_->PrepareTransferableResource(nullptr, &resource3, - &release_callback3)); - VerifyStateWasRestored(); + EXPECT_EQ(sii->shared_image_count(), 4u); EXPECT_TRUE(drawing_buffer_->MarkContentsChanged()); release_callback1->Run(gpu::SyncToken(), true /* lostResource */); - EXPECT_EQ(live, true); + EXPECT_EQ(sii->shared_image_count(), 3u); - drawing_buffer_->BeginDestruction(); - EXPECT_EQ(live, true); + release_callback2->Run(gpu::SyncToken(), true /* lostResource */); + EXPECT_EQ(sii->shared_image_count(), 2u); - EXPECT_FALSE(drawing_buffer_->MarkContentsChanged()); - release_callback2->Run(gpu::SyncToken(), false /* lostResource */); - EXPECT_EQ(live, true); - - DrawingBufferForTests* raw_ptr = drawing_buffer_.get(); - drawing_buffer_ = nullptr; - EXPECT_EQ(live, true); + // The resource is not marked lost so it's recycled after the callback. + release_callback3->Run(gpu::SyncToken(), false /* lostResource */); + EXPECT_EQ(sii->shared_image_count(), 2u); - EXPECT_FALSE(raw_ptr->MarkContentsChanged()); - release_callback3->Run(gpu::SyncToken(), true /* lostResource */); - EXPECT_EQ(live, false); + drawing_buffer_->BeginDestruction(); } TEST_F(DrawingBufferTest, VerifyOnlyOneRecycledResourceMustBeKept) { diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h index 61445894ffc..bf4f933c401 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h @@ -60,8 +60,8 @@ class WebGraphicsContext3DProviderForTests const WebglPreferences& GetWebglPreferences() const override { return webgl_preferences_; } - viz::GLHelper* GetGLHelper() override { return nullptr; } - void SetLostContextCallback(base::Closure) override {} + gpu::GLHelper* GetGLHelper() override { return nullptr; } + void SetLostContextCallback(base::RepeatingClosure) override {} void SetErrorMessageCallback( base::RepeatingCallback<void(const char*, int32_t id)>) override {} cc::ImageDecodeCache* ImageDecodeCache(SkColorType color_type) override { @@ -279,9 +279,9 @@ class GLES2InterfaceForTests : public gpu::gles2::GLES2InterfaceStub, // ImplementationBase implementation void GenSyncTokenCHROMIUM(GLbyte* sync_token) override { - static uint64_t unique_id = 1; - gpu::SyncToken source( - gpu::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(unique_id++), 2); + static gpu::CommandBufferId::Generator command_buffer_id_generator; + gpu::SyncToken source(gpu::GPU_IO, + command_buffer_id_generator.GenerateNextId(), 2); memcpy(sync_token, &source, sizeof(source)); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc index 50308ffc6b2..388559e7630 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.cc @@ -9,6 +9,7 @@ #include "base/memory/ptr_util.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/common/gles2_cmd_copy_texture_chromium_utils.h" #include "third_party/blink/renderer/platform/wtf/text/string_hash.h" namespace blink { @@ -75,6 +76,12 @@ bool Extensions3DUtil::IsExtensionEnabled(const String& name) { return enabled_extensions_.Contains(name); } +// static +bool Extensions3DUtil::CopyTextureCHROMIUMNeedsESSL3(GLenum dest_format) { + return gpu::gles2::CopyTextureCHROMIUMNeedsESSL3(dest_format); +} + +// static bool Extensions3DUtil::CanUseCopyTextureCHROMIUM(GLenum dest_target) { switch (dest_target) { case GL_TEXTURE_2D: diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h index 062807c497f..6a214156502 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h @@ -38,6 +38,7 @@ class PLATFORM_EXPORT Extensions3DUtil final { bool EnsureExtensionEnabled(const String& name); bool IsExtensionEnabled(const String& name); + static bool CopyTextureCHROMIUMNeedsESSL3(GLenum dest_format); static bool CanUseCopyTextureCHROMIUM(GLenum dest_target); private: diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc index 68c1c5063ca..2d15555c764 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.cc @@ -12,94 +12,8 @@ #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/gpu/GrContext.h" -namespace { - -struct GrTextureMailboxReleaseProcData { - GrTexture* gr_texture_; - base::WeakPtr<blink::WebGraphicsContext3DProviderWrapper> - context_provider_wrapper_; -}; - -void GrTextureMailboxReleaseProc(void* data) { - GrTextureMailboxReleaseProcData* release_proc_data = - static_cast<GrTextureMailboxReleaseProcData*>(data); - - if (release_proc_data->context_provider_wrapper_) { - release_proc_data->context_provider_wrapper_->Utils()->RemoveCachedMailbox( - release_proc_data->gr_texture_); - } - - delete release_proc_data; -} - -} // unnamed namespace - namespace blink { -bool GraphicsContext3DUtils::GetMailboxForSkImage(gpu::Mailbox& out_mailbox, - GLenum& out_texture_target, - const sk_sp<SkImage>& image, - GLenum filter) { - // This object is owned by context_provider_wrapper_, so that weak ref - // should never be null. - DCHECK(context_provider_wrapper_); - DCHECK(image->isTextureBacked()); - GrContext* gr = context_provider_wrapper_->ContextProvider()->GetGrContext(); - gpu::gles2::GLES2Interface* gl = - context_provider_wrapper_->ContextProvider()->ContextGL(); - - DCHECK(gr); - DCHECK(gl); - GrTexture* gr_texture = image->getTexture(); - if (!gr_texture) - return false; - - DCHECK(gr == gr_texture->getContext()); - - GrBackendTexture backend_texture = image->getBackendTexture(true); - DCHECK(backend_texture.isValid()); - - GrGLTextureInfo info; - bool result = backend_texture.getGLTextureInfo(&info); - DCHECK(result); - - GLuint texture_id = info.fID; - GLenum texture_target = info.fTarget; - out_texture_target = texture_target; - - auto it = cached_mailboxes_.find(gr_texture); - if (it != cached_mailboxes_.end()) { - out_mailbox = it->value; - } else { - gl->ProduceTextureDirectCHROMIUM(texture_id, out_mailbox.name); - - GrTextureMailboxReleaseProcData* release_proc_data = - new GrTextureMailboxReleaseProcData(); - release_proc_data->gr_texture_ = gr_texture; - release_proc_data->context_provider_wrapper_ = context_provider_wrapper_; - gr_texture->setRelease(GrTextureMailboxReleaseProc, release_proc_data); - cached_mailboxes_.insert(gr_texture, out_mailbox); - } - gl->BindTexture(texture_target, texture_id); - gl->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, filter); - gl->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, filter); - gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - gl->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - gl->BindTexture(texture_target, 0); - gr_texture->textureParamsModified(); - return true; -} - -void GraphicsContext3DUtils::RegisterMailbox(GrTexture* gr_texture, - const gpu::Mailbox& mailbox) { - DCHECK(cached_mailboxes_.find(gr_texture) == cached_mailboxes_.end()); - cached_mailboxes_.insert(gr_texture, mailbox); -} - -void GraphicsContext3DUtils::RemoveCachedMailbox(GrTexture* gr_texture) { - cached_mailboxes_.erase(gr_texture); -} - bool GraphicsContext3DUtils::Accelerated2DCanvasFeatureEnabled() { // Don't use accelerated canvas if compositor is in software mode. if (!SharedGpuContext::IsGpuCompositingEnabled()) diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h index 403becdd9f4..f18a0a18cd2 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/graphics_context_3d_utils.h @@ -12,7 +12,6 @@ #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" #include "third_party/skia/include/core/SkImage.h" -#include "third_party/skia/include/gpu/GrTexture.h" typedef unsigned int GLenum; @@ -30,22 +29,10 @@ class PLATFORM_EXPORT GraphicsContext3DUtils { context_provider_wrapper) : context_provider_wrapper_(std::move(context_provider_wrapper)) {} - // Use this service to create a new mailbox or possibly obtain a pre-existing - // mailbox for a given texture. The caching of pre-existing mailboxes survives - // when the texture gets recycled by skia for creating a new SkSurface or - // SkImage with a pre-existing GrTexture backing. - bool GetMailboxForSkImage(gpu::Mailbox&, - GLenum&, - const sk_sp<SkImage>&, - GLenum filter); - void RegisterMailbox(GrTexture*, const gpu::Mailbox&); - void RemoveCachedMailbox(GrTexture*); - bool Accelerated2DCanvasFeatureEnabled(); private: base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_; - WTF::HashMap<GrTexture*, gpu::Mailbox> cached_mailboxes_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc index 966ee2547d7..e8a21596af5 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc @@ -11,10 +11,13 @@ #include "components/viz/common/resources/shared_bitmap.h" #include "components/viz/common/resources/transferable_resource.h" #include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/client/shared_image_interface.h" +#include "gpu/command_buffer/common/shared_image_usage.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h" #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h" #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h" +#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h" #include "third_party/blink/renderer/platform/graphics/color_behavior.h" #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" @@ -24,6 +27,29 @@ #include "ui/gfx/geometry/size.h" namespace blink { +namespace { + +scoped_refptr<StaticBitmapImage> MakeAccelerated( + const scoped_refptr<StaticBitmapImage>& source, + base::WeakPtr<WebGraphicsContext3DProviderWrapper> + context_provider_wrapper) { + if (source->IsTextureBacked()) + return source; + + auto paint_image = source->PaintImageForCurrentFrame(); + auto provider = CanvasResourceProvider::CreateSharedImageProvider( + source->Size(), context_provider_wrapper, kLow_SkFilterQuality, + CanvasColorParams(paint_image.GetSkImage()->imageInfo()), + source->IsOriginTopLeft(), CanvasResourceProvider::RasterMode::kGPU, + gpu::SHARED_IMAGE_USAGE_DISPLAY); + if (!provider || !provider->IsAccelerated()) + return nullptr; + + provider->Canvas()->drawImage(paint_image, 0, 0, nullptr); + return provider->Snapshot(); +} + +} // namespace ImageLayerBridge::ImageLayerBridge(OpacityMode opacity_mode) : opacity_mode_(opacity_mode) { @@ -35,7 +61,6 @@ ImageLayerBridge::ImageLayerBridge(OpacityMode opacity_mode) layer_->SetContentsOpaque(true); layer_->SetBlendBackgroundColor(false); } - GraphicsLayer::RegisterContentsLayer(layer_.get()); } ImageLayerBridge::~ImageLayerBridge() { @@ -86,7 +111,6 @@ void ImageLayerBridge::SetUV(const FloatPoint& left_top, void ImageLayerBridge::Dispose() { if (layer_) { - GraphicsLayer::UnregisterContentsLayer(layer_.get()); layer_->ClearClient(); layer_ = nullptr; } @@ -122,19 +146,22 @@ bool ImageLayerBridge::PrepareTransferableResource( if (gpu_compositing) { scoped_refptr<StaticBitmapImage> image_for_compositor = - image_->MakeAccelerated(SharedGpuContext::ContextProviderWrapper()); - if (!image_for_compositor) + MakeAccelerated(image_, SharedGpuContext::ContextProviderWrapper()); + if (!image_for_compositor || !image_for_compositor->ContextProvider()) return false; const gfx::Size size(image_for_compositor->width(), image_for_compositor->height()); uint32_t filter = filter_quality_ == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR; - image_for_compositor->EnsureMailbox(kUnverifiedSyncToken, filter); + auto mailbox_holder = image_for_compositor->GetMailboxHolder(); + auto* sii = image_for_compositor->ContextProvider()->SharedImageInterface(); + bool is_overlay_candidate = sii->UsageForMailbox(mailbox_holder.mailbox) & + gpu::SHARED_IMAGE_USAGE_SCANOUT; + *out_resource = viz::TransferableResource::MakeGL( - image_for_compositor->GetMailbox(), filter, GL_TEXTURE_2D, - image_for_compositor->GetSyncToken(), size, - false /* is_overlay_candidate */); + mailbox_holder.mailbox, filter, mailbox_holder.texture_target, + mailbox_holder.sync_token, size, is_overlay_candidate); auto func = WTF::Bind(&ImageLayerBridge::ResourceReleasedGpu, WrapWeakPersistent(this), std::move(image_for_compositor)); diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h index d550eb47af5..acfd1b59240 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h @@ -57,7 +57,7 @@ class PLATFORM_EXPORT ImageLayerBridge bool IsAccelerated() { return image_ && image_->IsTextureBacked(); } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: // SharedMemory bitmap that was registered with SharedBitmapIdRegistrar. Used diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc index 1592804d496..82b5c4a5fd9 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc @@ -9,6 +9,7 @@ #include "base/test/null_task_runner.h" #include "components/viz/test/test_gles2_interface.h" #include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/common/capabilities.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h" @@ -38,7 +39,12 @@ class SharedGpuContextTestBase : public Test { -> std::unique_ptr<WebGraphicsContext3DProvider> { *gpu_compositing_disabled = false; gl->SetIsContextLost(false); - return std::make_unique<FakeWebGraphicsContext3DProvider>(gl); + auto fake_context = + std::make_unique<FakeWebGraphicsContext3DProvider>(gl); + gpu::Capabilities capabilities; + capabilities.max_texture_size = 20; + fake_context->SetCapabilities(capabilities); + return fake_context; }; SharedGpuContext::SetContextProviderFactoryForTesting( WTF::BindRepeating(factory, WTF::Unretained(&gl_))); @@ -120,6 +126,27 @@ class SoftwareCompositingTest : public Test { FakeGLES2Interface gl_; }; +class SharedGpuContextTestViz : public Test { + public: + void SetUp() override { + task_runner_ = base::MakeRefCounted<base::NullTaskRunner>(); + handle_ = std::make_unique<base::ThreadTaskRunnerHandle>(task_runner_); + test_context_provider_ = viz::TestContextProvider::Create(); + InitializeSharedGpuContext(test_context_provider_.get(), + /*cache = */ nullptr, + SetIsContextLost::kSetToFalse); + } + + void TearDown() override { + handle_.reset(); + task_runner_.reset(); + SharedGpuContext::ResetForTesting(); + } + scoped_refptr<base::NullTaskRunner> task_runner_; + std::unique_ptr<base::ThreadTaskRunnerHandle> handle_; + scoped_refptr<viz::TestContextProvider> test_context_provider_; +}; + TEST_F(SharedGpuContextTest, contextLossAutoRecovery) { EXPECT_NE(SharedGpuContext::ContextProviderWrapper(), nullptr); base::WeakPtr<WebGraphicsContext3DProviderWrapper> context = @@ -133,26 +160,6 @@ TEST_F(SharedGpuContextTest, contextLossAutoRecovery) { EXPECT_FALSE(!!context); } -TEST_F(SharedGpuContextTest, AccelerateImageBufferSurfaceAutoRecovery) { - // Verifies that after a context loss, attempting to allocate an - // AcceleratedImageBufferSurface will restore the context and succeed - gl_.SetIsContextLost(true); - EXPECT_FALSE(SharedGpuContext::IsValidWithoutRestoring()); - IntSize size(10, 10); - std::unique_ptr<CanvasResourceProvider> resource_provider = - CanvasResourceProvider::Create( - size, - CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage, - SharedGpuContext::ContextProviderWrapper(), - 0, // msaa_sample_count - kLow_SkFilterQuality, CanvasColorParams(), - CanvasResourceProvider::kDefaultPresentationMode, - nullptr // canvas_resource_dispatcher - ); - EXPECT_TRUE(resource_provider && resource_provider->IsValid()); - EXPECT_TRUE(SharedGpuContext::IsValidWithoutRestoring()); -} - TEST_F(SharedGpuContextTest, Canvas2DLayerBridgeAutoRecovery) { // Verifies that after a context loss, attempting to allocate a // Canvas2DLayerBridge will restore the context and succeed. @@ -181,20 +188,16 @@ TEST_F(BadSharedGpuContextTest, AllowSoftwareToAcceleratedCanvasUpgrade) { } TEST_F(BadSharedGpuContextTest, AccelerateImageBufferSurfaceCreationFails) { - // With a bad shared context, AccelerateImageBufferSurface creation should - // fail gracefully + // With a bad shared context, AccelerateImageBufferSurface should fail and + // return a nullptr provider IntSize size(10, 10); std::unique_ptr<CanvasResourceProvider> resource_provider = - CanvasResourceProvider::Create( - size, - CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage, - SharedGpuContext::ContextProviderWrapper(), - 0, // msaa_sample_count + CanvasResourceProvider::CreateSharedImageProvider( + size, SharedGpuContext::ContextProviderWrapper(), kLow_SkFilterQuality, CanvasColorParams(), - CanvasResourceProvider::kDefaultPresentationMode, - nullptr // canvas_resource_dispatcher - ); - EXPECT_FALSE(!resource_provider); + true /*is_origin_top_left*/, CanvasResourceProvider::RasterMode::kGPU, + 0u /*shared_image_usage_flags*/); + EXPECT_FALSE(resource_provider); } TEST_F(SharedGpuContextTest, CompositingMode) { @@ -209,51 +212,21 @@ TEST_F(SoftwareCompositingTest, CompositingMode) { EXPECT_FALSE(SharedGpuContext::IsGpuCompositingEnabled()); } -class MailboxSharedGpuContextTest : public Test { - public: - void SetUp() override { - task_runner_ = base::MakeRefCounted<base::NullTaskRunner>(); - handle_ = std::make_unique<base::ThreadTaskRunnerHandle>(task_runner_); - context_ = viz::TestContextProvider::Create(); - InitializeSharedGpuContext(context_.get()); - } - - scoped_refptr<viz::TestContextProvider> context_; - scoped_refptr<base::NullTaskRunner> task_runner_; - std::unique_ptr<base::ThreadTaskRunnerHandle> handle_; -}; - -TEST_F(MailboxSharedGpuContextTest, MailboxCaching) { +TEST_F(SharedGpuContextTestViz, AccelerateImageBufferSurfaceAutoRecovery) { + // Verifies that after a context loss, attempting to allocate an + // AcceleratedImageBufferSurface will restore the context and succeed + test_context_provider_->TestContextGL()->set_context_lost(true); + EXPECT_FALSE(SharedGpuContext::IsValidWithoutRestoring()); IntSize size(10, 10); std::unique_ptr<CanvasResourceProvider> resource_provider = - CanvasResourceProvider::Create( - size, - CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage, - SharedGpuContext::ContextProviderWrapper(), - 0, // msaa_sample_count + CanvasResourceProvider::CreateSharedImageProvider( + size, SharedGpuContext::ContextProviderWrapper(), kLow_SkFilterQuality, CanvasColorParams(), - CanvasResourceProvider::kDefaultPresentationMode, - nullptr // canvas_resource_dispatcher - ); - ASSERT_TRUE(resource_provider->IsAccelerated()); + true /*is_origin_top_left*/, CanvasResourceProvider::RasterMode::kGPU, + 0u /*shared_image_usage_flags*/); EXPECT_TRUE(resource_provider && resource_provider->IsValid()); - scoped_refptr<StaticBitmapImage> image = resource_provider->Snapshot(); - GLenum texture_target = GL_TEXTURE_2D; - gpu::Mailbox mailbox[3]; - - // Creating the SkImage representation from the shared image mailbox registers - // the same mailbox mapping to this SkImage with the cache. This ensures we - // don't recreate a non-shared image mailbox if going from SkImage to mailbox. - mailbox[0] = image->GetMailbox(); - SharedGpuContext::ContextProviderWrapper()->Utils()->GetMailboxForSkImage( - mailbox[1], texture_target, - image->PaintImageForCurrentFrame().GetSkImage(), GL_NEAREST); - EXPECT_EQ(mailbox[0], mailbox[1]); - - SharedGpuContext::ContextProviderWrapper()->Utils()->GetMailboxForSkImage( - mailbox[2], texture_target, - image->PaintImageForCurrentFrame().GetSkImage(), GL_NEAREST); - EXPECT_EQ(mailbox[1], mailbox[2]); + EXPECT_TRUE(resource_provider->IsAccelerated()); + EXPECT_TRUE(SharedGpuContext::IsValidWithoutRestoring()); } } // unnamed namespace diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc index 08aea5a6e62..0736906278c 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgl_image_conversion.cc @@ -2858,7 +2858,11 @@ void WebGLImageConversion::ImageExtractor::ExtractImage( if (!skia_image) return; - image_source_format_ = SK_B32_SHIFT ? kDataFormatRGBA8 : kDataFormatBGRA8; +#if SK_B32_SHIFT + image_source_format_ = kDataFormatRGBA8; +#else + image_source_format_ = kDataFormatBGRA8; +#endif image_source_unpack_alignment_ = 0; // FIXME: this seems to always be zero - why use at all? diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.cc new file mode 100644 index 00000000000..241c2c76d2a --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.cc @@ -0,0 +1,82 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h" + +#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h" + +namespace blink { + +namespace { +static constexpr uint64_t kDawnRowPitchAlignmentBits = 8; + +// Calculate row pitch for T2B/B2T copy +// TODO(shaobo.yan@intel.com): Using Dawn's constants once they are exposed +uint64_t AlignWebGPURowPitch(uint64_t bytesPerRow) { + return (((bytesPerRow - 1) >> kDawnRowPitchAlignmentBits) + 1) + << kDawnRowPitchAlignmentBits; +} + +} // anonymous namespace + +WebGPUImageUploadSizeInfo ComputeImageBitmapWebGPUUploadSizeInfo( + const IntRect& rect, + const CanvasColorParams& color_params) { + WebGPUImageUploadSizeInfo info; + uint64_t bytes_per_pixel = color_params.BytesPerPixel(); + + uint64_t row_pitch = AlignWebGPURowPitch(rect.Width() * bytes_per_pixel); + + // Currently, row pitch for buffer copy view in WebGPU is an uint32_t type + // value and the maximum value is std::numeric_limits<uint32_t>::max(). + DCHECK(row_pitch <= std::numeric_limits<uint32_t>::max()); + + info.wgpu_row_pitch = static_cast<uint32_t>(row_pitch); + info.size_in_bytes = row_pitch * rect.Height(); + + return info; +} + +bool CopyBytesFromImageBitmapForWebGPU(scoped_refptr<StaticBitmapImage> image, + base::span<uint8_t> dst, + const IntRect& rect, + const CanvasColorParams& color_params) { + DCHECK(image); + DCHECK_GT(dst.size(), static_cast<size_t>(0)); + DCHECK(image->width() - rect.X() >= rect.Width()); + DCHECK(image->height() - rect.Y() >= rect.Height()); + + WebGPUImageUploadSizeInfo wgpu_info = + ComputeImageBitmapWebGPUUploadSizeInfo(rect, color_params); + DCHECK_EQ(static_cast<uint64_t>(dst.size()), wgpu_info.size_in_bytes); + + // Prepare extract data from SkImage. + // TODO(shaobo.yan@intel.com): Make sure the data is in the correct format for + // copying to WebGPU + SkColorType color_type = + (color_params.GetSkColorType() == kRGBA_F16_SkColorType) + ? kRGBA_F16_SkColorType + : kRGBA_8888_SkColorType; + + // Read pixel request dst info. + SkImageInfo info = SkImageInfo::Make( + rect.Width(), rect.Height(), color_type, kUnpremul_SkAlphaType, + color_params.GetSkColorSpaceForSkSurfaces()); + + sk_sp<SkImage> sk_image = image->PaintImageForCurrentFrame().GetSkImage(); + + if (!sk_image) + return false; + + bool read_pixels_successful = sk_image->readPixels( + info, dst.data(), wgpu_info.wgpu_row_pitch, rect.X(), rect.Y()); + + if (!read_pixels_successful) { + return false; + } + + return true; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h new file mode 100644 index 00000000000..4f6c690d7b3 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h @@ -0,0 +1,32 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGPU_IMAGE_BITMAP_HANDLER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGPU_IMAGE_BITMAP_HANDLER_H_ + +#include "base/containers/span.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/ref_counted.h" + +namespace blink { + +struct WebGPUImageUploadSizeInfo { + uint64_t size_in_bytes; + uint32_t wgpu_row_pitch; +}; + +class CanvasColorParams; +class IntRect; +class StaticBitmapImage; + +WebGPUImageUploadSizeInfo PLATFORM_EXPORT +ComputeImageBitmapWebGPUUploadSizeInfo(const IntRect& rect, + const CanvasColorParams& color_params); +bool PLATFORM_EXPORT +CopyBytesFromImageBitmapForWebGPU(scoped_refptr<StaticBitmapImage> image, + base::span<uint8_t> dst, + const IntRect& rect, + const CanvasColorParams& color_params); +} // namespace blink +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_WEBGPU_IMAGE_BITMAP_HANDLER_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler_test.cc new file mode 100644 index 00000000000..f2c4766e921 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler_test.cc @@ -0,0 +1,110 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/graphics/gpu/webgpu_image_bitmap_handler.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h" + +namespace blink { + +static constexpr uint64_t kMaxArrayLength = 40000; + +class WebGPUImageBitmapHandlerTest : public testing::Test { + protected: + void SetUp() override {} + + void VerifyCopyBytesForWebGPU(uint64_t width, + uint64_t height, + SkImageInfo info, + CanvasColorParams param, + IntRect copyRect) { + const uint64_t content_length = width * height * param.BytesPerPixel(); + std::array<uint8_t, kMaxArrayLength> contents = {0}; + // Initialize contents. + for (size_t i = 0; i < content_length; ++i) { + contents[i] = i % std::numeric_limits<uint8_t>::max(); + } + + sk_sp<SkData> image_pixels = + SkData::MakeWithCopy(contents.data(), content_length); + scoped_refptr<StaticBitmapImage> image = + StaticBitmapImage::Create(std::move(image_pixels), info); + + WebGPUImageUploadSizeInfo wgpu_info = + ComputeImageBitmapWebGPUUploadSizeInfo(copyRect, param); + + const uint64_t result_length = wgpu_info.size_in_bytes; + std::array<uint8_t, kMaxArrayLength> results = {0}; + bool success = CopyBytesFromImageBitmapForWebGPU( + image, base::span<uint8_t>(results.data(), result_length), copyRect, + param); + ASSERT_EQ(success, true); + + // Compare content and results + uint32_t row_pitch = wgpu_info.wgpu_row_pitch; + uint32_t content_row_index = + (copyRect.Y() * width + copyRect.X()) * param.BytesPerPixel(); + uint32_t result_row_index = 0; + for (int i = 0; i < copyRect.Height(); ++i) { + EXPECT_EQ(0, + memcmp(&contents[content_row_index], &results[result_row_index], + copyRect.Width() * param.BytesPerPixel())); + content_row_index += width * param.BytesPerPixel(); + result_row_index += row_pitch; + } + } +}; + +// Test calculate size +TEST_F(WebGPUImageBitmapHandlerTest, VerifyGetWGPUResourceInfo) { + uint64_t imageWidth = 63; + uint64_t imageHeight = 1; + CanvasColorParams param(CanvasColorSpace::kSRGB, CanvasPixelFormat::kRGBA8, + OpacityMode::kNonOpaque); + + // Prebaked expected values. + uint32_t expected_row_pitch = 256; + uint64_t expected_size = 256; + + IntRect test_rect(0, 0, imageWidth, imageHeight); + WebGPUImageUploadSizeInfo info = + ComputeImageBitmapWebGPUUploadSizeInfo(test_rect, param); + ASSERT_EQ(expected_size, info.size_in_bytes); + ASSERT_EQ(expected_row_pitch, info.wgpu_row_pitch); +} + +// Copy full image bitmap test +TEST_F(WebGPUImageBitmapHandlerTest, VerifyCopyBytesFromImageBitmapForWebGPU) { + uint64_t imageWidth = 4; + uint64_t imageHeight = 2; + SkImageInfo info = SkImageInfo::Make( + imageWidth, imageHeight, SkColorType::kRGBA_8888_SkColorType, + SkAlphaType::kUnpremul_SkAlphaType, SkColorSpace::MakeSRGB()); + + IntRect image_data_rect(0, 0, imageWidth, imageHeight); + CanvasColorParams color_params(CanvasColorSpace::kSRGB, + CanvasPixelFormat::kRGBA8, + OpacityMode::kNonOpaque); + VerifyCopyBytesForWebGPU(imageWidth, imageHeight, info, color_params, + image_data_rect); +} + +// Copy sub image bitmap test +TEST_F(WebGPUImageBitmapHandlerTest, VerifyCopyBytesFromSubImageBitmap) { + uint64_t imageWidth = 63; + uint64_t imageHeight = 4; + SkImageInfo info = SkImageInfo::Make( + imageWidth, imageHeight, SkColorType::kRGBA_8888_SkColorType, + SkAlphaType::kUnpremul_SkAlphaType, SkColorSpace::MakeSRGB()); + + IntRect image_data_rect(2, 2, 60, 2); + CanvasColorParams color_params(CanvasColorSpace::kSRGB, + CanvasPixelFormat::kRGBA8, + OpacityMode::kNonOpaque); + VerifyCopyBytesForWebGPU(imageWidth, imageHeight, info, color_params, + image_data_rect); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc index d2fd96c760f..98dc0c889fc 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc @@ -29,10 +29,12 @@ viz::ResourceFormat WGPUFormatToViz(WGPUTextureFormat format) { WebGPUSwapBufferProvider::WebGPUSwapBufferProvider( Client* client, scoped_refptr<DawnControlClientHolder> dawn_control_client, + uint64_t device_client_id, WGPUTextureUsage usage, WGPUTextureFormat format) : dawn_control_client_(dawn_control_client), client_(client), + device_client_id_(device_client_id), usage_(usage), format_(WGPUFormatToViz(format)) { // Create a layer that will be used by the canvas and will ask for a @@ -48,8 +50,6 @@ WebGPUSwapBufferProvider::WebGPUSwapBufferProvider( // paths to keep the rendering correct in that cases. layer_->SetContentsOpaque(true); layer_->SetPremultipliedAlpha(true); - - GraphicsLayer::RegisterContentsLayer(layer_.get()); } WebGPUSwapBufferProvider::~WebGPUSwapBufferProvider() { @@ -67,7 +67,6 @@ void WebGPUSwapBufferProvider::Neuter() { } if (layer_) { - GraphicsLayer::UnregisterContentsLayer(layer_.get()); layer_->ClearClient(); layer_ = nullptr; } @@ -85,8 +84,7 @@ void WebGPUSwapBufferProvider::Neuter() { neutered_ = true; } -WGPUTexture WebGPUSwapBufferProvider::GetNewTexture(WGPUDevice device, - const IntSize& size) { +WGPUTexture WebGPUSwapBufferProvider::GetNewTexture(const IntSize& size) { DCHECK(!current_swap_buffer_ && !dawn_control_client_->IsDestroyed()); gpu::webgpu::WebGPUInterface* webgpu = dawn_control_client_->GetInterface(); @@ -97,28 +95,27 @@ WGPUTexture WebGPUSwapBufferProvider::GetNewTexture(WGPUDevice device, // TODO(cwallez@chromium.org): have some recycling mechanism. gpu::Mailbox mailbox = sii->CreateSharedImage( format_, static_cast<gfx::Size>(size), gfx::ColorSpace::CreateSRGB(), - gpu::SHARED_IMAGE_USAGE_WEBGPU | gpu::SHARED_IMAGE_USAGE_DISPLAY); + gpu::SHARED_IMAGE_USAGE_WEBGPU | + gpu::SHARED_IMAGE_USAGE_WEBGPU_SWAP_CHAIN_TEXTURE | + gpu::SHARED_IMAGE_USAGE_DISPLAY); gpu::SyncToken creation_token = sii->GenUnverifiedSyncToken(); current_swap_buffer_ = base::AdoptRef(new SwapBuffer( this, mailbox, creation_token, static_cast<gfx::Size>(size))); - // Make sure previous Dawn wire commands are sent so that for example the ID - // is freed before we associate the SharedImage. - webgpu->FlushCommands(); - // Ensure the shared image is allocated service-side before working with it webgpu->WaitSyncTokenCHROMIUM( current_swap_buffer_->access_finished_token.GetConstData()); // Associate the mailbox to a dawn_wire client DawnTexture object - gpu::webgpu::ReservedTexture reservation = webgpu->ReserveTexture(device); + gpu::webgpu::ReservedTexture reservation = + webgpu->ReserveTexture(device_client_id_); DCHECK(reservation.texture); wire_texture_id_ = reservation.id; wire_texture_generation_ = reservation.generation; webgpu->AssociateMailbox( - 0, 0, reservation.id, reservation.generation, usage_, + device_client_id_, 0, reservation.id, reservation.generation, usage_, reinterpret_cast<GLbyte*>(¤t_swap_buffer_->mailbox)); // When the page request a texture it means we'll need to present it on the @@ -146,7 +143,8 @@ bool WebGPUSwapBufferProvider::PrepareTransferableResource( // to the texture are errors. gpu::webgpu::WebGPUInterface* webgpu = dawn_control_client_->GetInterface(); DCHECK_NE(wire_texture_id_, 0u); - webgpu->DissociateMailbox(wire_texture_id_, wire_texture_generation_); + webgpu->DissociateMailbox(device_client_id_, wire_texture_id_, + wire_texture_generation_); // Make the compositor wait on previous Dawn commands. webgpu->GenUnverifiedSyncTokenCHROMIUM( diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h index 601e49b0f6c..e1a6ae443cf 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h @@ -35,13 +35,14 @@ class PLATFORM_EXPORT WebGPUSwapBufferProvider WebGPUSwapBufferProvider( Client* client, scoped_refptr<DawnControlClientHolder> dawn_control_client, + uint64_t device_client_id, WGPUTextureUsage usage, WGPUTextureFormat format); ~WebGPUSwapBufferProvider() override; cc::Layer* CcLayer(); void Neuter(); - WGPUTexture GetNewTexture(WGPUDevice device, const IntSize& size); + WGPUTexture GetNewTexture(const IntSize& size); // cc::TextureLayerClient implementation. bool PrepareTransferableResource( @@ -81,6 +82,7 @@ class PLATFORM_EXPORT WebGPUSwapBufferProvider scoped_refptr<DawnControlClientHolder> dawn_control_client_; Client* client_; + uint64_t device_client_id_; scoped_refptr<cc::TextureLayer> layer_; bool neutered_ = false; diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc index 6ff93d7e300..19100740ef6 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc @@ -19,7 +19,8 @@ namespace { class MockWebGPUInterface : public gpu::webgpu::WebGPUInterfaceStub { public: - MOCK_METHOD1(ReserveTexture, gpu::webgpu::ReservedTexture(WGPUDevice device)); + MOCK_METHOD1(ReserveTexture, + gpu::webgpu::ReservedTexture(uint64_t device_client_id)); // It is hard to use GMock with SyncTokens represented as GLByte*, instead we // remember which were the last sync tokens generated or waited upon. @@ -57,10 +58,15 @@ class WebGPUSwapBufferProviderForTests : public WebGPUSwapBufferProvider { WebGPUSwapBufferProviderForTests( bool* alive, Client* client, + uint64_t client_device_id_, scoped_refptr<DawnControlClientHolder> dawn_control_client, WGPUTextureUsage usage, WGPUTextureFormat format) - : WebGPUSwapBufferProvider(client, dawn_control_client, usage, format), + : WebGPUSwapBufferProvider(client, + dawn_control_client, + client_device_id_, + usage, + format), alive_(alive) {} ~WebGPUSwapBufferProviderForTests() override { *alive_ = false; } @@ -82,8 +88,10 @@ class WebGPUSwapBufferProviderTest : public testing::Test { dawn_control_client_ = base::MakeRefCounted<DawnControlClientHolder>(std::move(provider)); + + static const uint64_t kDeviceClientID = 1; provider_ = base::MakeRefCounted<WebGPUSwapBufferProviderForTests>( - &provider_alive_, &client_, dawn_control_client_, + &provider_alive_, &client_, kDeviceClientID, dawn_control_client_, WGPUTextureUsage_OutputAttachment, WGPUTextureFormat_RGBA8Unorm); } @@ -116,17 +124,17 @@ TEST_F(WebGPUSwapBufferProviderTest, // Produce resources. EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation1)); - provider_->GetNewTexture(nullptr, kSize); + provider_->GetNewTexture(kSize); EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource1, &release_callback1)); EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation2)); - provider_->GetNewTexture(nullptr, kSize); + provider_->GetNewTexture(kSize); EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource2, &release_callback2)); EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation3)); - provider_->GetNewTexture(nullptr, kSize); + provider_->GetNewTexture(kSize); EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource3, &release_callback3)); @@ -154,7 +162,7 @@ TEST_F(WebGPUSwapBufferProviderTest, VerifyResizingProperlyAffectsResources) { // Produce one resource of size kSize. EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation)); - provider_->GetNewTexture(nullptr, static_cast<IntSize>(kSize)); + provider_->GetNewTexture(static_cast<IntSize>(kSize)); EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource, &release_callback)); EXPECT_EQ(static_cast<gfx::Size>(kSize), sii_->MostRecentSize()); @@ -162,7 +170,7 @@ TEST_F(WebGPUSwapBufferProviderTest, VerifyResizingProperlyAffectsResources) { // Produce one resource of size kOtherSize. EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation)); - provider_->GetNewTexture(nullptr, static_cast<IntSize>(kOtherSize)); + provider_->GetNewTexture(static_cast<IntSize>(kOtherSize)); EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource, &release_callback)); EXPECT_EQ(static_cast<gfx::Size>(kOtherSize), sii_->MostRecentSize()); @@ -170,7 +178,7 @@ TEST_F(WebGPUSwapBufferProviderTest, VerifyResizingProperlyAffectsResources) { // Produce one resource of size kSize again. EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation)); - provider_->GetNewTexture(nullptr, static_cast<IntSize>(kSize)); + provider_->GetNewTexture(static_cast<IntSize>(kSize)); EXPECT_TRUE(provider_->PrepareTransferableResource(nullptr, &resource, &release_callback)); EXPECT_EQ(static_cast<gfx::Size>(kSize), sii_->MostRecentSize()); @@ -188,7 +196,7 @@ TEST_F(WebGPUSwapBufferProviderTest, VerifyInsertAndWaitSyncTokenCorrectly) { // Produce the first resource, check that WebGPU will wait for the creation of // the shared image EXPECT_CALL(*webgpu_, ReserveTexture(_)).WillOnce(Return(reservation)); - provider_->GetNewTexture(nullptr, static_cast<IntSize>(kSize)); + provider_->GetNewTexture(static_cast<IntSize>(kSize)); EXPECT_EQ(sii_->MostRecentGeneratedToken(), webgpu_->most_recent_waited_token); diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc index 63b92488b08..e456c90a383 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc @@ -18,9 +18,7 @@ namespace blink { XRFrameTransport::XRFrameTransport() : submit_frame_client_receiver_(this) {} -XRFrameTransport::~XRFrameTransport() { - CallPreviousFrameCallback(); -} +XRFrameTransport::~XRFrameTransport() = default; void XRFrameTransport::PresentChange() { frame_copier_ = nullptr; @@ -79,13 +77,6 @@ void XRFrameTransport::FramePreImage(gpu::gles2::GLES2Interface* gl) { } } -void XRFrameTransport::CallPreviousFrameCallback() { - if (previous_image_release_callback_) { - previous_image_release_callback_->Run(gpu::SyncToken(), false); - previous_image_release_callback_ = nullptr; - } -} - void XRFrameTransport::FrameSubmitMissing( device::mojom::blink::XRPresentationProvider* vr_presentation_provider, gpu::gles2::GLES2Interface* gl, @@ -101,7 +92,6 @@ void XRFrameTransport::FrameSubmit( gpu::gles2::GLES2Interface* gl, DrawingBuffer::Client* drawing_buffer_client, scoped_refptr<Image> image_ref, - std::unique_ptr<viz::SingleReleaseCallback> release_callback, int16_t vr_frame_id) { DCHECK(transport_options_); @@ -114,8 +104,6 @@ void XRFrameTransport::FrameSubmit( // without waiting. if (transport_options_->wait_for_transfer_notification) WaitForPreviousTransfer(); - CallPreviousFrameCallback(); - previous_image_release_callback_ = std::move(release_callback); if (!frame_copier_ || !last_transfer_succeeded_) { frame_copier_ = std::make_unique<GpuMemoryBufferImageCopy>(gl); } @@ -137,12 +125,11 @@ void XRFrameTransport::FrameSubmit( } // We decompose the cloned handle, and use it to create a - // mojo::ScopedHandle which will own cleanup of the handle, and will be + // mojo::PlatformHandle which will own cleanup of the handle, and will be // passed over IPC. gfx::GpuMemoryBufferHandle gpu_handle = gpu_memory_buffer->CloneHandle(); vr_presentation_provider->SubmitFrameWithTextureHandle( - vr_frame_id, - mojo::WrapPlatformFile(gpu_handle.dxgi_handle.GetHandle())); + vr_frame_id, mojo::PlatformHandle(std::move(gpu_handle.dxgi_handle))); #else NOTIMPLEMENTED(); #endif @@ -155,9 +142,7 @@ void XRFrameTransport::FrameSubmit( // image until the mailbox was consumed. StaticBitmapImage* static_image = static_cast<StaticBitmapImage*>(image_ref.get()); - TRACE_EVENT_BEGIN0("gpu", "XRFrameTransport::EnsureMailbox"); - static_image->EnsureMailbox(kVerifiedSyncToken, GL_NEAREST); - TRACE_EVENT_END0("gpu", "XRFrameTransport::EnsureMailbox"); + static_image->EnsureSyncTokenVerified(); // Conditionally wait for the previous render to finish. A late wait here // attempts to overlap work in parallel with the previous frame's @@ -173,19 +158,15 @@ void XRFrameTransport::FrameSubmit( if (transport_options_->wait_for_transfer_notification) WaitForPreviousTransfer(); previous_image_ = std::move(image_ref); - CallPreviousFrameCallback(); - previous_image_release_callback_ = std::move(release_callback); // Create mailbox and sync token for transfer. TRACE_EVENT_BEGIN0("gpu", "XRFrameTransport::GetMailbox"); - auto mailbox = static_image->GetMailbox(); + auto mailbox_holder = static_image->GetMailboxHolder(); TRACE_EVENT_END0("gpu", "XRFrameTransport::GetMailbox"); - auto sync_token = static_image->GetSyncToken(); TRACE_EVENT_BEGIN0("gpu", "XRFrameTransport::SubmitFrame"); - vr_presentation_provider->SubmitFrame( - vr_frame_id, gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_2D), - frame_wait_time_); + vr_presentation_provider->SubmitFrame(vr_frame_id, mailbox_holder, + frame_wait_time_); TRACE_EVENT_END0("gpu", "XRFrameTransport::SubmitFrame"); } else if (transport_options_->transport_method == device::mojom::blink::XRPresentationTransportMethod:: @@ -264,6 +245,6 @@ base::TimeDelta XRFrameTransport::WaitForGpuFenceReceived() { return base::TimeTicks::Now() - start; } -void XRFrameTransport::Trace(blink::Visitor* visitor) {} +void XRFrameTransport::Trace(Visitor* visitor) {} } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h index f0fa0523692..982f5ad3d33 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h @@ -52,20 +52,18 @@ class PLATFORM_EXPORT XRFrameTransport final gpu::gles2::GLES2Interface*, DrawingBuffer::Client*, scoped_refptr<Image> image_ref, - std::unique_ptr<viz::SingleReleaseCallback>, int16_t vr_frame_id); void FrameSubmitMissing(device::mojom::blink::XRPresentationProvider*, gpu::gles2::GLES2Interface*, int16_t vr_frame_id); - virtual void Trace(blink::Visitor*); + virtual void Trace(Visitor*); private: void WaitForPreviousTransfer(); base::TimeDelta WaitForPreviousRenderToFinish(); base::TimeDelta WaitForGpuFenceReceived(); - void CallPreviousFrameCallback(); // XRPresentationClient void OnSubmitFrameTransferred(bool success) override; @@ -78,7 +76,6 @@ class PLATFORM_EXPORT XRFrameTransport final // Used to keep the image alive until the next frame if using // waitForPreviousTransferToFinish. scoped_refptr<Image> previous_image_; - std::unique_ptr<viz::SingleReleaseCallback> previous_image_release_callback_; bool waiting_for_previous_frame_transfer_ = false; bool last_transfer_succeeded_ = false; diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc index f4660896cee..9578314ff56 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc @@ -12,6 +12,7 @@ #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h" #include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h" #include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h" +#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/wtf/functional.h" @@ -25,16 +26,27 @@ namespace blink { // some of the common bits into a base class? XRWebGLDrawingBuffer::ColorBuffer::ColorBuffer( - XRWebGLDrawingBuffer* drawing_buffer, + base::WeakPtr<XRWebGLDrawingBuffer> drawing_buffer, const IntSize& size, const gpu::Mailbox& mailbox, GLuint texture_id) - : drawing_buffer(drawing_buffer), + : owning_thread_ref(base::PlatformThread::CurrentRef()), + drawing_buffer(std::move(drawing_buffer)), size(size), texture_id(texture_id), mailbox(mailbox) {} XRWebGLDrawingBuffer::ColorBuffer::~ColorBuffer() { + if (base::PlatformThread::CurrentRef() != owning_thread_ref || + !drawing_buffer) { + // If the context has been destroyed no cleanup is necessary since all + // resources below are automatically destroyed. Note that if a ColorBuffer + // is being destroyed on a different thread, it implies that the owning + // thread was destroyed which means the associated context was also + // destroyed. + return; + } + gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL(); if (receive_sync_token.HasData()) gl->WaitSyncTokenCHROMIUM(receive_sync_token.GetConstData()); @@ -105,68 +117,6 @@ scoped_refptr<XRWebGLDrawingBuffer> XRWebGLDrawingBuffer::Create( return xr_drawing_buffer; } -void XRWebGLDrawingBuffer::MirrorClient::OnMirrorImageAvailable( - scoped_refptr<StaticBitmapImage> image, - std::unique_ptr<viz::SingleReleaseCallback> callback) { - // Replace the next image if we have one already. - if (next_image_ && next_release_callback_) { - next_release_callback_->Run(gpu::SyncToken(), false); - } - - // Set our new image. - next_image_ = image; - next_release_callback_ = std::move(callback); -} - -void XRWebGLDrawingBuffer::MirrorClient::BeginDestruction() { - // Call all callbacks we have to clean up associated resources. For - // next_release_callback_, we report the previous image as "not lost", meaning - // we can reuse the texture/image. For previous_release_callback_ - // and current_release_callback_, we report the image as lost, because we - // don't know if the consumer is still using them, so they should not be - // reused. - if (previous_release_callback_) { - previous_release_callback_->Run(gpu::SyncToken(), true); - previous_release_callback_ = nullptr; - } - - if (current_release_callback_) { - current_release_callback_->Run(gpu::SyncToken(), true); - current_release_callback_ = nullptr; - } - - if (next_release_callback_) { - next_release_callback_->Run(gpu::SyncToken(), false); - next_release_callback_ = nullptr; - } - - next_image_ = nullptr; -} - -scoped_refptr<StaticBitmapImage> -XRWebGLDrawingBuffer::MirrorClient::GetLastImage() { - if (!next_image_) - return nullptr; - - scoped_refptr<StaticBitmapImage> ret = next_image_; - next_image_ = nullptr; - DCHECK(!previous_release_callback_); - previous_release_callback_ = std::move(current_release_callback_); - DCHECK(!current_release_callback_); - current_release_callback_ = std::move(next_release_callback_); - return ret; -} - -void XRWebGLDrawingBuffer::MirrorClient::CallLastReleaseCallback() { - if (previous_release_callback_) - previous_release_callback_->Run(gpu::SyncToken(), false); - previous_release_callback_ = nullptr; -} - -XRWebGLDrawingBuffer::MirrorClient::~MirrorClient() { - BeginDestruction(); -} - XRWebGLDrawingBuffer::XRWebGLDrawingBuffer(DrawingBuffer* drawing_buffer, GLuint framebuffer, bool discard_framebuffer_supported, @@ -178,11 +128,10 @@ XRWebGLDrawingBuffer::XRWebGLDrawingBuffer(DrawingBuffer* drawing_buffer, discard_framebuffer_supported_(discard_framebuffer_supported), depth_(want_depth_buffer), stencil_(want_stencil_buffer), - alpha_(want_alpha_channel) {} + alpha_(want_alpha_channel), + weak_factory_(this) {} void XRWebGLDrawingBuffer::BeginDestruction() { - mirror_client_ = nullptr; - if (back_color_buffer_) { gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL(); gl->EndSharedImageAccessDirectCHROMIUM(back_color_buffer_->texture_id); @@ -242,18 +191,6 @@ gpu::gles2::GLES2Interface* XRWebGLDrawingBuffer::ContextGL() { return drawing_buffer_->ContextGL(); } -void XRWebGLDrawingBuffer::SetMirrorClient(scoped_refptr<MirrorClient> client) { - mirror_client_ = client; - if (mirror_client_) { - // Immediately send a black 1x1 image to the mirror client to ensure that - // it has content to show. - sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(1, 1); - mirror_client_->OnMirrorImageAvailable( - UnacceleratedStaticBitmapImage::Create(surface->makeImageSnapshot()), - nullptr); - } -} - bool XRWebGLDrawingBuffer::ContextLost() { return drawing_buffer_->destroyed(); } @@ -523,7 +460,8 @@ XRWebGLDrawingBuffer::CreateColorBuffer() { DrawingBuffer::Client* client = drawing_buffer_->client(); client->DrawingBufferClientRestoreTexture2DBinding(); - return base::AdoptRef(new ColorBuffer(this, size_, mailbox, texture_id)); + return base::MakeRefCounted<ColorBuffer>(weak_factory_.GetWeakPtr(), size_, + mailbox, texture_id); } scoped_refptr<XRWebGLDrawingBuffer::ColorBuffer> @@ -626,8 +564,7 @@ void XRWebGLDrawingBuffer::SwapColorBuffers() { } scoped_refptr<StaticBitmapImage> -XRWebGLDrawingBuffer::TransferToStaticBitmapImage( - std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) { +XRWebGLDrawingBuffer::TransferToStaticBitmapImage() { gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL(); scoped_refptr<ColorBuffer> buffer; bool success = false; @@ -661,47 +598,45 @@ XRWebGLDrawingBuffer::TransferToStaticBitmapImage( // This holds a ref on the XRWebGLDrawingBuffer that will keep it alive // until the mailbox is released (and while the callback is running). auto func = - WTF::Bind(mirror_client_ ? &XRWebGLDrawingBuffer::MailboxReleasedToMirror - : &XRWebGLDrawingBuffer::MailboxReleased, - scoped_refptr<XRWebGLDrawingBuffer>(this), buffer); + base::BindOnce(&XRWebGLDrawingBuffer::NotifyMailboxReleased, buffer); std::unique_ptr<viz::SingleReleaseCallback> release_callback = viz::SingleReleaseCallback::Create(std::move(func)); + const SkImageInfo sk_image_info = + SkImageInfo::MakeN32Premul(size_.Width(), size_.Height()); + + return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox( + buffer->mailbox, buffer->produce_sync_token, + /* shared_image_texture_id = */ 0, sk_image_info, GL_TEXTURE_2D, + /* is_origin_top_left = */ false, + drawing_buffer_->ContextProviderWeakPtr(), + base::PlatformThread::CurrentRef(), Thread::Current()->GetTaskRunner(), + std::move(release_callback)); +} - // Make our own textureId that is a reference on the same shared image being - // used as the front buffer. We do not need a - // Begin/EndSharedImageAccessDirectCHROMIUM as the texture id is just for - // lifetime, not actual access. We do not need to wait on the sync token - // since the GL context has already waited on it. Similarly, the release - // callback will run on the same context so we don't need to send a sync token - // for this consume action back to it. - GLuint texture_id = - gl->CreateAndTexStorage2DSharedImageCHROMIUM(buffer->mailbox.name); +// static +void XRWebGLDrawingBuffer::NotifyMailboxReleased( + scoped_refptr<ColorBuffer> color_buffer, + const gpu::SyncToken& sync_token, + bool lost_resource) { + DCHECK(color_buffer->owning_thread_ref == base::PlatformThread::CurrentRef()); - if (out_release_callback) { - *out_release_callback = std::move(release_callback); - } else { - release_callback->Run(gpu::SyncToken(), true /* lost_resource */); + // Update the SyncToken to ensure that we will wait for it even if we + // immediately destroy this buffer. + color_buffer->receive_sync_token = sync_token; + if (color_buffer->drawing_buffer) { + color_buffer->drawing_buffer->MailboxReleased(color_buffer, lost_resource); } - - return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage( - buffer->mailbox, buffer->produce_sync_token, texture_id, - drawing_buffer_->ContextProviderWeakPtr(), size_, false); } void XRWebGLDrawingBuffer::MailboxReleased( scoped_refptr<ColorBuffer> color_buffer, - const gpu::SyncToken& sync_token, bool lost_resource) { // If the mailbox has been returned by the compositor then it is no // longer being presented, and so is no longer the front buffer. if (color_buffer == front_color_buffer_) front_color_buffer_ = nullptr; - // Update the SyncToken to ensure that we will wait for it even if we - // immediately destroy this buffer. - color_buffer->receive_sync_token = sync_token; - if (drawing_buffer_->destroyed() || color_buffer->size != size_ || lost_resource) { return; @@ -714,35 +649,4 @@ void XRWebGLDrawingBuffer::MailboxReleased( recycled_color_buffer_queue_.push_front(color_buffer); } -void XRWebGLDrawingBuffer::MailboxReleasedToMirror( - scoped_refptr<ColorBuffer> color_buffer, - const gpu::SyncToken& sync_token, - bool lost_resource) { - if (!mirror_client_ || lost_resource) { - MailboxReleased(std::move(color_buffer), sync_token, lost_resource); - return; - } - - gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL(); - color_buffer->receive_sync_token = sync_token; - - auto func = - WTF::Bind(&XRWebGLDrawingBuffer::MailboxReleased, - scoped_refptr<XRWebGLDrawingBuffer>(this), color_buffer); - - std::unique_ptr<viz::SingleReleaseCallback> release_callback = - viz::SingleReleaseCallback::Create(std::move(func)); - - GLuint texture_id = - gl->CreateAndTexStorage2DSharedImageCHROMIUM(color_buffer->mailbox.name); - - scoped_refptr<StaticBitmapImage> image = - AcceleratedStaticBitmapImage::CreateFromWebGLContextImage( - color_buffer->mailbox, color_buffer->produce_sync_token, texture_id, - drawing_buffer_->ContextProviderWeakPtr(), color_buffer->size, false); - - mirror_client_->OnMirrorImageAvailable(std::move(image), - std::move(release_callback)); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h index 44649d80dd6..26f28d0cc11 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h @@ -43,28 +43,7 @@ class PLATFORM_EXPORT XRWebGLDrawingBuffer void Resize(const IntSize&); - scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage( - std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback); - - class PLATFORM_EXPORT MirrorClient : public RefCounted<MirrorClient> { - public: - void OnMirrorImageAvailable(scoped_refptr<StaticBitmapImage>, - std::unique_ptr<viz::SingleReleaseCallback>); - - void BeginDestruction(); - scoped_refptr<StaticBitmapImage> GetLastImage(); - void CallLastReleaseCallback(); - - ~MirrorClient(); - - private: - scoped_refptr<StaticBitmapImage> next_image_; - std::unique_ptr<viz::SingleReleaseCallback> next_release_callback_; - std::unique_ptr<viz::SingleReleaseCallback> current_release_callback_; - std::unique_ptr<viz::SingleReleaseCallback> previous_release_callback_; - }; - - void SetMirrorClient(scoped_refptr<MirrorClient> mirror_client); + scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage(); void UseSharedBuffer(const gpu::MailboxHolder&); void DoneWithSharedBuffer(); @@ -75,17 +54,22 @@ class PLATFORM_EXPORT XRWebGLDrawingBuffer void BeginDestruction(); private: - struct PLATFORM_EXPORT ColorBuffer : public RefCounted<ColorBuffer> { - ColorBuffer(XRWebGLDrawingBuffer*, + struct PLATFORM_EXPORT ColorBuffer + : public base::RefCountedThreadSafe<ColorBuffer> { + ColorBuffer(base::WeakPtr<XRWebGLDrawingBuffer>, const IntSize&, const gpu::Mailbox& mailbox, GLuint texture_id); ~ColorBuffer(); + // The thread on which the ColorBuffer is created and the DrawingBuffer is + // bound to. + const base::PlatformThreadRef owning_thread_ref; + // The owning XRWebGLDrawingBuffer. Note that DrawingBuffer is explicitly // destroyed by the BeginDestruction method, which will eventually drain all // of its ColorBuffers. - scoped_refptr<XRWebGLDrawingBuffer> drawing_buffer; + base::WeakPtr<XRWebGLDrawingBuffer> drawing_buffer; const IntSize size; // The id of the texture that imports the shared image into the @@ -126,12 +110,10 @@ class PLATFORM_EXPORT XRWebGLDrawingBuffer void ClearBoundFramebuffer(); - void MailboxReleased(scoped_refptr<ColorBuffer>, - const gpu::SyncToken&, - bool lost_resource); - void MailboxReleasedToMirror(scoped_refptr<ColorBuffer>, - const gpu::SyncToken&, - bool lost_resource); + static void NotifyMailboxReleased(scoped_refptr<ColorBuffer>, + const gpu::SyncToken&, + bool lost_resource); + void MailboxReleased(scoped_refptr<ColorBuffer>, bool lost_resource); // Reference to the DrawingBuffer that owns the GL context for this object. scoped_refptr<DrawingBuffer> drawing_buffer_; @@ -174,7 +156,7 @@ class PLATFORM_EXPORT XRWebGLDrawingBuffer int max_texture_size_ = 0; int sample_count_ = 0; - scoped_refptr<MirrorClient> mirror_client_; + base::WeakPtrFactory<XRWebGLDrawingBuffer> weak_factory_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc b/chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc index 6e0c7398786..00925bfa333 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc @@ -72,21 +72,21 @@ gfx::GpuMemoryBuffer* GpuMemoryBufferImageCopy::CopyImage(Image* image) { // Bind the read framebuffer to our image. StaticBitmapImage* static_image = static_cast<StaticBitmapImage*>(image); - static_image->EnsureMailbox(kOrderingBarrier, GL_NEAREST); - auto mailbox = static_image->GetMailbox(); - auto sync_token = static_image->GetSyncToken(); + auto mailbox_holder = static_image->GetMailboxHolder(); + // Not strictly necessary since we are on the same context, but keeping // for cleanliness and in case we ever move off the same context. - gl_->WaitSyncTokenCHROMIUM(sync_token.GetData()); + gl_->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetData()); GLuint source_texture_id; - if (mailbox.IsSharedImage()) { - source_texture_id = - gl_->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name); + if (mailbox_holder.mailbox.IsSharedImage()) { + source_texture_id = gl_->CreateAndTexStorage2DSharedImageCHROMIUM( + mailbox_holder.mailbox.name); gl_->BeginSharedImageAccessDirectCHROMIUM( source_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); } else { - source_texture_id = gl_->CreateAndConsumeTextureCHROMIUM(mailbox.name); + source_texture_id = + gl_->CreateAndConsumeTextureCHROMIUM(mailbox_holder.mailbox.name); } gl_->BindTexture(GL_TEXTURE_2D, 0); @@ -97,10 +97,14 @@ gfx::GpuMemoryBuffer* GpuMemoryBufferImageCopy::CopyImage(Image* image) { // Cleanup the read framebuffer, associated image and texture. gl_->BindTexture(GL_TEXTURE_2D, 0); - if (mailbox.IsSharedImage()) + if (mailbox_holder.mailbox.IsSharedImage()) gl_->EndSharedImageAccessDirectCHROMIUM(source_texture_id); gl_->DeleteTextures(1, &source_texture_id); + gpu::SyncToken copy_done_sync_token; + gl_->GenSyncTokenCHROMIUM(copy_done_sync_token.GetData()); + static_image->UpdateSyncToken(copy_done_sync_token); + // Cleanup the draw framebuffer, associated image and texture. gl_->BindTexture(target, dest_texture_id); gl_->ReleaseTexImage2DCHROMIUM(target, image_id); diff --git a/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc b/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc index f9fd7b25ee8..dbd7b216fc2 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.cc @@ -54,7 +54,8 @@ void GradientGeneratedImage::Draw(cc::PaintCanvas* canvas, } void GradientGeneratedImage::DrawTile(GraphicsContext& context, - const FloatRect& src_rect) { + const FloatRect& src_rect, + RespectImageOrientationEnum) { // TODO(ccameron): This function should not ignore |context|'s color behavior. // https://crbug.com/672306 PaintFlags gradient_flags(context.FillFlags()); diff --git a/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.h b/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.h index 66641fb359c..072a19c233b 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.h +++ b/chromium/third_party/blink/renderer/platform/graphics/gradient_generated_image.h @@ -59,7 +59,9 @@ class PLATFORM_EXPORT GradientGeneratedImage final : public GeneratedImage { RespectImageOrientationEnum, ImageClampingMode, ImageDecodingMode) override; - void DrawTile(GraphicsContext&, const FloatRect&) override; + void DrawTile(GraphicsContext&, + const FloatRect&, + RespectImageOrientationEnum) override; GradientGeneratedImage(scoped_refptr<Gradient> generator, const FloatSize& size) diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc b/chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc index 3bf0b074a5b..8473a8d0f75 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_context.cc @@ -58,6 +58,7 @@ #include "third_party/skia/include/effects/SkTableColorFilter.h" #include "third_party/skia/include/pathops/SkPathOps.h" #include "third_party/skia/include/utils/SkNullCanvas.h" +#include "ui/base/ui_base_features.h" namespace blink { @@ -89,7 +90,6 @@ class GraphicsContext::DarkModeFlags final { }; GraphicsContext::GraphicsContext(PaintController& paint_controller, - DisabledMode disable_context_or_painting, printing::MetafileSkia* metafile, paint_preview::PaintPreviewTracker* tracker) : canvas_(nullptr), @@ -102,7 +102,6 @@ GraphicsContext::GraphicsContext(PaintController& paint_controller, layer_count_(0), disable_destruction_checks_(false), #endif - disabled_state_(disable_context_or_painting), device_scale_factor_(1.0f), printing_(false), is_painting_preview_(false), @@ -111,13 +110,6 @@ GraphicsContext::GraphicsContext(PaintController& paint_controller, // allocate several here. paint_state_stack_.push_back(std::make_unique<GraphicsContextState>()); paint_state_ = paint_state_stack_.back().get(); - - if (ContextDisabled()) { - DEFINE_STATIC_LOCAL(SkCanvas*, null_sk_canvas, - (SkMakeNullCanvas().release())); - DEFINE_STATIC_LOCAL(SkiaPaintCanvas, null_canvas, (null_sk_canvas)); - canvas_ = &null_canvas; - } } GraphicsContext::~GraphicsContext() { @@ -132,9 +124,6 @@ GraphicsContext::~GraphicsContext() { } void GraphicsContext::Save() { - if (ContextDisabled()) - return; - paint_state_->IncrementSaveCount(); DCHECK(canvas_); @@ -142,9 +131,6 @@ void GraphicsContext::Save() { } void GraphicsContext::Restore() { - if (ContextDisabled()) - return; - if (!paint_state_index_ && !paint_state_->SaveCount()) { DLOG(ERROR) << "ERROR void GraphicsContext::restore() stack is empty"; return; @@ -179,19 +165,12 @@ void GraphicsContext::SetDarkMode(const DarkModeSettings& settings) { } void GraphicsContext::SaveLayer(const SkRect* bounds, const PaintFlags* flags) { - if (ContextDisabled()) - return; - DCHECK(canvas_); canvas_->saveLayer(bounds, flags); } void GraphicsContext::RestoreLayer() { - if (ContextDisabled()) - return; - DCHECK(canvas_); - canvas_->restore(); } @@ -201,6 +180,17 @@ void GraphicsContext::SetInDrawingRecorder(bool val) { in_drawing_recorder_ = val; } +void GraphicsContext::SetDOMNodeId(DOMNodeId new_node_id) { + if (canvas_) + canvas_->setNodeId(new_node_id); + + dom_node_id_ = new_node_id; +} + +DOMNodeId GraphicsContext::GetDOMNodeId() const { + return dom_node_id_; +} + void GraphicsContext::SetShadow( const FloatSize& offset, float blur, @@ -208,9 +198,6 @@ void GraphicsContext::SetShadow( DrawLooperBuilder::ShadowTransformMode shadow_transform_mode, DrawLooperBuilder::ShadowAlphaMode shadow_alpha_mode, ShadowMode shadow_mode) { - if (ContextDisabled()) - return; - DrawLooperBuilder draw_looper_builder; if (!color.Alpha()) { // When shadow-only but there is no shadow, we use an empty draw looper @@ -231,9 +218,6 @@ void GraphicsContext::SetShadow( } void GraphicsContext::SetDrawLooper(sk_sp<SkDrawLooper> draw_looper) { - if (ContextDisabled()) - return; - MutableState()->SetDrawLooper(std::move(draw_looper)); } @@ -253,11 +237,7 @@ void GraphicsContext::SetColorFilter(ColorFilter color_filter) { } void GraphicsContext::Concat(const SkMatrix& matrix) { - if (ContextDisabled()) - return; - DCHECK(canvas_); - canvas_->concat(matrix); } @@ -266,9 +246,6 @@ void GraphicsContext::BeginLayer(float opacity, const FloatRect* bounds, ColorFilter color_filter, sk_sp<PaintFilter> image_filter) { - if (ContextDisabled()) - return; - PaintFlags layer_flags; layer_flags.setAlpha(static_cast<unsigned char>(opacity * 255)); layer_flags.setBlendMode(xfermode); @@ -288,9 +265,6 @@ void GraphicsContext::BeginLayer(float opacity, } void GraphicsContext::EndLayer() { - if (ContextDisabled()) - return; - RestoreLayer(); #if DCHECK_IS_ON() @@ -299,9 +273,6 @@ void GraphicsContext::EndLayer() { } void GraphicsContext::BeginRecording(const FloatRect& bounds) { - if (ContextDisabled()) - return; - DCHECK(!canvas_); canvas_ = paint_recorder_.beginRecording(bounds); if (metafile_) @@ -310,25 +281,7 @@ void GraphicsContext::BeginRecording(const FloatRect& bounds) { canvas_->SetPaintPreviewTracker(tracker_); } -namespace { - -sk_sp<PaintRecord> CreateEmptyPaintRecord() { - PaintRecorder recorder; - recorder.beginRecording(SkRect::MakeEmpty()); - return recorder.finishRecordingAsPicture(); -} - -} // anonymous namespace - sk_sp<PaintRecord> GraphicsContext::EndRecording() { - if (ContextDisabled()) { - // Clients expect endRecording() to always return a non-null paint record. - // Cache an empty one to minimize overhead when disabled. - DEFINE_STATIC_LOCAL(const sk_sp<PaintRecord>, empty_paint_record, - (CreateEmptyPaintRecord())); - return empty_paint_record; - } - sk_sp<PaintRecord> record = paint_recorder_.finishRecordingAsPicture(); canvas_ = nullptr; DCHECK(record); @@ -336,7 +289,7 @@ sk_sp<PaintRecord> GraphicsContext::EndRecording() { } void GraphicsContext::DrawRecord(sk_sp<const PaintRecord> record) { - if (ContextDisabled() || !record || !record->size()) + if (!record || !record->size()) return; DCHECK(canvas_); @@ -347,7 +300,7 @@ void GraphicsContext::CompositeRecord(sk_sp<PaintRecord> record, const FloatRect& dest, const FloatRect& src, SkBlendMode op) { - if (ContextDisabled() || !record) + if (!record) return; DCHECK(canvas_); @@ -369,7 +322,16 @@ void GraphicsContext::CompositeRecord(sk_sp<PaintRecord> record, namespace { -int AdjustedFocusRingOffset(int offset, int width, bool is_outset) { +int AdjustedFocusRingOffset(int offset, + int default_offset, + int width, + bool is_outset) { + if (::features::IsFormControlsRefreshEnabled()) { + // For FormControlsRefresh the focus ring has a default offset that + // depends on the element type. + return default_offset; + } + #if defined(OS_MACOSX) return offset + 2; #else @@ -382,34 +344,45 @@ int AdjustedFocusRingOffset(int offset, int width, bool is_outset) { } // namespace int GraphicsContext::FocusRingOutsetExtent(int offset, + int default_offset, int width, bool is_outset) { // Unlike normal outlines (whole width is outside of the offset), focus // rings can be drawn with the center of the path aligned with the offset, so // only half of the width is outside of the offset. - return AdjustedFocusRingOffset(offset, width, is_outset) + (width + 1) / 2; + if (::features::IsFormControlsRefreshEnabled()) { + // For FormControlsRefresh 2/3 of the width is outside of the offset. + return AdjustedFocusRingOffset(offset, default_offset, width, is_outset) + + std::ceil(width / 3.f) * 2; + } + + return AdjustedFocusRingOffset(offset, /*default_offset=*/0, width, + is_outset) + + (width + 1) / 2; } void GraphicsContext::DrawFocusRingPath(const SkPath& path, const Color& color, - float width) { + float width, + float border_radius) { DrawPlatformFocusRing( path, canvas_, dark_mode_filter_ .InvertColorIfNeeded(color, DarkModeFilter::ElementRole::kBackground) .Rgb(), - width); + width, border_radius); } void GraphicsContext::DrawFocusRingRect(const SkRect& rect, const Color& color, - float width) { + float width, + float border_radius) { DrawPlatformFocusRing( rect, canvas_, dark_mode_filter_ .InvertColorIfNeeded(color, DarkModeFilter::ElementRole::kBackground) .Rgb(), - width); + width, border_radius); } void GraphicsContext::DrawFocusRing(const Path& focus_ring_path, @@ -417,26 +390,27 @@ void GraphicsContext::DrawFocusRing(const Path& focus_ring_path, int offset, const Color& color) { // FIXME: Implement support for offset. - if (ContextDisabled()) - return; - - DrawFocusRingPath(focus_ring_path.GetSkPath(), color, width); + DrawFocusRingPath(focus_ring_path.GetSkPath(), color, /*width=*/width, + /*radius=*/width); } void GraphicsContext::DrawFocusRingInternal(const Vector<IntRect>& rects, float width, int offset, + float border_radius, const Color& color, bool is_outset) { - if (ContextDisabled()) - return; - unsigned rect_count = rects.size(); if (!rect_count) return; SkRegion focus_ring_region; - offset = AdjustedFocusRingOffset(offset, std::ceil(width), is_outset); + if (!::features::IsFormControlsRefreshEnabled()) { + // For FormControlsRefresh the offset is already adjusted by + // GraphicsContext::DrawFocusRing. + offset = AdjustedFocusRingOffset(offset, /*default_offset=*/0, + std::ceil(width), is_outset); + } for (unsigned i = 0; i < rect_count; i++) { SkIRect r = rects[i]; if (r.isEmpty()) @@ -449,12 +423,12 @@ void GraphicsContext::DrawFocusRingInternal(const Vector<IntRect>& rects, return; if (focus_ring_region.isRect()) { - DrawFocusRingRect(SkRect::Make(focus_ring_region.getBounds()), color, - width); + DrawFocusRingRect(SkRect::Make(focus_ring_region.getBounds()), color, width, + border_radius); } else { SkPath path; if (focus_ring_region.getBoundaryPath(&path)) - DrawFocusRingPath(path, color, width); + DrawFocusRingPath(path, color, width, border_radius); } } @@ -478,22 +452,48 @@ bool ShouldDrawInnerFocusRingForContrast(bool is_outset, void GraphicsContext::DrawFocusRing(const Vector<IntRect>& rects, float width, int offset, + int default_offset, + float border_radius, + float min_border_width, const Color& color, bool is_outset) { - // If a focus ring is outset and the color is dark, it may be hard to see on - // dark backgrounds. In this case, we'll actually draw two focus rings, the - // outset focus ring with a white inner ring for contrast. - if (ShouldDrawInnerFocusRingForContrast(is_outset, width, color)) { - int contrast_offset = static_cast<int>(std::floor(width * 0.5)); - // We create a 1px gap for the contrast ring. The contrast ring is drawn - // first, and we overdraw by a pixel to ensure no gaps or AA artifacts. - DrawFocusRingInternal(rects, contrast_offset, offset, SK_ColorWHITE, - is_outset); - DrawFocusRingInternal(rects, width - contrast_offset, - offset + contrast_offset, color, is_outset); - + if (::features::IsFormControlsRefreshEnabled()) { + // The focus ring is made of two borders which have a 2:1 ratio. + const float first_border_width = (width / 3) * 2; + const float second_border_width = width - first_border_width; + + offset = AdjustedFocusRingOffset(offset, default_offset, std::ceil(width), + is_outset); + // How much space the focus ring would like to take from the actual border. + const float inside_border_width = 1; + if (min_border_width >= inside_border_width) { + offset -= inside_border_width; + } + // The white ring is drawn first, and we overdraw to ensure no gaps or AA + // artifacts. + DrawFocusRingInternal(rects, first_border_width, + offset + std::ceil(second_border_width), + border_radius, SK_ColorWHITE, is_outset); + DrawFocusRingInternal(rects, first_border_width, offset, border_radius, + color, is_outset); } else { - DrawFocusRingInternal(rects, width, offset, color, is_outset); + // If a focus ring is outset and the color is dark, it may be hard to see on + // dark backgrounds. In this case, we'll actually draw two focus rings, the + // outset focus ring with a white inner ring for contrast. + if (ShouldDrawInnerFocusRingForContrast(is_outset, width, color)) { + int contrast_offset = static_cast<int>(std::floor(width * 0.5)); + // We create a 1px gap for the contrast ring. The contrast ring is drawn + // first, and we overdraw by a pixel to ensure no gaps or AA artifacts. + DrawFocusRingInternal(rects, contrast_offset, offset, border_radius, + SK_ColorWHITE, is_outset); + DrawFocusRingInternal(rects, width - contrast_offset, + offset + contrast_offset, border_radius, color, + is_outset); + + } else { + DrawFocusRingInternal(rects, width, offset, border_radius, color, + is_outset); + } } } @@ -520,9 +520,6 @@ void GraphicsContext::DrawInnerShadow(const FloatRoundedRect& rect, float shadow_blur, float shadow_spread, Edges clipped_edges) { - if (ContextDisabled()) - return; - Color shadow_color = dark_mode_filter_.InvertColorIfNeeded( orig_shadow_color, DarkModeFilter::ElementRole::kBackground); @@ -679,8 +676,6 @@ static void EnforceDotsAtEndpoints(GraphicsContext& context, void GraphicsContext::DrawLine(const IntPoint& point1, const IntPoint& point2, const DarkModeFilter::ElementRole role) { - if (ContextDisabled()) - return; DCHECK(canvas_); StrokeStyle pen_style = GetStrokeStyle(); @@ -728,9 +723,6 @@ void GraphicsContext::DrawLine(const IntPoint& point1, } void GraphicsContext::DrawLineForText(const FloatPoint& pt, float width) { - if (ContextDisabled()) - return; - if (width <= 0) return; @@ -739,7 +731,7 @@ void GraphicsContext::DrawLineForText(const FloatPoint& pt, float width) { case kNoStroke: case kSolidStroke: case kDoubleStroke: { - int thickness = SkMax32(static_cast<int>(StrokeThickness()), 1); + int thickness = std::max(static_cast<int>(StrokeThickness()), 1); SkRect r; r.fLeft = WebCoreFloatToSkScalar(pt.X()); // Avoid anti-aliasing lines. Currently, these are always horizontal. @@ -770,9 +762,6 @@ void GraphicsContext::DrawLineForText(const FloatPoint& pt, float width) { // Draws a filled rectangle with a stroked border. void GraphicsContext::DrawRect(const IntRect& rect) { - if (ContextDisabled()) - return; - if (rect.IsEmpty()) return; @@ -798,9 +787,6 @@ void GraphicsContext::DrawText(const Font& font, const FloatPoint& point, const PaintFlags& flags, DOMNodeId node_id) { - if (ContextDisabled()) - return; - font.DrawText(canvas_, text_info, point, device_scale_factor_, node_id, DarkModeFlags(this, flags, DarkModeFilter::ElementRole::kText)); } @@ -829,9 +815,6 @@ void GraphicsContext::DrawTextInternal(const Font& font, const TextPaintInfo& text_info, const FloatPoint& point, DOMNodeId node_id) { - if (ContextDisabled()) - return; - DrawTextPasses([&](const PaintFlags& flags) { font.DrawText( canvas_, text_info, point, device_scale_factor_, node_id, @@ -858,9 +841,6 @@ void GraphicsContext::DrawEmphasisMarksInternal(const Font& font, const TextPaintInfo& text_info, const AtomicString& mark, const FloatPoint& point) { - if (ContextDisabled()) - return; - DrawTextPasses( [&font, &text_info, &mark, &point, this](const PaintFlags& flags) { font.DrawEmphasisMarks( @@ -889,9 +869,6 @@ void GraphicsContext::DrawBidiText( const TextRunPaintInfo& run_info, const FloatPoint& point, Font::CustomFontNotReadyAction custom_font_not_ready_action) { - if (ContextDisabled()) - return; - DrawTextPasses([&font, &run_info, &point, custom_font_not_ready_action, this](const PaintFlags& flags) { if (font.DrawBidiText( @@ -910,9 +887,6 @@ void GraphicsContext::DrawHighlightForText(const Font& font, const Color& background_color, int from, int to) { - if (ContextDisabled()) - return; - FillRect(font.SelectionRectForText(run, point, h, from, to), background_color); } @@ -925,7 +899,7 @@ void GraphicsContext::DrawImage( bool has_filter_property, SkBlendMode op, RespectImageOrientationEnum should_respect_image_orientation) { - if (ContextDisabled() || !image) + if (!image) return; const FloatRect src = src_ptr ? *src_ptr : FloatRect(image->Rect()); @@ -952,7 +926,7 @@ void GraphicsContext::DrawImageRRect( bool has_filter_property, SkBlendMode op, RespectImageOrientationEnum respect_orientation) { - if (ContextDisabled() || !image) + if (!image) return; if (!dest.IsRounded()) { @@ -978,7 +952,8 @@ void GraphicsContext::DrawImageRRect( &image_flags); bool use_shader = (visible_src == src_rect) && - (respect_orientation == kDoNotRespectImageOrientation); + (respect_orientation == kDoNotRespectImageOrientation || + image->HasDefaultOrientation()); if (use_shader) { const SkMatrix local_matrix = SkMatrix::MakeRectToRect( visible_src, dest.Rect(), SkMatrix::kFill_ScaleToFit); @@ -1026,62 +1001,52 @@ SkFilterQuality GraphicsContext::ComputeFilterQuality( std::min(resampling, ImageInterpolationQuality())); } -void GraphicsContext::DrawImageTiled(Image* image, - const FloatRect& dest_rect, - const FloatRect& src_rect, - const FloatSize& scale_src_to_dest, - const FloatPoint& phase, - const FloatSize& repeat_spacing, - SkBlendMode op) { - if (ContextDisabled() || !image) +void GraphicsContext::DrawImageTiled( + Image* image, + const FloatRect& dest_rect, + const FloatRect& src_rect, + const FloatSize& scale_src_to_dest, + const FloatPoint& phase, + const FloatSize& repeat_spacing, + SkBlendMode op, + RespectImageOrientationEnum respect_orientation) { + if (!image) return; image->DrawPattern(*this, src_rect, scale_src_to_dest, phase, op, dest_rect, - repeat_spacing); + repeat_spacing, respect_orientation); paint_controller_.SetImagePainted(); } void GraphicsContext::DrawOval(const SkRect& oval, const PaintFlags& flags, const DarkModeFilter::ElementRole role) { - if (ContextDisabled()) - return; DCHECK(canvas_); - canvas_->drawOval(oval, DarkModeFlags(this, flags, role)); } void GraphicsContext::DrawPath(const SkPath& path, const PaintFlags& flags, const DarkModeFilter::ElementRole role) { - if (ContextDisabled()) - return; DCHECK(canvas_); - canvas_->drawPath(path, DarkModeFlags(this, flags, role)); } void GraphicsContext::DrawRect(const SkRect& rect, const PaintFlags& flags, const DarkModeFilter::ElementRole role) { - if (ContextDisabled()) - return; DCHECK(canvas_); - canvas_->drawRect(rect, DarkModeFlags(this, flags, role)); } void GraphicsContext::DrawRRect(const SkRRect& rrect, const PaintFlags& flags) { - if (ContextDisabled()) - return; DCHECK(canvas_); - canvas_->drawRRect( rrect, DarkModeFlags(this, flags, DarkModeFilter::ElementRole::kBackground)); } void GraphicsContext::FillPath(const Path& path_to_fill) { - if (ContextDisabled() || path_to_fill.IsEmpty()) + if (path_to_fill.IsEmpty()) return; DrawPath(path_to_fill.GetSkPath(), ImmutableState()->FillFlags()); @@ -1098,9 +1063,6 @@ void GraphicsContext::FillRect(const IntRect& rect, } void GraphicsContext::FillRect(const FloatRect& rect) { - if (ContextDisabled()) - return; - DrawRect(rect, ImmutableState()->FillFlags()); } @@ -1114,9 +1076,6 @@ void GraphicsContext::FillRect(const FloatRect& rect, const Color& color, SkBlendMode xfer_mode, DarkModeFilter::ElementRole role) { - if (ContextDisabled()) - return; - PaintFlags flags = ImmutableState()->FillFlags(); flags.setColor(color.Rgb()); flags.setBlendMode(xfer_mode); @@ -1126,9 +1085,6 @@ void GraphicsContext::FillRect(const FloatRect& rect, void GraphicsContext::FillRoundedRect(const FloatRoundedRect& rrect, const Color& color) { - if (ContextDisabled()) - return; - if (!rrect.IsRounded() || !rrect.IsRenderable()) { FillRect(rrect.Rect(), color); return; @@ -1191,8 +1147,6 @@ bool IsSimpleDRRect(const FloatRoundedRect& outer, void GraphicsContext::FillDRRect(const FloatRoundedRect& outer, const FloatRoundedRect& inner, const Color& color) { - if (ContextDisabled()) - return; DCHECK(canvas_); if (!IsSimpleDRRect(outer, inner)) { @@ -1227,16 +1181,13 @@ void GraphicsContext::FillDRRect(const FloatRoundedRect& outer, } void GraphicsContext::FillEllipse(const FloatRect& ellipse) { - if (ContextDisabled()) - return; - DrawOval(ellipse, ImmutableState()->FillFlags()); } void GraphicsContext::StrokePath(const Path& path_to_stroke, const int length, const int dash_thickness) { - if (ContextDisabled() || path_to_stroke.IsEmpty()) + if (path_to_stroke.IsEmpty()) return; DrawPath(path_to_stroke.GetSkPath(), @@ -1244,9 +1195,6 @@ void GraphicsContext::StrokePath(const Path& path_to_stroke, } void GraphicsContext::StrokeRect(const FloatRect& rect, float line_width) { - if (ContextDisabled()) - return; - PaintFlags flags(ImmutableState()->StrokeFlags()); flags.setStrokeWidth(WebCoreFloatToSkScalar(line_width)); // Reset the dash effect to account for the width @@ -1271,18 +1219,12 @@ void GraphicsContext::StrokeRect(const FloatRect& rect, float line_width) { } void GraphicsContext::StrokeEllipse(const FloatRect& ellipse) { - if (ContextDisabled()) - return; - DrawOval(ellipse, ImmutableState()->StrokeFlags()); } void GraphicsContext::ClipRoundedRect(const FloatRoundedRect& rrect, SkClipOp clip_op, AntiAliasingMode should_antialias) { - if (ContextDisabled()) - return; - if (!rrect.IsRounded()) { ClipRect(rrect.Rect(), should_antialias, clip_op); return; @@ -1292,9 +1234,6 @@ void GraphicsContext::ClipRoundedRect(const FloatRoundedRect& rrect, } void GraphicsContext::ClipOut(const Path& path_to_clip) { - if (ContextDisabled()) - return; - // Use const_cast and temporarily toggle the inverse fill type instead of // copying the path. SkPath& path = const_cast<SkPath&>(path_to_clip.GetSkPath()); @@ -1304,54 +1243,37 @@ void GraphicsContext::ClipOut(const Path& path_to_clip) { } void GraphicsContext::ClipOutRoundedRect(const FloatRoundedRect& rect) { - if (ContextDisabled()) - return; - ClipRoundedRect(rect, SkClipOp::kDifference); } void GraphicsContext::ClipRect(const SkRect& rect, AntiAliasingMode aa, SkClipOp op) { - if (ContextDisabled()) - return; DCHECK(canvas_); - canvas_->clipRect(rect, op, aa == kAntiAliased); } void GraphicsContext::ClipPath(const SkPath& path, AntiAliasingMode aa, SkClipOp op) { - if (ContextDisabled()) - return; DCHECK(canvas_); - canvas_->clipPath(path, op, aa == kAntiAliased); } void GraphicsContext::ClipRRect(const SkRRect& rect, AntiAliasingMode aa, SkClipOp op) { - if (ContextDisabled()) - return; DCHECK(canvas_); - canvas_->clipRRect(rect, op, aa == kAntiAliased); } void GraphicsContext::Rotate(float angle_in_radians) { - if (ContextDisabled()) - return; DCHECK(canvas_); - canvas_->rotate( WebCoreFloatToSkScalar(angle_in_radians * (180.0f / 3.14159265f))); } void GraphicsContext::Translate(float x, float y) { - if (ContextDisabled()) - return; DCHECK(canvas_); if (!x && !y) @@ -1361,18 +1283,13 @@ void GraphicsContext::Translate(float x, float y) { } void GraphicsContext::Scale(float x, float y) { - if (ContextDisabled()) - return; DCHECK(canvas_); - canvas_->scale(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y)); } void GraphicsContext::SetURLForRect(const KURL& link, const IntRect& dest_rect) { - if (ContextDisabled()) - return; - DCHECK(canvas_); + DCHECK(canvas_ || tracker_); // Intercept URL rects when painting previews. if (IsPaintingPreview() && tracker_) { @@ -1387,9 +1304,7 @@ void GraphicsContext::SetURLForRect(const KURL& link, void GraphicsContext::SetURLFragmentForRect(const String& dest_name, const IntRect& rect) { - if (ContextDisabled()) - return; - DCHECK(canvas_); + DCHECK(canvas_ || tracker_); // Intercept URL rects when painting previews. if (IsPaintingPreview() && tracker_) { @@ -1404,8 +1319,6 @@ void GraphicsContext::SetURLFragmentForRect(const String& dest_name, void GraphicsContext::SetURLDestinationLocation(const String& name, const IntPoint& location) { - if (ContextDisabled()) - return; DCHECK(canvas_); SkRect rect = SkRect::MakeXYWH(location.X(), location.Y(), 0, 0); @@ -1422,9 +1335,6 @@ void GraphicsContext::FillRectWithRoundedHole( const FloatRect& rect, const FloatRoundedRect& rounded_hole_rect, const Color& color) { - if (ContextDisabled()) - return; - PaintFlags flags(ImmutableState()->FillFlags()); flags.setColor( dark_mode_filter_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_context.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_context.h index b33ed07b357..6e943b9810e 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/graphics_context.h +++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_context.h @@ -70,14 +70,7 @@ class PLATFORM_EXPORT GraphicsContext { USING_FAST_MALLOC(GraphicsContext); public: - enum DisabledMode { - kNothingDisabled = 0, // Run as normal. - kFullyDisabled = 1 // Do absolutely minimal work to remove the cost of - // the context from performance tests. - }; - explicit GraphicsContext(PaintController&, - DisabledMode = kNothingDisabled, printing::MetafileSkia* = nullptr, paint_preview::PaintPreviewTracker* = nullptr); @@ -91,8 +84,6 @@ class PLATFORM_EXPORT GraphicsContext { return paint_controller_; } - bool ContextDisabled() const { return disabled_state_; } - const DarkModeSettings& dark_mode_settings() const { return dark_mode_filter_.settings(); } @@ -242,22 +233,22 @@ class PLATFORM_EXPORT GraphicsContext { const FloatRect* src_rect = nullptr, bool has_filter_property = false, SkBlendMode = SkBlendMode::kSrcOver, - RespectImageOrientationEnum = kDoNotRespectImageOrientation); - void DrawImageRRect( - Image*, - Image::ImageDecodingMode, - const FloatRoundedRect& dest, - const FloatRect& src_rect, - bool has_filter_property = false, - SkBlendMode = SkBlendMode::kSrcOver, - RespectImageOrientationEnum = kDoNotRespectImageOrientation); + RespectImageOrientationEnum = kRespectImageOrientation); + void DrawImageRRect(Image*, + Image::ImageDecodingMode, + const FloatRoundedRect& dest, + const FloatRect& src_rect, + bool has_filter_property = false, + SkBlendMode = SkBlendMode::kSrcOver, + RespectImageOrientationEnum = kRespectImageOrientation); void DrawImageTiled(Image* image, const FloatRect& dest_rect, const FloatRect& src_rect, const FloatSize& scale_src_to_dest, const FloatPoint& phase, const FloatSize& repeat_spacing, - SkBlendMode = SkBlendMode::kSrcOver); + SkBlendMode = SkBlendMode::kSrcOver, + RespectImageOrientationEnum = kRespectImageOrientation); // These methods write to the canvas. // Also drawLine(const IntPoint& point1, const IntPoint& point2) and @@ -371,6 +362,9 @@ class PLATFORM_EXPORT GraphicsContext { void DrawFocusRing(const Vector<IntRect>&, float width, int offset, + int default_offset, + float border_radius, + float min_border_width, const Color&, bool is_outset); void DrawFocusRing(const Path&, float width, int offset, const Color&); @@ -428,14 +422,25 @@ class PLATFORM_EXPORT GraphicsContext { FloatPoint& p2, float stroke_width); - static int FocusRingOutsetExtent(int offset, int width, bool is_outset); + static int FocusRingOutsetExtent(int offset, + int default_offset, + int width, + bool is_outset); void SetInDrawingRecorder(bool); bool InDrawingRecorder() const { return in_drawing_recorder_; } + // Set the DOM Node Id on the canvas. This is used to associate + // the drawing commands with the structure tree for the page when + // creating a tagged PDF. Callers are responsible for restoring it. + void SetDOMNodeId(DOMNodeId); + DOMNodeId GetDOMNodeId() const; + static sk_sp<SkColorFilter> WebCoreColorFilterToSkiaColorFilter(ColorFilter); private: + friend class ScopedDarkModeElementRoleOverride; + const GraphicsContextState* ImmutableState() const { return paint_state_; } GraphicsContextState* MutableState() { @@ -462,12 +467,19 @@ class PLATFORM_EXPORT GraphicsContext { void RestoreLayer(); // Helpers for drawing a focus ring (drawFocusRing) - void DrawFocusRingPath(const SkPath&, const Color&, float width); - void DrawFocusRingRect(const SkRect&, const Color&, float width); + void DrawFocusRingPath(const SkPath&, + const Color&, + float width, + float border_radius); + void DrawFocusRingRect(const SkRect&, + const Color&, + float width, + float border_radius); void DrawFocusRingInternal(const Vector<IntRect>&, float width, int offset, + float border_radius, const Color&, bool is_outset); @@ -479,9 +491,6 @@ class PLATFORM_EXPORT GraphicsContext { // Apply deferred paint state saves void RealizePaintSave() { - if (ContextDisabled()) - return; - if (paint_state_->SaveCount()) { paint_state_->DecrementSaveCount(); ++paint_state_index_; @@ -503,7 +512,9 @@ class PLATFORM_EXPORT GraphicsContext { class DarkModeFlags; - // null indicates painting is contextDisabled. Never delete this object. + // This is owned by paint_recorder_. Never delete this object. + // Drawing operations are allowed only after the first BeginRecording() which + // initializes this to not null. cc::PaintCanvas* canvas_; PaintController& paint_controller_; @@ -529,8 +540,6 @@ class PLATFORM_EXPORT GraphicsContext { bool disable_destruction_checks_; #endif - const DisabledMode disabled_state_; - float device_scale_factor_; // TODO(gilmanmh): Investigate making this base::Optional<DarkModeFilter> @@ -540,6 +549,9 @@ class PLATFORM_EXPORT GraphicsContext { unsigned is_painting_preview_ : 1; unsigned in_drawing_recorder_ : 1; + // The current node ID, which is used for marked content in a tagged PDF. + DOMNodeId dom_node_id_ = kInvalidDOMNodeId; + DISALLOW_COPY_AND_ASSIGN(GraphicsContext); }; diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc index 7f39c9e8911..8cafc6b08ac 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.cc @@ -37,9 +37,7 @@ #include "cc/layers/picture_layer.h" #include "cc/paint/display_item_list.h" #include "third_party/blink/public/platform/platform.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_point.h" #include "third_party/blink/public/platform/web_size.h" #include "third_party/blink/renderer/platform/geometry/float_rect.h" #include "third_party/blink/renderer/platform/geometry/geometry_as_json.h" @@ -76,12 +74,18 @@ GraphicsLayer::GraphicsLayer(GraphicsLayerClient& client) contents_visible_(true), hit_testable_(false), needs_check_raster_invalidation_(false), + contents_layer_is_picture_image_layer_(false), painted_(false), painting_phase_(kGraphicsLayerPaintAllWithOverflowClip), parent_(nullptr), mask_layer_(nullptr), - contents_layer_(nullptr), - contents_layer_id_(0) { + raster_invalidation_function_( + base::BindRepeating(&GraphicsLayer::SetNeedsDisplayInRect, + base::Unretained(this))) { + // TODO(crbug.com/1033240): Debugging information for the referenced bug. + // Remove when it is fixed. + CHECK(&client_); + #if DCHECK_IS_ON() DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); client.VerifyNotPainting(); @@ -95,7 +99,7 @@ GraphicsLayer::GraphicsLayer(GraphicsLayerClient& client) GraphicsLayer::~GraphicsLayer() { CcLayer()->ClearClient(); - SetContentsLayer(nullptr); + contents_layer_ = nullptr; #if DCHECK_IS_ON() client_.VerifyNotPainting(); @@ -125,10 +129,12 @@ void GraphicsLayer::AppendAdditionalInfoAsJSON(LayerTreeFlags flags, if (&layer != layer_.get()) return; - if ((flags & kLayerTreeIncludesPaintInvalidations) && + if ((flags & (kLayerTreeIncludesInvalidations | + kLayerTreeIncludesDetailedInvalidations)) && Client().IsTrackingRasterInvalidations() && GetRasterInvalidationTracking()) { - GetRasterInvalidationTracking()->AsJSON(&json); + GetRasterInvalidationTracking()->AsJSON( + &json, flags & kLayerTreeIncludesDetailedInvalidations); } GraphicsLayerPaintingPhase painting_phase = PaintingPhase(); @@ -206,11 +212,10 @@ bool GraphicsLayer::SetChildren(const GraphicsLayerVector& new_children) { RemoveAllChildren(); - size_t list_size = new_children.size(); - for (size_t i = 0; i < list_size; ++i) - AddChildInternal(new_children[i]); + for (auto* new_child : new_children) + AddChildInternal(new_child); - UpdateChildList(); + NotifyChildListChange(); return true; } @@ -224,13 +229,13 @@ void GraphicsLayer::AddChildInternal(GraphicsLayer* child_layer) { child_layer->SetParent(this); children_.push_back(child_layer); - // Don't call updateChildList here, this function is used in cases where it - // should not be called until all children are processed. + // Don't call NotifyChildListChange here, this function is used in cases where + // it should not be called until all children are processed. } void GraphicsLayer::AddChild(GraphicsLayer* child_layer) { AddChildInternal(child_layer); - UpdateChildList(); + NotifyChildListChange(); } void GraphicsLayer::RemoveAllChildren() { @@ -258,8 +263,6 @@ void GraphicsLayer::SetOffsetFromLayoutObject(const IntSize& offset) { return; offset_from_layout_object_ = offset; - CcLayer()->SetFiltersOrigin(FloatPoint() - - FloatSize(offset_from_layout_object_)); // If the compositing layer offset changes, we need to repaint. SetNeedsDisplay(); @@ -287,6 +290,9 @@ bool GraphicsLayer::PaintRecursively() { void GraphicsLayer::PaintRecursivelyInternal( Vector<GraphicsLayer*>& repainted_layers) { + // TODO(crbug.com/1033240): Debugging information for the referenced bug. + // Remove when it is fixed. + CHECK(&client_); if (client_.PaintBlockedByDisplayLockIncludingAncestors( DisplayLockContextLifecycleTarget::kSelf)) { return; @@ -304,7 +310,7 @@ void GraphicsLayer::PaintRecursivelyInternal( child->PaintRecursivelyInternal(repainted_layers); } -bool GraphicsLayer::Paint(GraphicsContext::DisabledMode disabled_mode) { +bool GraphicsLayer::Paint() { #if !DCHECK_IS_ON() // TODO(crbug.com/853096): Investigate why we can ever reach here without // a valid layer state. Seems to only happen on Android builds. @@ -312,7 +318,7 @@ bool GraphicsLayer::Paint(GraphicsContext::DisabledMode disabled_mode) { return false; #endif - if (PaintWithoutCommit(disabled_mode)) { + if (PaintWithoutCommit()) { GetPaintController().CommitNewDisplayItems(); UpdateSafeOpaqueBackgroundColor(); } else if (!needs_check_raster_invalidation_) { @@ -330,6 +336,7 @@ bool GraphicsLayer::Paint(GraphicsContext::DisabledMode disabled_mode) { // Generate raster invalidations for SPv1. IntRect layer_bounds(layer_state_->offset, IntSize(Size())); EnsureRasterInvalidator().Generate( + raster_invalidation_function_, GetPaintController().GetPaintArtifactShared(), layer_bounds, layer_state_->state, VisualRectSubpixelOffset(), this); @@ -354,21 +361,17 @@ bool GraphicsLayer::Paint(GraphicsContext::DisabledMode disabled_mode) { void GraphicsLayer::UpdateSafeOpaqueBackgroundColor() { if (!DrawsContent()) return; - // Copy the first chunk's safe opaque background color over to the cc::Layer. - const auto& chunks = GetPaintController().GetPaintArtifact().PaintChunks(); CcLayer()->SetSafeOpaqueBackgroundColor( - chunks.size() ? chunks[0].safe_opaque_background_color : SK_ColorWHITE); + GetPaintController().GetPaintArtifact().SafeOpaqueBackgroundColor( + GetPaintController().GetPaintArtifact().PaintChunks())); } bool GraphicsLayer::PaintWithoutCommitForTesting( const base::Optional<IntRect>& interest_rect) { - return PaintWithoutCommit(GraphicsContext::kNothingDisabled, - base::OptionalOrNullptr(interest_rect)); + return PaintWithoutCommit(base::OptionalOrNullptr(interest_rect)); } -bool GraphicsLayer::PaintWithoutCommit( - GraphicsContext::DisabledMode disabled_mode, - const IntRect* interest_rect) { +bool GraphicsLayer::PaintWithoutCommit(const IntRect* interest_rect) { DCHECK(PaintsContentOrHitTest()); if (client_.ShouldThrottleRendering() || client_.IsUnderSVGHiddenContainer()) @@ -381,7 +384,7 @@ bool GraphicsLayer::PaintWithoutCommit( interest_rect = &new_interest_rect; } - if (!GetPaintController().SubsequenceCachingIsDisabled() && + if (!GetPaintController().ShouldForcePaintForBenchmark() && !client_.NeedsRepaint(*this) && !GetPaintController().CacheIsAllInvalid() && previous_interest_rect_ == *interest_rect) { @@ -389,9 +392,9 @@ bool GraphicsLayer::PaintWithoutCommit( return false; } - GraphicsContext context(GetPaintController(), disabled_mode, nullptr); + GraphicsContext context(GetPaintController()); DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName(); - GetPaintController().UpdateCurrentPaintChunkProperties(base::nullopt, + GetPaintController().UpdateCurrentPaintChunkProperties(nullptr, layer_state_->state); previous_interest_rect_ = *interest_rect; @@ -399,7 +402,7 @@ bool GraphicsLayer::PaintWithoutCommit( return true; } -void GraphicsLayer::UpdateChildList() { +void GraphicsLayer::NotifyChildListChange() { // cc::Layers are created in PaintArtifactCompositor. client_.GraphicsLayersDidChange(); } @@ -412,125 +415,56 @@ void GraphicsLayer::UpdateLayerIsDrawable() { // contentsVisible. CcLayer()->SetIsDrawable(draws_content_ && contents_visible_); - if (cc::Layer* contents_layer = ContentsLayerIfRegistered()) - contents_layer->SetIsDrawable(contents_visible_); + if (contents_layer_) + contents_layer_->SetIsDrawable(contents_visible_); if (draws_content_) CcLayer()->SetNeedsDisplay(); } -void GraphicsLayer::UpdateContentsRect() { - cc::Layer* contents_layer = ContentsLayerIfRegistered(); - if (!contents_layer) +void GraphicsLayer::UpdateContentsLayerBounds() { + if (!contents_layer_) return; - contents_layer->SetPosition( - FloatPoint(contents_rect_.X(), contents_rect_.Y())); - if (!image_layer_) { - contents_layer->SetBounds(static_cast<gfx::Size>(contents_rect_.Size())); - } else { - DCHECK_EQ(image_layer_.get(), contents_layer_); - // The image_layer_ has fixed bounds, and we apply bounds changes via the - // transform instead. Since we never change the transform on the - // |image_layer_| otherwise, we can assume it is identity and just apply - // the bounds to it directly. Same thing for transform origin. - DCHECK(image_layer_->transform_origin() == gfx::Point3F()); - - if (contents_rect_.Size().IsEmpty() || image_size_.IsEmpty()) { - image_layer_->SetTransform(gfx::Transform()); - contents_layer->SetBounds(static_cast<gfx::Size>(contents_rect_.Size())); - } else { - gfx::Transform image_transform; - image_transform.Scale( - static_cast<float>(contents_rect_.Width()) / image_size_.Width(), - static_cast<float>(contents_rect_.Height()) / image_size_.Height()); - image_layer_->SetTransform(image_transform); - image_layer_->SetBounds(static_cast<gfx::Size>(image_size_)); - } + IntSize contents_size = contents_rect_.Size(); + if (contents_layer_is_picture_image_layer_) { + if (!contents_size.IsEmpty() && !image_size_.IsEmpty()) + contents_size = image_size_; } + contents_layer_->SetBounds(gfx::Size(contents_size)); } -static HashSet<int>* g_registered_layer_set; - -void GraphicsLayer::RegisterContentsLayer(cc::Layer* layer) { - if (!g_registered_layer_set) - g_registered_layer_set = new HashSet<int>; - CHECK(!g_registered_layer_set->Contains(layer->id())); - g_registered_layer_set->insert(layer->id()); -} - -void GraphicsLayer::UnregisterContentsLayer(cc::Layer* layer) { - DCHECK(g_registered_layer_set); - CHECK(g_registered_layer_set->Contains(layer->id())); - g_registered_layer_set->erase(layer->id()); +void GraphicsLayer::SetContentsToCcLayer( + scoped_refptr<cc::Layer> contents_layer, + bool prevent_contents_opaque_changes) { + DCHECK_NE(contents_layer, layer_); + SetContentsTo(std::move(contents_layer), prevent_contents_opaque_changes); + contents_layer_is_picture_image_layer_ = false; } -void GraphicsLayer::SetContentsTo(cc::Layer* layer, +void GraphicsLayer::SetContentsTo(scoped_refptr<cc::Layer> layer, bool prevent_contents_opaque_changes) { - bool children_changed = false; if (layer) { - DCHECK(g_registered_layer_set); - CHECK(g_registered_layer_set->Contains(layer->id())); - if (contents_layer_id_ != layer->id()) { - SetupContentsLayer(layer); - children_changed = true; + if (contents_layer_ != layer) { + contents_layer_ = std::move(layer); + // It is necessary to call SetDrawsContent() as soon as we receive the new + // contents_layer, for the correctness of early exit conditions in + // SetDrawsContent() and SetContentsVisible(). + contents_layer_->SetIsDrawable(contents_visible_); + contents_layer_->SetHitTestable(contents_visible_); + NotifyChildListChange(); } - UpdateContentsRect(); + UpdateContentsLayerBounds(); prevent_contents_opaque_changes_ = prevent_contents_opaque_changes; - } else { - if (contents_layer_) { - children_changed = true; - - // The old contents layer will be removed via updateChildList. - SetContentsLayer(nullptr); - } + } else if (contents_layer_) { + contents_layer_ = nullptr; + NotifyChildListChange(); } - - if (children_changed) - UpdateChildList(); -} - -void GraphicsLayer::SetupContentsLayer(cc::Layer* contents_layer) { - DCHECK(contents_layer); - SetContentsLayer(contents_layer); - - contents_layer_->SetTransformOrigin(FloatPoint3D()); - contents_layer_->SetUseParentBackfaceVisibility(true); - - // It is necessary to call SetDrawsContent() as soon as we receive the new - // contents_layer, for the correctness of early exit conditions in - // SetDrawsContent() and SetContentsVisible(). - contents_layer_->SetIsDrawable(contents_visible_); - contents_layer_->SetHitTestable(contents_visible_); -} - -void GraphicsLayer::ClearContentsLayerIfUnregistered() { - if (!contents_layer_id_ || - g_registered_layer_set->Contains(contents_layer_id_)) - return; - - SetContentsLayer(nullptr); -} - -void GraphicsLayer::SetContentsLayer(cc::Layer* contents_layer) { - contents_layer_ = contents_layer; - if (!contents_layer_) { - contents_layer_id_ = 0; - return; - } - contents_layer_id_ = contents_layer_->id(); -} - -cc::Layer* GraphicsLayer::ContentsLayerIfRegistered() { - ClearContentsLayerIfUnregistered(); - return contents_layer_; } RasterInvalidator& GraphicsLayer::EnsureRasterInvalidator() { if (!raster_invalidator_) { - raster_invalidator_ = - std::make_unique<RasterInvalidator>(base::BindRepeating( - &GraphicsLayer::SetNeedsDisplayInRect, base::Unretained(this))); + raster_invalidator_ = std::make_unique<RasterInvalidator>(); raster_invalidator_->SetTracksRasterInvalidations( client_.IsTrackingRasterInvalidations()); } @@ -575,7 +509,7 @@ void GraphicsLayer::TrackRasterInvalidation(const DisplayItemClient& client, } String GraphicsLayer::DebugName(const cc::Layer* layer) const { - if (layer->id() == contents_layer_id_) + if (layer == contents_layer_.get()) return "ContentsLayer for " + client_.DebugName(this); if (layer == layer_.get()) @@ -601,15 +535,6 @@ void GraphicsLayer::SetSize(const gfx::Size& size) { // Note that we don't resize m_contentsLayer. It's up the caller to do that. } - -bool GraphicsLayer::MasksToBounds() const { - return CcLayer()->masks_to_bounds(); -} - -void GraphicsLayer::SetMasksToBounds(bool masks_to_bounds) { - CcLayer()->SetMasksToBounds(masks_to_bounds); -} - void GraphicsLayer::SetDrawsContent(bool draws_content) { // NOTE: This early-exit is only correct because we also properly call // cc::Layer::SetIsDrawable() whenever |contents_layer_| is set to a new @@ -651,7 +576,6 @@ bool GraphicsLayer::ContentsOpaque() const { void GraphicsLayer::SetContentsOpaque(bool opaque) { CcLayer()->SetContentsOpaque(opaque); - ClearContentsLayerIfUnregistered(); if (contents_layer_ && !prevent_contents_opaque_changes_) contents_layer_->SetContentsOpaque(opaque); } @@ -675,8 +599,8 @@ void GraphicsLayer::SetHitTestable(bool should_hit_test) { } void GraphicsLayer::SetContentsNeedsDisplay() { - if (cc::Layer* contents_layer = ContentsLayerIfRegistered()) { - contents_layer->SetNeedsDisplay(); + if (contents_layer_) { + contents_layer_->SetNeedsDisplay(); TrackRasterInvalidation(*this, contents_rect_, PaintInvalidationReason::kFullLayer); } @@ -710,7 +634,7 @@ void GraphicsLayer::SetContentsRect(const IntRect& rect) { return; contents_rect_ = rect; - UpdateContentsRect(); + UpdateContentsLayerBounds(); client_.GraphicsLayersDidChange(); } @@ -724,16 +648,17 @@ void GraphicsLayer::SetContentsToImage( ImageOrientation image_orientation = kOriginTopLeft; SkMatrix matrix; - if (paint_image && image->IsBitmapImage() && + auto* bitmap_image = DynamicTo<BitmapImage>(image); + if (paint_image && bitmap_image && respect_image_orientation == kRespectImageOrientation) { - image_orientation = ToBitmapImage(image)->CurrentFrameOrientation(); + image_orientation = bitmap_image->CurrentFrameOrientation(); image_size_ = IntSize(paint_image.width(), paint_image.height()); if (image_orientation.UsesWidthAsHeight()) image_size_ = image_size_.TransposedSize(); auto affine = image_orientation.TransformFromDefault(FloatSize(image_size_)); auto transform = affine.ToTransformationMatrix(); - matrix = TransformationMatrix::ToSkMatrix44(transform); + matrix = SkMatrix(TransformationMatrix::ToSkMatrix44(transform)); } else if (paint_image) { matrix = SkMatrix::I(); image_size_ = IntSize(paint_image.width(), paint_image.height()); @@ -742,27 +667,28 @@ void GraphicsLayer::SetContentsToImage( image_size_ = IntSize(); } + scoped_refptr<cc::PictureImageLayer> image_layer; if (paint_image) { paint_image = PaintImageBuilder::WithCopy(std::move(paint_image)) .set_decoding_mode(Image::ToPaintImageDecodingMode(decode_mode)) .TakePaintImage(); - if (!image_layer_) { - image_layer_ = cc::PictureImageLayer::Create(); - RegisterContentsLayer(image_layer_.get()); + if (!contents_layer_is_picture_image_layer_) { + image_layer = cc::PictureImageLayer::Create(); + contents_layer_is_picture_image_layer_ = true; + } else { + image_layer = static_cast<cc::PictureImageLayer*>(contents_layer_.get()); } - image_layer_->SetImage(std::move(paint_image), matrix, - image_orientation.UsesWidthAsHeight()); + image_layer->SetImage(std::move(paint_image), matrix, + image_orientation.UsesWidthAsHeight()); // Image layers can not be marked as opaque due to crbug.com/870857. - image_layer_->SetContentsOpaque(false); - UpdateContentsRect(); - } else if (image_layer_) { - UnregisterContentsLayer(image_layer_.get()); - image_layer_ = nullptr; + image_layer->SetContentsOpaque(false); + } else { + contents_layer_is_picture_image_layer_ = false; } - SetContentsTo(image_layer_.get(), - /*prevent_contents_opaque_changes=*/true); + SetContentsTo(std::move(image_layer), + /* prevent_contents_opaque_changes=*/true); } cc::PictureLayer* GraphicsLayer::CcLayer() const { @@ -770,8 +696,10 @@ cc::PictureLayer* GraphicsLayer::CcLayer() const { } void GraphicsLayer::SetFilterQuality(SkFilterQuality filter_quality) { - if (image_layer_) - image_layer_->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality); + if (contents_layer_is_picture_image_layer_) { + static_cast<cc::PictureImageLayer*>(contents_layer_.get()) + ->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality); + } } void GraphicsLayer::SetPaintingPhase(GraphicsLayerPaintingPhase phase) { @@ -857,35 +785,24 @@ scoped_refptr<cc::DisplayItemList> GraphicsLayer::PaintContentsToDisplayList( PaintingControlSetting painting_control) { TRACE_EVENT0("blink,benchmark", "GraphicsLayer::PaintContents"); - PaintController& paint_controller = GetPaintController(); - paint_controller.SetDisplayItemConstructionIsDisabled( - painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED); - paint_controller.SetSubsequenceCachingIsDisabled( - painting_control == SUBSEQUENCE_CACHING_DISABLED); - - if (painting_control == PARTIAL_INVALIDATION) - client_.InvalidateTargetElementForTesting(); + if (painting_control == SUBSEQUENCE_CACHING_DISABLED) + PaintController::SetSubsequenceCachingDisabledForBenchmark(); + else if (painting_control == PARTIAL_INVALIDATION) + PaintController::SetPartialInvalidationForBenchmark(); + PaintController& paint_controller = GetPaintController(); // We also disable caching when Painting or Construction are disabled. In both // cases we would like to compare assuming the full cost of recording, not the // cost of re-using cached content. - if (painting_control == DISPLAY_LIST_CACHING_DISABLED || - painting_control == DISPLAY_LIST_PAINTING_DISABLED || - painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED) + if (painting_control == DISPLAY_LIST_CACHING_DISABLED) paint_controller.InvalidateAll(); - GraphicsContext::DisabledMode disabled_mode = - GraphicsContext::kNothingDisabled; - if (painting_control == DISPLAY_LIST_PAINTING_DISABLED || - painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED) - disabled_mode = GraphicsContext::kFullyDisabled; - // Anything other than PAINTING_BEHAVIOR_NORMAL is for testing. In non-testing // scenarios, it is an error to call GraphicsLayer::Paint. Actual painting // occurs in LocalFrameView::PaintTree() which calls GraphicsLayer::Paint(); // this method merely copies the painted output to the cc::DisplayItemList. if (painting_control != PAINTING_BEHAVIOR_NORMAL) - Paint(disabled_mode); + Paint(); auto display_list = base::MakeRefCounted<cc::DisplayItemList>(); @@ -896,8 +813,7 @@ scoped_refptr<cc::DisplayItemList> GraphicsLayer::PaintContentsToDisplayList( VisualRectSubpixelOffset(), paint_controller.GetPaintArtifact().GetDisplayItemList(), *display_list); - paint_controller.SetDisplayItemConstructionIsDisabled(false); - paint_controller.SetSubsequenceCachingIsDisabled(false); + PaintController::ClearFlagsForBenchmark(); display_list->Finalize(); return display_list; diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h index 269eb43a3ef..14ed1bad11b 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h +++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer.h @@ -47,6 +47,7 @@ #include "third_party/blink/renderer/platform/graphics/image_orientation.h" #include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" +#include "third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h" #include "third_party/blink/renderer/platform/graphics/paint_invalidation_reason.h" #include "third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h" #include "third_party/blink/renderer/platform/heap/handle.h" @@ -57,7 +58,6 @@ #include "third_party/skia/include/core/SkRefCnt.h" namespace cc { -class PictureImageLayer; class PictureLayer; } // namespace cc @@ -127,15 +127,12 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient, void SetRenderingContext(int id); - bool MasksToBounds() const; - void SetMasksToBounds(bool); - bool DrawsContent() const { return draws_content_; } void SetDrawsContent(bool); - // False if no hit test display items will be painted onto this GraphicsLayer. - // This is different from |DrawsContent| because hit test display items are - // internal to blink and are not copied to the cc::Layer's display list. + // False if no hit test data will be recorded onto this GraphicsLayer. + // This is different from |DrawsContent| because hit test data are internal + // to blink and are not copied to the cc::Layer's display list. bool PaintsHitTest() const { return paints_hit_test_; } void SetPaintsHitTest(bool paints) { paints_hit_test_ = paints; } @@ -177,19 +174,15 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient, void SetContentsToImage( Image*, Image::ImageDecodingMode decode_mode, - RespectImageOrientationEnum = kDoNotRespectImageOrientation); + RespectImageOrientationEnum = kRespectImageOrientation); // If |prevent_contents_opaque_changes| is set to true, then calls to - // SetContentsOpaque() will not be passed on to the |layer|. Use when - // the client wants to have control of the opaqueness of the contents - // |layer| independently of what outcome painting produces. - void SetContentsToCcLayer(cc::Layer* layer, - bool prevent_contents_opaque_changes) { - SetContentsTo(layer, prevent_contents_opaque_changes); - } + // SetContentsOpaque() will not be passed on to |contents_layer|. Use when + // the client wants to have control of the opaqueness of |contents_layer| + // independently of what outcome painting produces. + void SetContentsToCcLayer(scoped_refptr<cc::Layer> contents_layer, + bool prevent_contents_opaque_changes); bool HasContentsLayer() const { return ContentsLayer(); } - cc::Layer* ContentsLayer() const { - return const_cast<GraphicsLayer*>(this)->ContentsLayerIfRegistered(); - } + cc::Layer* ContentsLayer() const { return contents_layer_.get(); } const IntRect& ContentsRect() const { return contents_rect_; } @@ -204,13 +197,10 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient, const IntRect&, PaintInvalidationReason); - static void RegisterContentsLayer(cc::Layer*); - static void UnregisterContentsLayer(cc::Layer*); - IntRect InterestRect(); bool PaintRecursively(); // Returns true if this layer is repainted. - bool Paint(GraphicsContext::DisabledMode = GraphicsContext::kNothingDisabled); + bool Paint(); PaintController& GetPaintController() const; @@ -275,12 +265,10 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient, void UpdateSafeOpaqueBackgroundColor(); // Returns true if PaintController::PaintArtifact() changed and needs commit. - bool PaintWithoutCommit( - GraphicsContext::DisabledMode = GraphicsContext::kNothingDisabled, - const IntRect* interest_rect = nullptr); + bool PaintWithoutCommit(const IntRect* interest_rect = nullptr); - // Adds a child without calling updateChildList(), so that adding children - // can be batched before updating. + // Adds a child without calling NotifyChildListChange(), so that adding + // children can be batched before updating. void AddChildInternal(GraphicsLayer*); #if DCHECK_IS_ON() @@ -288,15 +276,12 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient, #endif // Helper functions used by settors to keep layer's the state consistent. - void UpdateChildList(); + void NotifyChildListChange(); void UpdateLayerIsDrawable(); - void UpdateContentsRect(); + void UpdateContentsLayerBounds(); - void SetContentsTo(cc::Layer*, bool prevent_contents_opaque_changes); - void SetupContentsLayer(cc::Layer*); - void ClearContentsLayerIfUnregistered(); - cc::Layer* ContentsLayerIfRegistered(); - void SetContentsLayer(cc::Layer*); + void SetContentsTo(scoped_refptr<cc::Layer>, + bool prevent_contents_opaque_changes); RasterInvalidator& EnsureRasterInvalidator(); void SetNeedsDisplayInRect(const IntRect&); @@ -316,6 +301,7 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient, bool contents_visible_ : 1; bool hit_testable_ : 1; bool needs_check_raster_invalidation_ : 1; + bool contents_layer_is_picture_image_layer_ : 1; bool painted_ : 1; @@ -330,15 +316,8 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient, IntRect contents_rect_; scoped_refptr<cc::PictureLayer> layer_; - scoped_refptr<cc::PictureImageLayer> image_layer_; IntSize image_size_; - cc::Layer* contents_layer_; - // We don't have ownership of contents_layer_, but we do want to know if a - // given layer is the same as our current layer in SetContentsTo(). Since - // |contents_layer_| may be deleted at this point, we stash an ID away when we - // know |contents_layer_| is alive and use that for comparisons from that - // point on. - int contents_layer_id_; + scoped_refptr<cc::Layer> contents_layer_; SquashingDisallowedReasons squashing_disallowed_reasons_ = SquashingDisallowedReason::kNone; @@ -355,6 +334,7 @@ class PLATFORM_EXPORT GraphicsLayer : public DisplayItemClient, std::unique_ptr<LayerState> contents_layer_state_; std::unique_ptr<RasterInvalidator> raster_invalidator_; + RasterInvalidator::RasterInvalidationFunction raster_invalidation_function_; DOMNodeId owner_node_id_ = kInvalidDOMNodeId; CompositingReasons compositing_reasons_ = CompositingReason::kNone; diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_client.h b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_client.h index 091a8e66c36..0106ae1c9be 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_client.h +++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_client.h @@ -56,8 +56,6 @@ class PLATFORM_EXPORT GraphicsLayerClient { public: virtual ~GraphicsLayerClient() = default; - virtual void InvalidateTargetElementForTesting() {} - virtual IntRect ComputeInterestRect( const GraphicsLayer*, const IntRect& previous_interest_rect) const = 0; diff --git a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc index e1c3148b159..35ea37342e2 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc @@ -149,11 +149,10 @@ TEST_P(GraphicsLayerTest, SetDrawsContentFalse) { TEST_P(GraphicsLayerTest, ContentsLayer) { auto& graphics_layer = layers_.graphics_layer(); auto contents_layer = cc::Layer::Create(); - GraphicsLayer::RegisterContentsLayer(contents_layer.get()); - graphics_layer.SetContentsToCcLayer(contents_layer.get(), true); + graphics_layer.SetContentsToCcLayer(contents_layer, true); EXPECT_TRUE(graphics_layer.HasContentsLayer()); EXPECT_EQ(contents_layer.get(), graphics_layer.ContentsLayer()); - GraphicsLayer::UnregisterContentsLayer(contents_layer.get()); + graphics_layer.SetContentsToCcLayer(nullptr, true); EXPECT_FALSE(graphics_layer.HasContentsLayer()); EXPECT_EQ(nullptr, graphics_layer.ContentsLayer()); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/hit_test_rect.cc b/chromium/third_party/blink/renderer/platform/graphics/hit_test_rect.cc deleted file mode 100644 index ce10491b66e..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/hit_test_rect.cc +++ /dev/null @@ -1,32 +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 "third_party/blink/renderer/platform/graphics/hit_test_rect.h" - -#include "cc/base/region.h" -#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" - -namespace blink { - -// static -LayoutRect HitTestRect::GetBounds(const Vector<HitTestRect>& hit_test_rects) { - cc::Region region; - for (const HitTestRect& hit_test_rect : hit_test_rects) { - const LayoutRect& rect = hit_test_rect.rect; - region.Union(EnclosingIntRect(rect)); - } - const auto& rect = region.bounds(); - return LayoutRect(IntRect(rect)); -} - -String HitTestRect::ToString() const { - // TODO(pdr): Print the value of |allowed_touch_action|. - return rect.ToString(); -} - -std::ostream& operator<<(std::ostream& os, const HitTestRect& hit_test_rect) { - return os << hit_test_rect.ToString(); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/hit_test_rect.h b/chromium/third_party/blink/renderer/platform/graphics/hit_test_rect.h deleted file mode 100644 index 258b6848f7d..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/hit_test_rect.h +++ /dev/null @@ -1,39 +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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIT_TEST_RECT_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIT_TEST_RECT_H_ - -#include "third_party/blink/renderer/platform/geometry/layout_rect.h" -#include "third_party/blink/renderer/platform/graphics/touch_action.h" -#include "third_party/blink/renderer/platform/platform_export.h" - -namespace blink { - -// TODO(pdr): Rename this TouchActionRect. -struct PLATFORM_EXPORT HitTestRect { - LayoutRect rect; - TouchAction allowed_touch_action; - - HitTestRect(const LayoutRect& layout_rect) - : HitTestRect(layout_rect, TouchAction::kTouchActionNone) {} - HitTestRect(const LayoutRect& layout_rect, TouchAction action) - : rect(layout_rect), allowed_touch_action(action) {} - - static LayoutRect GetBounds(const Vector<HitTestRect>&); - - bool operator==(const HitTestRect& rhs) const { - return rect == rhs.rect && allowed_touch_action == rhs.allowed_touch_action; - } - - bool operator!=(const HitTestRect& rhs) const { return !(*this == rhs); } - - String ToString() const; -}; - -PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const HitTestRect&); - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_HIT_TEST_RECT_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/image.cc b/chromium/third_party/blink/renderer/platform/graphics/image.cc index bcb20fdcc85..444fec9627d 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/image.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/image.cc @@ -215,7 +215,8 @@ void Image::DrawPattern(GraphicsContext& context, const FloatPoint& phase, SkBlendMode composite_op, const FloatRect& dest_rect, - const FloatSize& repeat_spacing) { + const FloatSize& repeat_spacing, + RespectImageOrientationEnum) { TRACE_EVENT0("skia", "Image::drawPattern"); if (dest_rect.IsEmpty()) @@ -325,16 +326,22 @@ bool Image::ApplyShader(PaintFlags& flags, const SkMatrix& local_matrix) { return true; } +IntSize Image::Size( + RespectImageOrientationEnum respect_image_orientation) const { + if (respect_image_orientation == kRespectImageOrientation) + return SizeRespectingOrientation(); + return Size(); +} + SkBitmap Image::AsSkBitmapForCurrentFrame( - RespectImageOrientationEnum should_respect_image_orientation) { + RespectImageOrientationEnum respect_image_orientation) { PaintImage paint_image = PaintImageForCurrentFrame(); if (!paint_image) return {}; - if (should_respect_image_orientation == kRespectImageOrientation && - IsBitmapImage()) { - ImageOrientation orientation = - ToBitmapImage(this)->CurrentFrameOrientation(); + auto* bitmap_image = DynamicTo<BitmapImage>(this); + if (respect_image_orientation == kRespectImageOrientation && bitmap_image) { + ImageOrientation orientation = bitmap_image->CurrentFrameOrientation(); paint_image = ResizeAndOrientImage(paint_image, orientation); if (!paint_image) return {}; @@ -372,6 +379,15 @@ bool Image::GetBitmap(const FloatRect& src_rect, SkBitmap* bitmap) { return true; } +FloatRect Image::CorrectSrcRectForImageOrientation(FloatSize image_size, + FloatRect src_rect) const { + ImageOrientation orientation = CurrentFrameOrientation(); + DCHECK(orientation != kDefaultImageOrientation); + AffineTransform forward_map = orientation.TransformFromDefault(image_size); + AffineTransform inverse_map = forward_map.Inverse(); + return inverse_map.MapRect(src_rect); +} + DarkModeClassification Image::GetDarkModeClassification( const FloatRect& src_rect) { // Assuming that multiple uses of the same sprite region all have the same diff --git a/chromium/third_party/blink/renderer/platform/graphics/image.h b/chromium/third_party/blink/renderer/platform/graphics/image.h index 445dbd8f71f..40cce084170 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/image.h +++ b/chromium/third_party/blink/renderer/platform/graphics/image.h @@ -30,6 +30,7 @@ #include "base/macros.h" #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" +#include "third_party/blink/renderer/platform/geometry/float_point.h" #include "third_party/blink/renderer/platform/geometry/float_size.h" #include "third_party/blink/renderer/platform/geometry/int_rect.h" #include "third_party/blink/renderer/platform/graphics/graphics_types.h" @@ -57,7 +58,6 @@ class ImageDecodeCache; namespace blink { class DarkModeImageClassifier; -class FloatPoint; class FloatRect; class GraphicsContext; class Image; @@ -109,9 +109,16 @@ class PLATFORM_EXPORT Image : public ThreadSafeRefCounted<Image> { virtual bool HasIntrinsicSize() const { return true; } virtual IntSize Size() const = 0; + IntSize Size(RespectImageOrientationEnum) const; + virtual IntSize SizeRespectingOrientation() const { return Size(); } + virtual FloatSize SizeAsFloat( + RespectImageOrientationEnum respect_orientation) const { + return FloatSize(Size(respect_orientation)); + } IntRect Rect() const { return IntRect(IntPoint(), Size()); } int width() const { return Size().Width(); } int height() const { return Size().Height(); } + virtual bool GetHotSpot(IntPoint&) const { return false; } enum SizeAvailability { @@ -201,6 +208,22 @@ class PLATFORM_EXPORT Image : public ThreadSafeRefCounted<Image> { virtual PaintImage PaintImageForCurrentFrame() = 0; + virtual bool HasDefaultOrientation() const { return true; } + + // Most image types have the default orientation. Only bitmap derived image + // types need to override this method. + virtual ImageOrientation CurrentFrameOrientation() const { + return kDefaultImageOrientation; + } + + // Correct the src rect (rotate and maybe translate it) to account for a + // non-default image orientation. The image must have non-default orientation + // to call this method. The image_size is the oriented size of the image (i.e. + // after orientation has been applied). src_rect may be a subset of the image, + // also oriented. + FloatRect CorrectSrcRectForImageOrientation(FloatSize image_size, + FloatRect src_rect) const; + enum ImageClampingMode { kClampImageToSourceRect, kDoNotClampImageToSourceRect @@ -276,7 +299,8 @@ class PLATFORM_EXPORT Image : public ThreadSafeRefCounted<Image> { const FloatPoint& phase, SkBlendMode, const FloatRect&, - const FloatSize& repeat_spacing); + const FloatSize& repeat_spacing, + RespectImageOrientationEnum); // Creates and initializes a PaintImageBuilder with the metadata flags for the // PaintImage. @@ -285,7 +309,7 @@ class PLATFORM_EXPORT Image : public ThreadSafeRefCounted<Image> { // Whether or not size is available yet. virtual bool IsSizeAvailable() { return true; } - typedef FloatSize ClassificationKey; + typedef FloatPoint ClassificationKey; HashMap<ClassificationKey, DarkModeClassification> dark_mode_classifications_; private: @@ -304,10 +328,6 @@ class PLATFORM_EXPORT Image : public ThreadSafeRefCounted<Image> { DISALLOW_COPY_AND_ASSIGN(Image); }; -#define DEFINE_IMAGE_TYPE_CASTS(typeName) \ - DEFINE_TYPE_CASTS(typeName, Image, image, image->Is##typeName(), \ - image.Is##typeName()) - } // namespace blink #endif diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_decoder_wrapper.cc b/chromium/third_party/blink/renderer/platform/graphics/image_decoder_wrapper.cc index 5a6dab40814..4c261b515a9 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/image_decoder_wrapper.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/image_decoder_wrapper.cc @@ -112,7 +112,7 @@ bool ImageDecoderWrapper::Decode(ImageDecoderFactory* factory, // For multi-frame image decoders, we need to know how many frames are // in that image in order to release the decoder when all frames are - // decoded. frameCount() is reliable only if all data is received and set in + // decoded. FrameCount() is reliable only if all data is received and set in // decoder, particularly with GIF. if (all_data_received_) *frame_count = decoder->FrameCount(); diff --git a/chromium/third_party/blink/renderer/platform/graphics/image_observer.h b/chromium/third_party/blink/renderer/platform/graphics/image_observer.h index db0a5275b0a..1cf6d753c4d 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/image_observer.h +++ b/chromium/third_party/blink/renderer/platform/graphics/image_observer.h @@ -51,7 +51,7 @@ class PLATFORM_EXPORT ImageObserver : public GarbageCollectedMixin { // See the comment of Image::SetData(). virtual void AsyncLoadCompleted(const Image*) = 0; - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.h b/chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.h index bb7e49923e7..8ede5551e3c 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.h +++ b/chromium/third_party/blink/renderer/platform/graphics/intercepting_canvas.h @@ -86,19 +86,6 @@ class InterceptingCanvasBase : public SkCanvas { void onDrawOval(const SkRect&, const SkPaint&) override = 0; void onDrawRRect(const SkRRect&, const SkPaint&) override = 0; void onDrawPath(const SkPath&, const SkPaint&) override = 0; - void onDrawBitmap(const SkBitmap&, - SkScalar left, - SkScalar top, - const SkPaint*) override = 0; - void onDrawBitmapRect(const SkBitmap&, - const SkRect* src, - const SkRect& dst, - const SkPaint*, - SrcRectConstraint) override = 0; - void onDrawBitmapNine(const SkBitmap&, - const SkIRect& center, - const SkRect& dst, - const SkPaint*) override = 0; void onDrawImage(const SkImage*, SkScalar, SkScalar, @@ -127,7 +114,10 @@ class InterceptingCanvasBase : public SkCanvas { const SkMatrix*, const SkPaint*) override = 0; void didSetMatrix(const SkMatrix&) override = 0; + void didConcat44(const SkScalar[16]) override = 0; void didConcat(const SkMatrix&) override = 0; + void didScale(SkScalar, SkScalar) override = 0; + void didTranslate(SkScalar, SkScalar) override = 0; void willSave() override = 0; SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override = 0; void willRestore() override = 0; @@ -187,31 +177,6 @@ class InterceptingCanvas : public InterceptingCanvasBase { this->SkCanvas::onDrawPath(path, paint); } - void onDrawBitmap(const SkBitmap& bitmap, - SkScalar left, - SkScalar top, - const SkPaint* paint) override { - Interceptor interceptor(this); - this->SkCanvas::onDrawBitmap(bitmap, left, top, paint); - } - - void onDrawBitmapRect(const SkBitmap& bitmap, - const SkRect* src, - const SkRect& dst, - const SkPaint* paint, - SrcRectConstraint constraint) override { - Interceptor interceptor(this); - this->SkCanvas::onDrawBitmapRect(bitmap, src, dst, paint, constraint); - } - - void onDrawBitmapNine(const SkBitmap& bitmap, - const SkIRect& center, - const SkRect& dst, - const SkPaint* paint) override { - Interceptor interceptor(this); - this->SkCanvas::onDrawBitmapNine(bitmap, center, dst, paint); - } - void onDrawImage(const SkImage* image, SkScalar x, SkScalar y, @@ -285,12 +250,22 @@ class InterceptingCanvas : public InterceptingCanvasBase { void didSetMatrix(const SkMatrix& matrix) override { Interceptor interceptor(this); - this->SkCanvas::didSetMatrix(matrix); + } + + void didConcat44(const SkScalar m[16]) override { + Interceptor interceptor(this); } void didConcat(const SkMatrix& matrix) override { Interceptor interceptor(this); - this->SkCanvas::didConcat(matrix); + } + + void didScale(SkScalar x, SkScalar y) override { + Interceptor interceptor(this); + } + + void didTranslate(SkScalar x, SkScalar y) override { + Interceptor interceptor(this); } void willSave() override { diff --git a/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc b/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc index b22ea1d9e6d..9fe75a4f661 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.cc @@ -73,15 +73,6 @@ std::unique_ptr<JSONObject> ObjectForSkRect(const SkRect& rect) { return rect_item; } -std::unique_ptr<JSONObject> ObjectForSkIRect(const SkIRect& rect) { - auto rect_item = std::make_unique<JSONObject>(); - rect_item->SetDouble("left", rect.left()); - rect_item->SetDouble("top", rect.top()); - rect_item->SetDouble("right", rect.right()); - rect_item->SetDouble("bottom", rect.bottom()); - return rect_item; -} - String PointModeName(SkCanvas::PointMode mode) { switch (mode) { case SkCanvas::kPoints_PointMode: @@ -248,57 +239,6 @@ std::unique_ptr<JSONObject> ObjectForSkPath(const SkPath& path) { return path_item; } -String ColorTypeName(SkColorType color_type) { - switch (color_type) { - case kUnknown_SkColorType: - return "None"; - case kAlpha_8_SkColorType: - return "A8"; - case kRGB_565_SkColorType: - return "RGB565"; - case kARGB_4444_SkColorType: - return "ARGB4444"; - case kN32_SkColorType: - return "ARGB8888"; - default: - NOTREACHED(); - return "?"; - }; -} - -std::unique_ptr<JSONObject> ObjectForBitmapData(const SkBitmap& bitmap) { - Vector<unsigned char> output; - - SkPixmap src; - bool peekResult = bitmap.peekPixels(&src); - DCHECK(peekResult); - - SkPngEncoder::Options options; - options.fFilterFlags = SkPngEncoder::FilterFlag::kSub; - options.fZLibLevel = 3; - if (!ImageEncoder::Encode(&output, src, options)) { - return nullptr; - } - - auto data_item = std::make_unique<JSONObject>(); - data_item->SetString("base64", Base64Encode(output)); - data_item->SetString("mimeType", "image/png"); - return data_item; -} - -std::unique_ptr<JSONObject> ObjectForSkBitmap(const SkBitmap& bitmap) { - auto bitmap_item = std::make_unique<JSONObject>(); - bitmap_item->SetInteger("width", bitmap.width()); - bitmap_item->SetInteger("height", bitmap.height()); - bitmap_item->SetString("config", ColorTypeName(bitmap.colorType())); - bitmap_item->SetBoolean("opaque", bitmap.isOpaque()); - bitmap_item->SetBoolean("immutable", bitmap.isImmutable()); - bitmap_item->SetBoolean("volatile", bitmap.isVolatile()); - bitmap_item->SetInteger("genID", bitmap.getGenerationID()); - bitmap_item->SetObject("data", ObjectForBitmapData(bitmap)); - return bitmap_item; -} - std::unique_ptr<JSONObject> ObjectForSkImage(const SkImage* image) { auto image_item = std::make_unique<JSONObject>(); image_item->SetInteger("width", image->width()); @@ -315,6 +255,14 @@ std::unique_ptr<JSONArray> ArrayForSkMatrix(const SkMatrix& matrix) { return matrix_array; } +std::unique_ptr<JSONArray> ArrayForSkScalars(size_t count, + const SkScalar array[]) { + auto points_array_item = std::make_unique<JSONArray>(); + for (size_t i = 0; i < count; ++i) + points_array_item->PushDouble(array[i]); + return points_array_item; +} + std::unique_ptr<JSONObject> ObjectForSkShader(const SkShader& shader) { return std::make_unique<JSONObject>(); } @@ -520,51 +468,6 @@ void LoggingCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { this->SkCanvas::onDrawPath(path, paint); } -void LoggingCanvas::onDrawBitmap(const SkBitmap& bitmap, - SkScalar left, - SkScalar top, - const SkPaint* paint) { - AutoLogger logger(this); - JSONObject* params = logger.LogItemWithParams("drawBitmap"); - params->SetDouble("left", left); - params->SetDouble("top", top); - params->SetObject("bitmap", ObjectForSkBitmap(bitmap)); - if (paint) - params->SetObject("paint", ObjectForSkPaint(*paint)); - this->SkCanvas::onDrawBitmap(bitmap, left, top, paint); -} - -void LoggingCanvas::onDrawBitmapRect(const SkBitmap& bitmap, - const SkRect* src, - const SkRect& dst, - const SkPaint* paint, - SrcRectConstraint constraint) { - AutoLogger logger(this); - JSONObject* params = logger.LogItemWithParams("drawBitmapRectToRect"); - params->SetObject("bitmap", ObjectForSkBitmap(bitmap)); - if (src) - params->SetObject("src", ObjectForSkRect(*src)); - params->SetObject("dst", ObjectForSkRect(dst)); - if (paint) - params->SetObject("paint", ObjectForSkPaint(*paint)); - params->SetInteger("flags", constraint); - this->SkCanvas::onDrawBitmapRect(bitmap, src, dst, paint, constraint); -} - -void LoggingCanvas::onDrawBitmapNine(const SkBitmap& bitmap, - const SkIRect& center, - const SkRect& dst, - const SkPaint* paint) { - AutoLogger logger(this); - JSONObject* params = logger.LogItemWithParams("drawBitmapNine"); - params->SetObject("bitmap", ObjectForSkBitmap(bitmap)); - params->SetObject("center", ObjectForSkIRect(center)); - params->SetObject("dst", ObjectForSkRect(dst)); - if (paint) - params->SetObject("paint", ObjectForSkPaint(*paint)); - this->SkCanvas::onDrawBitmapNine(bitmap, center, dst, paint); -} - void LoggingCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top, @@ -677,7 +580,12 @@ void LoggingCanvas::didSetMatrix(const SkMatrix& matrix) { AutoLogger logger(this); JSONObject* params = logger.LogItemWithParams("setMatrix"); params->SetArray("matrix", ArrayForSkMatrix(matrix)); - this->SkCanvas::didSetMatrix(matrix); +} + +void LoggingCanvas::didConcat44(const SkScalar m[16]) { + AutoLogger logger(this); + JSONObject* params = logger.LogItemWithParams("concat44"); + params->SetArray("matrix44", ArrayForSkScalars(16, m)); } void LoggingCanvas::didConcat(const SkMatrix& matrix) { @@ -701,7 +609,20 @@ void LoggingCanvas::didConcat(const SkMatrix& matrix) { params = logger.LogItemWithParams("concat"); params->SetArray("matrix", ArrayForSkMatrix(matrix)); } - this->SkCanvas::didConcat(matrix); +} + +void LoggingCanvas::didScale(SkScalar x, SkScalar y) { + AutoLogger logger(this); + JSONObject* params = logger.LogItemWithParams("scale"); + params->SetDouble("scaleX", x); + params->SetDouble("scaleY", y); +} + +void LoggingCanvas::didTranslate(SkScalar x, SkScalar y) { + AutoLogger logger(this); + JSONObject* params = logger.LogItemWithParams("translate"); + params->SetDouble("dx", x); + params->SetDouble("dy", y); } void LoggingCanvas::willSave() { diff --git a/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.h b/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.h index 3951bbf51da..82b00979c9f 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.h +++ b/chromium/third_party/blink/renderer/platform/graphics/logging_canvas.h @@ -53,19 +53,6 @@ class LoggingCanvas : public InterceptingCanvasBase { void onDrawOval(const SkRect&, const SkPaint&) override; void onDrawRRect(const SkRRect&, const SkPaint&) override; void onDrawPath(const SkPath&, const SkPaint&) override; - void onDrawBitmap(const SkBitmap&, - SkScalar left, - SkScalar top, - const SkPaint*) override; - void onDrawBitmapRect(const SkBitmap&, - const SkRect* src, - const SkRect& dst, - const SkPaint*, - SrcRectConstraint) override; - void onDrawBitmapNine(const SkBitmap&, - const SkIRect& center, - const SkRect& dst, - const SkPaint*) override; void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override; void onDrawImageRect(const SkImage*, const SkRect* src, @@ -91,7 +78,10 @@ class LoggingCanvas : public InterceptingCanvasBase { const SkMatrix*, const SkPaint*) override; void didSetMatrix(const SkMatrix&) override; + void didConcat44(const SkScalar[16]) override; void didConcat(const SkMatrix&) override; + void didScale(SkScalar, SkScalar) override; + void didTranslate(SkScalar, SkScalar) override; void willSave() override; SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; void willRestore() override; diff --git a/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc b/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc deleted file mode 100644 index d0ac4be0f10..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.cc +++ /dev/null @@ -1,196 +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 "third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h" - -#include "gpu/command_buffer/client/gles2_interface.h" -#include "third_party/blink/public/platform/platform.h" -#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" -#include "third_party/blink/renderer/platform/graphics/skia_texture_holder.h" -#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" -#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" -#include "third_party/skia/include/gpu/GrContext.h" - -namespace blink { - -namespace { - -void ReleaseTexture( - bool is_converted_from_skia_texture, - unsigned texture_id, - std::unique_ptr<gpu::Mailbox> mailbox, - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider, - std::unique_ptr<gpu::SyncToken> sync_token) { - if (!is_converted_from_skia_texture && texture_id && context_provider) { - context_provider->ContextProvider()->ContextGL()->WaitSyncTokenCHROMIUM( - sync_token->GetData()); - context_provider->ContextProvider()->ContextGL()->DeleteTextures( - 1, &texture_id); - } -} - -} // namespace - -MailboxTextureHolder::MailboxTextureHolder( - const gpu::Mailbox& mailbox, - const gpu::SyncToken& sync_token, - unsigned texture_id_to_delete_after_mailbox_consumed, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&& - context_provider_wrapper, - IntSize mailbox_size, - bool is_origin_top_left) - : TextureHolder(std::move(context_provider_wrapper), - base::MakeRefCounted<MailboxRef>(nullptr), - is_origin_top_left), - mailbox_(mailbox), - texture_id_(texture_id_to_delete_after_mailbox_consumed), - is_converted_from_skia_texture_(false), - thread_id_(0), - sk_image_info_(SkImageInfo::MakeN32Premul(mailbox_size.Width(), - mailbox_size.Height())), - texture_target_(GL_TEXTURE_2D) { - mailbox_ref()->set_sync_token(sync_token); - InitCommon(); -} - -MailboxTextureHolder::MailboxTextureHolder( - const gpu::Mailbox& mailbox, - const gpu::SyncToken& sync_token, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&& - context_provider_wrapper, - scoped_refptr<MailboxRef> mailbox_ref, - PlatformThreadId context_thread_id, - const SkImageInfo& sk_image_info, - GLenum texture_target, - bool is_origin_top_left) - : TextureHolder(std::move(context_provider_wrapper), - std::move(mailbox_ref), - is_origin_top_left), - mailbox_(mailbox), - texture_id_(0), - is_converted_from_skia_texture_(false), - thread_id_(context_thread_id), - sk_image_info_(sk_image_info), - texture_target_(texture_target) { - DCHECK(thread_id_); - DCHECK(!IsCrossThread() || sync_token.verified_flush()); - this->mailbox_ref()->set_sync_token(sync_token); -} - -MailboxTextureHolder::MailboxTextureHolder( - const SkiaTextureHolder* texture_holder, - GLenum filter) - : TextureHolder(texture_holder->ContextProviderWrapper(), - texture_holder->mailbox_ref(), - texture_holder->IsOriginTopLeft()), - texture_id_(0), - is_converted_from_skia_texture_(true), - thread_id_(0) { - sk_sp<SkImage> image = texture_holder->GetSkImage(); - DCHECK(image); - sk_image_info_ = image->imageInfo(); - - if (!ContextProviderWrapper()) - return; - - if (!ContextProviderWrapper()->Utils()->GetMailboxForSkImage( - mailbox_, texture_target_, image, filter)) - return; - - InitCommon(); -} - -void MailboxTextureHolder::Sync(MailboxSyncMode mode) { - gpu::SyncToken sync_token = mailbox_ref()->sync_token(); - - if (IsCrossThread()) { - // Was originally created on another thread. Should already have a sync - // token from the original source context, already verified if needed. - DCHECK(sync_token.HasData()); - DCHECK(mode != kVerifiedSyncToken || sync_token.verified_flush()); - return; - } - - if (!ContextProviderWrapper()) - return; - - TRACE_EVENT0("blink", "MailboxTextureHolder::Sync"); - - gpu::gles2::GLES2Interface* gl = - ContextProviderWrapper()->ContextProvider()->ContextGL(); - - if (mode == kOrderingBarrier) { - if (!did_issue_ordering_barrier_) { - gl->OrderingBarrierCHROMIUM(); - did_issue_ordering_barrier_ = true; - } - return; - } - - if (!sync_token.HasData()) { - if (mode == kVerifiedSyncToken) { - gl->GenSyncTokenCHROMIUM(sync_token.GetData()); - } else { - gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData()); - } - mailbox_ref()->set_sync_token(sync_token); - return; - } - - // At this point we have a pre-existing sync token. We just need to verify - // it if needed. Providing a verified sync token when unverified is requested - // is fine. - if (mode == kVerifiedSyncToken && !sync_token.verified_flush()) { - int8_t* token_data = sync_token.GetData(); - // TODO(junov): Batch this verification in the case where there are multiple - // offscreen canvases being committed. - gl->ShallowFlushCHROMIUM(); - gl->VerifySyncTokensCHROMIUM(&token_data, 1); - sync_token.SetVerifyFlush(); - mailbox_ref()->set_sync_token(sync_token); - } -} - -void MailboxTextureHolder::InitCommon() { - DCHECK(!thread_id_); - thread_id_ = base::PlatformThread::CurrentId(); - texture_thread_task_runner_ = Thread::Current()->GetTaskRunner(); -} - -bool MailboxTextureHolder::IsValid() const { - if (IsCrossThread()) { - // If context is is from another thread, validity cannot be verified. - // Just assume valid. Potential problem will be detected later. - return true; - } - return !!ContextProviderWrapper(); -} - -bool MailboxTextureHolder::IsCrossThread() const { - return thread_id_ != base::PlatformThread::CurrentId(); -} - -MailboxTextureHolder::~MailboxTextureHolder() { - std::unique_ptr<gpu::SyncToken> passed_sync_token( - new gpu::SyncToken(mailbox_ref()->sync_token())); - std::unique_ptr<gpu::Mailbox> passed_mailbox(new gpu::Mailbox(mailbox_)); - - if (texture_thread_task_runner_ && IsCrossThread()) { - PostCrossThreadTask( - *texture_thread_task_runner_, FROM_HERE, - CrossThreadBindOnce(&ReleaseTexture, is_converted_from_skia_texture_, - texture_id_, WTF::Passed(std::move(passed_mailbox)), - WTF::Passed(ContextProviderWrapper()), - WTF::Passed(std::move(passed_sync_token)))); - } else { - ReleaseTexture(is_converted_from_skia_texture_, texture_id_, - std::move(passed_mailbox), ContextProviderWrapper(), - std::move(passed_sync_token)); - } - - texture_id_ = 0u; // invalidate the texture. - texture_thread_task_runner_ = nullptr; -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h b/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h deleted file mode 100644 index da4d6800f99..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h +++ /dev/null @@ -1,83 +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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MAILBOX_TEXTURE_HOLDER_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MAILBOX_TEXTURE_HOLDER_H_ - -#include "base/memory/weak_ptr.h" -#include "base/single_thread_task_runner.h" -#include "third_party/blink/renderer/platform/graphics/graphics_types.h" -#include "third_party/blink/renderer/platform/graphics/texture_holder.h" -#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h" -#include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/scheduler/public/thread.h" -#include "third_party/khronos/GLES2/gl2.h" -#include "third_party/skia/include/core/SkImageInfo.h" - -namespace blink { -class SkiaTextureHolder; - -class PLATFORM_EXPORT MailboxTextureHolder final : public TextureHolder { - public: - ~MailboxTextureHolder() override; - - // TextureHolder impl. - IntSize Size() const final { - return IntSize(sk_image_info_.width(), sk_image_info_.height()); - } - bool CurrentFrameKnownToBeOpaque() const final { return false; } - bool IsValid() const final; - - bool IsCrossThread() const; - const gpu::Mailbox& GetMailbox() const { return mailbox_; } - const gpu::SyncToken& GetSyncToken() const { - return mailbox_ref()->sync_token(); - } - void UpdateSyncToken(gpu::SyncToken sync_token) { - mailbox_ref()->set_sync_token(sync_token); - } - const SkImageInfo& sk_image_info() const { return sk_image_info_; } - GLenum texture_target() const { return texture_target_; } - - void Sync(MailboxSyncMode); - // In WebGL's commit or transferToImageBitmap calls, it will call the - // DrawingBuffer::transferToStaticBitmapImage function, which produces the - // input parameters for this method. - MailboxTextureHolder(const gpu::Mailbox&, - const gpu::SyncToken&, - unsigned texture_id_to_delete_after_mailbox_consumed, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&, - IntSize mailbox_size, - bool is_origin_top_left); - // This function turns a texture-backed SkImage into a mailbox and a - // syncToken. - MailboxTextureHolder(const SkiaTextureHolder*, GLenum filter); - // This function may be used when the MailboxTextureHolder is created on a - // different thread. The caller must provide a verified sync token if it is - // created cross-thread. - MailboxTextureHolder(const gpu::Mailbox&, - const gpu::SyncToken&, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&, - scoped_refptr<MailboxRef> mailbox_ref, - PlatformThreadId context_thread_id, - const SkImageInfo& sk_image_info, - GLenum texture_target, - bool is_origin_top_left); - - private: - void InitCommon(); - - gpu::Mailbox mailbox_; - unsigned texture_id_; - bool is_converted_from_skia_texture_; - scoped_refptr<base::SingleThreadTaskRunner> texture_thread_task_runner_; - base::PlatformThreadId thread_id_; - bool did_issue_ordering_barrier_ = false; - SkImageInfo sk_image_info_; - GLenum texture_target_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MAILBOX_TEXTURE_HOLDER_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc b/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc new file mode 100644 index 00000000000..3659a862b3e --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc @@ -0,0 +1,49 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h" + +namespace blink { + +MemoryManagedPaintCanvas::MemoryManagedPaintCanvas( + cc::DisplayItemList* list, + const SkRect& bounds, + base::RepeatingClosure set_needs_flush_callback) + : RecordPaintCanvas(list, bounds), + set_needs_flush_callback_(std::move(set_needs_flush_callback)) {} + +MemoryManagedPaintCanvas::~MemoryManagedPaintCanvas() = default; + +void MemoryManagedPaintCanvas::drawImage(const cc::PaintImage& image, + SkScalar left, + SkScalar top, + const cc::PaintFlags* flags) { + DCHECK(!image.IsPaintWorklet()); + RecordPaintCanvas::drawImage(image, left, top, flags); + UpdateMemoryUsage(image); +} + +void MemoryManagedPaintCanvas::drawImageRect( + const cc::PaintImage& image, + const SkRect& src, + const SkRect& dst, + const cc::PaintFlags* flags, + PaintCanvas::SrcRectConstraint constraint) { + RecordPaintCanvas::drawImageRect(image, src, dst, flags, constraint); + UpdateMemoryUsage(image); +} + +void MemoryManagedPaintCanvas::UpdateMemoryUsage(const cc::PaintImage& image) { + if (cached_image_ids_.contains(image.GetContentIdForFrame(0u))) + return; + + cached_image_ids_.insert(image.GetContentIdForFrame(0u)); + total_stored_image_memory_ += + image.GetSkImage()->imageInfo().computeMinByteSize(); + + if (total_stored_image_memory_ > kMaxPinnedMemory) + set_needs_flush_callback_.Run(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h b/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h new file mode 100644 index 00000000000..444d771f899 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h @@ -0,0 +1,52 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MEMORY_MANAGED_PAINT_CANVAS_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MEMORY_MANAGED_PAINT_CANVAS_H_ + +#include <memory> + +#include "cc/paint/record_paint_canvas.h" +#include "third_party/blink/public/platform/platform.h" + +namespace blink { + +// MemoryManagedPaintCanvas overrides the potentially memory intensive image +// drawing methods of PaintCanvas and keeps track of how much memory is +// being pinned between flushes. This allows the rendering context to flush if +// too much memory is used. +class PLATFORM_EXPORT MemoryManagedPaintCanvas final + : public cc::RecordPaintCanvas { + public: + MemoryManagedPaintCanvas(cc::DisplayItemList* list, + const SkRect& bounds, + base::RepeatingClosure set_needs_flush_callback); + explicit MemoryManagedPaintCanvas(const cc::RecordPaintCanvas&) = delete; + ~MemoryManagedPaintCanvas() override; + + void drawImage(const cc::PaintImage& image, + SkScalar left, + SkScalar top, + const cc::PaintFlags* flags) override; + void drawImageRect(const cc::PaintImage& image, + const SkRect& src, + const SkRect& dst, + const cc::PaintFlags* flags, + SrcRectConstraint constraint) override; + + private: + void UpdateMemoryUsage(const cc::PaintImage& image); + + base::flat_set<int> cached_image_ids_; + uint64_t total_stored_image_memory_ = 0; + + base::RepeatingClosure set_needs_flush_callback_; + + // The same value as is used in content::WebGraphicsConext3DProviderImpl. + static constexpr uint64_t kMaxPinnedMemory = 64 * 1024 * 1024; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MEMORY_MANAGED_PAINT_CANVAS_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc b/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc new file mode 100644 index 00000000000..1f179973d11 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.cc @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h" + +namespace blink { + +MemoryManagedPaintRecorder::MemoryManagedPaintRecorder( + base::RepeatingClosure set_needs_flush_callback) + : set_needs_flush_callback_(std::move(set_needs_flush_callback)) {} + +std::unique_ptr<cc::RecordPaintCanvas> MemoryManagedPaintRecorder::CreateCanvas( + cc::DisplayItemList* list, + const SkRect& bounds) { + return std::make_unique<MemoryManagedPaintCanvas>(list, bounds, + set_needs_flush_callback_); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h b/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h new file mode 100644 index 00000000000..1450219da57 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MEMORY_MANAGED_PAINT_RECORDER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MEMORY_MANAGED_PAINT_RECORDER_H_ + +#include "cc/paint/paint_recorder.h" +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h" + +namespace blink { + +class PLATFORM_EXPORT MemoryManagedPaintRecorder : public cc::PaintRecorder { + public: + MemoryManagedPaintRecorder(base::RepeatingClosure set_needs_flush_callback); + + protected: + std::unique_ptr<cc::RecordPaintCanvas> CreateCanvas( + cc::DisplayItemList* list, + const SkRect& bounds) override; + + private: + base::RepeatingClosure set_needs_flush_callback_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MEMORY_MANAGED_PAINT_RECORDER_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/README.md b/chromium/third_party/blink/renderer/platform/graphics/paint/README.md index 6d367c9d5d3..ea389f5ca03 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/README.md +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/README.md @@ -156,6 +156,13 @@ emit display items to a `PaintController` (using `GraphicsContext`). Holds a `PaintRecord` which contains the paint operations required to draw some atom of content. +#### [GraphicsLayerDisplayItem](graphics_layer_display_item.h) + +Placeholder for `GraphicsLayers` allocated by the pre-CompositeAfterPaint +compositing logic. Each one of these may or may not ultimately produce a +`cc::PictureLayer`, depending on the layer squashing mechanism. This class +becomes obsolete with CompositeAfterPaint. + #### [ForeignLayerDisplayItem](foreign_layer_display_item.h) Draws an atom of content, but using a `cc::Layer` produced by some agent outside diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h index b4094008ba1..79156919709 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h @@ -9,6 +9,7 @@ #include "base/memory/scoped_refptr.h" #include "base/optional.h" #include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h" +#include "third_party/blink/renderer/platform/geometry/layout_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_property_node.h" #include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h" @@ -32,14 +33,32 @@ class PLATFORM_EXPORT ClipPaintPropertyNode // To make it less verbose and more readable to construct and update a node, // a struct with default values is used to represent the state. struct State { + State(scoped_refptr<const TransformPaintPropertyNode> local_transform_space, + const FloatRoundedRect& clip_rect) + : State(local_transform_space, clip_rect, clip_rect) {} + + State(scoped_refptr<const TransformPaintPropertyNode> + local_transform_space_arg, + const FloatRoundedRect& clip_rect, + const FloatRoundedRect& pixel_snapped_clip_rect) + : local_transform_space(local_transform_space_arg) { + SetClipRect(clip_rect, pixel_snapped_clip_rect); + } + scoped_refptr<const TransformPaintPropertyNode> local_transform_space; - FloatRoundedRect clip_rect; base::Optional<FloatClipRect> clip_rect_excluding_overlay_scrollbars; scoped_refptr<const RefCountedPath> clip_path; + void SetClipRect(const FloatRoundedRect& clip_rect_arg, + const FloatRoundedRect& pixel_snapped_clip_rect_arg) { + clip_rect = clip_rect_arg; + pixel_snapped_clip_rect = pixel_snapped_clip_rect_arg; + } + PaintPropertyChangeType ComputeChange(const State& other) const { if (local_transform_space != other.local_transform_space || - clip_rect != other.clip_rect || clip_path != other.clip_path) { + pixel_snapped_clip_rect != other.pixel_snapped_clip_rect || + clip_path != other.clip_path) { return PaintPropertyChangeType::kChangedOnlyValues; } if (clip_rect_excluding_overlay_scrollbars != @@ -48,6 +67,12 @@ class PLATFORM_EXPORT ClipPaintPropertyNode } return PaintPropertyChangeType::kUnchanged; } + + friend class ClipPaintPropertyNode; + + private: + FloatRoundedRect clip_rect; + FloatRoundedRect pixel_snapped_clip_rect; }; // This node is really a sentinel, and does not represent a real clip space. @@ -109,8 +134,13 @@ class PLATFORM_EXPORT ClipPaintPropertyNode // a parent alias. return *Unalias().state_.local_transform_space; } - const FloatRoundedRect& ClipRect() const { return state_.clip_rect; } - const FloatClipRect ClipRectExcludingOverlayScrollbars() const { + // The pixel-snapped clip rect may be the same as the unsnapped one, in cases + // where pixel snapping is not desirable for a clip, such as for SVG. + const FloatRoundedRect& PixelSnappedClipRect() const { + return state_.pixel_snapped_clip_rect; + } + const FloatRoundedRect UnsnappedClipRect() const { return state_.clip_rect; } + const FloatClipRect UnsnappedClipRectExcludingOverlayScrollbars() const { return state_.clip_rect_excluding_overlay_scrollbars ? *state_.clip_rect_excluding_overlay_scrollbars : FloatClipRect(state_.clip_rect); diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.cc index bb0ae38e08c..83d30acbb8b 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.cc @@ -42,7 +42,7 @@ static WTF::String PaintPhaseAsDebugString(int paint_phase) { case 8: return "PaintPhaseDescendantOutlinesOnly"; case 9: - return "PaintPhaseOverlayScrollbars"; + return "PaintPhaseOverlayOverflowControls"; case 10: return "PaintPhaseSelection"; case 11: @@ -78,10 +78,10 @@ static WTF::String SpecialDrawingTypeAsDebugString(DisplayItem::Type type) { DEBUG_STRING_CASE(ClippingMask); DEBUG_STRING_CASE(ColumnRules); DEBUG_STRING_CASE(DebugDrawing); + DEBUG_STRING_CASE(DocumentRootBackdrop); DEBUG_STRING_CASE(DocumentBackground); DEBUG_STRING_CASE(DragImage); DEBUG_STRING_CASE(DragCaret); - DEBUG_STRING_CASE(EmptyContentForFilters); DEBUG_STRING_CASE(ForcedColorsModeBackplate); DEBUG_STRING_CASE(SVGImage); DEBUG_STRING_CASE(LinkHighlight); @@ -123,7 +123,7 @@ static String ForeignLayerTypeAsDebugString(DisplayItem::Type type) { DEBUG_STRING_CASE(ForeignLayerDevToolsOverlay); DEBUG_STRING_CASE(ForeignLayerPlugin); DEBUG_STRING_CASE(ForeignLayerVideo); - DEBUG_STRING_CASE(ForeignLayerWrapper); + DEBUG_STRING_CASE(ForeignLayerRemoteFrame); DEBUG_STRING_CASE(ForeignLayerContentsWrapper); DEBUG_STRING_CASE(ForeignLayerLinkHighlight); DEBUG_STRING_CASE(ForeignLayerViewportScroll); @@ -132,6 +132,13 @@ static String ForeignLayerTypeAsDebugString(DisplayItem::Type type) { } } +static String GraphicsLayerWrapperTypeAsDebugString(DisplayItem::Type type) { + switch (type) { + DEBUG_STRING_CASE(GraphicsLayerWrapper); + DEFAULT_CASE; + } +} + WTF::String DisplayItem::TypeAsDebugString(Type type) { if (IsDrawingType(type)) return DrawingTypeAsDebugString(type); @@ -139,6 +146,9 @@ WTF::String DisplayItem::TypeAsDebugString(Type type) { if (IsForeignLayerType(type)) return ForeignLayerTypeAsDebugString(type); + if (IsGraphicsLayerWrapperType(type)) + return GraphicsLayerWrapperTypeAsDebugString(type); + PAINT_PHASE_BASED_DEBUG_STRINGS(Clip); PAINT_PHASE_BASED_DEBUG_STRINGS(Scroll); PAINT_PHASE_BASED_DEBUG_STRINGS(SVGTransform); @@ -149,12 +159,8 @@ WTF::String DisplayItem::TypeAsDebugString(Type type) { DEBUG_STRING_CASE(ScrollHitTest); DEBUG_STRING_CASE(ResizerScrollHitTest); DEBUG_STRING_CASE(PluginScrollHitTest); - DEBUG_STRING_CASE(LayerChunkBackground); - DEBUG_STRING_CASE(LayerChunkNegativeZOrderChildren); - DEBUG_STRING_CASE(LayerChunkDescendantBackgrounds); - DEBUG_STRING_CASE(LayerChunkFloat); + DEBUG_STRING_CASE(LayerChunk); DEBUG_STRING_CASE(LayerChunkForeground); - DEBUG_STRING_CASE(LayerChunkNormalFlowAndPositiveZOrderChildren); DEBUG_STRING_CASE(ScrollbarHorizontal); DEBUG_STRING_CASE(ScrollbarVertical); DEBUG_STRING_CASE(UninitializedType); diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.h index 320f0d0bee5..6d9a09eff7e 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item.h @@ -61,10 +61,10 @@ class PLATFORM_EXPORT DisplayItem { kClippingMask, kColumnRules, kDebugDrawing, + kDocumentRootBackdrop, kDocumentBackground, kDragImage, kDragCaret, - kEmptyContentForFilters, kForcedColorsModeBackplate, kSVGImage, kLinkHighlight, @@ -100,13 +100,17 @@ class PLATFORM_EXPORT DisplayItem { kForeignLayerDevToolsOverlay, kForeignLayerPlugin, kForeignLayerVideo, - kForeignLayerWrapper, + kForeignLayerRemoteFrame, kForeignLayerContentsWrapper, kForeignLayerLinkHighlight, kForeignLayerViewportScroll, kForeignLayerViewportScrollbar, kForeignLayerLast = kForeignLayerViewportScrollbar, + kGraphicsLayerWrapperFirst, + kGraphicsLayerWrapper = kGraphicsLayerWrapperFirst, + kGraphicsLayerWrapperLast = kGraphicsLayerWrapper, + kClipPaintPhaseFirst, kClipPaintPhaseLast = kClipPaintPhaseFirst + kPaintPhaseMax, @@ -119,26 +123,29 @@ class PLATFORM_EXPORT DisplayItem { kSVGEffectPaintPhaseFirst, kSVGEffectPaintPhaseLast = kSVGEffectPaintPhaseFirst + kPaintPhaseMax, + // The following hit test types are for paint chunks containing hit test + // data, when we don't have an previously set explicit chunk id when + // creating the paint chunk, or we need dedicated paint chunk for the hit + // test data. + // Compositor hit testing requires that layers are created and sized to - // include content that does not paint. Hit test display items ensure - // a layer exists and is sized properly even if no content would otherwise - // be painted. + // include content that does not paint. Hit test data ensure a layer exists + // and is sized properly even if no content would otherwise be painted. kHitTest, // Used both for specifying the paint-order scroll location, and for non- - // composited scroll hit testing (see: scroll_hit_test_display_item.h). + // composited scroll hit testing (see: hit_test_data.h). kScrollHitTest, // Used to prevent composited scrolling on the resize handle. kResizerScrollHitTest, // Used to prevent composited scrolling on plugins with wheel handlers. kPluginScrollHitTest, - kLayerChunkBackground, - kLayerChunkNegativeZOrderChildren, - kLayerChunkDescendantBackgrounds, - kLayerChunkFloat, + // These are for paint chunks that are forced for layers. + kLayerChunk, + // This is used if a layer has any negative-z-index children. Otherwise the + // foreground is in the kLayerChunk chunk. kLayerChunkForeground, - kLayerChunkNormalFlowAndPositiveZOrderChildren, // The following 2 types are For ScrollbarDisplayItem. kScrollbarHorizontal, @@ -152,7 +159,7 @@ class PLATFORM_EXPORT DisplayItem { // later paint cycles when |client| may have been destroyed. DisplayItem(const DisplayItemClient& client, Type type, - size_t derived_size, + wtf_size_t derived_size, bool draws_content = false) : client_(&client), visual_rect_(client.VisualRect()), @@ -161,10 +168,11 @@ class PLATFORM_EXPORT DisplayItem { draws_content_(draws_content), fragment_(0), is_cacheable_(client.IsCacheable()), - is_tombstone_(false) { + is_tombstone_(false), + is_moved_from_cached_subsequence_(false) { // |derived_size| must fit in |derived_size_|. // If it doesn't, enlarge |derived_size_| and fix this assert. - SECURITY_DCHECK(derived_size < (1 << 8)); + SECURITY_DCHECK(derived_size < (1 << 7)); SECURITY_DCHECK(derived_size >= sizeof(*this)); derived_size_ = static_cast<unsigned>(derived_size); } @@ -211,7 +219,7 @@ class PLATFORM_EXPORT DisplayItem { // This is not sizeof(*this), because it needs to account for the size of // the derived class (i.e. runtime type). Derived classes are expected to // supply this to the DisplayItem constructor. - size_t DerivedSize() const { return derived_size_; } + wtf_size_t DerivedSize() const { return derived_size_; } // The fragment is part of the id, to uniquely identify display items in // different fragments for the same client and type. @@ -221,6 +229,8 @@ class PLATFORM_EXPORT DisplayItem { fragment_ = fragment; } + void SetVisualRectForTesting(const IntRect& r) { visual_rect_ = r; } + // See comments of enum Type for usage of the following macros. #define DEFINE_CATEGORY_METHODS(Category) \ static constexpr bool Is##Category##Type(Type type) { \ @@ -243,20 +253,13 @@ class PLATFORM_EXPORT DisplayItem { DEFINE_PAINT_PHASE_CONVERSION_METHOD(Drawing) DEFINE_CATEGORY_METHODS(ForeignLayer) + DEFINE_CATEGORY_METHODS(GraphicsLayerWrapper) DEFINE_PAINT_PHASE_CONVERSION_METHOD(Clip) DEFINE_PAINT_PHASE_CONVERSION_METHOD(Scroll) DEFINE_PAINT_PHASE_CONVERSION_METHOD(SVGTransform) DEFINE_PAINT_PHASE_CONVERSION_METHOD(SVGEffect) - bool IsHitTest() const { return type_ == kHitTest; } - bool IsScrollHitTest() const { - return type_ == kScrollHitTest || IsResizerScrollHitTest() || - IsPluginScrollHitTest(); - } - bool IsResizerScrollHitTest() const { return type_ == kResizerScrollHitTest; } - bool IsPluginScrollHitTest() const { return type_ == kPluginScrollHitTest; } - bool IsScrollbar() const { return type_ == kScrollbarHorizontal || type_ == kScrollbarVertical; } @@ -264,6 +267,13 @@ class PLATFORM_EXPORT DisplayItem { bool IsCacheable() const { return is_cacheable_; } void SetUncacheable() { is_cacheable_ = false; } + bool IsMovedFromCachedSubsequence() const { + return is_moved_from_cached_subsequence_; + } + void SetMovedFromCachedSubsequence(bool b) { + is_moved_from_cached_subsequence_ = b; + } + virtual bool Equals(const DisplayItem& other) const { // Failure of this DCHECK would cause bad casts in subclasses. SECURITY_CHECK(!is_tombstone_); @@ -304,10 +314,11 @@ class PLATFORM_EXPORT DisplayItem { static_assert(kTypeLast < (1 << 7), "DisplayItem::Type should fit in 7 bits"); unsigned type_ : 7; unsigned draws_content_ : 1; - unsigned derived_size_ : 8; // size of the actual derived class + unsigned derived_size_ : 7; // size of the actual derived class unsigned fragment_ : 14; unsigned is_cacheable_ : 1; unsigned is_tombstone_ : 1; + unsigned is_moved_from_cached_subsequence_ : 1; }; inline bool operator==(const DisplayItem::Id& a, const DisplayItem::Id& b) { diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h index 5ded583d929..d702c6c758f 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_cache_skipper.h @@ -13,10 +13,11 @@ namespace blink { class DisplayItemCacheSkipper final { - DISALLOW_NEW(); + STACK_ALLOCATED(); public: - DisplayItemCacheSkipper(GraphicsContext& context) : context_(context) { + explicit DisplayItemCacheSkipper(GraphicsContext& context) + : context_(context) { context.GetPaintController().BeginSkippingCache(); } ~DisplayItemCacheSkipper() { diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.h b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.h index d29f0ecee1f..6e108747031 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_client.h @@ -66,20 +66,6 @@ class PLATFORM_EXPORT DisplayItemClient { // Called by PaintController::FinishCycle() for all clients after painting. virtual void ClearPartialInvalidationVisualRect() const {} - // This is declared here instead of in LayoutObject for verifying the - // condition in DrawingRecorder. - // Returns true if the object itself will not generate any effective painted - // output no matter what size the object is. For example, this function can - // return false for an object whose size is currently 0x0 but would have - // effective painted output if it was set a non-empty size. It's used to skip - // unforced paint invalidation of LayoutObjects (which is when - // shouldDoFullPaintInvalidation is false, but mayNeedPaintInvalidation or - // childShouldCheckForPaintInvalidation is true) to avoid unnecessary paint - // invalidations of empty areas covered by such objects. - virtual bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { - return false; - } - // Indicates that the client will paint display items different from the ones // cached by PaintController. However, PaintController allows a client to // paint new display items that are not cached or to no longer paint some diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.cc index bb15ec6a712..d2f05789294 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.cc @@ -24,17 +24,17 @@ DisplayItemList::ItemsInPaintChunk(const PaintChunk& paint_chunk) const { #if DCHECK_IS_ON() -std::unique_ptr<JSONArray> DisplayItemList::SubsequenceAsJSON( - size_t begin_index, - size_t end_index, +std::unique_ptr<JSONArray> DisplayItemList::DisplayItemsAsJSON( + wtf_size_t begin_index, + wtf_size_t end_index, JsonFlags flags) const { auto json_array = std::make_unique<JSONArray>(); AppendSubsequenceAsJSON(begin_index, end_index, flags, *json_array); return json_array; } -void DisplayItemList::AppendSubsequenceAsJSON(size_t begin_index, - size_t end_index, +void DisplayItemList::AppendSubsequenceAsJSON(wtf_size_t begin_index, + wtf_size_t end_index, JsonFlags flags, JSONArray& json_array) const { if (flags & kCompact) { @@ -42,12 +42,12 @@ void DisplayItemList::AppendSubsequenceAsJSON(size_t begin_index, << "kCompact cannot show paint records"; DCHECK(!(flags & kShowOnlyDisplayItemTypes)) << "kCompact cannot show display item types"; - for (size_t i = begin_index; i < end_index; ++i) { + for (auto i = begin_index; i < end_index; ++i) { const auto& item = (*this)[i]; json_array.PushString(item.GetId().ToString()); } } else { - for (size_t i = begin_index; i < end_index; ++i) { + for (auto i = begin_index; i < end_index; ++i) { auto json = std::make_unique<JSONObject>(); const auto& item = (*this)[i]; diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.h b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.h index d1f45b6e0d9..78b664c7902 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_list.h @@ -19,14 +19,16 @@ struct PaintChunk; // each derived display item; the ideal value is the least common multiple. // The validity of kDisplayItemAlignment and kMaximumDisplayItemSize are checked // in PaintController::CreateAndAppend(). -static const size_t kDisplayItemAlignment = alignof(ScrollbarDisplayItem); -static const size_t kMaximumDisplayItemSize = sizeof(ScrollbarDisplayItem); +static constexpr wtf_size_t kDisplayItemAlignment = + alignof(ScrollbarDisplayItem); +static constexpr wtf_size_t kMaximumDisplayItemSize = + sizeof(ScrollbarDisplayItem); // A container for a list of display items. class PLATFORM_EXPORT DisplayItemList : public ContiguousContainer<DisplayItem, kDisplayItemAlignment> { public: - DisplayItemList(size_t initial_size_bytes) + DisplayItemList(wtf_size_t initial_size_bytes) : ContiguousContainer(kMaximumDisplayItemSize, initial_size_bytes) {} DisplayItemList(DisplayItemList&& source) : ContiguousContainer(std::move(source)) {} @@ -42,7 +44,8 @@ class PLATFORM_EXPORT DisplayItemList ContiguousContainer::AppendByMoving(item, item.DerivedSize()); // ContiguousContainer::AppendByMoving() calls an in-place constructor // on item which replaces it with a tombstone/"dead display item" that - // can be safely destructed but should never be used except for debugging. + // can be safely destructed but should never be used except for debugging + // and raster invalidation (see below). DCHECK(item.IsTombstone()); // We need |visual_rect_| and |outset_for_raster_effects_| of the old // display item for raster invalidation. Also, the fields that make up the @@ -56,6 +59,7 @@ class PLATFORM_EXPORT DisplayItemList DCHECK(item.GetId() == result.GetId()); item.visual_rect_ = result.visual_rect_; item.outset_for_raster_effects_ = result.outset_for_raster_effects_; + result.SetMovedFromCachedSubsequence(false); return result; } @@ -87,11 +91,11 @@ class PLATFORM_EXPORT DisplayItemList }; typedef unsigned JsonFlags; - std::unique_ptr<JSONArray> SubsequenceAsJSON(size_t begin_index, - size_t end_index, - JsonFlags) const; - void AppendSubsequenceAsJSON(size_t begin_index, - size_t end_index, + std::unique_ptr<JSONArray> DisplayItemsAsJSON(wtf_size_t begin_index, + wtf_size_t end_index, + JsonFlags) const; + void AppendSubsequenceAsJSON(wtf_size_t begin_index, + wtf_size_t end_index, JsonFlags, JSONArray&) const; #endif // DCHECK_IS_ON() diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.cc index 2d2433bf88a..4cba6b8b266 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.cc @@ -22,12 +22,12 @@ void DisplayItemRasterInvalidator::Generate() { Vector<bool> old_display_items_matched; old_display_items_matched.resize(old_chunk_.size()); - size_t next_old_item_to_match = old_chunk_.begin_index; - size_t max_cached_old_index = next_old_item_to_match; + auto next_old_item_to_match = old_chunk_.begin_index; + auto max_cached_old_index = next_old_item_to_match; for (const auto& new_item : new_paint_artifact_.GetDisplayItemList().ItemsInPaintChunk(new_chunk_)) { - size_t matched_old_index = + auto matched_old_index = MatchNewDisplayItemInOldChunk(new_item, next_old_item_to_match); if (matched_old_index == kNotFound) { if (new_item.DrawsContent()) { @@ -69,7 +69,7 @@ void DisplayItemRasterInvalidator::Generate() { value.reason = reason; } - size_t offset = matched_old_index - old_chunk_.begin_index; + wtf_size_t offset = matched_old_index - old_chunk_.begin_index; DCHECK(!old_display_items_matched[offset]); old_display_items_matched[offset] = true; @@ -80,7 +80,7 @@ void DisplayItemRasterInvalidator::Generate() { } // Invalidate remaining unmatched (disappeared or uncacheable) old items. - for (size_t i = old_chunk_.begin_index; i < old_chunk_.end_index; ++i) { + for (auto i = old_chunk_.begin_index; i < old_chunk_.end_index; ++i) { if (old_display_items_matched[i - old_chunk_.begin_index]) continue; @@ -97,9 +97,9 @@ void DisplayItemRasterInvalidator::Generate() { } } -size_t DisplayItemRasterInvalidator::MatchNewDisplayItemInOldChunk( +wtf_size_t DisplayItemRasterInvalidator::MatchNewDisplayItemInOldChunk( const DisplayItem& new_item, - size_t& next_old_item_to_match) { + wtf_size_t& next_old_item_to_match) { if (!new_item.IsCacheable()) return kNotFound; for (; next_old_item_to_match < old_chunk_.end_index; @@ -111,7 +111,7 @@ size_t DisplayItemRasterInvalidator::MatchNewDisplayItemInOldChunk( if (old_item.GetId() == new_item.GetId()) return next_old_item_to_match++; // Add the skipped old item into index. - old_display_items_index_.insert(&old_item.Client(), Vector<size_t>()) + old_display_items_index_.insert(&old_item.Client(), Vector<wtf_size_t>()) .stored_value->value.push_back(next_old_item_to_match); } @@ -119,7 +119,7 @@ size_t DisplayItemRasterInvalidator::MatchNewDisplayItemInOldChunk( auto it = old_display_items_index_.find(&new_item.Client()); if (it == old_display_items_index_.end()) return kNotFound; - for (size_t i : it->value) { + for (auto i : it->value) { const auto& old_item = old_paint_artifact_.GetDisplayItemList()[i]; if (old_item.GetId() == new_item.GetId()) return i; @@ -136,7 +136,8 @@ void DisplayItemRasterInvalidator::AddRasterInvalidation( if (r.IsEmpty()) return; - invalidator_.AddRasterInvalidation(r, client, reason, old_or_new); + invalidator_.AddRasterInvalidation(raster_invalidation_function_, r, client, + reason, old_or_new); } void DisplayItemRasterInvalidator::GenerateRasterInvalidation( diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.h b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.h index b37e15998eb..10828508e58 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator.h @@ -15,13 +15,16 @@ class DisplayItemRasterInvalidator { STACK_ALLOCATED(); public: - DisplayItemRasterInvalidator(RasterInvalidator& invalidator, - const PaintArtifact& old_paint_artifact, - const PaintArtifact& new_paint_artifact, - const PaintChunk& old_chunk, - const PaintChunk& new_chunk, - const ChunkToLayerMapper& mapper) + DisplayItemRasterInvalidator( + RasterInvalidator& invalidator, + RasterInvalidator::RasterInvalidationFunction function, + const PaintArtifact& old_paint_artifact, + const PaintArtifact& new_paint_artifact, + const PaintChunk& old_chunk, + const PaintChunk& new_chunk, + const ChunkToLayerMapper& mapper) : invalidator_(invalidator), + raster_invalidation_function_(function), old_paint_artifact_(old_paint_artifact), new_paint_artifact_(new_paint_artifact), old_chunk_(old_chunk), @@ -40,9 +43,9 @@ class DisplayItemRasterInvalidator { const IntRect&, PaintInvalidationReason, RasterInvalidator::ClientIsOldOrNew); - ALWAYS_INLINE size_t + ALWAYS_INLINE wtf_size_t MatchNewDisplayItemInOldChunk(const DisplayItem& new_item, - size_t& next_old_item_to_match); + wtf_size_t& next_old_item_to_match); ALWAYS_INLINE void GenerateRasterInvalidation(const DisplayItemClient&, const IntRect* old_visual_rect, const IntRect* new_visual_rect, @@ -58,13 +61,15 @@ class DisplayItemRasterInvalidator { PaintInvalidationReason reason); RasterInvalidator& invalidator_; + RasterInvalidator::RasterInvalidationFunction raster_invalidation_function_; const PaintArtifact& old_paint_artifact_; const PaintArtifact& new_paint_artifact_; const PaintChunk& old_chunk_; const PaintChunk& new_chunk_; const ChunkToLayerMapper& mapper_; // Maps clients to indices of display items in old_chunk_. - HashMap<const DisplayItemClient*, Vector<size_t>> old_display_items_index_; + HashMap<const DisplayItemClient*, Vector<wtf_size_t>> + old_display_items_index_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc index f3d5ff7a79f..b0eee71af71 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc @@ -19,12 +19,12 @@ using ::testing::UnorderedElementsAre; class DisplayItemRasterInvalidatorTest : public PaintControllerTestBase, public PaintTestConfigurations { protected: - DisplayItemRasterInvalidatorTest() : invalidator_(base::DoNothing()) {} + DisplayItemRasterInvalidatorTest() = default; Vector<RasterInvalidationInfo> GenerateRasterInvalidations() { GetPaintController().CommitNewDisplayItems(); invalidator_.Generate( - GetPaintController().GetPaintArtifactShared(), + base::DoNothing(), GetPaintController().GetPaintArtifactShared(), // The layer rect is big enough not to clip display item raster // invalidation rects. IntRect(0, 0, 20000, 20000), PropertyTreeState::Root()); @@ -461,25 +461,27 @@ TEST_P(DisplayItemRasterInvalidatorTest, SwapOrderCrossingChunks) { auto container2_properties = DefaultPaintChunkProperties(); container2_properties.SetEffect(*container2_effect); - GetPaintController().UpdateCurrentPaintChunkProperties( - PaintChunk::Id(container1, kBackgroundType), container1_properties); + PaintChunk::Id container1_id(container1, kBackgroundType); + PaintChunk::Id container2_id(container2, kBackgroundType); + GetPaintController().UpdateCurrentPaintChunkProperties(&container1_id, + container1_properties); DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100)); DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200)); - GetPaintController().UpdateCurrentPaintChunkProperties( - PaintChunk::Id(container2, kBackgroundType), container2_properties); + GetPaintController().UpdateCurrentPaintChunkProperties(&container2_id, + container2_properties); DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100)); DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200)); GenerateRasterInvalidations(); // Move content2 into container1, without invalidation. invalidator_.SetTracksRasterInvalidations(true); - GetPaintController().UpdateCurrentPaintChunkProperties( - PaintChunk::Id(container1, kBackgroundType), container1_properties); + GetPaintController().UpdateCurrentPaintChunkProperties(&container1_id, + container1_properties); DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100)); DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200)); DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200)); - GetPaintController().UpdateCurrentPaintChunkProperties( - PaintChunk::Id(container2, kBackgroundType), container2_properties); + GetPaintController().UpdateCurrentPaintChunkProperties(&container2_id, + container2_properties); DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100)); EXPECT_THAT(GenerateRasterInvalidations(), diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.cc index 0ab4e34249c..5e150bb70f9 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.cc @@ -11,14 +11,9 @@ #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkData.h" -namespace blink { +#include "third_party/blink/renderer/platform/graphics/logging_canvas.h" -#if DCHECK_IS_ON() -void DrawingDisplayItem::PropertiesAsJSON(JSONObject& json) const { - DisplayItem::PropertiesAsJSON(json); - json.SetBoolean("opaque", known_to_be_opaque_); -} -#endif +namespace blink { static SkBitmap RecordToBitmap(sk_sp<const PaintRecord> record, const IntRect& bounds) { @@ -83,4 +78,114 @@ bool DrawingDisplayItem::Equals(const DisplayItem& other) const { return BitmapsEqual(std::move(record), std::move(other_record), bounds); } +SkColor DrawingDisplayItem::BackgroundColor() const { + if (GetType() != DisplayItem::kBoxDecorationBackground && + GetType() != DisplayItem::kDocumentBackground) + return SK_ColorTRANSPARENT; + + if (!record_) + return SK_ColorTRANSPARENT; + + for (cc::PaintOpBuffer::Iterator it(record_.get()); it; ++it) { + const auto* op = *it; + if (op->GetType() == cc::PaintOpType::DrawRect || + op->GetType() == cc::PaintOpType::DrawRRect) { + const auto& flags = static_cast<const cc::PaintOpWithFlags*>(op)->flags; + // Skip op with looper or shader which may modify the color. + if (!flags.getLooper() && !flags.getShader() && + flags.getStyle() == cc::PaintFlags::kFill_Style) + return flags.getColor(); + } + } + return SK_ColorTRANSPARENT; +} + +// This is not a PaintRecord method because it's not a general opaqueness +// detection algorithm (which might be more complex and slower), but works well +// and fast for most blink painted results. +bool DrawingDisplayItem::CalculateKnownToBeOpaque( + const PaintRecord* record) const { + if (!record) + return false; + + // This limit keeps the algorithm fast, while allowing check of enough paint + // operations for most blink painted results. + constexpr wtf_size_t kOpCountLimit = 4; + wtf_size_t op_count = 0; + for (cc::PaintOpBuffer::Iterator it(record); it; ++it) { + if (++op_count > kOpCountLimit) + return false; + + const auto* op = *it; + // Deal with the common pattern of clipped bleed avoiding images like: + // Save, ClipRect, Draw..., Restore. + if (op->GetType() == cc::PaintOpType::Save) + continue; + if (op->GetType() == cc::PaintOpType::ClipRect) { + const auto* clip_rect_op = static_cast<const cc::ClipRectOp*>(op); + if (!EnclosedIntRect(clip_rect_op->rect).Contains(VisualRect())) + return false; + continue; + } + + if (!op->IsDrawOp()) + return false; + + if (op->GetType() == cc::PaintOpType::DrawRecord) { + return CalculateKnownToBeOpaque( + static_cast<const cc::DrawRecordOp*>(op)->record.get()); + } + + if (!op->IsPaintOpWithFlags()) + continue; + + const auto& flags = static_cast<const cc::PaintOpWithFlags*>(op)->flags; + if (flags.getStyle() != cc::PaintFlags::kFill_Style || flags.getLooper() || + (flags.getBlendMode() != SkBlendMode::kSrc && + flags.getBlendMode() != SkBlendMode::kSrcOver) || + flags.getMaskFilter() || flags.getColorFilter() || + flags.getImageFilter() || flags.getAlpha() != SK_AlphaOPAQUE || + (flags.getShader() && !flags.getShader()->IsOpaque())) + continue; + + IntRect opaque_rect; + switch (op->GetType()) { + case cc::PaintOpType::DrawRect: + opaque_rect = + EnclosedIntRect(static_cast<const cc::DrawRectOp*>(op)->rect); + break; + case cc::PaintOpType::DrawIRect: + opaque_rect = IntRect(static_cast<const cc::DrawIRectOp*>(op)->rect); + break; + case cc::PaintOpType::DrawImage: { + const auto* draw_image_op = static_cast<const cc::DrawImageOp*>(op); + const auto& image = draw_image_op->image; + if (!image.IsOpaque()) + continue; + opaque_rect = IntRect(draw_image_op->left, draw_image_op->top, + image.width(), image.height()); + break; + } + case cc::PaintOpType::DrawImageRect: { + const auto* draw_image_rect_op = + static_cast<const cc::DrawImageRectOp*>(op); + const auto& image = draw_image_rect_op->image; + DCHECK(SkRect::MakeWH(image.width(), image.height()) + .contains(draw_image_rect_op->src)); + if (!image.IsOpaque()) + continue; + opaque_rect = EnclosedIntRect(draw_image_rect_op->dst); + break; + } + default: + continue; + } + + // We should never paint outside of the visual rect. + if (opaque_rect.Contains(VisualRect())) + return true; + } + return false; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h index 9f3c691c5ff..a6fa2cd2fa4 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h @@ -25,46 +25,45 @@ namespace blink { // PaintRecord, and is in the space of the DisplayItemList. This allows the // visual_rect to be compared between DrawingDisplayItems, and to give bounds // around what the user can actually see from the PaintRecord. -class PLATFORM_EXPORT DrawingDisplayItem final : public DisplayItem { +class PLATFORM_EXPORT DrawingDisplayItem : public DisplayItem { public: DISABLE_CFI_PERF DrawingDisplayItem(const DisplayItemClient& client, Type type, - sk_sp<const PaintRecord> record, - bool known_to_be_opaque = false); + sk_sp<const PaintRecord> record); const sk_sp<const PaintRecord>& GetPaintRecord() const { return record_; } + bool Equals(const DisplayItem& other) const final; + bool KnownToBeOpaque() const { - DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); - return known_to_be_opaque_; + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) + return false; + if (!known_to_be_opaque_.has_value()) + known_to_be_opaque_.emplace(CalculateKnownToBeOpaque(record_.get())); + return *known_to_be_opaque_; } + void SetKnownToBeOpaqueForTesting() { known_to_be_opaque_.emplace(true); } - bool Equals(const DisplayItem& other) const final; + SkColor BackgroundColor() const; private: -#if DCHECK_IS_ON() - void PropertiesAsJSON(JSONObject&) const final; -#endif + bool CalculateKnownToBeOpaque(const PaintRecord*) const; sk_sp<const PaintRecord> record_; - - // True if there are no transparent areas. Only used for CompositeAfterPaint. - const bool known_to_be_opaque_; + mutable base::Optional<bool> known_to_be_opaque_; }; // TODO(dcheng): Move this ctor back inline once the clang plugin is fixed. DISABLE_CFI_PERF inline DrawingDisplayItem::DrawingDisplayItem(const DisplayItemClient& client, Type type, - sk_sp<const PaintRecord> record, - bool known_to_be_opaque) + sk_sp<const PaintRecord> record) : DisplayItem(client, type, sizeof(*this), /* draws_content*/ record && record->size()), - record_(DrawsContent() ? std::move(record) : nullptr), - known_to_be_opaque_(known_to_be_opaque) { + record_(DrawsContent() ? std::move(record) : nullptr) { DCHECK(IsDrawingType(type)); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.cc index ccf85e0d22f..837a65d6dc8 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.cc @@ -23,31 +23,36 @@ DrawingRecorder::DrawingRecorder(GraphicsContext& context, DisplayItem::Type display_item_type) : context_(context), client_(display_item_client), - type_(display_item_type), - known_to_be_opaque_(false) + type_(display_item_type) #if DCHECK_IS_ON() , initial_display_item_list_size_( context_.GetPaintController().NewDisplayItemList().size()) #endif { - if (context.GetPaintController().DisplayItemConstructionIsDisabled()) - return; - // Must check DrawingRecorder::UseCachedDrawingIfPossible before creating the // DrawingRecorder. DCHECK(RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() || + context_.GetPaintController().ShouldForcePaintForBenchmark() || !UseCachedDrawingIfPossible(context_, client_, type_)); DCHECK(DisplayItem::IsDrawingType(display_item_type)); context.SetInDrawingRecorder(true); context.BeginRecording(FloatRect()); + + if (context.Printing()) { + DOMNodeId dom_node_id = display_item_client.OwnerNodeId(); + if (dom_node_id != kInvalidDOMNodeId) { + dom_node_id_to_restore_ = context.GetDOMNodeId(); + context.SetDOMNodeId(dom_node_id); + } + } } DrawingRecorder::~DrawingRecorder() { - if (context_.GetPaintController().DisplayItemConstructionIsDisabled()) - return; + if (context_.Printing() && dom_node_id_to_restore_) + context_.SetDOMNodeId(dom_node_id_to_restore_.value()); context_.SetInDrawingRecorder(false); @@ -58,20 +63,8 @@ DrawingRecorder::~DrawingRecorder() { } #endif - sk_sp<const PaintRecord> picture = context_.EndRecording(); - -#if DCHECK_IS_ON() - // When skipping cache (e.g. in PaintRecordBuilder with a temporary - // PaintController), the client's painting might be different from its normal - // painting. - if (!context_.GetPaintController().IsSkippingCache() && - client_.PaintedOutputOfObjectHasNoEffectRegardlessOfSize()) { - DCHECK_EQ(0u, picture->size()) << client_.DebugName(); - } -#endif - context_.GetPaintController().CreateAndAppend<DrawingDisplayItem>( - client_, type_, std::move(picture), known_to_be_opaque_); + client_, type_, context_.EndRecording()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h index 3c97d2c2445..3437f8d6ed6 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h @@ -21,7 +21,7 @@ namespace blink { class GraphicsContext; class PLATFORM_EXPORT DrawingRecorder final { - DISALLOW_NEW(); + STACK_ALLOCATED(); public: static bool UseCachedDrawingIfPossible(GraphicsContext& context, @@ -50,22 +50,15 @@ class PLATFORM_EXPORT DrawingRecorder final { ~DrawingRecorder(); - void SetKnownToBeOpaque() { - DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); - known_to_be_opaque_ = true; - } - private: GraphicsContext& context_; const DisplayItemClient& client_; const DisplayItem::Type type_; - - // True if there are no transparent areas. Only used for CompositeAfterPaint. - bool known_to_be_opaque_; + base::Optional<DOMNodeId> dom_node_id_to_restore_; #if DCHECK_IS_ON() // Ensures the list size does not change during the recorder's scope. - size_t initial_display_item_list_size_; + wtf_size_t initial_display_item_list_size_; #endif DISALLOW_COPY_AND_ASSIGN(DrawingRecorder); diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h b/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h index 480ff218deb..9c039137fd9 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h @@ -249,6 +249,13 @@ class PLATFORM_EXPORT EffectPaintPropertyNode // CompositingReason::kActiveBackdropFilterAnimation; } + // Whether the effect node uses the backdrop as an input. This includes + // exotic blending modes and backdrop filters. + bool HasBackdropEffect() const { + return BlendMode() != SkBlendMode::kSrcOver || + !BackdropFilter().IsEmpty() || HasActiveBackdropFilterAnimation(); + } + CompositingReasons DirectCompositingReasonsForDebugging() const { return DirectCompositingReasons(); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h b/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h index c11b72e0597..d25a8428606 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h @@ -132,6 +132,16 @@ inline bool operator!=(const FloatClipRect& a, const FloatClipRect& b) { return !(a == b); } +// The difference between FloatClipRect() (infinite by default) and +// InfiniteLooseFloatClipRect() is that the former means no clip at all, and the +// latter means a clip with unknown bounds. The intersection of the former with +// a tight clip rect is tight, and that of the latter is loose. +inline FloatClipRect InfiniteLooseFloatClipRect() { + FloatClipRect rect; + rect.ClearIsTight(); + return rect; +} + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_FLOAT_CLIP_RECT_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.cc index daa76c1b077..690644d7dd0 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.cc @@ -93,30 +93,25 @@ void ForeignLayerDisplayItem::PropertiesAsJSON(JSONObject& json) const { } #endif -static void RecordForeignLayerInternal( - GraphicsContext& context, - const DisplayItemClient& client, - DisplayItem::Type type, - scoped_refptr<cc::Layer> layer, - const FloatPoint& offset, - const LayerAsJSONClient* json_client, - const base::Optional<PropertyTreeState>& properties) { +static void RecordForeignLayerInternal(GraphicsContext& context, + const DisplayItemClient& client, + DisplayItem::Type type, + scoped_refptr<cc::Layer> layer, + const FloatPoint& offset, + const LayerAsJSONClient* json_client, + const PropertyTreeState* properties) { PaintController& paint_controller = context.GetPaintController(); - if (paint_controller.DisplayItemConstructionIsDisabled()) - return; - // This is like ScopedPaintChunkProperties but uses null id because foreign // layer chunk doesn't need an id nor a client. base::Optional<PropertyTreeState> previous_properties; if (properties) { previous_properties.emplace(paint_controller.CurrentPaintChunkProperties()); - paint_controller.UpdateCurrentPaintChunkProperties(base::nullopt, - *properties); + paint_controller.UpdateCurrentPaintChunkProperties(nullptr, *properties); } paint_controller.CreateAndAppend<ForeignLayerDisplayItem>( client, type, std::move(layer), offset, json_client); if (properties) { - paint_controller.UpdateCurrentPaintChunkProperties(base::nullopt, + paint_controller.UpdateCurrentPaintChunkProperties(nullptr, *previous_properties); } } @@ -126,25 +121,9 @@ void RecordForeignLayer(GraphicsContext& context, DisplayItem::Type type, scoped_refptr<cc::Layer> layer, const FloatPoint& offset, - const base::Optional<PropertyTreeState>& properties) { + const PropertyTreeState* properties) { RecordForeignLayerInternal(context, client, type, std::move(layer), offset, nullptr, properties); } -void RecordGraphicsLayerAsForeignLayer(GraphicsContext& context, - DisplayItem::Type type, - const GraphicsLayer& graphics_layer) { - // In pre-CompositeAfterPaint, the GraphicsLayer hierarchy is still built - // during CompositingUpdate, and we have to clear them here to ensure no - // extraneous layers are still attached. In future we will disable all - // those layer hierarchy code so we won't need this line. - DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); - graphics_layer.CcLayer()->RemoveAllChildren(); - - RecordForeignLayerInternal( - context, graphics_layer, type, graphics_layer.CcLayer(), - FloatPoint(graphics_layer.GetOffsetFromTransformNode()), &graphics_layer, - graphics_layer.GetPropertyTreeState()); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h index 95e3c4162f5..7e662a566ef 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h @@ -13,7 +13,6 @@ namespace blink { class GraphicsContext; -class GraphicsLayer; class LayerAsJSONClient; // Represents foreign content (produced outside Blink) which draws to a layer. @@ -22,7 +21,7 @@ class LayerAsJSONClient; // // Before CAP, this content is not painted, but is instead inserted into the // GraphicsLayer tree. -class PLATFORM_EXPORT ForeignLayerDisplayItem final : public DisplayItem { +class PLATFORM_EXPORT ForeignLayerDisplayItem : public DisplayItem { public: ForeignLayerDisplayItem(const DisplayItemClient& client, Type, @@ -36,9 +35,9 @@ class PLATFORM_EXPORT ForeignLayerDisplayItem final : public DisplayItem { const LayerAsJSONClient* GetLayerAsJSONClient() const; // DisplayItem - bool Equals(const DisplayItem&) const override; + bool Equals(const DisplayItem&) const final; #if DCHECK_IS_ON() - void PropertiesAsJSON(JSONObject&) const override; + void PropertiesAsJSON(JSONObject&) const final; #endif FloatPoint Offset() const { return offset_; } @@ -75,13 +74,7 @@ PLATFORM_EXPORT void RecordForeignLayer( DisplayItem::Type type, scoped_refptr<cc::Layer> layer, const FloatPoint& offset, - const base::Optional<PropertyTreeState>& = base::nullopt); - -// Records a graphics layer into a GraphicsContext. -PLATFORM_EXPORT void RecordGraphicsLayerAsForeignLayer( - GraphicsContext& context, - DisplayItem::Type type, - const GraphicsLayer& graphics_layer); + const PropertyTreeState* properties = nullptr); } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc index 901ab3cfdfe..ba8f4937747 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc @@ -13,8 +13,10 @@ GeometryMapper::Translation2DOrMatrix GeometryMapper::SourceToDestinationProjection( const TransformPaintPropertyNode& source, const TransformPaintPropertyNode& destination) { - bool success; - return SourceToDestinationProjectionInternal(source, destination, success); + bool has_animation = false; + bool success = false; + return SourceToDestinationProjectionInternal(source, destination, + has_animation, success); } // Returns flatten(destination_to_screen)^-1 * flatten(source_to_screen) @@ -60,32 +62,35 @@ GeometryMapper::Translation2DOrMatrix GeometryMapper::SourceToDestinationProjectionInternal( const TransformPaintPropertyNode& source_arg, const TransformPaintPropertyNode& destination_arg, + bool& has_animation, bool& success) { const auto& source = source_arg.Unalias(); const auto& destination = destination_arg.Unalias(); + has_animation = false; + success = true; - if (&source == &destination) { - success = true; + if (&source == &destination) return Translation2DOrMatrix(); - } if (source.Parent() && &destination == &source.Parent()->Unalias()) { if (source.IsIdentityOr2DTranslation()) { - success = true; + // We always use full matrix for animating transforms. + DCHECK(!source.HasActiveTransformAnimation()); return Translation2DOrMatrix(source.Translation2D()); } // The result will be translate(origin)*matrix*translate(-origin) which // equals to matrix if the origin is zero or if the matrix is just // identity or 2d translation. if (source.Origin().IsZero()) { - success = true; + has_animation = source.HasActiveTransformAnimation(); return Translation2DOrMatrix(source.Matrix()); } } if (destination.IsIdentityOr2DTranslation() && destination.Parent() && &source == &destination.Parent()->Unalias()) { - success = true; + // We always use full matrix for animating transforms. + DCHECK(!destination.HasActiveTransformAnimation()); return Translation2DOrMatrix(-destination.Translation2D()); } @@ -96,7 +101,7 @@ GeometryMapper::SourceToDestinationProjectionInternal( // the same 2d translation root. if (source_cache.root_of_2d_translation() == destination_cache.root_of_2d_translation()) { - success = true; + // We always use full matrix for animating transforms. return Translation2DOrMatrix(source_cache.to_2d_translation_root() - destination_cache.to_2d_translation_root()); } @@ -105,7 +110,8 @@ GeometryMapper::SourceToDestinationProjectionInternal( // Even if destination may have invertible screen projection, // this formula is likely to be numerically more stable. if (source_cache.plane_root() == destination_cache.plane_root()) { - success = true; + has_animation = source_cache.has_animation_to_plane_root() || + destination_cache.has_animation_to_plane_root(); if (&source == destination_cache.plane_root()) { return Translation2DOrMatrix(destination_cache.from_plane_root()); } @@ -124,6 +130,8 @@ GeometryMapper::SourceToDestinationProjectionInternal( // Screen transform data are updated lazily because they are rarely used. source.UpdateScreenTransform(); destination.UpdateScreenTransform(); + has_animation = source_cache.has_animation_to_screen() || + destination_cache.has_animation_to_screen(); if (!destination_cache.projection_from_screen_is_valid()) { success = false; return Translation2DOrMatrix(); @@ -132,7 +140,6 @@ GeometryMapper::SourceToDestinationProjectionInternal( // Case 3: Compute: // flatten(destination_to_screen)^-1 * flatten(source_to_screen) const auto& root = TransformPaintPropertyNode::Root(); - success = true; if (&source == &root) return Translation2DOrMatrix(destination_cache.projection_from_screen()); TransformationMatrix matrix; @@ -147,11 +154,12 @@ bool GeometryMapper::LocalToAncestorVisualRect( const PropertyTreeState& ancestor_state, FloatClipRect& mapping_rect, OverlayScrollbarClipBehavior clip_behavior, - InclusiveIntersectOrNot inclusive_behavior) { + InclusiveIntersectOrNot inclusive_behavior, + ExpandVisualRectForAnimationOrNot expand_for_animation) { bool success = false; - bool result = LocalToAncestorVisualRectInternal(local_state, ancestor_state, - mapping_rect, clip_behavior, - inclusive_behavior, success); + bool result = LocalToAncestorVisualRectInternal( + local_state, ancestor_state, mapping_rect, clip_behavior, + inclusive_behavior, expand_for_animation, success); DCHECK(success); return result; } @@ -162,6 +170,7 @@ bool GeometryMapper::LocalToAncestorVisualRectInternal( FloatClipRect& rect_to_map, OverlayScrollbarClipBehavior clip_behavior, InclusiveIntersectOrNot inclusive_behavior, + ExpandVisualRectForAnimationOrNot expand_for_animation, bool& success) { if (local_state == ancestor_state) { success = true; @@ -171,11 +180,13 @@ bool GeometryMapper::LocalToAncestorVisualRectInternal( if (&local_state.Effect().Unalias() != &ancestor_state.Effect().Unalias()) { return SlowLocalToAncestorVisualRectWithEffects( local_state, ancestor_state, rect_to_map, clip_behavior, - inclusive_behavior, success); + inclusive_behavior, expand_for_animation, success); } + bool has_animation = false; const auto& translation_2d_or_matrix = SourceToDestinationProjectionInternal( - local_state.Transform(), ancestor_state.Transform(), success); + local_state.Transform(), ancestor_state.Transform(), has_animation, + success); if (!success) { // A failure implies either source-to-plane or destination-to-plane being // singular. A notable example of singular source-to-plane from valid CSS: @@ -192,11 +203,19 @@ bool GeometryMapper::LocalToAncestorVisualRectInternal( rect_to_map = FloatClipRect(FloatRect()); return false; } - translation_2d_or_matrix.MapFloatClipRect(rect_to_map); + + if (has_animation && expand_for_animation == kExpandVisualRectForAnimation) { + // Assume during the animation the transform can map |rect_to_map| to + // anywhere. Ancestor clips will still apply. + // TODO(crbug.com/1026653): Use animation bounds instead of infinite rect. + rect_to_map = InfiniteLooseFloatClipRect(); + } else { + translation_2d_or_matrix.MapFloatClipRect(rect_to_map); + } FloatClipRect clip_rect = LocalToAncestorClipRectInternal( local_state.Clip(), ancestor_state.Clip(), ancestor_state.Transform(), - clip_behavior, inclusive_behavior, success); + clip_behavior, inclusive_behavior, expand_for_animation, success); if (success) { // This is where we propagate the roundedness and tightness of |clip_rect| // to |rect_to_map|. @@ -225,6 +244,7 @@ bool GeometryMapper::SlowLocalToAncestorVisualRectWithEffects( FloatClipRect& mapping_rect, OverlayScrollbarClipBehavior clip_behavior, InclusiveIntersectOrNot inclusive_behavior, + ExpandVisualRectForAnimationOrNot expand_for_animation, bool& success) { PropertyTreeState last_transform_and_clip_state( local_state.Transform(), local_state.Clip(), @@ -234,16 +254,26 @@ bool GeometryMapper::SlowLocalToAncestorVisualRectWithEffects( for (const auto* effect = &local_state.Effect().Unalias(); effect && effect != &ancestor_effect; effect = SafeUnalias(effect->Parent())) { + if (effect->HasActiveFilterAnimation() && + expand_for_animation == kExpandVisualRectForAnimation) { + // Assume during the animation the filter can map |rect_to_map| to + // anywhere. Ancestor clips will still apply. + // TODO(crbug.com/1026653): Use animation bounds instead of infinite rect. + mapping_rect = InfiniteLooseFloatClipRect(); + last_transform_and_clip_state.SetTransform(effect->LocalTransformSpace()); + last_transform_and_clip_state.SetClip(*effect->OutputClip()); + continue; + } + if (!effect->HasFilterThatMovesPixels()) continue; - DCHECK(effect->OutputClip()); PropertyTreeState transform_and_clip_state(effect->LocalTransformSpace(), *effect->OutputClip(), EffectPaintPropertyNode::Root()); bool intersects = LocalToAncestorVisualRectInternal( last_transform_and_clip_state, transform_and_clip_state, mapping_rect, - clip_behavior, inclusive_behavior, success); + clip_behavior, inclusive_behavior, expand_for_animation, success); if (!success || !intersects) { success = true; mapping_rect = FloatClipRect(FloatRect()); @@ -259,7 +289,8 @@ bool GeometryMapper::SlowLocalToAncestorVisualRectWithEffects( EffectPaintPropertyNode::Root()); bool intersects = LocalToAncestorVisualRectInternal( last_transform_and_clip_state, final_transform_and_clip_state, - mapping_rect, clip_behavior, inclusive_behavior, success); + mapping_rect, clip_behavior, inclusive_behavior, expand_for_animation, + success); // Many effects (e.g. filters, clip-paths) can make a clip rect not tight. mapping_rect.ClearIsTight(); @@ -278,7 +309,7 @@ FloatClipRect GeometryMapper::LocalToAncestorClipRect( bool success = false; auto result = LocalToAncestorClipRectInternal( local_clip, ancestor_clip, ancestor_state.Transform(), clip_behavior, - kNonInclusiveIntersect, success); + kNonInclusiveIntersect, kDontExpandVisualRectForAnimation, success); DCHECK(success); // Many effects (e.g. filters, clip-paths) can make a clip rect not tight. @@ -293,8 +324,8 @@ static FloatClipRect GetClipRect(const ClipPaintPropertyNode& clip_node_arg, const auto& clip_node = clip_node_arg.Unalias(); FloatClipRect clip_rect( UNLIKELY(clip_behavior == kExcludeOverlayScrollbarSizeForHitTesting) - ? clip_node.ClipRectExcludingOverlayScrollbars() - : FloatClipRect(clip_node.ClipRect())); + ? clip_node.UnsnappedClipRectExcludingOverlayScrollbars() + : FloatClipRect(clip_node.UnsnappedClipRect())); if (clip_node.ClipPath()) clip_rect.ClearIsTight(); return clip_rect; @@ -306,6 +337,7 @@ FloatClipRect GeometryMapper::LocalToAncestorClipRectInternal( const TransformPaintPropertyNode& ancestor_transform_arg, OverlayScrollbarClipBehavior clip_behavior, InclusiveIntersectOrNot inclusive_behavior, + ExpandVisualRectForAnimationOrNot expand_for_animation, bool& success) { const auto& descendant_clip = descendant_clip_arg.Unalias(); const auto& ancestor_clip = ancestor_clip_arg.Unalias(); @@ -330,13 +362,19 @@ FloatClipRect GeometryMapper::LocalToAncestorClipRectInternal( // Iterate over the path from localState.clip to ancestor_state.clip. Stop if // we've found a memoized (precomputed) clip for any particular node. while (clip_node && clip_node != &ancestor_clip) { - const FloatClipRect* cached_clip = nullptr; + const GeometryMapperClipCache::ClipCacheEntry* cached_clip = nullptr; // Inclusive intersected clips are not cached at present. if (inclusive_behavior != kInclusiveIntersect) cached_clip = clip_node->GetClipCache().GetCachedClip(clip_and_transform); + if (cached_clip && cached_clip->has_transform_animation && + expand_for_animation == kExpandVisualRectForAnimation) { + // Don't use cached clip if it's transformed by any animating transform. + cached_clip = nullptr; + } + if (cached_clip) { - clip = *cached_clip; + clip = cached_clip->clip_rect; break; } @@ -354,23 +392,27 @@ FloatClipRect GeometryMapper::LocalToAncestorClipRectInternal( // Ignore it for SPv1 for now. success = true; } - FloatClipRect loose_infinite; - loose_infinite.ClearIsTight(); - return loose_infinite; + return InfiniteLooseFloatClipRect(); } // Iterate down from the top intermediate node found in the previous loop, // computing and memoizing clip rects as we go. for (auto it = intermediate_nodes.rbegin(); it != intermediate_nodes.rend(); ++it) { + bool has_animation = false; const auto& translation_2d_or_matrix = SourceToDestinationProjectionInternal((*it)->LocalTransformSpace(), - ancestor_transform, success); + ancestor_transform, has_animation, + success); if (!success) { success = true; return FloatClipRect(FloatRect()); } + // Don't apply this clip if it's transformed by any animating transform. + if (has_animation && expand_for_animation == kExpandVisualRectForAnimation) + continue; + // This is where we generate the roundedness and tightness of clip rect // from clip and transform properties, and propagate them to |clip|. FloatClipRect mapped_rect(GetClipRect(**it, clip_behavior)); @@ -380,13 +422,18 @@ FloatClipRect GeometryMapper::LocalToAncestorClipRectInternal( } else { clip.Intersect(mapped_rect); // Inclusive intersected clips are not cached at present. - (*it)->GetClipCache().SetCachedClip(clip_and_transform, clip); + (*it)->GetClipCache().SetCachedClip( + GeometryMapperClipCache::ClipCacheEntry{clip_and_transform, clip, + has_animation}); } } - // Inclusive intersected clips are not cached at present. + // Clips that are inclusive intersected or expanded for animation are not + // cached at present. DCHECK(inclusive_behavior == kInclusiveIntersect || - *descendant_clip.GetClipCache().GetCachedClip(clip_and_transform) == - clip); + expand_for_animation == kExpandVisualRectForAnimation || + descendant_clip.GetClipCache() + .GetCachedClip(clip_and_transform) + ->clip_rect == clip); success = true; return clip; } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h index 30c98a0280e..7a55af4fdb5 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h @@ -18,6 +18,17 @@ namespace blink { // Clips can use FloatRect::Intersect or FloatRect::InclusiveIntersect. enum InclusiveIntersectOrNot { kNonInclusiveIntersect, kInclusiveIntersect }; +// Whether to expand the visual or clip rect to infinity when we meet any +// animating transform or filter when walking from a descendant state to an +// ancestor state, when mapping a visual rect or getting the accumulated clip +// rect. After we expanded the rect, we will still apply ancestor clips when +// continuing walking up the tree. TODO(crbug.com/1026653): Consider animation +// bounds instead of using infinite rect. +enum ExpandVisualRectForAnimationOrNot { + kDontExpandVisualRectForAnimation, + kExpandVisualRectForAnimation, +}; + // GeometryMapper is a helper class for fast computations of transformed and // visual rects in different PropertyTreeStates. The design document has a // number of details on use cases, algorithmic definitions, and running times. @@ -100,7 +111,16 @@ class PLATFORM_EXPORT GeometryMapper { return SkMatrix::MakeTrans(Translation2D().Width(), Translation2D().Height()); } - return TransformationMatrix::ToSkMatrix44(Matrix()); + return SkMatrix(TransformationMatrix::ToSkMatrix44(Matrix())); + } + + bool operator==(const Translation2DOrMatrix& other) { + return translation_2d_ == other.translation_2d_ && + matrix_ == other.matrix_; + } + + bool operator!=(const Translation2DOrMatrix& other) { + return !(*this == other); } private: @@ -149,9 +169,10 @@ class PLATFORM_EXPORT GeometryMapper { return; } + bool has_animation = false; bool success = false; - const auto& source_to_destination = - SourceToDestinationProjectionInternal(source, destination, success); + const auto& source_to_destination = SourceToDestinationProjectionInternal( + source, destination, has_animation, success); if (!success) mapping_rect = Rect(); else @@ -164,6 +185,9 @@ class PLATFORM_EXPORT GeometryMapper { // on contents of |local_state|, it's not affected by any effect nodes between // |local_state| and |ancestor_state|. // + // The UnsnappedClipRect of any clip nodes is used, *not* the + // PixelSnappedClipRect. + // // Note that the clip of |ancestor_state| is *not* applied. // // The output FloatClipRect may contain false positives for rounded-ness @@ -215,7 +239,8 @@ class PLATFORM_EXPORT GeometryMapper { const PropertyTreeState& ancestor_state, FloatClipRect& mapping_rect, OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize, - InclusiveIntersectOrNot = kNonInclusiveIntersect); + InclusiveIntersectOrNot = kNonInclusiveIntersect, + ExpandVisualRectForAnimationOrNot = kDontExpandVisualRectForAnimation); static void ClearCache(); @@ -228,6 +253,7 @@ class PLATFORM_EXPORT GeometryMapper { static Translation2DOrMatrix SourceToDestinationProjectionInternal( const TransformPaintPropertyNode& source, const TransformPaintPropertyNode& destination, + bool& has_animation, bool& success); static FloatClipRect LocalToAncestorClipRectInternal( @@ -236,6 +262,7 @@ class PLATFORM_EXPORT GeometryMapper { const TransformPaintPropertyNode& ancestor_transform, OverlayScrollbarClipBehavior, InclusiveIntersectOrNot, + ExpandVisualRectForAnimationOrNot, bool& success); // The return value has the same meaning as that for @@ -246,6 +273,7 @@ class PLATFORM_EXPORT GeometryMapper { FloatClipRect& mapping_rect, OverlayScrollbarClipBehavior, InclusiveIntersectOrNot, + ExpandVisualRectForAnimationOrNot, bool& success); // The return value has the same meaning as that for @@ -256,6 +284,7 @@ class PLATFORM_EXPORT GeometryMapper { FloatClipRect& mapping_rect, OverlayScrollbarClipBehavior, InclusiveIntersectOrNot, + ExpandVisualRectForAnimationOrNot, bool& success); static void MoveRect(FloatRect& rect, const FloatSize& delta) { diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.cc index 374c7b41ad4..5feb0102cc1 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.cc @@ -31,28 +31,23 @@ void GeometryMapperClipCache::InvalidateCacheIfNeeded() { } } -const FloatClipRect* GeometryMapperClipCache::GetCachedClip( +const GeometryMapperClipCache::ClipCacheEntry* +GeometryMapperClipCache::GetCachedClip( const ClipAndTransform& clip_and_transform) { InvalidateCacheIfNeeded(); for (const auto& entry : clip_cache_) { if (entry.clip_and_transform == clip_and_transform) { - return &entry.clip_rect; + return &entry; } } return nullptr; } -void GeometryMapperClipCache::SetCachedClip( - const ClipAndTransform& clip_and_transform, - const FloatClipRect& clip) { +void GeometryMapperClipCache::SetCachedClip(const ClipCacheEntry& entry) { InvalidateCacheIfNeeded(); -#if DCHECK_IS_ON() - for (const auto& entry : clip_cache_) { - if (entry.clip_and_transform == clip_and_transform) - DCHECK(false); // There should be no existing entry. - } -#endif - clip_cache_.push_back(ClipCacheEntry(clip_and_transform, clip)); + // There should be no existing entry. + DCHECK(!GetCachedClip(entry.clip_and_transform)); + clip_cache_.push_back(entry); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h index 470b482e760..820475aca56 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_clip_cache.h @@ -45,28 +45,29 @@ class PLATFORM_EXPORT GeometryMapperClipCache { } }; + struct ClipCacheEntry { + const ClipAndTransform clip_and_transform; + // The clip visual rect of the associated clip node in the space of + // |clip_and_transform|. + const FloatClipRect clip_rect; + // Whether there is any transform animation between the transform space + // of the associated clip node and |clip_and_transform|. + const bool has_transform_animation; + }; + // Returns the clip visual rect of the owning // clip of |this| in the space of |ancestors|, if there is one cached. // Otherwise returns null. - const FloatClipRect* GetCachedClip(const ClipAndTransform& ancestors); + const ClipCacheEntry* GetCachedClip(const ClipAndTransform& ancestors); - // Stores the "clip visual rect" of |this in the space of |ancestors|, + // Stores cached the "clip visual rect" of |this| in the space of |ancestors|, // into a local cache. - void SetCachedClip(const ClipAndTransform&, const FloatClipRect&); + void SetCachedClip(const ClipCacheEntry&); static void ClearCache(); bool IsValid() const; private: - struct ClipCacheEntry { - const ClipAndTransform clip_and_transform; - const FloatClipRect clip_rect; - ClipCacheEntry(const ClipAndTransform& clip_and_transform_arg, - const FloatClipRect& clip_rect_arg) - : clip_and_transform(clip_and_transform_arg), - clip_rect(clip_rect_arg) {} - }; - void InvalidateCacheIfNeeded(); Vector<ClipCacheEntry> clip_cache_; diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc index fea4d4d387c..9e32de68f0c 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc @@ -20,7 +20,7 @@ namespace blink { class GeometryMapperTest : public testing::Test, public PaintTestConfigurations { public: - const FloatClipRect* GetCachedClip( + const GeometryMapperClipCache::ClipCacheEntry* GetCachedClip( const ClipPaintPropertyNode& descendant_clip, const PropertyTreeState& ancestor_property_tree_state) { GeometryMapperClipCache::ClipAndTransform clip_and_transform( @@ -37,18 +37,28 @@ class GeometryMapperTest : public testing::Test, bool& success) { GeometryMapper::LocalToAncestorVisualRectInternal( local_state, ancestor_state, mapping_rect, - kIgnorePlatformOverlayScrollbarSize, kNonInclusiveIntersect, success); + kIgnorePlatformOverlayScrollbarSize, kNonInclusiveIntersect, + kDontExpandVisualRectForAnimation, success); } - // Variables required by CHECK_MAPPINGS(). The tests should set these - // variables with proper values before calling CHECK_MAPPINGS(). + void CheckMappings(); + void CheckLocalToAncestorVisualRect(); + void CheckLocalToAncestorClipRect(); + void CheckSourceToDestinationRect(); + void CheckSourceToDestinationProjection(); + void CheckCachedClip(); + + // Variables required by CheckMappings(). The tests should set these + // variables with proper values before calling CheckMappings(). PropertyTreeState local_state = PropertyTreeState::Root(); PropertyTreeState ancestor_state = PropertyTreeState::Root(); FloatRect input_rect; FloatClipRect expected_visual_rect; + base::Optional<FloatClipRect> expected_visual_rect_expanded_for_animation; FloatSize expected_translation_2d; base::Optional<TransformationMatrix> expected_transform; FloatClipRect expected_clip; + bool expected_clip_has_transform_animation = false; FloatRect expected_transformed_rect; }; @@ -76,89 +86,88 @@ INSTANTIATE_PAINT_TEST_SUITE_P(GeometryMapperTest); EXPECT_FLOAT_RECT_NEAR((expected).Rect(), (actual).Rect()); \ } while (false) -#define CHECK_LOCAL_TO_ANCESTOR_VISUAL_RECT() \ - do { \ - SCOPED_TRACE("Check LocalToAncestorVisualRect"); \ - FloatClipRect actual_visual_rect(input_rect); \ - GeometryMapper::LocalToAncestorVisualRect(local_state, ancestor_state, \ - actual_visual_rect); \ - EXPECT_CLIP_RECT_EQ(expected_visual_rect, actual_visual_rect); \ - } while (false) +void GeometryMapperTest::CheckLocalToAncestorVisualRect() { + FloatClipRect actual_visual_rect(input_rect); + GeometryMapper::LocalToAncestorVisualRect(local_state, ancestor_state, + actual_visual_rect); + EXPECT_CLIP_RECT_EQ(expected_visual_rect, actual_visual_rect); -#define CHECK_LOCAL_TO_ANCESTOR_CLIP_RECT() \ - do { \ - SCOPED_TRACE("Check LocalToAncestorClipRect"); \ - FloatClipRect actual_clip_rect; \ - actual_clip_rect = \ - GeometryMapper::LocalToAncestorClipRect(local_state, ancestor_state); \ - EXPECT_CLIP_RECT_EQ(expected_clip, actual_clip_rect); \ - } while (false) + actual_visual_rect = FloatClipRect(input_rect); + GeometryMapper::LocalToAncestorVisualRect( + local_state, ancestor_state, actual_visual_rect, + kIgnorePlatformOverlayScrollbarSize, kNonInclusiveIntersect, + kExpandVisualRectForAnimation); + EXPECT_CLIP_RECT_EQ(expected_visual_rect_expanded_for_animation + ? *expected_visual_rect_expanded_for_animation + : expected_visual_rect, + actual_visual_rect); +} -#define CHECK_SOURCE_TO_DESTINATION_RECT() \ - do { \ - SCOPED_TRACE("Check SourceToDestinationRect"); \ - auto actual_transformed_rect = input_rect; \ - GeometryMapper::SourceToDestinationRect(local_state.Transform(), \ - ancestor_state.Transform(), \ - actual_transformed_rect); \ - EXPECT_FLOAT_RECT_NEAR(expected_transformed_rect, \ - actual_transformed_rect); \ - } while (false) +void GeometryMapperTest::CheckLocalToAncestorClipRect() { + FloatClipRect actual_clip_rect = + GeometryMapper::LocalToAncestorClipRect(local_state, ancestor_state); + EXPECT_CLIP_RECT_EQ(expected_clip, actual_clip_rect); +} -#define CHECK_SOURCE_TO_DESTINATION_PROJECTION() \ - do { \ - SCOPED_TRACE("Check SourceToDestinationProjection"); \ - const auto& actual_transform_to_ancestor = \ - GeometryMapper::SourceToDestinationProjection( \ - local_state.Transform(), ancestor_state.Transform()); \ - if (expected_transform) { \ - EXPECT_EQ(*expected_transform, actual_transform_to_ancestor.Matrix()); \ - } else { \ - EXPECT_EQ(expected_translation_2d, \ - actual_transform_to_ancestor.Translation2D()); \ - } \ - } while (false) +void GeometryMapperTest::CheckSourceToDestinationRect() { + auto actual_transformed_rect = input_rect; + GeometryMapper::SourceToDestinationRect(local_state.Transform(), + ancestor_state.Transform(), + actual_transformed_rect); + EXPECT_FLOAT_RECT_NEAR(expected_transformed_rect, actual_transformed_rect); +} -#define CHECK_CACHED_CLIP() \ - do { \ - if (&ancestor_state.Effect() != &local_state.Effect()) \ - break; \ - SCOPED_TRACE("Check cached clip"); \ - const auto& local_clip = local_state.Clip().Unalias(); \ - const auto* cached_clip = GetCachedClip(local_clip, ancestor_state); \ - if (&ancestor_state.Clip() == &local_clip || \ - (&ancestor_state.Clip() == local_clip.Parent() && \ - &ancestor_state.Transform() == &local_clip.LocalTransformSpace())) { \ - EXPECT_EQ(nullptr, cached_clip); \ - break; \ - } \ - ASSERT_NE(nullptr, cached_clip); \ - EXPECT_CLIP_RECT_EQ(expected_clip, *cached_clip); \ - } while (false) +void GeometryMapperTest::CheckSourceToDestinationProjection() { + const auto& actual_transform_to_ancestor = + GeometryMapper::SourceToDestinationProjection(local_state.Transform(), + ancestor_state.Transform()); + if (expected_transform) { + EXPECT_EQ(*expected_transform, actual_transform_to_ancestor.Matrix()); + } else { + EXPECT_EQ(expected_translation_2d, + actual_transform_to_ancestor.Translation2D()); + } +} + +void GeometryMapperTest::CheckCachedClip() { + if (&ancestor_state.Effect() != &local_state.Effect()) + return; + const auto& local_clip = local_state.Clip().Unalias(); + const auto* cached_clip = GetCachedClip(local_clip, ancestor_state); + if (&ancestor_state.Clip() == &local_clip || + (&ancestor_state.Clip() == local_clip.Parent() && + &ancestor_state.Transform() == &local_clip.LocalTransformSpace())) { + EXPECT_EQ(nullptr, cached_clip); + return; + } + ASSERT_NE(nullptr, cached_clip); + EXPECT_CLIP_RECT_EQ(expected_clip, cached_clip->clip_rect); + EXPECT_EQ(expected_clip_has_transform_animation, + cached_clip->has_transform_animation); +} // See the data fields of GeometryMapperTest for variables that will be used in // this macro. -#define CHECK_MAPPINGS() \ - do { \ - CHECK_LOCAL_TO_ANCESTOR_VISUAL_RECT(); \ - CHECK_LOCAL_TO_ANCESTOR_CLIP_RECT(); \ - CHECK_SOURCE_TO_DESTINATION_RECT(); \ - CHECK_SOURCE_TO_DESTINATION_PROJECTION(); \ - { \ - SCOPED_TRACE("Repeated check to test caching"); \ - CHECK_LOCAL_TO_ANCESTOR_VISUAL_RECT(); \ - CHECK_LOCAL_TO_ANCESTOR_CLIP_RECT(); \ - CHECK_SOURCE_TO_DESTINATION_RECT(); \ - CHECK_SOURCE_TO_DESTINATION_PROJECTION(); \ - } \ - CHECK_CACHED_CLIP(); \ - } while (false) +void GeometryMapperTest::CheckMappings() { + CheckLocalToAncestorVisualRect(); + CheckLocalToAncestorClipRect(); + CheckSourceToDestinationRect(); + CheckSourceToDestinationProjection(); + { + SCOPED_TRACE("Repeated check to test caching"); + CheckLocalToAncestorVisualRect(); + CheckLocalToAncestorClipRect(); + CheckSourceToDestinationRect(); + CheckSourceToDestinationProjection(); + } + CheckCachedClip(); +} TEST_P(GeometryMapperTest, Root) { input_rect = FloatRect(0, 0, 100, 100); expected_visual_rect = FloatClipRect(input_rect); expected_transformed_rect = input_rect; - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, IdentityTransform) { @@ -168,7 +177,7 @@ TEST_P(GeometryMapperTest, IdentityTransform) { input_rect = FloatRect(0, 0, 100, 100); expected_transformed_rect = input_rect; expected_visual_rect = FloatClipRect(input_rect); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, TranslationTransform) { @@ -180,7 +189,7 @@ TEST_P(GeometryMapperTest, TranslationTransform) { expected_transformed_rect = input_rect; expected_transformed_rect.Move(expected_translation_2d); expected_visual_rect = FloatClipRect(expected_transformed_rect); - CHECK_MAPPINGS(); + CheckMappings(); FloatRect rect = expected_transformed_rect; GeometryMapper::SourceToDestinationRect(t0(), local_state.Transform(), rect); @@ -197,7 +206,7 @@ TEST_P(GeometryMapperTest, TranslationTransformWithAlias) { expected_transformed_rect = input_rect; expected_transformed_rect.Move(expected_translation_2d); expected_visual_rect = FloatClipRect(expected_transformed_rect); - CHECK_MAPPINGS(); + CheckMappings(); FloatRect rect = expected_transformed_rect; GeometryMapper::SourceToDestinationRect(t0(), local_state.Transform(), rect); @@ -213,7 +222,7 @@ TEST_P(GeometryMapperTest, RotationAndScaleTransform) { expected_transformed_rect = expected_transform->MapRect(input_rect); expected_visual_rect = FloatClipRect(expected_transformed_rect); expected_visual_rect.ClearIsTight(); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, RotationAndScaleTransformWithAlias) { @@ -226,7 +235,7 @@ TEST_P(GeometryMapperTest, RotationAndScaleTransformWithAlias) { expected_transformed_rect = expected_transform->MapRect(input_rect); expected_visual_rect = FloatClipRect(expected_transformed_rect); expected_visual_rect.ClearIsTight(); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, RotationAndScaleTransformWithTransformOrigin) { @@ -240,7 +249,7 @@ TEST_P(GeometryMapperTest, RotationAndScaleTransformWithTransformOrigin) { expected_transformed_rect = expected_transform->MapRect(input_rect); expected_visual_rect = FloatClipRect(expected_transformed_rect); expected_visual_rect.ClearIsTight(); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, NestedTransforms) { @@ -256,7 +265,7 @@ TEST_P(GeometryMapperTest, NestedTransforms) { expected_transformed_rect = expected_transform->MapRect(input_rect); expected_visual_rect = FloatClipRect(expected_transformed_rect); expected_visual_rect.ClearIsTight(); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, NestedTransformsFlattening) { @@ -277,7 +286,7 @@ TEST_P(GeometryMapperTest, NestedTransformsFlattening) { expected_transformed_rect = expected_transform->MapRect(input_rect); expected_visual_rect = FloatClipRect(expected_transformed_rect); expected_visual_rect.ClearIsTight(); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, NestedTransformsScaleAndTranslation) { @@ -295,7 +304,7 @@ TEST_P(GeometryMapperTest, NestedTransformsScaleAndTranslation) { expected_transformed_rect = expected_transform->MapRect(input_rect); expected_visual_rect = FloatClipRect(expected_transformed_rect); expected_visual_rect.ClearIsTight(); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, NestedTransformsIntermediateDestination) { @@ -313,7 +322,7 @@ TEST_P(GeometryMapperTest, NestedTransformsIntermediateDestination) { expected_transformed_rect = expected_transform->MapRect(input_rect); expected_visual_rect = FloatClipRect(expected_transformed_rect); expected_visual_rect.ClearIsTight(); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, SimpleClip) { @@ -322,9 +331,23 @@ TEST_P(GeometryMapperTest, SimpleClip) { input_rect = FloatRect(0, 0, 100, 100); expected_transformed_rect = input_rect; // not clipped. - expected_clip = FloatClipRect(clip->ClipRect()); + expected_clip = FloatClipRect(clip->UnsnappedClipRect()); expected_visual_rect = expected_clip; - CHECK_MAPPINGS(); + CheckMappings(); +} + +TEST_P(GeometryMapperTest, SimpleClipPixelSnapped) { + auto clip = CreateClip(c0(), t0(), FloatRoundedRect(10, 10, 50.5, 50.5), + FloatRoundedRect(10, 10, 50, 51)); + local_state.SetClip(*clip); + + input_rect = FloatRect(0, 0, 100, 100); + expected_transformed_rect = input_rect; // not clipped. + + // GeometryMapper does not use the PixelSnappedClipRect. + expected_clip = FloatClipRect(clip->UnsnappedClipRect()); + expected_visual_rect = expected_clip; + CheckMappings(); } TEST_P(GeometryMapperTest, SimpleClipWithAlias) { @@ -334,15 +357,14 @@ TEST_P(GeometryMapperTest, SimpleClipWithAlias) { input_rect = FloatRect(0, 0, 100, 100); expected_transformed_rect = input_rect; // not clipped. - expected_clip = FloatClipRect(clip->Unalias().ClipRect()); + expected_clip = FloatClipRect(clip->Unalias().UnsnappedClipRect()); expected_visual_rect = expected_clip; - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, SimpleClipOverlayScrollbars) { - ClipPaintPropertyNode::State clip_state; - clip_state.local_transform_space = &t0(); - clip_state.clip_rect = FloatRoundedRect(10, 10, 50, 50); + ClipPaintPropertyNode::State clip_state(&t0(), + FloatRoundedRect(10, 10, 50, 50)); clip_state.clip_rect_excluding_overlay_scrollbars = FloatClipRect(FloatRect(10, 10, 45, 43)); auto clip = ClipPaintPropertyNode::Create(c0(), std::move(clip_state)); @@ -439,10 +461,10 @@ TEST_P(GeometryMapperTest, RoundedClip) { input_rect = FloatRect(0, 0, 100, 100); expected_transformed_rect = input_rect; - expected_clip = FloatClipRect(clip->ClipRect()); + expected_clip = FloatClipRect(clip->UnsnappedClipRect()); EXPECT_TRUE(expected_clip.HasRadius()); expected_visual_rect = expected_clip; - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, ClipPath) { @@ -457,7 +479,7 @@ TEST_P(GeometryMapperTest, ClipPath) { expected_clip = FloatClipRect(FloatRect(10, 10, 50, 50)); expected_clip.ClearIsTight(); expected_visual_rect = expected_clip; - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, TwoClips) { @@ -472,19 +494,19 @@ TEST_P(GeometryMapperTest, TwoClips) { input_rect = FloatRect(0, 0, 100, 100); expected_transformed_rect = input_rect; - expected_clip = FloatClipRect(clip1->ClipRect()); + expected_clip = FloatClipRect(clip1->UnsnappedClipRect()); EXPECT_TRUE(expected_clip.HasRadius()); expected_visual_rect = expected_clip; - CHECK_MAPPINGS(); + CheckMappings(); ancestor_state.SetClip(*clip1); - expected_clip = FloatClipRect(clip2->ClipRect()); + expected_clip = FloatClipRect(clip2->UnsnappedClipRect()); expected_visual_rect = expected_clip; - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, TwoClipsTransformAbove) { - auto transform = CreateTransform(t0(), TransformationMatrix()); + auto transform = Create2DTranslation(t0(), 0, 0); FloatRoundedRect clip_rect1( FloatRect(10, 10, 50, 50), @@ -497,16 +519,16 @@ TEST_P(GeometryMapperTest, TwoClipsTransformAbove) { input_rect = FloatRect(0, 0, 100, 100); expected_transformed_rect = input_rect; - expected_clip = FloatClipRect(clip2->ClipRect()); + expected_clip = FloatClipRect(clip2->UnsnappedClipRect()); expected_clip.SetHasRadius(); expected_visual_rect = expected_clip; - CHECK_MAPPINGS(); + CheckMappings(); - expected_clip = FloatClipRect(clip1->ClipRect()); + expected_clip = FloatClipRect(clip1->UnsnappedClipRect()); EXPECT_TRUE(expected_clip.HasRadius()); local_state.SetClip(*clip1); expected_visual_rect = expected_clip; - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, ClipBeforeTransform) { @@ -518,14 +540,36 @@ TEST_P(GeometryMapperTest, ClipBeforeTransform) { input_rect = FloatRect(0, 0, 100, 100); expected_visual_rect = FloatClipRect(input_rect); - expected_visual_rect.Intersect(FloatClipRect(clip->ClipRect())); + expected_visual_rect.Intersect(FloatClipRect(clip->UnsnappedClipRect())); + expected_visual_rect.Map(*expected_transform); + EXPECT_FALSE(expected_visual_rect.IsTight()); + expected_clip = FloatClipRect(clip->UnsnappedClipRect()); + expected_clip.Map(*expected_transform); + EXPECT_FALSE(expected_clip.IsTight()); + expected_transformed_rect = expected_transform->MapRect(input_rect); + CheckMappings(); +} + +TEST_P(GeometryMapperTest, ExpandVisualRectWithClipBeforeAnimatingTransform) { + expected_transform = TransformationMatrix().Rotate(45); + auto transform = CreateAnimatingTransform(t0(), *expected_transform); + auto clip = CreateClip(c0(), *transform, FloatRoundedRect(10, 10, 50, 50)); + local_state.SetClip(*clip); + local_state.SetTransform(*transform); + + input_rect = FloatRect(0, 0, 100, 100); + expected_visual_rect = FloatClipRect(input_rect); + expected_visual_rect.Intersect(FloatClipRect(clip->UnsnappedClipRect())); expected_visual_rect.Map(*expected_transform); + // The clip has animating transform, so it doesn't apply to the visual rect. + expected_visual_rect_expanded_for_animation = InfiniteLooseFloatClipRect(); EXPECT_FALSE(expected_visual_rect.IsTight()); - expected_clip = FloatClipRect(clip->ClipRect()); + expected_clip = FloatClipRect(clip->UnsnappedClipRect()); expected_clip.Map(*expected_transform); EXPECT_FALSE(expected_clip.IsTight()); + expected_clip_has_transform_animation = true; expected_transformed_rect = expected_transform->MapRect(input_rect); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, ClipAfterTransform) { @@ -539,11 +583,33 @@ TEST_P(GeometryMapperTest, ClipAfterTransform) { expected_transformed_rect = expected_transform->MapRect(input_rect); expected_visual_rect = FloatClipRect(input_rect); expected_visual_rect.Map(*expected_transform); - expected_visual_rect.Intersect(FloatClipRect(clip->ClipRect())); + expected_visual_rect.Intersect(FloatClipRect(clip->UnsnappedClipRect())); EXPECT_FALSE(expected_visual_rect.IsTight()); - expected_clip = FloatClipRect(clip->ClipRect()); + expected_clip = FloatClipRect(clip->UnsnappedClipRect()); EXPECT_TRUE(expected_clip.IsTight()); - CHECK_MAPPINGS(); + CheckMappings(); +} + +TEST_P(GeometryMapperTest, ExpandVisualRectWithClipAfterAnimatingTransform) { + expected_transform = TransformationMatrix().Rotate(45); + auto transform = CreateAnimatingTransform(t0(), *expected_transform); + auto clip = CreateClip(c0(), t0(), FloatRoundedRect(10, 10, 200, 200)); + local_state.SetClip(*clip); + local_state.SetTransform(*transform); + + input_rect = FloatRect(0, 0, 100, 100); + expected_transformed_rect = expected_transform->MapRect(input_rect); + expected_visual_rect = FloatClipRect(input_rect); + expected_visual_rect.Map(*expected_transform); + expected_visual_rect.Intersect(FloatClipRect(clip->UnsnappedClipRect())); + EXPECT_FALSE(expected_visual_rect.IsTight()); + expected_clip = FloatClipRect(clip->UnsnappedClipRect()); + EXPECT_TRUE(expected_clip.IsTight()); + // The visual rect is expanded first to infinity because of the transform + // animation, then clipped by the clip. + expected_visual_rect_expanded_for_animation = expected_clip; + expected_visual_rect_expanded_for_animation->ClearIsTight(); + CheckMappings(); } TEST_P(GeometryMapperTest, TwoClipsWithTransformBetween) { @@ -552,43 +618,58 @@ TEST_P(GeometryMapperTest, TwoClipsWithTransformBetween) { auto transform = CreateTransform(t0(), *expected_transform); auto clip2 = CreateClip(*clip1, *transform, FloatRoundedRect(10, 10, 200, 200)); + local_state.SetClip(*clip2); + local_state.SetTransform(*transform); input_rect = FloatRect(0, 0, 100, 100); expected_transformed_rect = expected_transform->MapRect(input_rect); - { - local_state.SetClip(*clip1); - local_state.SetTransform(*transform); - - expected_visual_rect = FloatClipRect(input_rect); - expected_visual_rect.Map(*expected_transform); - expected_visual_rect.Intersect(FloatClipRect(clip1->ClipRect())); - EXPECT_FALSE(expected_visual_rect.IsTight()); - expected_clip = FloatClipRect(clip1->ClipRect()); - EXPECT_TRUE(expected_clip.IsTight()); - CHECK_MAPPINGS(); - } + expected_clip = FloatClipRect(clip2->UnsnappedClipRect()); + expected_clip.Map(*expected_transform); + expected_clip.Intersect(FloatClipRect(clip1->UnsnappedClipRect())); + EXPECT_FALSE(expected_clip.IsTight()); - { - local_state.SetClip(*clip2); - local_state.SetTransform(*transform); - - expected_clip = FloatClipRect(clip2->ClipRect()); - expected_clip.Map(*expected_transform); - expected_clip.Intersect(FloatClipRect(clip1->ClipRect())); - EXPECT_FALSE(expected_clip.IsTight()); - - // All clips are performed in the space of the ancestor. In cases such as - // this, this means the clip is not tight. - expected_visual_rect = FloatClipRect(input_rect); - expected_visual_rect.Map(*expected_transform); - // Intersect with all clips between local and ancestor, independently mapped - // to ancestor space. - expected_visual_rect.Intersect(expected_clip); - EXPECT_FALSE(expected_visual_rect.IsTight()); - - CHECK_MAPPINGS(); - } + // All clips are performed in the space of the ancestor. In cases such as + // this, this means the clip is not tight. + expected_visual_rect = FloatClipRect(input_rect); + expected_visual_rect.Map(*expected_transform); + // Intersect with all clips between local and ancestor, independently mapped + // to ancestor space. + expected_visual_rect.Intersect(expected_clip); + EXPECT_FALSE(expected_visual_rect.IsTight()); + + CheckMappings(); +} + +TEST_P(GeometryMapperTest, + ExpandVisualRectWithTwoClipsWithAnimatingTransformBetween) { + auto clip1 = CreateClip(c0(), t0(), FloatRoundedRect(10, 10, 200, 200)); + expected_transform = TransformationMatrix().Rotate(45); + auto transform = CreateAnimatingTransform(t0(), *expected_transform); + auto clip2 = + CreateClip(*clip1, *transform, FloatRoundedRect(10, 10, 200, 200)); + local_state.SetClip(*clip2); + local_state.SetTransform(*transform); + + input_rect = FloatRect(0, 0, 100, 100); + expected_transformed_rect = expected_transform->MapRect(input_rect); + + expected_clip = FloatClipRect(clip2->UnsnappedClipRect()); + expected_clip.Map(*expected_transform); + expected_clip.Intersect(FloatClipRect(clip1->UnsnappedClipRect())); + EXPECT_FALSE(expected_clip.IsTight()); + expected_clip_has_transform_animation = true; + expected_visual_rect = FloatClipRect(input_rect); + expected_visual_rect.Map(*expected_transform); + expected_visual_rect.Intersect(expected_clip); + EXPECT_FALSE(expected_visual_rect.IsTight()); + // The visual rect is expanded to infinity because of the transform animation, + // then clipped by clip1. clip2 doesn't apply because it's below the animating + // transform. + expected_visual_rect_expanded_for_animation = + FloatClipRect(clip1->UnsnappedClipRect()); + expected_visual_rect_expanded_for_animation->ClearIsTight(); + CheckMappings(); } TEST_P(GeometryMapperTest, SiblingTransforms) { @@ -701,13 +782,13 @@ TEST_P(GeometryMapperTest, FilterWithClipsAndTransforms) { auto output = input_rect; output.Move(transform_below_effect->Translation2D()); // 2. clipBelowEffect - output.Intersect(clip_below_effect->ClipRect().Rect()); + output.Intersect(clip_below_effect->UnsnappedClipRect().Rect()); EXPECT_EQ(FloatRect(20, 30, 90, 80), output); // 3. effect (the outset is 3 times of blur amount). output = filters.MapRect(output); EXPECT_EQ(FloatRect(-40, -30, 210, 200), output); // 4. clipAboveEffect - output.Intersect(clip_above_effect->ClipRect().Rect()); + output.Intersect(clip_above_effect->UnsnappedClipRect().Rect()); EXPECT_EQ(FloatRect(-40, -30, 140, 130), output); // 5. transformAboveEffect output.Move(transform_above_effect->Translation2D()); @@ -721,7 +802,7 @@ TEST_P(GeometryMapperTest, FilterWithClipsAndTransforms) { expected_visual_rect.ClearIsTight(); expected_clip = FloatClipRect(FloatRect(50, 60, 90, 90)); expected_clip.ClearIsTight(); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, FilterWithClipsAndTransformsWithAlias) { @@ -751,13 +832,13 @@ TEST_P(GeometryMapperTest, FilterWithClipsAndTransformsWithAlias) { auto output = input_rect; output.Move(transform_below_effect->Translation2D()); // 2. clipBelowEffect - output.Intersect(clip_below_effect->ClipRect().Rect()); + output.Intersect(clip_below_effect->UnsnappedClipRect().Rect()); EXPECT_EQ(FloatRect(20, 30, 90, 80), output); // 3. effect (the outset is 3 times of blur amount). output = filters.MapRect(output); EXPECT_EQ(FloatRect(-40, -30, 210, 200), output); // 4. clipAboveEffect - output.Intersect(clip_above_effect->ClipRect().Rect()); + output.Intersect(clip_above_effect->UnsnappedClipRect().Rect()); EXPECT_EQ(FloatRect(-40, -30, 140, 130), output); // 5. transformAboveEffect output.Move(transform_above_effect->Translation2D()); @@ -771,7 +852,36 @@ TEST_P(GeometryMapperTest, FilterWithClipsAndTransformsWithAlias) { expected_visual_rect.ClearIsTight(); expected_clip = FloatClipRect(FloatRect(50, 60, 90, 90)); expected_clip.ClearIsTight(); - CHECK_MAPPINGS(); + CheckMappings(); +} + +TEST_P(GeometryMapperTest, + ExpandVisualRectWithTwoClipsWithAnimatingFilterBetween) { + auto clip1 = CreateClip(c0(), t0(), FloatRoundedRect(10, 10, 200, 200)); + auto effect = CreateAnimatingFilterEffect(e0(), CompositorFilterOperations(), + clip1.get()); + auto clip2 = CreateClip(*clip1, t0(), FloatRoundedRect(50, 0, 200, 50)); + local_state.SetClip(*clip2); + local_state.SetEffect(*effect); + + input_rect = FloatRect(0, 0, 100, 100); + expected_transformed_rect = input_rect; + auto output = input_rect; + output.Intersect(clip2->UnsnappedClipRect().Rect()); + output.Intersect(clip1->UnsnappedClipRect().Rect()); + EXPECT_EQ(FloatRect(50, 10, 50, 40), output); + expected_visual_rect = FloatClipRect(output); + expected_visual_rect.ClearIsTight(); + expected_clip = FloatClipRect(clip2->UnsnappedClipRect()); + expected_clip.Intersect(FloatClipRect(clip1->UnsnappedClipRect())); + expected_clip.ClearIsTight(); + // The visual rect is expanded to infinity because of the filter animation, + // the clipped by clip1. clip2 doesn't apply because it's below the animating + // filter. + expected_visual_rect_expanded_for_animation = + FloatClipRect(clip1->UnsnappedClipRect()); + expected_visual_rect_expanded_for_animation->ClearIsTight(); + CheckMappings(); } TEST_P(GeometryMapperTest, ReflectionWithPaintOffset) { @@ -787,7 +897,7 @@ TEST_P(GeometryMapperTest, ReflectionWithPaintOffset) { expected_visual_rect = FloatClipRect(FloatRect(50, 100, 100, 50)); expected_visual_rect.ClearIsTight(); - CHECK_MAPPINGS(); + CheckMappings(); } TEST_P(GeometryMapperTest, InvertedClip) { @@ -814,8 +924,8 @@ TEST_P(GeometryMapperTest, InvertedClip) { TEST_P(GeometryMapperTest, Precision) { auto t1 = CreateTransform(t0(), TransformationMatrix().Scale(32767)); auto t2 = CreateTransform(*t1, TransformationMatrix().Rotate(1)); - auto t3 = CreateTransform(*t2, TransformationMatrix()); - auto t4 = CreateTransform(*t3, TransformationMatrix()); + auto t3 = Create2DTranslation(*t2, 0, 0); + auto t4 = Create2DTranslation(*t3, 0, 0); EXPECT_TRUE( GeometryMapper::SourceToDestinationProjection(*t4, *t4).IsIdentity()); EXPECT_TRUE( diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc index e6bb3172f6f..1a8bdc3cb94 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.cc @@ -42,6 +42,8 @@ void GeometryMapperTransformCache::Update( screen_transform_ = nullptr; if (node.IsIdentityOr2DTranslation()) { + // We always use full matrix for animating transforms. + DCHECK(!node.HasActiveTransformAnimation()); root_of_2d_translation_ = parent.root_of_2d_translation_; to_2d_translation_root_ = parent.to_2d_translation_root_; const auto& translation = node.Translation2D(); @@ -57,6 +59,9 @@ void GeometryMapperTransformCache::Update( plane_root_transform_->from_plane_root = parent.from_plane_root(); plane_root_transform_->from_plane_root.PostTranslate( -translation.Width(), -translation.Height()); + plane_root_transform_->has_animation = + parent.plane_root_transform_->has_animation || + node.HasActiveTransformAnimation(); } else { // The parent doesn't have plane_root_transform_ means that the parent's // plane root is the same as the 2d translation root, so this node @@ -86,6 +91,7 @@ void GeometryMapperTransformCache::Update( plane_root_transform_->plane_root = &node; plane_root_transform_->to_plane_root.MakeIdentity(); plane_root_transform_->from_plane_root.MakeIdentity(); + plane_root_transform_->has_animation = false; } else { plane_root_transform_->plane_root = parent.plane_root(); plane_root_transform_->to_plane_root.MakeIdentity(); @@ -93,6 +99,9 @@ void GeometryMapperTransformCache::Update( plane_root_transform_->to_plane_root.Multiply(local); plane_root_transform_->from_plane_root = local.Inverse(); parent.ApplyFromPlaneRoot(plane_root_transform_->from_plane_root); + plane_root_transform_->has_animation = + parent.has_animation_to_plane_root() || + node.HasActiveTransformAnimation(); } } @@ -130,6 +139,8 @@ void GeometryMapperTransformCache::UpdateScreenTransform( to_screen_flattened.IsInvertible(); if (screen_transform_->projection_from_screen_is_valid) screen_transform_->projection_from_screen = to_screen_flattened.Inverse(); + + screen_transform_->has_animation |= node.HasActiveTransformAnimation(); } #if DCHECK_IS_ON() diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h index ec25d6d76cf..b722bbf64ab 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache.h @@ -71,6 +71,13 @@ class PLATFORM_EXPORT GeometryMapperTransformCache { else ApplyFromPlaneRoot(m); } + bool has_animation_to_screen() const { +#if DCHECK_IS_ON() + CheckScreenTransformUpdated(); +#endif + return UNLIKELY(screen_transform_) ? screen_transform_->has_animation + : has_animation_to_plane_root(); + } const TransformationMatrix& to_plane_root() const { DCHECK(plane_root_transform_); @@ -100,6 +107,10 @@ class PLATFORM_EXPORT GeometryMapperTransformCache { return UNLIKELY(plane_root_transform_) ? plane_root_transform_->plane_root : root_of_2d_translation(); } + bool has_animation_to_plane_root() const { + return UNLIKELY(plane_root_transform_) && + plane_root_transform_->has_animation; + } private: friend class GeometryMapperTransformCacheTest; @@ -180,6 +191,7 @@ class PLATFORM_EXPORT GeometryMapperTransformCache { TransformationMatrix to_plane_root; TransformationMatrix from_plane_root; const TransformPaintPropertyNode* plane_root; + bool has_animation; }; std::unique_ptr<PlaneRootTransform> plane_root_transform_; @@ -187,6 +199,7 @@ class PLATFORM_EXPORT GeometryMapperTransformCache { TransformationMatrix to_screen; TransformationMatrix projection_from_screen; bool projection_from_screen_is_valid; + bool has_animation; }; std::unique_ptr<ScreenTransform> screen_transform_; diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache_test.cc index f04c76e4759..90511afcc61 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_transform_cache_test.cc @@ -137,9 +137,9 @@ class GeometryMapperTransformCacheTest : public testing::Test { }; TEST_F(GeometryMapperTransformCacheTest, All2dTranslations) { - auto t1 = CreateTransform(t0(), TransformationMatrix().Translate(1, 2)); - auto t2 = CreateTransform(*t1, TransformationMatrix()); - auto t3 = CreateTransform(*t2, TransformationMatrix().Translate(7, 8)); + auto t1 = Create2DTranslation(t0(), 1, 2); + auto t2 = Create2DTranslation(*t1, 0, 0); + auto t3 = Create2DTranslation(*t2, 7, 8); Check2dTranslationToRoot(t0(), 0, 0); Check2dTranslationToRoot(*t1, 1, 2); @@ -148,9 +148,9 @@ TEST_F(GeometryMapperTransformCacheTest, All2dTranslations) { } TEST_F(GeometryMapperTransformCacheTest, RootAsPlaneRootWithIntermediateScale) { - auto t1 = CreateTransform(t0(), TransformationMatrix().Translate(1, 2)); + auto t1 = Create2DTranslation(t0(), 1, 2); auto t2 = CreateTransform(*t1, TransformationMatrix().Scale(3)); - auto t3 = CreateTransform(*t2, TransformationMatrix().Translate(7, 8)); + auto t3 = Create2DTranslation(*t2, 7, 8); Check2dTranslationToRoot(t0(), 0, 0); Check2dTranslationToRoot(*t1, 1, 2); @@ -162,9 +162,9 @@ TEST_F(GeometryMapperTransformCacheTest, RootAsPlaneRootWithIntermediateScale) { TEST_F(GeometryMapperTransformCacheTest, IntermediatePlaneRootSameAs2dTranslationRoot) { - auto t1 = CreateTransform(t0(), TransformationMatrix().Translate(1, 2)); + auto t1 = Create2DTranslation(t0(), 1, 2); auto t2 = CreateTransform(*t1, TransformationMatrix().Rotate3d(0, 45, 0)); - auto t3 = CreateTransform(*t2, TransformationMatrix().Translate(7, 8)); + auto t3 = Create2DTranslation(*t2, 7, 8); Check2dTranslationToRoot(t0(), 0, 0); Check2dTranslationToRoot(*t1, 1, 2); @@ -176,10 +176,10 @@ TEST_F(GeometryMapperTransformCacheTest, TEST_F(GeometryMapperTransformCacheTest, IntermediatePlaneRootDifferent2dTranslationRoot) { - auto t1 = CreateTransform(t0(), TransformationMatrix().Translate(1, 2)); + auto t1 = Create2DTranslation(t0(), 1, 2); auto t2 = CreateTransform(*t1, TransformationMatrix().Rotate3d(0, 45, 0)); auto t3 = CreateTransform(*t2, TransformationMatrix().Scale(3)); - auto t4 = CreateTransform(*t3, TransformationMatrix().Translate(7, 8)); + auto t4 = Create2DTranslation(*t3, 7, 8); Check2dTranslationToRoot(t0(), 0, 0); Check2dTranslationToRoot(*t1, 1, 2); @@ -199,9 +199,9 @@ TEST_F(GeometryMapperTransformCacheTest, } TEST_F(GeometryMapperTransformCacheTest, TransformUpdate) { - auto t1 = CreateTransform(t0(), TransformationMatrix().Translate(1, 2)); - auto t2 = CreateTransform(*t1, TransformationMatrix()); - auto t3 = CreateTransform(*t2, TransformationMatrix().Translate(7, 8)); + auto t1 = Create2DTranslation(t0(), 1, 2); + auto t2 = Create2DTranslation(*t1, 0, 0); + auto t3 = Create2DTranslation(*t2, 7, 8); Check2dTranslationToRoot(t0(), 0, 0); Check2dTranslationToRoot(*t1, 1, 2); diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/graphics_layer_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/graphics_layer_display_item.cc new file mode 100644 index 00000000000..5550c716901 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/graphics_layer_display_item.cc @@ -0,0 +1,63 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/graphics/paint/graphics_layer_display_item.h" + +#include <utility> + +#include "cc/layers/layer.h" +#include "cc/layers/picture_layer.h" +#include "third_party/blink/renderer/platform/graphics/compositing/layers_as_json.h" +#include "third_party/blink/renderer/platform/graphics/graphics_context.h" +#include "third_party/blink/renderer/platform/graphics/graphics_layer.h" +#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" +#include "third_party/blink/renderer/platform/wtf/assertions.h" + +namespace blink { + +GraphicsLayerDisplayItem::GraphicsLayerDisplayItem( + const GraphicsLayer& graphics_layer) + : DisplayItem(graphics_layer, kGraphicsLayerWrapper, sizeof(*this)), + graphics_layer_(graphics_layer) {} + +bool GraphicsLayerDisplayItem::Equals(const DisplayItem& other) const { + return GetType() == other.GetType() && + GetGraphicsLayer() == + static_cast<const GraphicsLayerDisplayItem&>(other) + .GetGraphicsLayer(); +} + +#if DCHECK_IS_ON() +void GraphicsLayerDisplayItem::PropertiesAsJSON(JSONObject& json) const { + DisplayItem::PropertiesAsJSON(json); + json.SetInteger("layer", graphics_layer_.CcLayer()->id()); + FloatPoint offset(graphics_layer_.GetOffsetFromTransformNode()); + json.SetDouble("offset_x", offset.X()); + json.SetDouble("offset_y", offset.Y()); +} +#endif + +void RecordGraphicsLayer(GraphicsContext& context, + const GraphicsLayer& graphics_layer) { + // In pre-CompositeAfterPaint, the GraphicsLayer hierarchy is still built + // during CompositingUpdate, and we have to clear them here to ensure no + // extraneous layers are still attached. In future we will disable all + // those layer hierarchy code so we won't need this line. + DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); + graphics_layer.CcLayer()->RemoveAllChildren(); + + PaintController& paint_controller = context.GetPaintController(); + + // This is like ScopedPaintChunkProperties but uses null id because graphics + // layer chunk doesn't need an id nor a client. + const PropertyTreeState& properties = graphics_layer.GetPropertyTreeState(); + PropertyTreeState previous_properties( + paint_controller.CurrentPaintChunkProperties()); + paint_controller.UpdateCurrentPaintChunkProperties(nullptr, properties); + paint_controller.CreateAndAppend<GraphicsLayerDisplayItem>(graphics_layer); + paint_controller.UpdateCurrentPaintChunkProperties(nullptr, + previous_properties); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/graphics_layer_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/graphics_layer_display_item.h new file mode 100644 index 00000000000..2e468dd629c --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/graphics_layer_display_item.h @@ -0,0 +1,47 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GRAPHICS_LAYER_DISPLAY_ITEM_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GRAPHICS_LAYER_DISPLAY_ITEM_H_ + +#include "cc/layers/layer.h" +#include "third_party/blink/renderer/platform/graphics/paint/display_item.h" +#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h" +#include "third_party/blink/renderer/platform/platform_export.h" + +namespace blink { + +class GraphicsContext; +class GraphicsLayer; + +// Before CompositeAfterPaint, this is a placeholder for a GraphicsLayer +// allocated before paint. With CompositeAfterPaint, this class will be +// obsolete and should be removed. +// +// Before SquashAfterPaint, a cc::Layer will always be allocated to draw the +// GraphicsLayer's contents. With SquashAfter Paint, that may not be true; the +// GraphicsLayer may end up getting squashed by PaintArtifactCompositor. +class PLATFORM_EXPORT GraphicsLayerDisplayItem final : public DisplayItem { + public: + GraphicsLayerDisplayItem(const GraphicsLayer&); + + const GraphicsLayer& GetGraphicsLayer() const { return graphics_layer_; } + + // DisplayItem + bool Equals(const DisplayItem&) const override; +#if DCHECK_IS_ON() + void PropertiesAsJSON(JSONObject&) const override; +#endif + + private: + const GraphicsLayer& graphics_layer_; +}; + +// Records a graphics layer into a GraphicsContext. +PLATFORM_EXPORT void RecordGraphicsLayer(GraphicsContext& context, + const GraphicsLayer& graphics_layer); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_GRAPHICS_LAYER_DISPLAY_ITEM_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_data.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_data.cc index 6e5c25573ad..b5aee1a0c7c 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_data.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_data.cc @@ -9,7 +9,7 @@ namespace blink { -static String HitTestRectsAsString(const Vector<HitTestRect>& rects) { +static String TouchActionRectsAsString(const Vector<TouchActionRect>& rects) { StringBuilder sb; sb.Append("["); bool first = true; @@ -32,22 +32,25 @@ String HitTestData::ToString() const { bool printed_top_level_field = false; if (!touch_action_rects.IsEmpty()) { sb.Append("touch_action_rects: "); - sb.Append(HitTestRectsAsString(touch_action_rects)); + sb.Append(TouchActionRectsAsString(touch_action_rects)); printed_top_level_field = true; } - if (scroll_hit_test) { + if (!scroll_hit_test_rect.IsEmpty()) { if (printed_top_level_field) sb.Append(", "); - sb.AppendFormat( - "scroll_hit_test: \"%s\" with offset %p", - scroll_hit_test->scroll_container_bounds.ToString().Utf8().data(), - scroll_hit_test->scroll_offset); + sb.Append("scroll_hit_test_rect: "); + sb.Append(scroll_hit_test_rect.ToString()); printed_top_level_field = true; } - sb.Append("}"); + if (scroll_translation) { + if (printed_top_level_field) + sb.Append(", "); + sb.AppendFormat("scroll_translation: %p", scroll_translation); + } + sb.Append("}"); return sb.ToString(); } @@ -55,4 +58,8 @@ std::ostream& operator<<(std::ostream& os, const HitTestData& data) { return os << data.ToString().Utf8(); } +std::ostream& operator<<(std::ostream& os, const HitTestData* data) { + return os << (data ? data->ToString().Utf8() : "null"); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_data.h b/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_data.h index 824d9397133..cc785893ca5 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_data.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_data.h @@ -5,51 +5,35 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_HIT_TEST_DATA_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_HIT_TEST_DATA_H_ -#include "third_party/blink/renderer/platform/graphics/hit_test_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h" +#include "third_party/blink/renderer/platform/graphics/touch_action_rect.h" #include "third_party/blink/renderer/platform/platform_export.h" namespace blink { struct PLATFORM_EXPORT HitTestData { - Vector<HitTestRect> touch_action_rects; - struct ScrollHitTest { - const TransformPaintPropertyNode* scroll_offset; - IntRect scroll_container_bounds; - bool operator==(const ScrollHitTest& rhs) const { - return scroll_offset == rhs.scroll_offset && - scroll_container_bounds == rhs.scroll_container_bounds; - } - }; - base::Optional<ScrollHitTest> scroll_hit_test; - - HitTestData() = default; - HitTestData(const HitTestData& other) - : touch_action_rects(other.touch_action_rects), - scroll_hit_test(other.scroll_hit_test) {} + Vector<TouchActionRect> touch_action_rects; + + // If scroll_translation is nullptr or in pre-CompositeAfterPaint, this marks + // a region in which composited scroll is not allowed. In CompositeAfterPaint + // when scroll_translation is not nullptr, this is the bounds of the scroll + // container, and whether the region allows composited scrolling depends + // whether the scroll_translation is composited. + IntRect scroll_hit_test_rect; + const TransformPaintPropertyNode* scroll_translation = nullptr; bool operator==(const HitTestData& rhs) const { return touch_action_rects == rhs.touch_action_rects && - scroll_hit_test == rhs.scroll_hit_test; - } - - void AppendTouchActionRect(const HitTestRect& rect) { - touch_action_rects.push_back(rect); + scroll_hit_test_rect == rhs.scroll_hit_test_rect && + scroll_translation == rhs.scroll_translation; } - - void SetScrollHitTest(const TransformPaintPropertyNode* scroll_offset, - const IntRect& scroll_container_bounds) { - DCHECK(!scroll_offset || scroll_offset->ScrollNode()); - scroll_hit_test = base::make_optional( - ScrollHitTest{scroll_offset, scroll_container_bounds}); - } - bool operator!=(const HitTestData& rhs) const { return !(*this == rhs); } String ToString() const; }; PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const HitTestData&); +PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const HitTestData*); } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.cc deleted file mode 100644 index 679754ff661..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.cc +++ /dev/null @@ -1,38 +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 "third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h" - -#include "third_party/blink/renderer/platform/graphics/graphics_context.h" -#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" - -namespace blink { - -void HitTestDisplayItem::Record(GraphicsContext& context, - const DisplayItemClient& client, - const HitTestRect& hit_test_rect) { - auto& paint_controller = context.GetPaintController(); - if (paint_controller.DisplayItemConstructionIsDisabled()) - return; - - if (paint_controller.UseCachedItemIfPossible(client, DisplayItem::kHitTest)) - return; - - paint_controller.CreateAndAppend<HitTestDisplayItem>(client, hit_test_rect); -} - -#if DCHECK_IS_ON() -void HitTestDisplayItem::PropertiesAsJSON(JSONObject& json) const { - DisplayItem::PropertiesAsJSON(json); - json.SetString("hitTestRect", hit_test_rect_.ToString()); -} -#endif - -bool HitTestDisplayItem::Equals(const DisplayItem& other) const { - return DisplayItem::Equals(other) && - hit_test_rect_ == - static_cast<const HitTestDisplayItem&>(other).hit_test_rect_; -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h deleted file mode 100644 index cd9a9676def..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h +++ /dev/null @@ -1,43 +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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_HIT_TEST_DISPLAY_ITEM_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_HIT_TEST_DISPLAY_ITEM_H_ - -#include "third_party/blink/renderer/platform/graphics/paint/display_item.h" - -#include "third_party/blink/renderer/platform/graphics/hit_test_rect.h" - -namespace blink { - -class GraphicsContext; - -// A special DisplayItem containing hit test data. -class PLATFORM_EXPORT HitTestDisplayItem final : public DisplayItem { - public: - HitTestDisplayItem(const DisplayItemClient& client, - const HitTestRect& hit_test_rect) - : DisplayItem(client, kHitTest, sizeof(*this)), - hit_test_rect_(hit_test_rect) { - } - - const HitTestRect& GetHitTestRect() const { return hit_test_rect_; } - - static void Record(GraphicsContext&, - const DisplayItemClient&, - const HitTestRect&); - - bool Equals(const DisplayItem& other) const final; - - private: -#if DCHECK_IS_ON() - void PropertiesAsJSON(JSONObject&) const override; -#endif - - HitTestRect hit_test_rect_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_HIT_TEST_DISPLAY_ITEM_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.cc index 58a1eaa2b76..648cba4ab0b 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.cc @@ -7,98 +7,14 @@ #include "cc/paint/display_item_list.h" #include "third_party/blink/renderer/platform/geometry/int_rect.h" #include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h" -#include "third_party/blink/renderer/platform/graphics/graphics_layer.h" +#include "third_party/blink/renderer/platform/graphics/graphics_context.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h" -#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" -#include "third_party/blink/renderer/platform/graphics/paint/hit_test_display_item.h" -#include "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" -#include "third_party/skia/include/core/SkRegion.h" namespace blink { namespace { -static SkColor DisplayItemBackgroundColor(const DisplayItem& item) { - if (item.GetType() != DisplayItem::kBoxDecorationBackground && - item.GetType() != DisplayItem::kDocumentBackground) - return SK_ColorTRANSPARENT; - - const auto& drawing_item = static_cast<const DrawingDisplayItem&>(item); - const auto record = drawing_item.GetPaintRecord(); - if (!record) - return SK_ColorTRANSPARENT; - - for (cc::PaintOpBuffer::Iterator it(record.get()); it; ++it) { - const auto* op = *it; - if (op->GetType() == cc::PaintOpType::DrawRect || - op->GetType() == cc::PaintOpType::DrawRRect) { - const auto& flags = static_cast<const cc::PaintOpWithFlags*>(op)->flags; - // Skip op with looper which may modify the color. - if (!flags.getLooper() && flags.getStyle() == cc::PaintFlags::kFill_Style) - return flags.getColor(); - } - } - return SK_ColorTRANSPARENT; -} - -void ComputeChunkDerivedData(const DisplayItemList& display_items, - PaintChunk& chunk) { - // This happens in tests testing paint chunks without display items. - if (!chunk.size()) - return; - - SkRegion known_to_be_opaque_region; - auto items = display_items.ItemsInPaintChunk(chunk); - for (const DisplayItem& item : items) { - chunk.bounds.Unite(item.VisualRect()); - chunk.outset_for_raster_effects = std::max(chunk.outset_for_raster_effects, - item.OutsetForRasterEffects()); - - if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && - item.IsDrawing()) { - const auto& drawing = static_cast<const DrawingDisplayItem&>(item); - if (drawing.GetPaintRecord() && drawing.KnownToBeOpaque()) { - known_to_be_opaque_region.op(SkIRect(drawing.VisualRect()), - SkRegion::kUnion_Op); - } - } - - if (item.IsHitTest()) { - const auto& hit_test = static_cast<const HitTestDisplayItem&>(item); - if (!chunk.hit_test_data) - chunk.hit_test_data = std::make_unique<HitTestData>(); - chunk.hit_test_data->AppendTouchActionRect(hit_test.GetHitTestRect()); - } - - // Because ScrollHitTestDisplayItems force new paint chunks (see: - // PaintChunker::IncrementDisplayItemIndex), they should only be the first - // item in a paint chunk. - DCHECK(!item.IsScrollHitTest() || item.Equals(*items.begin())); - } - - // Because ScrollHitTestDisplayItems force new paint chunks (see: - // PaintChunker::IncrementDisplayItemIndex), they should only be the first - // item in a paint chunk. - if (items.begin()->IsScrollHitTest()) { - const auto& scroll_hit_test_item = - static_cast<const ScrollHitTestDisplayItem&>(*items.begin()); - if (!chunk.hit_test_data) - chunk.hit_test_data = std::make_unique<HitTestData>(); - chunk.hit_test_data->SetScrollHitTest( - scroll_hit_test_item.scroll_offset_node(), - scroll_hit_test_item.scroll_container_bounds()); - } - - if (known_to_be_opaque_region.contains(chunk.bounds)) - chunk.known_to_be_opaque = true; - - if (items.begin() != items.end()) { - chunk.safe_opaque_background_color = - DisplayItemBackgroundColor(*items.begin()); - } -} - // For PaintArtifact::AppendDebugDrawing(). class DebugDrawingClient final : public DisplayItemClient { public: @@ -114,8 +30,6 @@ PaintArtifact::PaintArtifact() : display_item_list_(0) {} PaintArtifact::PaintArtifact(DisplayItemList display_items, Vector<PaintChunk> chunks) : display_item_list_(std::move(display_items)), chunks_(std::move(chunks)) { - for (auto& chunk : chunks_) - ComputeChunkDerivedData(display_item_list_, chunk); } PaintArtifact::~PaintArtifact() = default; @@ -153,7 +67,8 @@ void PaintArtifact::AppendDebugDrawing( // Create a PaintChunk for the debug drawing. chunks_.emplace_back(display_item_list_.size() - 1, display_item_list_.size(), display_item.GetId(), property_tree_state); - ComputeChunkDerivedData(display_item_list_, chunks_.back()); + chunks_.back().bounds = chunks_.back().drawable_bounds = + display_item_list_.Last().VisualRect(); } void PaintArtifact::Replay(GraphicsContext& graphics_context, @@ -179,6 +94,18 @@ sk_sp<PaintRecord> PaintArtifact::GetPaintRecord( ->ReleaseAsRecord(); } +SkColor PaintArtifact::SafeOpaqueBackgroundColor( + const PaintChunkSubset& chunks) const { + // Find the background color from the first drawing display item. + for (const auto& chunk : chunks) { + for (const auto& item : display_item_list_.ItemsInPaintChunk(chunk)) { + if (item.IsDrawing() && item.DrawsContent()) + return static_cast<const DrawingDisplayItem&>(item).BackgroundColor(); + } + } + return SK_ColorTRANSPARENT; +} + void PaintArtifact::FinishCycle() { // Until CompositeAfterPaint, PaintController::ClearPropertyTreeChangedStateTo // is used for clearing the property tree changed state at the end of paint diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.h index ee3be027d50..e6b18b20343 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_artifact.h @@ -12,6 +12,7 @@ #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/vector.h" +#include "third_party/skia/include/core/SkColor.h" namespace cc { class PaintCanvas; @@ -60,11 +61,6 @@ class PLATFORM_EXPORT PaintArtifact final : public RefCounted<PaintArtifact> { return PaintChunkSubset(PaintChunks(), subset_indices); } - Vector<PaintChunk>::const_iterator FindChunkByDisplayItemIndex( - size_t index) const { - return FindChunkInVectorByDisplayItemIndex(PaintChunks(), index); - } - // Returns the approximate memory usage, excluding memory likely to be // shared with the embedder after copying to cc::DisplayItemList. size_t ApproximateUnsharedMemoryUsage() const; @@ -86,6 +82,8 @@ class PLATFORM_EXPORT PaintArtifact final : public RefCounted<PaintArtifact> { sk_sp<PaintRecord> GetPaintRecord(const PropertyTreeState& replay_state, const IntPoint& offset = IntPoint()) const; + SkColor SafeOpaqueBackgroundColor(const PaintChunkSubset&) const; + // Called when the caller finishes updating a full document life cycle. // Will cleanup data (e.g. raster invalidations) that will no longer be used // for the next cycle, and update status to be ready for the next cycle. diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.cc index c5926d2f284..2b14e465929 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.cc @@ -10,13 +10,13 @@ namespace blink { struct SameSizeAsPaintChunk { - size_t begin_index; - size_t end_index; + wtf_size_t begin_index; + wtf_size_t end_index; PaintChunk::Id id; PropertyTreeState properties; - FloatRect bounds; + IntRect bounds; + IntRect drawable_bounds; float outset_for_raster_effects; - SkColor safe_opaque_background_color; unsigned bools; // known_to_be_opaque, is_cacheable, client_is_just_created void* pointers[1]; // hit_test_data }; @@ -24,10 +24,34 @@ struct SameSizeAsPaintChunk { static_assert(sizeof(PaintChunk) == sizeof(SameSizeAsPaintChunk), "PaintChunk should stay small"); +bool PaintChunk::EqualsForUnderInvalidationChecking( + const PaintChunk& other) const { + return size() == other.size() && id == other.id && + properties == other.properties && bounds == other.bounds && + drawable_bounds == other.drawable_bounds && + outset_for_raster_effects == other.outset_for_raster_effects && + ((!hit_test_data && !other.hit_test_data) || + (hit_test_data && other.hit_test_data && + *hit_test_data == *other.hit_test_data)); + // known_to_be_opaque is not checked because it's updated when we create the + // next chunk or release chunks. We ensure its correctness with unit tests and + // under-invalidation checking of display items. +} + +size_t PaintChunk::MemoryUsageInBytes() const { + size_t total_size = sizeof(*this); + if (hit_test_data) { + total_size += sizeof(*hit_test_data); + total_size += + hit_test_data->touch_action_rects.capacity() * sizeof(TouchActionRect); + } + return total_size; +} + String PaintChunk::ToString() const { StringBuilder sb; sb.AppendFormat( - "PaintChunk(begin=%zu, end=%zu, id=%s cacheable=%d props=(%s) bounds=%s " + "PaintChunk(begin=%u, end=%u, id=%s cacheable=%d props=(%s) bounds=%s " "known_to_be_opaque=%d", begin_index, end_index, id.ToString().Utf8().c_str(), is_cacheable, properties.ToString().Utf8().c_str(), bounds.ToString().Utf8().c_str(), diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h index 2024df32a40..277bc25e0cc 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h @@ -28,8 +28,8 @@ struct PLATFORM_EXPORT PaintChunk { using Id = DisplayItem::Id; - PaintChunk(size_t begin, - size_t end, + PaintChunk(wtf_size_t begin, + wtf_size_t end, const Id& id, const PropertyTreeState& props) : begin_index(begin), @@ -39,7 +39,27 @@ struct PLATFORM_EXPORT PaintChunk { is_cacheable(id.client.IsCacheable()), client_is_just_created(id.client.IsJustCreated()) {} - size_t size() const { + // Move a paint chunk from a cached subsequence. + PaintChunk(wtf_size_t begin, PaintChunk&& other) + : begin_index(begin), + end_index(begin + other.size()), + id(other.id), + properties(other.properties), + hit_test_data(std::move(other.hit_test_data)), + bounds(other.bounds), + drawable_bounds(other.drawable_bounds), + outset_for_raster_effects(other.outset_for_raster_effects), + known_to_be_opaque(other.known_to_be_opaque), + is_cacheable(other.is_cacheable), + client_is_just_created(false), + is_moved_from_cached_subsequence(true) { +#if DCHECK_IS_ON() + DCHECK(other.id.client.IsAlive()); + DCHECK(!other.id.client.IsJustCreated()); +#endif + } + + wtf_size_t size() const { DCHECK_GE(end_index, begin_index); return end_index - begin_index; } @@ -63,24 +83,24 @@ struct PLATFORM_EXPORT PaintChunk { return !client_is_just_created; } - size_t MemoryUsageInBytes() const { - size_t total_size = sizeof(*this); - if (hit_test_data) { - total_size += sizeof(*hit_test_data); - total_size += - hit_test_data->touch_action_rects.capacity() * sizeof(HitTestRect); - } - return total_size; + bool EqualsForUnderInvalidationChecking(const PaintChunk& other) const; + + HitTestData& EnsureHitTestData() { + if (!hit_test_data) + hit_test_data = std::make_unique<HitTestData>(); + return *hit_test_data; } + size_t MemoryUsageInBytes() const; + String ToString() const; // Index of the first drawing in this chunk. - size_t begin_index; + wtf_size_t begin_index; // Index of the first drawing not in this chunk, so that there are // |endIndex - beginIndex| drawings in the chunk. - size_t end_index; + wtf_size_t end_index; // Identifier of this chunk. It should be unique if |is_cacheable| is true. // This is used to match a new chunk to a cached old chunk to track changes @@ -90,23 +110,27 @@ struct PLATFORM_EXPORT PaintChunk { // The paint properties which apply to this chunk. RefCountedPropertyTreeState properties; - // The following fields are not initialized when the chunk is created because - // they depend on the display items in this chunk. They are updated by the - // constructor of PaintArtifact. - std::unique_ptr<HitTestData> hit_test_data; - // The total bounds of this paint chunk's contents, in the coordinate space of - // the containing transform node. + // The following fields depend on the display items in this chunk. + // They are updated when a display item is added into the chunk. + + // The total bounds of visual rects of all display items in this paint chunk, + // and extra bounds that are not from display items for e.g. hit test. + // It's in the coordinate space of the containing transform node. This can be + // larger than |drawble_bounds|, because of non-drawable display items and + // extra bounds. IntRect bounds; + // The total bounds of visual rects of drawable display items in this paint + // chunk. + IntRect drawable_bounds; + // Some raster effects can exceed |bounds| in the rasterization space. This // is the maximum DisplayItemClient::VisualRectOutsetForRasterEffects() of // all clients of items in this chunk. float outset_for_raster_effects = 0; - SkColor safe_opaque_background_color = 0; - // True if the bounds are filled entirely with opaque contents. bool known_to_be_opaque = false; @@ -114,29 +138,9 @@ struct PLATFORM_EXPORT PaintChunk { // The following fields are put here to avoid memory gap. bool is_cacheable; bool client_is_just_created; + bool is_moved_from_cached_subsequence = false; }; -inline bool ChunkLessThanIndex(const PaintChunk& chunk, size_t index) { - return chunk.end_index <= index; -} - -inline Vector<PaintChunk>::iterator FindChunkInVectorByDisplayItemIndex( - Vector<PaintChunk>& chunks, - size_t index) { - auto* chunk = - std::lower_bound(chunks.begin(), chunks.end(), index, ChunkLessThanIndex); - DCHECK(chunk == chunks.end() || - (index >= chunk->begin_index && index < chunk->end_index)); - return chunk; -} - -inline Vector<PaintChunk>::const_iterator FindChunkInVectorByDisplayItemIndex( - const Vector<PaintChunk>& chunks, - size_t index) { - return FindChunkInVectorByDisplayItemIndex( - const_cast<Vector<PaintChunk>&>(chunks), index); -} - PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const PaintChunk&); } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h index 7c9f1c75117..c28b7285818 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h @@ -42,7 +42,7 @@ class PaintChunkSubset { } // The index in the whole paint chunks set. - size_t OriginalIndex() const { return subset_.OriginalIndex(offset_); } + wtf_size_t OriginalIndex() const { return subset_.OriginalIndex(offset_); } private: friend class PaintChunkSubset; diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc index 67168a48af8..8766927440c 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc @@ -4,11 +4,13 @@ #include "third_party/blink/renderer/platform/graphics/paint/paint_chunker.h" +#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h" + namespace blink { PaintChunker::PaintChunker() : current_properties_(PropertyTreeState::Uninitialized()), - force_new_chunk_(false) {} + force_new_chunk_(true) {} PaintChunker::~PaintChunker() = default; @@ -23,7 +25,7 @@ bool PaintChunker::IsInInitialState() const { #endif void PaintChunker::UpdateCurrentPaintChunkProperties( - const base::Optional<PaintChunk::Id>& chunk_id, + const PaintChunk::Id* chunk_id, const PropertyTreeState& properties) { // If properties are the same, continue to use the previously set // |next_chunk_id_| because the id of the outer painting is likely to be @@ -37,14 +39,14 @@ void PaintChunker::UpdateCurrentPaintChunkProperties( current_properties_ = properties; } -void PaintChunker::ForceNewChunk() { - force_new_chunk_ = true; - // Always use a new chunk id for a force chunk which may be for a subsequence - // which needs the chunk id to be independence with previous chunks. - next_chunk_id_ = base::nullopt; +void PaintChunker::AppendByMoving(PaintChunk&& chunk) { + UpdateLastChunkKnownToBeOpaque(); + wtf_size_t next_chunk_begin_index = + chunks_.IsEmpty() ? 0 : LastChunk().end_index; + chunks_.emplace_back(next_chunk_begin_index, std::move(chunk)); } -bool PaintChunker::IncrementDisplayItemIndex(const DisplayItem& item) { +PaintChunk& PaintChunker::EnsureCurrentChunk(const PaintChunk::Id& id) { #if DCHECK_IS_ON() // If this DCHECKs are hit we are missing a call to update the properties. // See: ScopedPaintChunkProperties. @@ -53,47 +55,118 @@ bool PaintChunker::IncrementDisplayItemIndex(const DisplayItem& item) { DCHECK(current_properties_.IsInitialized()); #endif - bool item_forces_new_chunk = - item.IsForeignLayer() || item.IsScrollHitTest() || item.IsScrollbar(); + if (WillForceNewChunk() || current_properties_ != LastChunk().properties) { + if (!next_chunk_id_) + next_chunk_id_.emplace(id); + UpdateLastChunkKnownToBeOpaque(); + wtf_size_t begin = chunks_.IsEmpty() ? 0 : LastChunk().end_index; + chunks_.emplace_back(begin, begin, *next_chunk_id_, current_properties_); + next_chunk_id_ = base::nullopt; + force_new_chunk_ = false; + } + return LastChunk(); +} + +bool PaintChunker::IncrementDisplayItemIndex(const DisplayItem& item) { + bool item_forces_new_chunk = item.IsForeignLayer() || + item.IsGraphicsLayerWrapper() || + item.IsScrollbar(); + if (item_forces_new_chunk) + SetForceNewChunk(true); + + auto previous_size = size(); + auto& chunk = EnsureCurrentChunk(item.GetId()); + bool created_new_chunk = size() > previous_size; + + chunk.bounds.Unite(item.VisualRect()); + if (item.DrawsContent()) + chunk.drawable_bounds.Unite(item.VisualRect()); + + constexpr wtf_size_t kMaxRegionComplexity = 10; + if (item.IsDrawing() && + static_cast<const DrawingDisplayItem&>(item).KnownToBeOpaque() && + last_chunk_known_to_be_opaque_region_.Complexity() < kMaxRegionComplexity) + last_chunk_known_to_be_opaque_region_.Unite(item.VisualRect()); + + chunk.outset_for_raster_effects = + std::max(chunk.outset_for_raster_effects, item.OutsetForRasterEffects()); + + chunk.end_index++; + + // When forcing a new chunk, we still need to force new chunk for the next + // display item. Otherwise reset force_new_chunk_ to false. + DCHECK(!force_new_chunk_); if (item_forces_new_chunk) { - force_new_chunk_ = true; - next_chunk_id_.emplace(item.GetId()); + DCHECK(created_new_chunk); + SetForceNewChunk(true); + } + + return created_new_chunk; +} + +void PaintChunker::AddHitTestDataToCurrentChunk(const PaintChunk::Id& id, + const IntRect& rect, + TouchAction touch_action) { + // In CompositeAfterPaint, we ensure a paint chunk for correct composited + // hit testing. In pre-CompositeAfterPaint, this is unnecessary, except that + // there is special touch action, and that we have a non-root effect so that + // PaintChunksToCcLayer will emit paint operations for filters. + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled() && + touch_action == TouchAction::kAuto && + ¤t_properties_.Effect() == &EffectPaintPropertyNode::Root()) + return; + + auto& chunk = EnsureCurrentChunk(id); + chunk.bounds.Unite(rect); + if (touch_action != TouchAction::kAuto) { + chunk.EnsureHitTestData().touch_action_rects.push_back( + TouchActionRect{rect, touch_action}); } +} - size_t new_chunk_begin_index; - if (chunks_.IsEmpty()) { - new_chunk_begin_index = 0; +void PaintChunker::CreateScrollHitTestChunk( + const PaintChunk::Id& id, + const TransformPaintPropertyNode* scroll_translation, + const IntRect& rect) { +#if DCHECK_IS_ON() + if (id.type == DisplayItem::Type::kResizerScrollHitTest || + id.type == DisplayItem::Type::kPluginScrollHitTest) { + // Resizer and plugin scroll hit tests are only used to prevent composited + // scrolling and should not have a scroll offset node. + DCHECK(!scroll_translation); + } else if (id.type == DisplayItem::Type::kScrollHitTest) { + DCHECK(scroll_translation); + // The scroll offset transform node should have an associated scroll node. + DCHECK(scroll_translation->ScrollNode()); } else { - auto& last_chunk = LastChunk(); - if (!force_new_chunk_ && current_properties_ == last_chunk.properties) { - // Continue the current chunk. - last_chunk.end_index++; - // We don't create a new chunk when UpdateCurrentPaintChunkProperties() - // just changed |next_chunk_id_| but not |current_properties_|. Clear - // |next_chunk_id_| which has been ignored. - next_chunk_id_ = base::nullopt; - return false; - } - new_chunk_begin_index = last_chunk.end_index; + NOTREACHED(); } +#endif - chunks_.emplace_back(new_chunk_begin_index, new_chunk_begin_index + 1, - next_chunk_id_ ? *next_chunk_id_ : item.GetId(), - current_properties_); - next_chunk_id_ = base::nullopt; + SetForceNewChunk(true); + auto& chunk = EnsureCurrentChunk(id); + chunk.bounds.Unite(rect); + auto& hit_test_data = chunk.EnsureHitTestData(); + hit_test_data.scroll_translation = scroll_translation; + hit_test_data.scroll_hit_test_rect = rect; + SetForceNewChunk(true); +} - // When forcing a new chunk, we still need to force new chunk for the next - // display item. Otherwise reset force_new_chunk_ to false. - if (!item_forces_new_chunk) - force_new_chunk_ = false; +void PaintChunker::UpdateLastChunkKnownToBeOpaque() { + if (chunks_.IsEmpty() || LastChunk().is_moved_from_cached_subsequence) + return; - return true; + LastChunk().known_to_be_opaque = + last_chunk_known_to_be_opaque_region_.Contains(LastChunk().bounds); + last_chunk_known_to_be_opaque_region_ = Region(); } Vector<PaintChunk> PaintChunker::ReleasePaintChunks() { + UpdateLastChunkKnownToBeOpaque(); next_chunk_id_ = base::nullopt; current_properties_ = PropertyTreeState::Uninitialized(); chunks_.ShrinkToFit(); + force_new_chunk_ = true; return std::move(chunks_); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h index c65ebe79e87..2b2dcdb57be 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h @@ -7,6 +7,7 @@ #include "base/macros.h" #include "base/optional.h" +#include "third_party/blink/renderer/platform/geometry/region.h" #include "third_party/blink/renderer/platform/graphics/paint/display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h" @@ -34,38 +35,50 @@ class PLATFORM_EXPORT PaintChunker final { const PropertyTreeState& CurrentPaintChunkProperties() const { return current_properties_; } - void UpdateCurrentPaintChunkProperties(const base::Optional<PaintChunk::Id>&, + void UpdateCurrentPaintChunkProperties(const PaintChunk::Id*, const PropertyTreeState&); - void ForceNewChunk(); + // Sets the forcing new chunk status on or off. If the status is on, even the + // properties haven't change, we'll force a new paint chunk for the next + // display item and then automatically resets the status. Some special display + // item (e.g. ForeignLayerDisplayItem) also automatically sets the status on + // before and after the item to force a dedicated paint chunk. + void SetForceNewChunk(bool force) { + force_new_chunk_ = force; + next_chunk_id_ = base::nullopt; + } + bool WillForceNewChunk() const { + return force_new_chunk_ || chunks_.IsEmpty(); + } + + void AppendByMoving(PaintChunk&&); // Returns true if a new chunk is created. bool IncrementDisplayItemIndex(const DisplayItem&); const Vector<PaintChunk>& PaintChunks() const { return chunks_; } + wtf_size_t size() const { return chunks_.size(); } - PaintChunk& PaintChunkAt(wtf_size_t i) { return chunks_[i]; } - wtf_size_t LastChunkIndex() const { - return chunks_.IsEmpty() ? kNotFound : chunks_.size() - 1; - } PaintChunk& LastChunk() { return chunks_.back(); } - - PaintChunk& FindChunkByDisplayItemIndex(size_t index) { - auto* chunk = FindChunkInVectorByDisplayItemIndex(chunks_, index); - DCHECK(chunk != chunks_.end()); - return *chunk; - } + const PaintChunk& LastChunk() const { return chunks_.back(); } + + // The id will be used when we need to create a new current chunk. + // Otherwise it's ignored. + void AddHitTestDataToCurrentChunk(const PaintChunk::Id&, + const IntRect&, + TouchAction); + void CreateScrollHitTestChunk( + const PaintChunk::Id&, + const TransformPaintPropertyNode* scroll_translation, + const IntRect&); // Releases the generated paint chunk list and raster invalidations and // resets the state of this object. Vector<PaintChunk> ReleasePaintChunks(); private: - size_t ChunkIndex(const PaintChunk& chunk) const { - size_t index = &chunk - &chunks_.front(); - DCHECK_LT(index, chunks_.size()); - return index; - } + PaintChunk& EnsureCurrentChunk(const PaintChunk::Id&); + void UpdateLastChunkKnownToBeOpaque(); Vector<PaintChunk> chunks_; @@ -79,6 +92,8 @@ class PLATFORM_EXPORT PaintChunker final { PropertyTreeState current_properties_; + Region last_chunk_known_to_be_opaque_region_; + // True when an item forces a new chunk (e.g., foreign display items), and for // the item following a forced chunk. PaintController also forces new chunks // before and after subsequences by calling ForceNewChunk(). diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc index 38c19a5348b..d474d4ea698 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_chunker_test.cc @@ -6,8 +6,12 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h" +#include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h" +#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h" #include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" using testing::ElementsAre; @@ -17,28 +21,43 @@ namespace { class PaintChunkerTest : public testing::Test { protected: - class TestDisplayItemClient : public DisplayItemClient { - String DebugName() const final { return "Test"; } - IntRect VisualRect() const final { return IntRect(); } - }; - TestDisplayItemClient client_; + FakeDisplayItemClient client_; }; DisplayItem::Type DisplayItemType(int offset) { - return static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + offset); + auto type = + static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + offset); + DCHECK(DisplayItem::IsDrawingType(type)); + return type; } -class TestChunkerDisplayItem : public DisplayItem { +class TestChunkerDisplayItem : public DrawingDisplayItem { public: - TestChunkerDisplayItem(const DisplayItemClient& client, - DisplayItem::Type type = DisplayItem::kDrawingFirst) - : DisplayItem(client, type, sizeof(*this)) {} + explicit TestChunkerDisplayItem( + const DisplayItemClient& client, + DisplayItem::Type type = DisplayItem::kDrawingFirst) + : DrawingDisplayItem(client, type, nullptr) {} }; -class TestDisplayItemRequiringSeparateChunk : public TestChunkerDisplayItem { +class TestChunkerOpaqueDisplayItem : public DrawingDisplayItem { public: - TestDisplayItemRequiringSeparateChunk(const DisplayItemClient& client) - : TestChunkerDisplayItem(client, DisplayItem::kForeignLayerPlugin) {} + explicit TestChunkerOpaqueDisplayItem( + const DisplayItemClient& client, + DisplayItem::Type type = DisplayItem::kDrawingFirst) + : DrawingDisplayItem(client, type, nullptr) { + SetKnownToBeOpaqueForTesting(); + } +}; + +class TestDisplayItemRequiringSeparateChunk : public ForeignLayerDisplayItem { + public: + explicit TestDisplayItemRequiringSeparateChunk( + const DisplayItemClient& client) + : ForeignLayerDisplayItem(client, + DisplayItem::kForeignLayerPlugin, + cc::Layer::Create(), + FloatPoint(), + nullptr) {} }; TEST_F(PaintChunkerTest, Empty) { @@ -52,7 +71,7 @@ TEST_F(PaintChunkerTest, Empty) { TEST_F(PaintChunkerTest, SingleNonEmptyRange) { PaintChunker chunker; PaintChunk::Id id(client_, DisplayItemType(1)); - chunker.UpdateCurrentPaintChunkProperties(id, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id, DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); @@ -69,10 +88,10 @@ TEST_F(PaintChunkerTest, SingleNonEmptyRange) { TEST_F(PaintChunkerTest, SamePropertiesTwiceCombineIntoOneChunk) { PaintChunker chunker; PaintChunk::Id id(client_, DisplayItemType(1)); - chunker.UpdateCurrentPaintChunkProperties(id, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id, DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); - chunker.UpdateCurrentPaintChunkProperties(id, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id, DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); const auto& chunks = chunker.PaintChunks(); @@ -88,7 +107,8 @@ TEST_F(PaintChunkerTest, SamePropertiesTwiceCombineIntoOneChunk) { TEST_F(PaintChunkerTest, BuildMultipleChunksWithSinglePropertyChanging) { PaintChunker chunker; PaintChunk::Id id1(client_, DisplayItemType(1)); - chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id1, + DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); @@ -98,7 +118,7 @@ TEST_F(PaintChunkerTest, BuildMultipleChunksWithSinglePropertyChanging) { simple_transform.SetTransform(*simple_transform_node); PaintChunk::Id id2(client_, DisplayItemType(2)); - chunker.UpdateCurrentPaintChunkProperties(id2, simple_transform); + chunker.UpdateCurrentPaintChunkProperties(&id2, simple_transform); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); auto another_transform_node = CreateTransform( @@ -106,7 +126,7 @@ TEST_F(PaintChunkerTest, BuildMultipleChunksWithSinglePropertyChanging) { auto another_transform = DefaultPaintChunkProperties(); another_transform.SetTransform(*another_transform_node); PaintChunk::Id id3(client_, DisplayItemType(3)); - chunker.UpdateCurrentPaintChunkProperties(id3, another_transform); + chunker.UpdateCurrentPaintChunkProperties(&id3, another_transform); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); EXPECT_THAT( @@ -119,7 +139,8 @@ TEST_F(PaintChunkerTest, BuildMultipleChunksWithSinglePropertyChanging) { TEST_F(PaintChunkerTest, BuildMultipleChunksWithDifferentPropertyChanges) { PaintChunker chunker; PaintChunk::Id id1(client_, DisplayItemType(1)); - chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id1, + DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); auto simple_transform_node = CreateTransform( @@ -127,7 +148,7 @@ TEST_F(PaintChunkerTest, BuildMultipleChunksWithDifferentPropertyChanges) { auto simple_transform = DefaultPaintChunkProperties(); simple_transform.SetTransform(*simple_transform_node); PaintChunk::Id id2(client_, DisplayItemType(2)); - chunker.UpdateCurrentPaintChunkProperties(id2, simple_transform); + chunker.UpdateCurrentPaintChunkProperties(&id2, simple_transform); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); @@ -136,7 +157,7 @@ TEST_F(PaintChunkerTest, BuildMultipleChunksWithDifferentPropertyChanges) { simple_transform_and_effect.SetTransform(*simple_transform_node); simple_transform_and_effect.SetEffect(*simple_effect_node); PaintChunk::Id id3(client_, DisplayItemType(3)); - chunker.UpdateCurrentPaintChunkProperties(id3, simple_transform_and_effect); + chunker.UpdateCurrentPaintChunkProperties(&id3, simple_transform_and_effect); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); @@ -151,13 +172,13 @@ TEST_F(PaintChunkerTest, BuildMultipleChunksWithDifferentPropertyChanges) { *new_effect_node); PaintChunk::Id id4(client_, DisplayItemType(4)); chunker.UpdateCurrentPaintChunkProperties( - id4, simple_transform_and_effect_with_updated_transform); + &id4, simple_transform_and_effect_with_updated_transform); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); // Test that going back to a previous chunk property still creates a new // chunk. - chunker.UpdateCurrentPaintChunkProperties(base::nullopt, + chunker.UpdateCurrentPaintChunkProperties(nullptr, simple_transform_and_effect); TestChunkerDisplayItem item_after_restore(client_, DisplayItemType(10)); chunker.IncrementDisplayItemIndex(item_after_restore); @@ -187,7 +208,8 @@ TEST_F(PaintChunkerTest, BuildChunksFromNestedTransforms) { // </root xform> PaintChunker chunker; PaintChunk::Id id1(client_, DisplayItemType(1)); - chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id1, + DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); auto simple_transform_node = CreateTransform( @@ -195,11 +217,11 @@ TEST_F(PaintChunkerTest, BuildChunksFromNestedTransforms) { auto simple_transform = DefaultPaintChunkProperties(); simple_transform.SetTransform(*simple_transform_node); PaintChunk::Id id2(client_, DisplayItemType(2)); - chunker.UpdateCurrentPaintChunkProperties(id2, simple_transform); + chunker.UpdateCurrentPaintChunkProperties(&id2, simple_transform); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); - chunker.UpdateCurrentPaintChunkProperties(base::nullopt, + chunker.UpdateCurrentPaintChunkProperties(nullptr, DefaultPaintChunkProperties()); TestChunkerDisplayItem item_after_restore(client_, DisplayItemType(10)); chunker.IncrementDisplayItemIndex(item_after_restore); @@ -216,7 +238,8 @@ TEST_F(PaintChunkerTest, ChangingPropertiesWithoutItems) { // Test that properties can change without display items being generated. PaintChunker chunker; PaintChunk::Id id1(client_, DisplayItemType(1)); - chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id1, + DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); auto first_transform_node = CreateTransform( @@ -224,14 +247,14 @@ TEST_F(PaintChunkerTest, ChangingPropertiesWithoutItems) { auto first_transform = DefaultPaintChunkProperties(); first_transform.SetTransform(*first_transform_node); PaintChunk::Id id2(client_, DisplayItemType(2)); - chunker.UpdateCurrentPaintChunkProperties(base::nullopt, first_transform); + chunker.UpdateCurrentPaintChunkProperties(nullptr, first_transform); auto second_transform_node = CreateTransform( t0(), TransformationMatrix(9, 8, 7, 6, 5, 4), FloatPoint3D(3, 2, 1)); auto second_transform = DefaultPaintChunkProperties(); second_transform.SetTransform(*second_transform_node); PaintChunk::Id id3(client_, DisplayItemType(3)); - chunker.UpdateCurrentPaintChunkProperties(id3, second_transform); + chunker.UpdateCurrentPaintChunkProperties(&id3, second_transform); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); EXPECT_THAT( @@ -244,22 +267,24 @@ TEST_F(PaintChunkerTest, CreatesSeparateChunksWhenRequested) { // Tests that the chunker creates a separate chunks for display items which // require it. PaintChunker chunker; - TestDisplayItemClient client1; + FakeDisplayItemClient client1; TestDisplayItemRequiringSeparateChunk i1(client1); - TestDisplayItemClient client2; + FakeDisplayItemClient client2; TestDisplayItemRequiringSeparateChunk i2(client2); - TestDisplayItemClient client3; + FakeDisplayItemClient client3; TestDisplayItemRequiringSeparateChunk i3(client3); PaintChunk::Id id0(client_, DisplayItemType(0)); - chunker.UpdateCurrentPaintChunkProperties(id0, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id0, + DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(i1); chunker.IncrementDisplayItemIndex(i2); TestChunkerDisplayItem after_i2(client_, DisplayItemType(10)); chunker.IncrementDisplayItemIndex(after_i2); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); - chunker.UpdateCurrentPaintChunkProperties(id0, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id0, + DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(i3); EXPECT_THAT( @@ -275,20 +300,36 @@ TEST_F(PaintChunkerTest, CreatesSeparateChunksWhenRequested) { TEST_F(PaintChunkerTest, ForceNewChunkWithNewId) { PaintChunker chunker; PaintChunk::Id id0(client_, DisplayItemType(0)); - chunker.UpdateCurrentPaintChunkProperties(id0, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id0, + DefaultPaintChunkProperties()); + EXPECT_TRUE(chunker.WillForceNewChunk()); + EXPECT_EQ(0u, chunker.size()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); + EXPECT_FALSE(chunker.WillForceNewChunk()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); + EXPECT_EQ(1u, chunker.size()); - chunker.ForceNewChunk(); - PaintChunk::Id id1(client_, DisplayItemType(10)); - chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties()); + chunker.SetForceNewChunk(true); + EXPECT_TRUE(chunker.WillForceNewChunk()); + EXPECT_EQ(1u, chunker.size()); + PaintChunk::Id id1(client_, DisplayItemType(1)); + chunker.UpdateCurrentPaintChunkProperties(&id1, + DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); + EXPECT_EQ(2u, chunker.size()); + EXPECT_FALSE(chunker.WillForceNewChunk()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); + EXPECT_EQ(2u, chunker.size()); - chunker.ForceNewChunk(); - PaintChunk::Id id2(client_, DisplayItemType(20)); - chunker.UpdateCurrentPaintChunkProperties(id2, DefaultPaintChunkProperties()); + chunker.SetForceNewChunk(true); + PaintChunk::Id id2(client_, DisplayItemType(2)); + EXPECT_TRUE(chunker.WillForceNewChunk()); + chunker.UpdateCurrentPaintChunkProperties(&id2, + DefaultPaintChunkProperties()); + EXPECT_EQ(2u, chunker.size()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); + EXPECT_EQ(3u, chunker.size()); + EXPECT_FALSE(chunker.WillForceNewChunk()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); EXPECT_THAT( @@ -301,25 +342,37 @@ TEST_F(PaintChunkerTest, ForceNewChunkWithNewId) { TEST_F(PaintChunkerTest, ForceNewChunkWithoutNewId) { PaintChunker chunker; PaintChunk::Id id0(client_, DisplayItemType(0)); - chunker.UpdateCurrentPaintChunkProperties(base::nullopt, + chunker.UpdateCurrentPaintChunkProperties(nullptr, DefaultPaintChunkProperties()); + EXPECT_TRUE(chunker.WillForceNewChunk()); + EXPECT_EQ(0u, chunker.size()); chunker.IncrementDisplayItemIndex( TestChunkerDisplayItem(id0.client, id0.type)); + EXPECT_FALSE(chunker.WillForceNewChunk()); + EXPECT_EQ(1u, chunker.size()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); - chunker.ForceNewChunk(); - PaintChunk::Id id1(client_, DisplayItemType(10)); + chunker.SetForceNewChunk(true); + EXPECT_TRUE(chunker.WillForceNewChunk()); + EXPECT_EQ(1u, chunker.size()); + PaintChunk::Id id1(client_, DisplayItemType(1)); chunker.IncrementDisplayItemIndex( TestChunkerDisplayItem(id1.client, id1.type)); + EXPECT_FALSE(chunker.WillForceNewChunk()); + EXPECT_EQ(2u, chunker.size()); chunker.IncrementDisplayItemIndex( - TestChunkerDisplayItem(client_, DisplayItemType(11))); + TestChunkerDisplayItem(client_, DisplayItemType(2))); - chunker.ForceNewChunk(); - PaintChunk::Id id2(client_, DisplayItemType(20)); + chunker.SetForceNewChunk(true); + EXPECT_TRUE(chunker.WillForceNewChunk()); + EXPECT_EQ(2u, chunker.size()); + PaintChunk::Id id2(client_, DisplayItemType(3)); chunker.IncrementDisplayItemIndex( TestChunkerDisplayItem(id2.client, id2.type)); + EXPECT_FALSE(chunker.WillForceNewChunk()); + EXPECT_EQ(3u, chunker.size()); chunker.IncrementDisplayItemIndex( - TestChunkerDisplayItem(client_, DisplayItemType(21))); + TestChunkerDisplayItem(client_, DisplayItemType(4))); EXPECT_THAT( chunker.PaintChunks(), @@ -328,19 +381,48 @@ TEST_F(PaintChunkerTest, ForceNewChunkWithoutNewId) { IsPaintChunk(4, 6, id2, DefaultPaintChunkProperties()))); } +TEST_F(PaintChunkerTest, SetAndImmediatelyUnsetForceNewChunk) { + PaintChunker chunker; + PaintChunk::Id id0(client_, DisplayItemType(0)); + chunker.UpdateCurrentPaintChunkProperties(nullptr, + DefaultPaintChunkProperties()); + EXPECT_TRUE(chunker.WillForceNewChunk()); + EXPECT_EQ(0u, chunker.size()); + chunker.IncrementDisplayItemIndex( + TestChunkerDisplayItem(id0.client, id0.type)); + EXPECT_FALSE(chunker.WillForceNewChunk()); + EXPECT_EQ(1u, chunker.size()); + chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); + + // This should not force a new chunk. Simulates a ScopedPaintChunkHint + // without any painting in the scope. + chunker.SetForceNewChunk(true); + chunker.SetForceNewChunk(false); + EXPECT_FALSE(chunker.WillForceNewChunk()); + chunker.IncrementDisplayItemIndex( + TestChunkerDisplayItem(client_, DisplayItemType(1))); + EXPECT_EQ(1u, chunker.size()); + + EXPECT_THAT( + chunker.PaintChunks(), + ElementsAre(IsPaintChunk(0, 3, id0, DefaultPaintChunkProperties()))); +} + TEST_F(PaintChunkerTest, NoNewChunkForSamePropertyDifferentIds) { PaintChunker chunker; PaintChunk::Id id0(client_, DisplayItemType(0)); - chunker.UpdateCurrentPaintChunkProperties(id0, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id0, + DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); PaintChunk::Id id1(client_, DisplayItemType(1)); - chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id1, + DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); - chunker.UpdateCurrentPaintChunkProperties(base::nullopt, + chunker.UpdateCurrentPaintChunkProperties(nullptr, DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); @@ -350,29 +432,24 @@ TEST_F(PaintChunkerTest, NoNewChunkForSamePropertyDifferentIds) { ElementsAre(IsPaintChunk(0, 6, id0, DefaultPaintChunkProperties()))); } -class TestScrollHitTestRequiringSeparateChunk : public TestChunkerDisplayItem { - public: - TestScrollHitTestRequiringSeparateChunk(const DisplayItemClient& client) - : TestChunkerDisplayItem(client, DisplayItem::kScrollHitTest) {} -}; - // Ensure that items following a forced chunk begin using the next display // item's id. TEST_F(PaintChunkerTest, ChunksFollowingForcedChunk) { PaintChunker chunker; - TestDisplayItemClient client; - TestChunkerDisplayItem before_forced1(client, DisplayItemType(9)); - TestChunkerDisplayItem before_forced2(client, DisplayItemType(10)); - TestScrollHitTestRequiringSeparateChunk forced(client); - TestChunkerDisplayItem after_forced1(client, DisplayItemType(11)); - TestChunkerDisplayItem after_forced2(client, DisplayItemType(12)); - - PaintChunk::Id id0(client, DisplayItemType(8)); - chunker.UpdateCurrentPaintChunkProperties(id0, DefaultPaintChunkProperties()); + FakeDisplayItemClient client; + TestChunkerDisplayItem before_forced1(client, DisplayItemType(1)); + TestChunkerDisplayItem before_forced2(client, DisplayItemType(2)); + TestDisplayItemRequiringSeparateChunk forced(client); + TestChunkerDisplayItem after_forced1(client, DisplayItemType(3)); + TestChunkerDisplayItem after_forced2(client, DisplayItemType(4)); + + PaintChunk::Id id0(client, DisplayItemType(5)); + chunker.UpdateCurrentPaintChunkProperties(&id0, + DefaultPaintChunkProperties()); // Both before_forced items should be in a chunk together. chunker.IncrementDisplayItemIndex(before_forced1); chunker.IncrementDisplayItemIndex(before_forced2); - // The forced scroll hit test item should be in its own chunk. + // |forced| forces a dedicted paint chunk. chunker.IncrementDisplayItemIndex(forced); // Both after_forced items should be in a chunk together. chunker.IncrementDisplayItemIndex(after_forced1); @@ -391,7 +468,8 @@ TEST_F(PaintChunkerTest, ChunkIdsSkippingCache) { PaintChunker chunker; PaintChunk::Id id1(client_, DisplayItemType(1)); - chunker.UpdateCurrentPaintChunkProperties(id1, DefaultPaintChunkProperties()); + chunker.UpdateCurrentPaintChunkProperties(&id1, + DefaultPaintChunkProperties()); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_)); @@ -400,10 +478,10 @@ TEST_F(PaintChunkerTest, ChunkIdsSkippingCache) { auto simple_transform = DefaultPaintChunkProperties(); simple_transform.SetTransform(*simple_transform_node); - TestDisplayItemClient uncacheable_client; + FakeDisplayItemClient uncacheable_client; uncacheable_client.Invalidate(PaintInvalidationReason::kUncacheable); PaintChunk::Id id2(uncacheable_client, DisplayItemType(2)); - chunker.UpdateCurrentPaintChunkProperties(id2, simple_transform); + chunker.UpdateCurrentPaintChunkProperties(&id2, simple_transform); TestChunkerDisplayItem uncacheable_item(uncacheable_client); chunker.IncrementDisplayItemIndex(uncacheable_item); @@ -416,7 +494,7 @@ TEST_F(PaintChunkerTest, ChunkIdsSkippingCache) { TestChunkerDisplayItem after_separate_chunk(client_, DisplayItemType(3)); chunker.IncrementDisplayItemIndex(after_separate_chunk); - chunker.UpdateCurrentPaintChunkProperties(base::nullopt, + chunker.UpdateCurrentPaintChunkProperties(nullptr, DefaultPaintChunkProperties()); TestChunkerDisplayItem after_restore(client_, DisplayItemType(4)); chunker.IncrementDisplayItemIndex(after_restore); @@ -439,5 +517,205 @@ TEST_F(PaintChunkerTest, ChunkIdsSkippingCache) { EXPECT_TRUE(chunks[4].is_cacheable); } +TEST_F(PaintChunkerTest, AddHitTestDataToCurrentChunk) { + PaintChunker chunker; + client_.SetVisualRect(IntRect(0, 0, 10, 10)); + + PaintChunk::Id id1(client_, DisplayItemType(1)); + + chunker.UpdateCurrentPaintChunkProperties(&id1, + DefaultPaintChunkProperties()); + chunker.IncrementDisplayItemIndex( + TestChunkerDisplayItem(client_, DisplayItemType(2))); + + PaintChunk::Id id2(client_, DisplayItemType(3)); + auto transform = Create2DTranslation(t0(), 10, 20); + PropertyTreeState properties(*transform, c0(), e0()); + chunker.UpdateCurrentPaintChunkProperties(&id2, properties); + // This is not used as id of the chunk because we already have |id2|. + PaintChunk::Id hit_test_id(client_, DisplayItem::kHitTest); + chunker.AddHitTestDataToCurrentChunk(hit_test_id, IntRect(10, 20, 30, 40), + TouchAction::kAuto); + chunker.AddHitTestDataToCurrentChunk(hit_test_id, IntRect(20, 30, 40, 50), + TouchAction::kPan); + + chunker.SetForceNewChunk(true); + PaintChunk::Id id3(client_, DisplayItemType(4)); + chunker.AddHitTestDataToCurrentChunk(id3, IntRect(40, 50, 60, 70), + TouchAction::kAuto); + chunker.IncrementDisplayItemIndex( + TestChunkerDisplayItem(client_, DisplayItemType(5))); + + HitTestData hit_test_data; + hit_test_data.touch_action_rects = { + {IntRect(20, 30, 40, 50), TouchAction::kPan}}; + if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { + EXPECT_THAT( + chunker.PaintChunks(), + ElementsAre(IsPaintChunk(0, 1, id1, DefaultPaintChunkProperties(), + nullptr, IntRect(0, 0, 10, 10)), + IsPaintChunk(1, 1, id2, properties, &hit_test_data, + IntRect(10, 20, 50, 60)), + IsPaintChunk(1, 2, id3, properties, nullptr, + IntRect(0, 0, 100, 120)))); + } else { + EXPECT_THAT( + chunker.PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, id1, DefaultPaintChunkProperties(), nullptr, + IntRect(0, 0, 10, 10)), + IsPaintChunk(1, 1, id2, properties, &hit_test_data, + IntRect(20, 30, 40, 50)), + IsPaintChunk(1, 2, PaintChunk::Id(client_, DisplayItemType(5)), + properties, nullptr, IntRect(0, 0, 10, 10)))); + } +} + +TEST_F(PaintChunkerTest, ChunkBoundsAndKnownToBeOpaqueAllOpaqueItems) { + ScopedCompositeAfterPaintForTest cap(true); + PaintChunker chunker; + FakeDisplayItemClient client1("client1", IntRect(0, 0, 100, 100)); + FakeDisplayItemClient client2("client2", IntRect(0, 100, 100, 50)); + FakeDisplayItemClient client3("client3", IntRect(50, 50, 100, 100)); + + auto properties = DefaultPaintChunkProperties(); + chunker.UpdateCurrentPaintChunkProperties(nullptr, properties); + // Single opaque item. + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client1, DisplayItemType(0))); + chunker.SetForceNewChunk(true); + // Two opaque items. No empty area in the united bounds. + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client1, DisplayItemType(1))); + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client2, DisplayItemType(2))); + chunker.SetForceNewChunk(true); + // Two opaque items. Has empty area in the united bounds. + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client1, DisplayItemType(3))); + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client3, DisplayItemType(4))); + + Vector<PaintChunk> chunks = chunker.ReleasePaintChunks(); + EXPECT_THAT( + chunks, + ElementsAre( + IsPaintChunk(0, 1, PaintChunk::Id(client1, DisplayItemType(0)), + properties, nullptr, IntRect(0, 0, 100, 100)), + IsPaintChunk(1, 3, PaintChunk::Id(client1, DisplayItemType(1)), + properties, nullptr, IntRect(0, 0, 100, 150)), + IsPaintChunk(3, 5, PaintChunk::Id(client1, DisplayItemType(3)), + properties, nullptr, IntRect(0, 0, 150, 150)))); + ASSERT_EQ(3u, chunks.size()); + EXPECT_TRUE(chunks[0].known_to_be_opaque); + EXPECT_TRUE(chunks[1].known_to_be_opaque); + EXPECT_FALSE(chunks[2].known_to_be_opaque); +} + +TEST_F(PaintChunkerTest, ChunkBoundsAndKnownToBeOpaqueWithHitTest) { + ScopedCompositeAfterPaintForTest cap(true); + PaintChunker chunker; + FakeDisplayItemClient client1("client1", IntRect(0, 0, 100, 100)); + FakeDisplayItemClient client2("client2", IntRect(0, 100, 100, 50)); + FakeDisplayItemClient client3("client3", IntRect(50, 50, 100, 100)); + + auto properties = DefaultPaintChunkProperties(); + chunker.UpdateCurrentPaintChunkProperties(nullptr, properties); + // Hit test rect only. + chunker.AddHitTestDataToCurrentChunk( + PaintChunk::Id(client1, DisplayItemType(0)), IntRect(10, 20, 30, 40), + TouchAction::kAuto); + chunker.SetForceNewChunk(true); + // Hit test rect is smaller than the opaque item. + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client1, DisplayItemType(1))); + chunker.AddHitTestDataToCurrentChunk( + PaintChunk::Id(client1, DisplayItemType(2)), IntRect(0, 0, 50, 100), + TouchAction::kAuto); + chunker.SetForceNewChunk(true); + // Hit test rect is the same as the opaque item. + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client1, DisplayItemType(3))); + chunker.AddHitTestDataToCurrentChunk( + PaintChunk::Id(client1, DisplayItemType(4)), IntRect(0, 0, 100, 100), + TouchAction::kAuto); + chunker.SetForceNewChunk(true); + // Hit test rect is bigger than the opaque item. + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client1, DisplayItemType(5))); + chunker.AddHitTestDataToCurrentChunk( + PaintChunk::Id(client1, DisplayItemType(6)), IntRect(0, 100, 200, 100), + TouchAction::kAuto); + + Vector<PaintChunk> chunks = chunker.ReleasePaintChunks(); + EXPECT_THAT( + chunks, + ElementsAre( + IsPaintChunk(0, 0, PaintChunk::Id(client1, DisplayItemType(0)), + properties, nullptr, IntRect(10, 20, 30, 40)), + IsPaintChunk(0, 1, PaintChunk::Id(client1, DisplayItemType(1)), + properties, nullptr, IntRect(0, 0, 100, 100)), + IsPaintChunk(1, 2, PaintChunk::Id(client1, DisplayItemType(3)), + properties, nullptr, IntRect(0, 0, 100, 100)), + IsPaintChunk(2, 3, PaintChunk::Id(client1, DisplayItemType(5)), + properties, nullptr, IntRect(0, 0, 200, 200)))); + ASSERT_EQ(4u, chunks.size()); + EXPECT_FALSE(chunks[0].known_to_be_opaque); + EXPECT_TRUE(chunks[1].known_to_be_opaque); + EXPECT_TRUE(chunks[2].known_to_be_opaque); + EXPECT_FALSE(chunks[3].known_to_be_opaque); +} + +TEST_F(PaintChunkerTest, ChunkBoundsAndKnownToBeOpaqueMixedOpaquenessItems) { + ScopedCompositeAfterPaintForTest cap(true); + PaintChunker chunker; + FakeDisplayItemClient client1("client1", IntRect(0, 0, 100, 100)); + FakeDisplayItemClient client3("client2", IntRect(50, 50, 50, 50)); + + auto properties = DefaultPaintChunkProperties(); + chunker.UpdateCurrentPaintChunkProperties(nullptr, properties); + // Single translucent item . + chunker.IncrementDisplayItemIndex( + TestChunkerDisplayItem(client1, DisplayItemType(1))); + chunker.SetForceNewChunk(true); + // Two items, one translucent, one opaque. The opaque item doesn't contain + // the translucent item. + chunker.IncrementDisplayItemIndex( + TestChunkerDisplayItem(client1, DisplayItemType(2))); + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client3, DisplayItemType(3))); + chunker.SetForceNewChunk(true); + // Two items, one translucent, one opaque, with the same visual rect. + chunker.IncrementDisplayItemIndex( + TestChunkerDisplayItem(client1, DisplayItemType(4))); + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client1, DisplayItemType(5))); + chunker.SetForceNewChunk(true); + // Two items, one opaque, one translucent. The opaque item contains the + // translucent item. + chunker.IncrementDisplayItemIndex( + TestChunkerOpaqueDisplayItem(client1, DisplayItemType(6))); + chunker.IncrementDisplayItemIndex( + TestChunkerDisplayItem(client3, DisplayItemType(7))); + + Vector<PaintChunk> chunks = chunker.ReleasePaintChunks(); + EXPECT_THAT( + chunks, + ElementsAre( + IsPaintChunk(0, 1, PaintChunk::Id(client1, DisplayItemType(1)), + properties, nullptr, IntRect(0, 0, 100, 100)), + IsPaintChunk(1, 3, PaintChunk::Id(client1, DisplayItemType(2)), + properties, nullptr, IntRect(0, 0, 100, 100)), + IsPaintChunk(3, 5, PaintChunk::Id(client1, DisplayItemType(4)), + properties, nullptr, IntRect(0, 0, 100, 100)), + IsPaintChunk(5, 7, PaintChunk::Id(client1, DisplayItemType(6)), + properties, nullptr, IntRect(0, 0, 100, 100)))); + ASSERT_EQ(4u, chunks.size()); + EXPECT_FALSE(chunks[0].known_to_be_opaque); + EXPECT_FALSE(chunks[1].known_to_be_opaque); + EXPECT_TRUE(chunks[2].known_to_be_opaque); + EXPECT_TRUE(chunks[3].known_to_be_opaque); +} + } // namespace } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc index ff52743090b..fa69422b443 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc @@ -31,15 +31,57 @@ PaintController::~PaintController() { DCHECK(usage_ == kTransient || new_display_item_list_.IsEmpty()); } +// For micro benchmarks of record time. +static bool g_subsequence_caching_disabled = false; +static bool g_partial_invalidation = false; +static int g_partial_invalidation_display_item_count = 0; +static int g_partial_invalidation_subsequence_count = 0; + +// This is used to invalidate one out of every |kInvalidateDisplayItemInterval| +// display items for the micro benchmark of record time with partial +// invalidation. +static bool ShouldInvalidateDisplayItemForBenchmark() { + constexpr int kInvalidateDisplayItemInterval = 8; + return g_partial_invalidation && + !(g_partial_invalidation_display_item_count++ % + kInvalidateDisplayItemInterval); +} +// Similar to the above, but for subsequences. +static bool ShouldInvalidateSubsequenceForBenchmark() { + constexpr int kInvalidateSubsequenceInterval = 2; + return g_partial_invalidation && + !(g_partial_invalidation_subsequence_count++ % + kInvalidateSubsequenceInterval); +} + +void PaintController::SetSubsequenceCachingDisabledForBenchmark() { + g_subsequence_caching_disabled = true; +} + +void PaintController::SetPartialInvalidationForBenchmark() { + g_partial_invalidation = true; + g_partial_invalidation_display_item_count = 0; + g_partial_invalidation_subsequence_count = 0; +} + +bool PaintController::ShouldForcePaintForBenchmark() { + return g_subsequence_caching_disabled || g_partial_invalidation; +} + +void PaintController::ClearFlagsForBenchmark() { + g_subsequence_caching_disabled = false; + g_partial_invalidation = false; +} + bool PaintController::UseCachedItemIfPossible(const DisplayItemClient& client, DisplayItem::Type type) { if (usage_ == kTransient) return false; - if (DisplayItemConstructionIsDisabled()) + if (!ClientCacheIsValid(client)) return false; - if (!ClientCacheIsValid(client)) + if (ShouldInvalidateDisplayItemForBenchmark()) return false; if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() && @@ -49,7 +91,7 @@ bool PaintController::UseCachedItemIfPossible(const DisplayItemClient& client, return false; } - size_t cached_item = + auto cached_item = FindCachedItem(DisplayItem::Id(client, type, current_fragment_)); if (cached_item == kNotFound) { // See FindOutOfOrderCachedItemForward() for explanation of the situation. @@ -89,7 +131,10 @@ bool PaintController::UseCachedSubsequenceIfPossible( if (usage_ == kTransient) return false; - if (DisplayItemConstructionIsDisabled() || SubsequenceCachingIsDisabled()) + if (g_subsequence_caching_disabled) + return false; + + if (ShouldInvalidateSubsequenceForBenchmark()) return false; if (!ClientCacheIsValid(client)) @@ -109,7 +154,14 @@ bool PaintController::UseCachedSubsequenceIfPossible( return false; } - if (current_paint_artifact_->GetDisplayItemList()[markers->start] + wtf_size_t start_item_index = + current_paint_artifact_->PaintChunks()[markers->start_chunk_index] + .begin_index; + wtf_size_t end_item_index = + current_paint_artifact_->PaintChunks()[markers->end_chunk_index - 1] + .end_index; + if (end_item_index > start_item_index && + current_paint_artifact_->GetDisplayItemList()[start_item_index] .IsTombstone()) { // The subsequence has already been copied, indicating that the same client // created multiple subsequences. If DCHECK_IS_ON(), then we should have @@ -121,23 +173,23 @@ bool PaintController::UseCachedSubsequenceIfPossible( EnsureNewDisplayItemListInitialCapacity(); - if (next_item_to_match_ == markers->start) { + if (next_item_to_match_ == start_item_index) { // We are matching new and cached display items sequentially. Skip the // subsequence for later sequential matching of individual display items. - next_item_to_match_ = markers->end; + next_item_to_match_ = end_item_index; // Items before |next_item_to_match_| have been copied so we don't need to // index them. if (next_item_to_match_ > next_item_to_index_) next_item_to_index_ = next_item_to_match_; } - num_cached_new_items_ += markers->end - markers->start; + num_cached_new_items_ += end_item_index - start_item_index; ++num_cached_new_subsequences_; if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) { DCHECK(!IsCheckingUnderInvalidation()); - under_invalidation_checking_begin_ = markers->start; - under_invalidation_checking_end_ = markers->end; + under_invalidation_checking_begin_ = start_item_index; + under_invalidation_checking_end_ = end_item_index; under_invalidation_message_prefix_ = "(In cached subsequence for " + client.DebugName() + ")"; // Return false to let the painter actually paint. We will check if the new @@ -145,9 +197,9 @@ bool PaintController::UseCachedSubsequenceIfPossible( return false; } - size_t start = BeginSubsequence(); - CopyCachedSubsequence(markers->start, markers->end); - EndSubsequence(client, start); + auto new_start_chunk_index = BeginSubsequence(); + CopyCachedSubsequence(markers->start_chunk_index, markers->end_chunk_index); + EndSubsequence(client, new_start_chunk_index); return true; } @@ -159,33 +211,50 @@ PaintController::SubsequenceMarkers* PaintController::GetSubsequenceMarkers( return &result->value; } -size_t PaintController::BeginSubsequence() { +wtf_size_t PaintController::BeginSubsequence() { // Force new paint chunk which is required for subsequence caching. - new_paint_chunks_.ForceNewChunk(); - return new_display_item_list_.size(); + SetForceNewChunk(true); + return new_paint_chunks_.size(); } void PaintController::EndSubsequence(const DisplayItemClient& client, - size_t start) { - size_t end = new_display_item_list_.size(); + wtf_size_t start_chunk_index) { + auto end_chunk_index = new_paint_chunks_.size(); if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() && IsCheckingUnderInvalidation()) { SubsequenceMarkers* markers = GetSubsequenceMarkers(client); - if (!markers && start != end) { - ShowSequenceUnderInvalidationError( - "under-invalidation : unexpected subsequence", client, start, end); - CHECK(false); - } - if (markers && markers->end - markers->start != end - start) { - ShowSequenceUnderInvalidationError( - "under-invalidation: new subsequence wrong length", client, start, - end); - CHECK(false); + if (!markers) { + if (start_chunk_index != end_chunk_index) { + ShowSequenceUnderInvalidationError( + "under-invalidation : unexpected subsequence", client); + CHECK(false); + } + } else { + if (markers->end_chunk_index - markers->start_chunk_index != + end_chunk_index - start_chunk_index) { + ShowSequenceUnderInvalidationError( + "under-invalidation: new subsequence wrong length", client); + CHECK(false); + } + auto old_chunk_index = markers->start_chunk_index; + for (auto new_chunk_index = start_chunk_index; + new_chunk_index < end_chunk_index; + ++new_chunk_index, ++old_chunk_index) { + const auto& old_chunk = + current_paint_artifact_->PaintChunks()[old_chunk_index]; + const auto& new_chunk = + new_paint_chunks_.PaintChunks()[new_chunk_index]; + if (!old_chunk.EqualsForUnderInvalidationChecking(new_chunk)) { + ShowSequenceUnderInvalidationError( + "under-invalidation: chunk changed", client); + CHECK(false) << "Changed chunk: " << new_chunk; + } + } } } - if (start == end) { + if (start_chunk_index == end_chunk_index) { // Omit the empty subsequence. The forcing-new-chunk flag set by // BeginSubsequence() still applies, but this not a big deal because empty // subsequences are not common. Also we should not clear the flag because @@ -194,35 +263,24 @@ void PaintController::EndSubsequence(const DisplayItemClient& client, } // Force new paint chunk which is required for subsequence caching. - new_paint_chunks_.ForceNewChunk(); + SetForceNewChunk(true); DCHECK(!new_cached_subsequences_.Contains(&client)) << "Multiple subsequences for client: " << client.DebugName(); - new_cached_subsequences_.insert(&client, SubsequenceMarkers(start, end)); + new_cached_subsequences_.insert( + &client, SubsequenceMarkers{start_chunk_index, end_chunk_index}); } -void PaintController::ProcessNewItem(DisplayItem& display_item) { - DCHECK(!construction_disabled_); - - if (IsSkippingCache() && usage_ == kMultiplePaints) { - display_item.Client().Invalidate(PaintInvalidationReason::kUncacheable); - display_item.SetUncacheable(); - } - - bool chunk_added = new_paint_chunks_.IncrementDisplayItemIndex(display_item); +void PaintController::DidAppendItem(DisplayItem& display_item) { + if (usage_ == kTransient) + return; #if DCHECK_IS_ON() - if (chunk_added && CurrentPaintChunk().is_cacheable) { - AddToIndicesByClientMap(CurrentPaintChunk().id.client, - new_paint_chunks_.LastChunkIndex(), - new_paint_chunk_indices_by_client_); - } - - if (usage_ == kMultiplePaints && display_item.IsCacheable()) { - size_t index = FindMatchingItemFromIndex( - display_item.GetId(), new_display_item_indices_by_client_, - new_display_item_list_); + if (display_item.IsCacheable()) { + auto index = FindMatchingItemFromIndex(display_item.GetId(), + new_display_item_indices_by_client_, + new_display_item_list_); if (index != kNotFound) { ShowDebugData(); NOTREACHED() << "DisplayItem " << display_item.AsDebugString().Utf8() @@ -234,13 +292,20 @@ void PaintController::ProcessNewItem(DisplayItem& display_item) { new_display_item_list_.size() - 1, new_display_item_indices_by_client_); } -#else // DCHECK_IS_ON() - std::ignore = chunk_added; #endif - if (usage_ == kMultiplePaints && - RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) + if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) CheckUnderInvalidation(); +} + +void PaintController::ProcessNewItem(DisplayItem& display_item) { + if (IsSkippingCache() && usage_ == kMultiplePaints) { + display_item.Client().Invalidate(PaintInvalidationReason::kUncacheable); + display_item.SetUncacheable(); + } + + if (new_paint_chunks_.IncrementDisplayItemIndex(display_item)) + DidAppendChunk(); if (!frame_first_paints_.back().first_painted && display_item.IsDrawing() && // Here we ignore all document-background paintings because we don't @@ -251,13 +316,26 @@ void PaintController::ProcessNewItem(DisplayItem& display_item) { display_item.DrawsContent()) { SetFirstPainted(); } + + DidAppendItem(display_item); } -DisplayItem& PaintController::MoveItemFromCurrentListToNewList(size_t index) { +DisplayItem& PaintController::MoveItemFromCurrentListToNewList( + wtf_size_t index) { return new_display_item_list_.AppendByMoving( current_paint_artifact_->GetDisplayItemList()[index]); } +void PaintController::DidAppendChunk() { +#if DCHECK_IS_ON() + if (new_paint_chunks_.LastChunk().is_cacheable) { + AddToIndicesByClientMap(new_paint_chunks_.LastChunk().id.client, + new_paint_chunks_.size() - 1, + new_paint_chunk_indices_by_client_); + } +#endif +} + void PaintController::InvalidateAll() { DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); InvalidateAllInternal(); @@ -278,6 +356,25 @@ bool PaintController::CacheIsAllInvalid() const { return cache_is_all_invalid_; } +void PaintController::UpdateCurrentPaintChunkProperties( + const PaintChunk::Id* id, + const PropertyTreeState& properties) { + if (id) { + PaintChunk::Id id_with_fragment(*id, current_fragment_); + new_paint_chunks_.UpdateCurrentPaintChunkProperties(&id_with_fragment, + properties); + CheckDuplicatePaintChunkId(id_with_fragment); + } else { + new_paint_chunks_.UpdateCurrentPaintChunkProperties(nullptr, properties); + } +} + +void PaintController::AppendChunkByMoving(PaintChunk&& chunk) { + CheckDuplicatePaintChunkId(chunk.id); + new_paint_chunks_.AppendByMoving(std::move(chunk)); + DidAppendChunk(); +} + bool PaintController::ClientCacheIsValid( const DisplayItemClient& client) const { #if DCHECK_IS_ON() @@ -288,7 +385,7 @@ bool PaintController::ClientCacheIsValid( return client.IsValid(); } -size_t PaintController::FindMatchingItemFromIndex( +wtf_size_t PaintController::FindMatchingItemFromIndex( const DisplayItem::Id& id, const IndicesByClientMap& display_item_indices_by_client, const DisplayItemList& list) { @@ -297,8 +394,7 @@ size_t PaintController::FindMatchingItemFromIndex( if (it == display_item_indices_by_client.end()) return kNotFound; - const Vector<size_t>& indices = it->value; - for (size_t index : indices) { + for (auto index : it->value) { const DisplayItem& existing_item = list[index]; if (existing_item.IsTombstone()) continue; @@ -311,17 +407,17 @@ size_t PaintController::FindMatchingItemFromIndex( } void PaintController::AddToIndicesByClientMap(const DisplayItemClient& client, - size_t index, + wtf_size_t index, IndicesByClientMap& map) { auto it = map.find(&client); auto& indices = it == map.end() - ? map.insert(&client, Vector<size_t>()).stored_value->value + ? map.insert(&client, Vector<wtf_size_t>()).stored_value->value : it->value; indices.push_back(index); } -size_t PaintController::FindCachedItem(const DisplayItem::Id& id) { +wtf_size_t PaintController::FindCachedItem(const DisplayItem::Id& id) { DCHECK(ClientCacheIsValid(id.client)); if (next_item_to_match_ < @@ -342,7 +438,7 @@ size_t PaintController::FindCachedItem(const DisplayItem::Id& id) { } } - size_t found_index = + wtf_size_t found_index = FindMatchingItemFromIndex(id, out_of_order_item_indices_, current_paint_artifact_->GetDisplayItemList()); if (found_index != kNotFound) { @@ -356,9 +452,9 @@ size_t PaintController::FindCachedItem(const DisplayItem::Id& id) { } // Find forward for the item and index all skipped indexable items. -size_t PaintController::FindOutOfOrderCachedItemForward( +wtf_size_t PaintController::FindOutOfOrderCachedItemForward( const DisplayItem::Id& id) { - for (size_t i = next_item_to_index_; + for (auto i = next_item_to_index_; i < current_paint_artifact_->GetDisplayItemList().size(); ++i) { const DisplayItem& item = current_paint_artifact_->GetDisplayItemList()[i]; if (item.IsTombstone()) @@ -398,64 +494,48 @@ size_t PaintController::FindOutOfOrderCachedItemForward( // When paintUnderInvaldiationCheckingEnabled() we'll not actually // copy the subsequence, but mark the begin and end of the subsequence for // under-invalidation checking. -void PaintController::CopyCachedSubsequence(size_t begin_index, - size_t end_index) { +void PaintController::CopyCachedSubsequence(wtf_size_t start_chunk_index, + wtf_size_t end_chunk_index) { +#if DCHECK_IS_ON() DCHECK(!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()); - - const DisplayItem* cached_item = - ¤t_paint_artifact_->GetDisplayItemList()[begin_index]; - - auto* cached_chunk = - current_paint_artifact_->FindChunkByDisplayItemIndex(begin_index); - DCHECK(cached_chunk != current_paint_artifact_->PaintChunks().end()); auto properties_before_subsequence = new_paint_chunks_.CurrentPaintChunkProperties(); - UpdateCurrentPaintChunkPropertiesUsingIdWithFragment( - cached_chunk->id, cached_chunk->properties.GetPropertyTreeState()); - - for (size_t current_index = begin_index; current_index < end_index; - ++current_index) { - cached_item = ¤t_paint_artifact_->GetDisplayItemList()[current_index]; - SECURITY_CHECK(!cached_item->IsTombstone()); -#if DCHECK_IS_ON() - DCHECK(cached_item->Client().IsAlive()); #endif - if (current_index == cached_chunk->end_index) { - ++cached_chunk; - DCHECK(cached_chunk != current_paint_artifact_->PaintChunks().end()); - new_paint_chunks_.ForceNewChunk(); - UpdateCurrentPaintChunkPropertiesUsingIdWithFragment( - cached_chunk->id, cached_chunk->properties.GetPropertyTreeState()); - } - + for (auto chunk_index = start_chunk_index; chunk_index < end_chunk_index; + ++chunk_index) { + auto& cached_chunk = current_paint_artifact_->PaintChunks()[chunk_index]; + auto cached_item_index = cached_chunk.begin_index; + for (auto& cached_item : + current_paint_artifact_->GetDisplayItemList().ItemsInPaintChunk( + cached_chunk)) { + SECURITY_CHECK(!cached_item.IsTombstone()); #if DCHECK_IS_ON() - // Visual rect change should not happen in a cached subsequence. - // However, because of different method of pixel snapping in different - // paths, there are false positives. Just log an error. - if (cached_item->VisualRect() != cached_item->Client().VisualRect()) { - DLOG(ERROR) << "Visual rect changed in a cached subsequence: " - << cached_item->Client().DebugName() - << " old=" << cached_item->VisualRect() - << " new=" << cached_item->Client().VisualRect(); - } + DCHECK(cached_item.Client().IsAlive()); + // Visual rect change should not happen in a cached subsequence. + // However, because of different method of pixel snapping in different + // paths, there are false positives. Just log an error. + if (cached_item.VisualRect() != cached_item.Client().VisualRect()) { + DLOG(ERROR) << "Visual rect changed in a cached subsequence: " + << cached_item.Client().DebugName() + << " old=" << cached_item.VisualRect() + << " new=" << cached_item.Client().VisualRect(); + } #endif + auto& item = MoveItemFromCurrentListToNewList(cached_item_index++); + item.SetMovedFromCachedSubsequence(true); + DidAppendItem(item); + } - ProcessNewItem(MoveItemFromCurrentListToNewList(current_index)); - DCHECK((!CurrentPaintChunk().is_cacheable && !cached_chunk->is_cacheable) || - CurrentPaintChunk().Matches(*cached_chunk)); + DCHECK_EQ(cached_item_index, cached_chunk.end_index); + AppendChunkByMoving(std::move(cached_chunk)); } - if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) { - under_invalidation_checking_end_ = end_index; - DCHECK(IsCheckingUnderInvalidation()); - } else { - // Restore properties and force new chunk for any trailing display items - // after the cached subsequence without new properties. - new_paint_chunks_.ForceNewChunk(); - UpdateCurrentPaintChunkProperties(base::nullopt, - properties_before_subsequence); - } + SetForceNewChunk(true); + +#if DCHECK_IS_ON() + DCHECK_EQ(properties_before_subsequence, CurrentPaintChunkProperties()); +#endif } void PaintController::ResetCurrentListIndices() { @@ -527,13 +607,31 @@ void PaintController::FinishCycle() { } for (const auto& item : current_paint_artifact_->GetDisplayItemList()) { const auto& client = item.Client(); + if (item.IsMovedFromCachedSubsequence()) { + // We don't need to validate the clients of a display item that is + // copied from a cached subsequence, because it should be already + // valid. See http://crbug.com/1050090 for more details. +#if DCHECK_IS_ON() + DCHECK(client.IsAlive()); + DCHECK(client.IsValid() || !client.IsCacheable()); +#endif + continue; + } client.ClearPartialInvalidationVisualRect(); if (client.IsCacheable()) client.Validate(); } for (const auto& chunk : current_paint_artifact_->PaintChunks()) { - if (chunk.id.client.IsCacheable()) - chunk.id.client.Validate(); + const auto& client = chunk.id.client; + if (chunk.is_moved_from_cached_subsequence) { +#if DCHECK_IS_ON() + DCHECK(client.IsAlive()); + DCHECK(client.IsValid() || !client.IsCacheable()); +#endif + continue; + } + if (client.IsCacheable()) + client.Validate(); } } @@ -638,9 +736,7 @@ void PaintController::ShowUnderInvalidationError( void PaintController::ShowSequenceUnderInvalidationError( const char* reason, - const DisplayItemClient& client, - int start, - int end) { + const DisplayItemClient& client) { LOG(ERROR) << under_invalidation_message_prefix_ << " " << reason; LOG(ERROR) << "Subsequence client: " << client.DebugName(); #if DCHECK_IS_ON() @@ -669,7 +765,7 @@ void PaintController::CheckUnderInvalidation() { } const DisplayItem& new_item = new_display_item_list_.Last(); - size_t old_item_index = under_invalidation_checking_begin_; + auto old_item_index = under_invalidation_checking_begin_; DisplayItem* old_item = old_item_index < current_paint_artifact_->GetDisplayItemList().size() ? ¤t_paint_artifact_->GetDisplayItemList()[old_item_index] @@ -720,19 +816,21 @@ FrameFirstPaint PaintController::EndFrame(const void* frame) { return result; } -#if DCHECK_IS_ON() void PaintController::CheckDuplicatePaintChunkId(const PaintChunk::Id& id) { +#if DCHECK_IS_ON() if (IsSkippingCache()) return; - if (DisplayItem::IsForeignLayerType(id.type)) + if (DisplayItem::IsGraphicsLayerWrapperType(id.type) || + DisplayItem::IsForeignLayerType(id.type)) { return; + } auto it = new_paint_chunk_indices_by_client_.find(&id.client); if (it != new_paint_chunk_indices_by_client_.end()) { const auto& indices = it->value; for (auto index : indices) { - const auto& chunk = new_paint_chunks_.PaintChunkAt(index); + const auto& chunk = new_paint_chunks_.PaintChunks()[index]; if (chunk.id == id) { ShowDebugData(); NOTREACHED() << "New paint chunk id " << id @@ -740,8 +838,8 @@ void PaintController::CheckDuplicatePaintChunkId(const PaintChunk::Id& id) { } } } -} #endif +} size_t PaintController::sum_num_items_ = 0; size_t PaintController::sum_num_cached_items_ = 0; diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.h index 118f538d49e..1aaea13fdda 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.h @@ -28,7 +28,7 @@ namespace blink { -static const size_t kInitialDisplayItemListCapacityBytes = 512; +static constexpr wtf_size_t kInitialDisplayItemListCapacityBytes = 512; // FrameFirstPaint stores first-paint, text or image painted for the // corresponding frame. They are never reset to false. First-paint is defined in @@ -73,33 +73,42 @@ class PLATFORM_EXPORT PaintController { // These methods are called during painting. // Provide a new set of paint chunk properties to apply to recorded display - // items. - void UpdateCurrentPaintChunkProperties( - const base::Optional<PaintChunk::Id>& id, - const PropertyTreeState& properties) { - if (id) { - PaintChunk::Id id_with_fragment(*id, current_fragment_); - UpdateCurrentPaintChunkPropertiesUsingIdWithFragment(id_with_fragment, - properties); -#if DCHECK_IS_ON() - CheckDuplicatePaintChunkId(id_with_fragment); -#endif - } else { - new_paint_chunks_.UpdateCurrentPaintChunkProperties(base::nullopt, - properties); - } - } - + // items. If id is nullptr, the id of the first display item will be used as + // the id of the paint chunk if needed. + void UpdateCurrentPaintChunkProperties(const PaintChunk::Id*, + const PropertyTreeState&); const PropertyTreeState& CurrentPaintChunkProperties() const { return new_paint_chunks_.CurrentPaintChunkProperties(); } + // See PaintChunker for documentation of the following methods. + wtf_size_t NumNewChunks() const { return new_paint_chunks_.size(); } + void SetForceNewChunk(bool force) { + new_paint_chunks_.SetForceNewChunk(force); + } + bool WillForceNewChunk() const { + return new_paint_chunks_.WillForceNewChunk(); + } + const IntRect& LastChunkBounds() const { + return new_paint_chunks_.LastChunk().bounds; + } - PaintChunk& CurrentPaintChunk() { return new_paint_chunks_.LastChunk(); } - - void ForceNewChunk(const DisplayItemClient& client, DisplayItem::Type type) { - new_paint_chunks_.ForceNewChunk(); - new_paint_chunks_.UpdateCurrentPaintChunkProperties( - PaintChunk::Id(client, type), CurrentPaintChunkProperties()); + void RecordHitTestData(const DisplayItemClient& client, + const IntRect& rect, + TouchAction touch_action) { + if (rect.IsEmpty()) + return; + PaintChunk::Id id(client, DisplayItem::kHitTest, current_fragment_); + CheckDuplicatePaintChunkId(id); + new_paint_chunks_.AddHitTestDataToCurrentChunk(id, rect, touch_action); + } + void RecordScrollHitTestData( + const DisplayItemClient& client, + DisplayItem::Type type, + const TransformPaintPropertyNode* scroll_translation, + const IntRect& rect) { + PaintChunk::Id id(client, type, current_fragment_); + CheckDuplicatePaintChunkId(id); + new_paint_chunks_.CreateScrollHitTestChunk(id, scroll_translation, rect); } template <typename DisplayItemClass, typename... Args> @@ -113,9 +122,6 @@ class PLATFORM_EXPORT PaintController { "DisplayItem subclass alignment is not a factor of " "kDisplayItemAlignment."); - if (DisplayItemConstructionIsDisabled()) - return; - EnsureNewDisplayItemListInitialCapacity(); DisplayItemClass& display_item = new_display_item_list_.AllocateAndConstruct<DisplayItemClass>( @@ -134,10 +140,11 @@ class PLATFORM_EXPORT PaintController { // true. Otherwise returns false. bool UseCachedSubsequenceIfPossible(const DisplayItemClient&); - size_t BeginSubsequence(); + // Returns the index of the paint chunk that is forced for the subsequence. + wtf_size_t BeginSubsequence(); // The |start| parameter should be the return value of the corresponding // BeginSubsequence(). - void EndSubsequence(const DisplayItemClient&, size_t start); + void EndSubsequence(const DisplayItemClient&, wtf_size_t start_chunk_index); void BeginSkippingCache() { if (usage_ == kTransient) @@ -197,23 +204,11 @@ class PLATFORM_EXPORT PaintController { return GetPaintArtifact().PaintChunks(); } - // For micro benchmarking of record time. If true, display item construction - // is disabled to isolate the costs of construction in performance metrics. - bool DisplayItemConstructionIsDisabled() const { - return construction_disabled_; - } - void SetDisplayItemConstructionIsDisabled(bool disable) { - construction_disabled_ = disable; - } - - // For micro benchmarking of record time. If true, subsequence caching is - // disabled to test the cost of display item caching. - bool SubsequenceCachingIsDisabled() const { - return subsequence_caching_disabled_; - } - void SetSubsequenceCachingIsDisabled(bool disable) { - subsequence_caching_disabled_ = disable; - } + // For micro benchmarks of record time. + static void SetSubsequenceCachingDisabledForBenchmark(); + static void SetPartialInvalidationForBenchmark(); + static bool ShouldForcePaintForBenchmark(); + static void ClearFlagsForBenchmark(); void SetFirstPainted(); void SetTextPainted(); @@ -278,30 +273,29 @@ class PLATFORM_EXPORT PaintController { } } - // Set new item state (cache skipping, etc) for a new item. + // Set new item state (cache skipping, etc) for the last new display item. void ProcessNewItem(DisplayItem&); - DisplayItem& MoveItemFromCurrentListToNewList(size_t); + + void DidAppendItem(DisplayItem&); + DisplayItem& MoveItemFromCurrentListToNewList(wtf_size_t); + void DidAppendChunk(); // Maps clients to indices of display items or chunks of each client. - using IndicesByClientMap = HashMap<const DisplayItemClient*, Vector<size_t>>; + using IndicesByClientMap = + HashMap<const DisplayItemClient*, Vector<wtf_size_t>>; - static size_t FindMatchingItemFromIndex(const DisplayItem::Id&, - const IndicesByClientMap&, - const DisplayItemList&); + static wtf_size_t FindMatchingItemFromIndex(const DisplayItem::Id&, + const IndicesByClientMap&, + const DisplayItemList&); static void AddToIndicesByClientMap(const DisplayItemClient&, - size_t index, + wtf_size_t index, IndicesByClientMap&); - size_t FindCachedItem(const DisplayItem::Id&); - size_t FindOutOfOrderCachedItemForward(const DisplayItem::Id&); - void CopyCachedSubsequence(size_t begin_index, size_t end_index); - - void UpdateCurrentPaintChunkPropertiesUsingIdWithFragment( - const PaintChunk::Id& id_with_fragment, - const PropertyTreeState& properties) { - new_paint_chunks_.UpdateCurrentPaintChunkProperties(id_with_fragment, - properties); - } + wtf_size_t FindCachedItem(const DisplayItem::Id&); + wtf_size_t FindOutOfOrderCachedItemForward(const DisplayItem::Id&); + void CopyCachedSubsequence(wtf_size_t start_chunk_index, + wtf_size_t end_chunk_index); + void AppendChunkByMoving(PaintChunk&&); // Resets the indices (e.g. next_item_to_match_) of // current_paint_artifact_.GetDisplayItemList() to their initial values. This @@ -316,9 +310,7 @@ class PLATFORM_EXPORT PaintController { const DisplayItem* old_item) const; void ShowSequenceUnderInvalidationError(const char* reason, - const DisplayItemClient&, - int start, - int end); + const DisplayItemClient&); void CheckUnderInvalidation(); bool IsCheckingUnderInvalidation() const { @@ -327,19 +319,17 @@ class PLATFORM_EXPORT PaintController { } struct SubsequenceMarkers { - SubsequenceMarkers() : start(0), end(0) {} - SubsequenceMarkers(size_t start_arg, size_t end_arg) - : start(start_arg), end(end_arg) {} - // The start and end (not included) index within current_paint_artifact_ - // of this subsequence. - size_t start; - size_t end; + // The start and end (not included) index of paint chunks in this + // subsequence. + wtf_size_t start_chunk_index = 0; + wtf_size_t end_chunk_index = 0; }; SubsequenceMarkers* GetSubsequenceMarkers(const DisplayItemClient&); -#if DCHECK_IS_ON() void CheckDuplicatePaintChunkId(const PaintChunk::Id&); + +#if DCHECK_IS_ON() void ShowDebugDataInternal(DisplayItemList::JsonFlags) const; #endif @@ -355,9 +345,6 @@ class PLATFORM_EXPORT PaintController { DisplayItemList new_display_item_list_; PaintChunker new_paint_chunks_; - bool construction_disabled_ = false; - bool subsequence_caching_disabled_ = false; - bool cache_is_all_invalid_ = true; bool committed_ = false; @@ -366,8 +353,8 @@ class PLATFORM_EXPORT PaintController { unsigned skipping_cache_count_ = 0; - size_t num_cached_new_items_ = 0; - size_t num_cached_new_subsequences_ = 0; + wtf_size_t num_cached_new_items_ = 0; + wtf_size_t num_cached_new_subsequences_ = 0; // Stores indices to valid cacheable display items in // current_paint_artifact_.GetDisplayItemList() that have not been matched by @@ -381,16 +368,16 @@ class PLATFORM_EXPORT PaintController { IndicesByClientMap out_of_order_item_indices_; // The next item in the current list for sequential match. - size_t next_item_to_match_ = 0; + wtf_size_t next_item_to_match_ = 0; // The next item in the current list to be indexed for out-of-order cache // requests. - size_t next_item_to_index_ = 0; + wtf_size_t next_item_to_index_ = 0; #if DCHECK_IS_ON() - size_t num_indexed_items_ = 0; - size_t num_sequential_matches_ = 0; - size_t num_out_of_order_matches_ = 0; + wtf_size_t num_indexed_items_ = 0; + wtf_size_t num_sequential_matches_ = 0; + wtf_size_t num_out_of_order_matches_ = 0; // This is used to check duplicated ids during CreateAndAppend(). IndicesByClientMap new_display_item_indices_by_client_; @@ -404,8 +391,8 @@ class PLATFORM_EXPORT PaintController { // end of the cached drawing or subsequence in the current list. The functions // return false to let the client do actual painting, and PaintController will // check if the actual painting results are the same as the cached. - size_t under_invalidation_checking_begin_ = 0; - size_t under_invalidation_checking_end_ = 0; + wtf_size_t under_invalidation_checking_begin_ = 0; + wtf_size_t under_invalidation_checking_end_ = 0; String under_invalidation_message_prefix_; @@ -413,7 +400,7 @@ class PLATFORM_EXPORT PaintController { HashMap<const DisplayItemClient*, SubsequenceMarkers>; CachedSubsequenceMap current_cached_subsequences_; CachedSubsequenceMap new_cached_subsequences_; - size_t last_cached_subsequence_end_ = 0; + wtf_size_t last_cached_subsequence_end_ = 0; unsigned current_fragment_ = 0; diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_debug_data.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_debug_data.cc index 938214c8573..a0fe789b49b 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_debug_data.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_debug_data.cc @@ -20,29 +20,25 @@ class PaintController::DisplayItemListAsJSON { DisplayItemList::JsonFlags); String ToString() { - return SubsequenceAsJSONArrayRecursive(0, list_.size()) - ->ToPrettyJSONString(); + return ChunksAsJSONArrayRecursive(0, chunks_.size())->ToPrettyJSONString(); } private: std::unique_ptr<JSONObject> SubsequenceAsJSONObjectRecursive(); - std::unique_ptr<JSONArray> SubsequenceAsJSONArrayRecursive(size_t, size_t); - void AppendSubsequenceAsJSON(size_t, size_t, JSONArray&); + std::unique_ptr<JSONArray> ChunksAsJSONArrayRecursive(wtf_size_t, wtf_size_t); + void AppendChunksAsJSON(wtf_size_t, wtf_size_t, JSONArray&); String ClientName(const DisplayItemClient&) const; struct SubsequenceInfo { - SubsequenceInfo(const DisplayItemClient* client, size_t start, size_t end) - : client(client), start(start), end(end) {} const DisplayItemClient* client; - size_t start; - size_t end; + wtf_size_t start_chunk_index; + wtf_size_t end_chunk_index; }; const DisplayItemList& list_; Vector<SubsequenceInfo> subsequences_; Vector<SubsequenceInfo>::const_iterator current_subsequence_; const Vector<PaintChunk>& chunks_; - Vector<PaintChunk>::const_iterator current_chunk_; DisplayItemList::JsonFlags flags_; }; @@ -53,15 +49,16 @@ PaintController::DisplayItemListAsJSON::DisplayItemListAsJSON( DisplayItemList::JsonFlags flags) : list_(list), chunks_(chunks), - current_chunk_(chunks.begin()), flags_(flags) { for (const auto& item : subsequence_map) { - subsequences_.push_back( - SubsequenceInfo(item.key, item.value.start, item.value.end)); + subsequences_.push_back(SubsequenceInfo{ + item.key, item.value.start_chunk_index, item.value.end_chunk_index}); } std::sort(subsequences_.begin(), subsequences_.end(), [](const SubsequenceInfo& a, const SubsequenceInfo& b) { - return a.start == b.start ? a.end > b.end : a.start < b.start; + return a.start_chunk_index == b.start_chunk_index + ? a.end_chunk_index > b.end_chunk_index + : a.start_chunk_index < b.start_chunk_index; }); current_subsequence_ = subsequences_.begin(); @@ -77,70 +74,59 @@ PaintController::DisplayItemListAsJSON::SubsequenceAsJSONObjectRecursive() { json_object->SetString("subsequence", String::Format("client: %p ", subsequence.client) + ClientName(*subsequence.client)); - json_object->SetArray("chunks", SubsequenceAsJSONArrayRecursive( - subsequence.start, subsequence.end)); + json_object->SetArray( + "chunks", ChunksAsJSONArrayRecursive(subsequence.start_chunk_index, + subsequence.end_chunk_index)); return json_object; } std::unique_ptr<JSONArray> -PaintController::DisplayItemListAsJSON::SubsequenceAsJSONArrayRecursive( - size_t start_item, - size_t end_item) { +PaintController::DisplayItemListAsJSON::ChunksAsJSONArrayRecursive( + wtf_size_t start_chunk_index, + wtf_size_t end_chunk_index) { auto array = std::make_unique<JSONArray>(); - size_t item_index = start_item; + auto chunk_index = start_chunk_index; while (current_subsequence_ != subsequences_.end() && - current_subsequence_->start < end_item) { + current_subsequence_->start_chunk_index < end_chunk_index) { const auto& subsequence = *current_subsequence_; - DCHECK(subsequence.start >= item_index); - DCHECK(subsequence.end <= end_item); + DCHECK_GE(subsequence.start_chunk_index, chunk_index); + DCHECK_LE(subsequence.end_chunk_index, end_chunk_index); - if (item_index < subsequence.start) - AppendSubsequenceAsJSON(item_index, subsequence.start, *array); + if (chunk_index < subsequence.start_chunk_index) + AppendChunksAsJSON(chunk_index, subsequence.start_chunk_index, *array); array->PushObject(SubsequenceAsJSONObjectRecursive()); - item_index = subsequence.end; + chunk_index = subsequence.end_chunk_index; } - if (item_index < end_item) - AppendSubsequenceAsJSON(item_index, end_item, *array); + if (chunk_index < end_chunk_index) + AppendChunksAsJSON(chunk_index, end_chunk_index, *array); return array; } -void PaintController::DisplayItemListAsJSON::AppendSubsequenceAsJSON( - size_t start_item, - size_t end_item, +void PaintController::DisplayItemListAsJSON::AppendChunksAsJSON( + wtf_size_t start_chunk_index, + wtf_size_t end_chunk_index, JSONArray& json_array) { - DCHECK(end_item > start_item); - if (current_chunk_ == chunks_.end()) { - // We are in the middle of painting with incomplete chunks. - auto json_object = std::make_unique<JSONObject>(); - json_object->SetString("chunk", "incomplete"); - json_object->SetArray( - "displayItems", list_.SubsequenceAsJSON(start_item, end_item, flags_)); - json_array.PushObject(std::move(json_object)); - return; - } - - DCHECK(current_chunk_->begin_index == start_item); - while (current_chunk_ != chunks_.end() && - current_chunk_->end_index <= end_item) { - const auto& chunk = *current_chunk_; + DCHECK_GT(end_chunk_index, start_chunk_index); + for (auto i = start_chunk_index; i < end_chunk_index; ++i) { + const auto& chunk = chunks_[i]; auto json_object = std::make_unique<JSONObject>(); json_object->SetString( "chunk", ClientName(chunk.id.client) + " " + chunk.id.ToString()); json_object->SetString("state", chunk.properties.ToString()); + json_object->SetString("bounds", chunk.bounds.ToString()); if (flags_ & DisplayItemList::kShowPaintRecords) json_object->SetString("chunkData", chunk.ToString()); json_object->SetArray( "displayItems", - list_.SubsequenceAsJSON(chunk.begin_index, chunk.end_index, flags_)); + list_.DisplayItemsAsJSON(chunk.begin_index, chunk.end_index, flags_)); json_array.PushObject(std::move(json_object)); - ++current_chunk_; } } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc index 098e3f3182e..cacaa2c7a95 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc @@ -565,7 +565,7 @@ TEST_P(PaintControllerTest, CachedSubsequenceForcePaintChunk) { FakeDisplayItemClient root("root"); auto root_properties = DefaultPaintChunkProperties(); PaintChunk::Id root_id(root, DisplayItem::kCaret); - GetPaintController().UpdateCurrentPaintChunkProperties(root_id, + GetPaintController().UpdateCurrentPaintChunkProperties(&root_id, root_properties); DrawRect(context, root, kBackgroundType, FloatRect(100, 100, 100, 100)); @@ -575,7 +575,7 @@ TEST_P(PaintControllerTest, CachedSubsequenceForcePaintChunk) { { SubsequenceRecorder r(context, container); GetPaintController().UpdateCurrentPaintChunkProperties( - container_id, container_properties); + &container_id, container_properties); DrawRect(context, container, kBackgroundType, FloatRect(100, 100, 100, 100)); DrawRect(context, container, kForegroundType, @@ -595,7 +595,7 @@ TEST_P(PaintControllerTest, CachedSubsequenceForcePaintChunk) { IsPaintChunk(3, 4, PaintChunk::Id(root, kForegroundType), root_properties))); - GetPaintController().UpdateCurrentPaintChunkProperties(root_id, + GetPaintController().UpdateCurrentPaintChunkProperties(&root_id, root_properties); DrawRect(context, root, kBackgroundType, FloatRect(100, 100, 100, 100)); EXPECT_TRUE(GetPaintController().UseCachedSubsequenceIfPossible(container)); @@ -631,7 +631,7 @@ TEST_P(PaintControllerTest, CachedSubsequenceSwapOrder) { { GetPaintController().UpdateCurrentPaintChunkProperties( - container1_id, container1_properties); + &container1_id, container1_properties); SubsequenceRecorder r(context, container1); DrawRect(context, container1, kBackgroundType, @@ -643,7 +643,7 @@ TEST_P(PaintControllerTest, CachedSubsequenceSwapOrder) { } { GetPaintController().UpdateCurrentPaintChunkProperties( - container2_id, container2_properties); + &container2_id, container2_properties); SubsequenceRecorder r(context, container2); DrawRect(context, container2, kBackgroundType, @@ -666,15 +666,10 @@ TEST_P(PaintControllerTest, CachedSubsequenceSwapOrder) { IsSameId(&content2, kForegroundType), IsSameId(&container2, kForegroundType))); - auto* markers = GetSubsequenceMarkers(container1); - CHECK(markers); - EXPECT_EQ(0u, markers->start); - EXPECT_EQ(4u, markers->end); - - markers = GetSubsequenceMarkers(container2); - CHECK(markers); - EXPECT_EQ(4u, markers->start); - EXPECT_EQ(8u, markers->end); + EXPECT_SUBSEQUENCE(container1, 0, 1); + EXPECT_NO_SUBSEQUENCE(content1); + EXPECT_SUBSEQUENCE(container2, 1, 2); + EXPECT_NO_SUBSEQUENCE(content2); EXPECT_THAT( GetPaintController().PaintChunks(), @@ -685,13 +680,13 @@ TEST_P(PaintControllerTest, CachedSubsequenceSwapOrder) { // than that of |container2|. if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) { // When under-invalidation-checking is enabled, - // useCachedSubsequenceIfPossible is forced off, and the client is expected + // UseCachedSubsequenceIfPossible is forced off, and the client is expected // to create the same painting as in the previous paint. EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible( context, container2)); { GetPaintController().UpdateCurrentPaintChunkProperties( - container2_id, container2_properties); + &container2_id, container2_properties); SubsequenceRecorder r(context, container2); DrawRect(context, container2, kBackgroundType, @@ -707,7 +702,7 @@ TEST_P(PaintControllerTest, CachedSubsequenceSwapOrder) { context, container1)); { GetPaintController().UpdateCurrentPaintChunkProperties( - container1_id, container1_properties); + &container1_id, container1_properties); SubsequenceRecorder r(context, container1); DrawRect(context, container1, kBackgroundType, @@ -746,15 +741,10 @@ TEST_P(PaintControllerTest, CachedSubsequenceSwapOrder) { IsSameId(&content1, kForegroundType), IsSameId(&container1, kForegroundType))); - markers = GetSubsequenceMarkers(container2); - CHECK(markers); - EXPECT_EQ(0u, markers->start); - EXPECT_EQ(4u, markers->end); - - markers = GetSubsequenceMarkers(container1); - CHECK(markers); - EXPECT_EQ(4u, markers->start); - EXPECT_EQ(8u, markers->end); + EXPECT_SUBSEQUENCE(container1, 1, 2); + EXPECT_NO_SUBSEQUENCE(content1); + EXPECT_SUBSEQUENCE(container2, 0, 1); + EXPECT_NO_SUBSEQUENCE(content2); EXPECT_THAT( GetPaintController().PaintChunks(), @@ -763,12 +753,15 @@ TEST_P(PaintControllerTest, CachedSubsequenceSwapOrder) { } TEST_P(PaintControllerTest, CachedSubsequenceAndDisplayItemsSwapOrder) { - FakeDisplayItemClient root("root", IntRect(0, 0, 300, 300)); FakeDisplayItemClient content1("content1", IntRect(100, 100, 50, 200)); FakeDisplayItemClient container2("container2", IntRect(100, 200, 100, 100)); FakeDisplayItemClient content2("content2", IntRect(100, 200, 50, 200)); GraphicsContext context(GetPaintController()); + PaintChunk::Id content1_id(content1, kBackgroundType); + PaintChunk::Id container2_id(container2, kBackgroundType); + PaintChunk::Id content2_id(content2, kBackgroundType); + InitRootChunk(); DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200)); @@ -792,17 +785,26 @@ TEST_P(PaintControllerTest, CachedSubsequenceAndDisplayItemsSwapOrder) { IsSameId(&container2, kForegroundType), IsSameId(&content1, kForegroundType))); - auto* markers = GetSubsequenceMarkers(container2); - CHECK(markers); - EXPECT_EQ(1u, markers->start); - EXPECT_EQ(5u, markers->end); + EXPECT_NO_SUBSEQUENCE(content1); + EXPECT_SUBSEQUENCE(container2, 1, 2); + EXPECT_NO_SUBSEQUENCE(content2); + + EXPECT_THAT( + GetPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 1, DefaultRootChunkId(), + DefaultPaintChunkProperties()), + IsPaintChunk(1, 5, PaintChunk::Id(container2, kBackgroundType), + DefaultPaintChunkProperties()), + IsPaintChunk(5, 6, PaintChunk::Id(content1, kForegroundType), + DefaultPaintChunkProperties()))); // Simulate the situation when |container2| gets a z-index that is smaller // than that of |content1|. InitRootChunk(); if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) { // When under-invalidation-checking is enabled, - // useCachedSubsequenceIfPossible is forced off, and the client is expected + // UseCachedSubsequenceIfPossible is forced off, and the client is expected // to create the same painting as in the previous paint. EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible( context, container2)); @@ -846,22 +848,29 @@ TEST_P(PaintControllerTest, CachedSubsequenceAndDisplayItemsSwapOrder) { IsSameId(&content1, kBackgroundType), IsSameId(&content1, kForegroundType))); - markers = GetSubsequenceMarkers(container2); - CHECK(markers); - EXPECT_EQ(0u, markers->start); - EXPECT_EQ(4u, markers->end); + EXPECT_NO_SUBSEQUENCE(content1); + EXPECT_SUBSEQUENCE(container2, 0, 1); + EXPECT_NO_SUBSEQUENCE(content2); + + EXPECT_THAT( + GetPaintController().PaintChunks(), + ElementsAre( + IsPaintChunk(0, 4, PaintChunk::Id(container2, kBackgroundType), + DefaultPaintChunkProperties()), + IsPaintChunk(4, 6, PaintChunk::Id(content1, kBackgroundType), + DefaultPaintChunkProperties()))); } TEST_P(PaintControllerTest, CachedSubsequenceContainingFragments) { GraphicsContext context(GetPaintController()); FakeDisplayItemClient root("root"); - constexpr size_t kFragmentCount = 3; + constexpr wtf_size_t kFragmentCount = 3; FakeDisplayItemClient container("container"); // The first paint. auto paint_container = [this, &context, &container]() { SubsequenceRecorder r(context, container); - for (size_t i = 0; i < kFragmentCount; ++i) { + for (wtf_size_t i = 0; i < kFragmentCount; ++i) { ScopedDisplayItemFragment scoped_fragment(context, i); ScopedPaintChunkProperties content_chunk_properties( GetPaintController(), DefaultPaintChunkProperties(), container, @@ -936,11 +945,11 @@ TEST_P(PaintControllerTest, UpdateSwapOrderCrossingChunks) { auto container2_properties = DefaultPaintChunkProperties(); container2_properties.SetEffect(*container2_effect); - GetPaintController().UpdateCurrentPaintChunkProperties(container1_id, + GetPaintController().UpdateCurrentPaintChunkProperties(&container1_id, container1_properties); DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100)); DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200)); - GetPaintController().UpdateCurrentPaintChunkProperties(container2_id, + GetPaintController().UpdateCurrentPaintChunkProperties(&container2_id, container2_properties); DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100)); DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200)); @@ -958,12 +967,12 @@ TEST_P(PaintControllerTest, UpdateSwapOrderCrossingChunks) { IsPaintChunk(2, 4, container2_id, container2_properties))); // Move content2 into container1, without invalidation. - GetPaintController().UpdateCurrentPaintChunkProperties(container1_id, + GetPaintController().UpdateCurrentPaintChunkProperties(&container1_id, container1_properties); DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100)); DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200)); DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200)); - GetPaintController().UpdateCurrentPaintChunkProperties(container2_id, + GetPaintController().UpdateCurrentPaintChunkProperties(&container2_id, container2_properties); DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100)); @@ -1051,34 +1060,34 @@ TEST_P(PaintControllerTest, CachedNestedSubsequenceUpdate) { { SubsequenceRecorder r(context, container1); GetPaintController().UpdateCurrentPaintChunkProperties( - container1_background_id, container1_background_properties); + &container1_background_id, container1_background_properties); DrawRect(context, container1, kBackgroundType, FloatRect(100, 100, 100, 100)); { SubsequenceRecorder r(context, content1); GetPaintController().UpdateCurrentPaintChunkProperties( - content1_id, content1_properties); + &content1_id, content1_properties); DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200)); DrawRect(context, content1, kForegroundType, FloatRect(100, 100, 50, 200)); } GetPaintController().UpdateCurrentPaintChunkProperties( - container1_foreground_id, container1_foreground_properties); + &container1_foreground_id, container1_foreground_properties); DrawRect(context, container1, kForegroundType, FloatRect(100, 100, 100, 100)); } { SubsequenceRecorder r(context, container2); GetPaintController().UpdateCurrentPaintChunkProperties( - container2_background_id, container2_background_properties); + &container2_background_id, container2_background_properties); DrawRect(context, container2, kBackgroundType, FloatRect(100, 200, 100, 100)); { SubsequenceRecorder r(context, content2); GetPaintController().UpdateCurrentPaintChunkProperties( - content2_id, content2_properties); + &content2_id, content2_properties); DrawRect(context, content2, kBackgroundType, FloatRect(100, 200, 50, 200)); } @@ -1093,25 +1102,10 @@ TEST_P(PaintControllerTest, CachedNestedSubsequenceUpdate) { IsSameId(&container2, kBackgroundType), IsSameId(&content2, kBackgroundType))); - auto* markers = GetSubsequenceMarkers(container1); - CHECK(markers); - EXPECT_EQ(0u, markers->start); - EXPECT_EQ(4u, markers->end); - - markers = GetSubsequenceMarkers(content1); - CHECK(markers); - EXPECT_EQ(1u, markers->start); - EXPECT_EQ(3u, markers->end); - - markers = GetSubsequenceMarkers(container2); - CHECK(markers); - EXPECT_EQ(4u, markers->start); - EXPECT_EQ(6u, markers->end); - - markers = GetSubsequenceMarkers(content2); - CHECK(markers); - EXPECT_EQ(5u, markers->start); - EXPECT_EQ(6u, markers->end); + EXPECT_SUBSEQUENCE(container1, 0, 3); + EXPECT_SUBSEQUENCE(content1, 1, 2); + EXPECT_SUBSEQUENCE(container2, 3, 5); + EXPECT_SUBSEQUENCE(content2, 4, 5); EXPECT_THAT( GetPaintController().PaintChunks(), @@ -1139,7 +1133,7 @@ TEST_P(PaintControllerTest, CachedNestedSubsequenceUpdate) { // Content2 now outputs foreground only. { SubsequenceRecorder r(context, content2); - GetPaintController().UpdateCurrentPaintChunkProperties(content2_id, + GetPaintController().UpdateCurrentPaintChunkProperties(&content2_id, content2_properties); DrawRect(context, content2, kForegroundType, FloatRect(100, 200, 50, 200)); } @@ -1151,13 +1145,13 @@ TEST_P(PaintControllerTest, CachedNestedSubsequenceUpdate) { // Use cached subsequence of content1. if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) { // When under-invalidation-checking is enabled, - // useCachedSubsequenceIfPossible is forced off, and the client is + // UseCachedSubsequenceIfPossible is forced off, and the client is // expected to create the same painting as in the previous paint. EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible( context, content1)); SubsequenceRecorder r(context, content1); GetPaintController().UpdateCurrentPaintChunkProperties( - content1_id, content1_properties); + &content1_id, content1_properties); DrawRect(context, content1, kBackgroundType, FloatRect(100, 100, 50, 200)); DrawRect(context, content1, kForegroundType, @@ -1167,7 +1161,7 @@ TEST_P(PaintControllerTest, CachedNestedSubsequenceUpdate) { context, content1)); } GetPaintController().UpdateCurrentPaintChunkProperties( - container1_foreground_id, container1_foreground_properties); + &container1_foreground_id, container1_foreground_properties); DrawRect(context, container1, kForegroundType, FloatRect(100, 100, 100, 100)); } @@ -1188,20 +1182,10 @@ TEST_P(PaintControllerTest, CachedNestedSubsequenceUpdate) { IsSameId(&content1, kForegroundType), IsSameId(&container1, kForegroundType))); - markers = GetSubsequenceMarkers(content2); - CHECK(markers); - EXPECT_EQ(0u, markers->start); - EXPECT_EQ(1u, markers->end); - - markers = GetSubsequenceMarkers(container1); - CHECK(markers); - EXPECT_EQ(1u, markers->start); - EXPECT_EQ(4u, markers->end); - - markers = GetSubsequenceMarkers(content1); - CHECK(markers); - EXPECT_EQ(1u, markers->start); - EXPECT_EQ(3u, markers->end); + EXPECT_NO_SUBSEQUENCE(container2); + EXPECT_SUBSEQUENCE(content2, 0, 1); + EXPECT_SUBSEQUENCE(container1, 1, 3); + EXPECT_SUBSEQUENCE(content1, 1, 2); EXPECT_THAT(GetPaintController().PaintChunks(), ElementsAre(IsPaintChunk(0, 1, content2_id, content2_properties), @@ -1637,9 +1621,7 @@ TEST_P(PaintControllerTest, DuplicatedSubsequences) { #if DCHECK_IS_ON() EXPECT_DEATH(paint_duplicated_subsequences(), "Multiple subsequences for client: \"test\""); - return; -#endif - +#else // The following is for non-DCHECK path. No security CHECK should trigger. paint_duplicated_subsequences(); // Paint again. @@ -1658,6 +1640,37 @@ TEST_P(PaintControllerTest, DuplicatedSubsequences) { DrawRect(context, client, kForegroundType, FloatRect(100, 100, 100, 100)); } CommitAndFinishCycle(); +#endif +} + +TEST_P(PaintControllerTest, DeletedClientInUnderInvaldiatedSubsequence) { + if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) + return; + + FakeDisplayItemClient container("container"); + auto content = std::make_unique<FakeDisplayItemClient>("content"); + GraphicsContext context(GetPaintController()); + + InitRootChunk(); + { + SubsequenceRecorder r(context, container); + DrawRect(context, *content, kBackgroundType, FloatRect(100, 100, 300, 300)); + } + CommitAndFinishCycle(); + + content = nullptr; + InitRootChunk(); + // Leave container not invalidated. +#if DCHECK_IS_ON() + ASSERT_DEATH( + SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container), + ""); +#else + // This should not crash. + EXPECT_TRUE( + SubsequenceRecorder::UseCachedSubsequenceIfPossible(context, container)); + CommitAndFinishCycle(); +#endif } class PaintControllerUnderInvalidationTest @@ -1863,7 +1876,7 @@ TEST_F(PaintControllerUnderInvalidationTest, MoreDrawingInSubsequence) { TEST_F(PaintControllerUnderInvalidationTest, LessDrawingInSubsequence) { EXPECT_DEATH(TestLessDrawingInSubsequence(), "In cached subsequence for first.*" - "under-invalidation: new subsequence wrong length"); + "under-invalidation: chunk changed"); } TEST_F(PaintControllerUnderInvalidationTest, InvalidationInSubsequence) { diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h index 11f903de4e0..3f1b1142cce 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h @@ -8,6 +8,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h" +#include "third_party/blink/renderer/platform/graphics/paint/hit_test_data.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" #include "third_party/blink/renderer/platform/testing/fake_display_item_client.h" #include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h" @@ -47,7 +48,7 @@ class PaintControllerTestBase : public testing::Test { void InitRootChunk() { InitRootChunk(GetPaintController()); } void InitRootChunk(PaintController& paint_controller) { paint_controller.UpdateCurrentPaintChunkProperties( - root_paint_chunk_id_, DefaultPaintChunkProperties()); + &root_paint_chunk_id_, DefaultPaintChunkProperties()); } const PaintChunk::Id DefaultRootChunkId() const { return root_paint_chunk_id_; @@ -55,20 +56,20 @@ class PaintControllerTestBase : public testing::Test { PaintController& GetPaintController() { return *paint_controller_; } - size_t NumCachedNewItems() const { + wtf_size_t NumCachedNewItems() const { return paint_controller_->num_cached_new_items_; } - size_t NumCachedNewSubsequences() const { + wtf_size_t NumCachedNewSubsequences() const { return paint_controller_->num_cached_new_subsequences_; } #if DCHECK_IS_ON() - size_t NumIndexedItems() const { + wtf_size_t NumIndexedItems() const { return paint_controller_->num_indexed_items_; } - size_t NumSequentialMatches() const { + wtf_size_t NumSequentialMatches() const { return paint_controller_->num_sequential_matches_; } - size_t NumOutOfOrderMatches() const { + wtf_size_t NumOutOfOrderMatches() const { return paint_controller_->num_out_of_order_matches_; } #endif @@ -116,22 +117,42 @@ MATCHER_P2(IsSameId, client, type, "") { // ELementsAre(IsPaintChunk(0, 1, id1, properties1), // IsPaintChunk(1, 3, id2, properties2))); inline bool CheckChunk(const PaintChunk& chunk, - size_t begin, - size_t end, + wtf_size_t begin, + wtf_size_t end) { + return chunk.begin_index == begin && chunk.end_index == end; +} +inline bool CheckChunk(const PaintChunk& chunk, + wtf_size_t begin, + wtf_size_t end, const PaintChunk::Id& id, const PropertyTreeState& properties, - const HitTestData* hit_test_data = nullptr) { + const HitTestData* hit_test_data = nullptr, + const IntRect* bounds = nullptr) { return chunk.begin_index == begin && chunk.end_index == end && chunk.id == id && chunk.properties == properties && ((!chunk.hit_test_data && !hit_test_data) || (chunk.hit_test_data && hit_test_data && - *chunk.hit_test_data == *hit_test_data)); + *chunk.hit_test_data == *hit_test_data)) && + (!bounds || chunk.bounds == *bounds); +} +MATCHER_P2(IsPaintChunk, begin, end, "") { + return CheckChunk(arg, begin, end); } MATCHER_P4(IsPaintChunk, begin, end, id, properties, "") { return CheckChunk(arg, begin, end, id, properties); } MATCHER_P5(IsPaintChunk, begin, end, id, properties, hit_test_data, "") { - return CheckChunk(arg, begin, end, id, properties, &hit_test_data); + return CheckChunk(arg, begin, end, id, properties, hit_test_data); +} +MATCHER_P6(IsPaintChunk, + begin, + end, + id, + properties, + hit_test_data, + bounds, + "") { + return CheckChunk(arg, begin, end, id, properties, hit_test_data, &bounds); } // Shorter names for frequently used display item types in tests. @@ -140,9 +161,22 @@ const DisplayItem::Type kForegroundType = static_cast<DisplayItem::Type>(DisplayItem::kDrawingPaintPhaseFirst + 5); const DisplayItem::Type kDocumentBackgroundType = DisplayItem::kDocumentBackground; -const DisplayItem::Type kScrollHitTestType = DisplayItem::kScrollHitTest; const DisplayItem::Type kClipType = DisplayItem::kClipPaintPhaseFirst; +#define EXPECT_SUBSEQUENCE(client, expected_start_chunk_index, \ + expected_end_chunk_index) \ + do { \ + auto* subsequence = GetSubsequenceMarkers(client); \ + ASSERT_NE(nullptr, subsequence); \ + EXPECT_EQ(static_cast<wtf_size_t>(expected_start_chunk_index), \ + subsequence->start_chunk_index); \ + EXPECT_EQ(static_cast<wtf_size_t>(expected_end_chunk_index), \ + subsequence->end_chunk_index); \ + } while (false) + +#define EXPECT_NO_SUBSEQUENCE(client) \ + EXPECT_EQ(nullptr, GetSubsequenceMarkers(client)) + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_CONTROLLER_TEST_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc index 85133e1a387..3228623d6bd 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_property_node_test.cc @@ -32,16 +32,11 @@ class PaintPropertyNodeTest : public testing::Test { // grandchild1 grandchild2 transform.root = &TransformPaintPropertyNode::Root(); - transform.ancestor = - CreateTransform(*transform.root, TransformationMatrix()); - transform.child1 = - CreateTransform(*transform.ancestor, TransformationMatrix()); - transform.child2 = - CreateTransform(*transform.ancestor, TransformationMatrix()); - transform.grandchild1 = - CreateTransform(*transform.child1, TransformationMatrix()); - transform.grandchild2 = - CreateTransform(*transform.child2, TransformationMatrix()); + transform.ancestor = Create2DTranslation(*transform.root, 0, 0); + transform.child1 = Create2DTranslation(*transform.ancestor, 0, 0); + transform.child2 = Create2DTranslation(*transform.ancestor, 0, 0); + transform.grandchild1 = Create2DTranslation(*transform.child1, 0, 0); + transform.grandchild2 = Create2DTranslation(*transform.child2, 0, 0); clip.root = &ClipPaintPropertyNode::Root(); clip.ancestor = @@ -321,8 +316,7 @@ TEST_F(PaintPropertyNodeTest, ChangeDirectCompositingReason) { ResetAllChanged(); ExpectUnchangedState(); TransformPaintPropertyNode::State state; - state.direct_compositing_reasons = - CompositingReason::kActiveTransformAnimation; + state.direct_compositing_reasons = CompositingReason::kWillChangeTransform; transform.child1->Update(*transform.ancestor, std::move(state)); EXPECT_CHANGE_EQ(PaintPropertyChangeType::kChangedOnlyNonRerasterValues, diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc index b7b3241f107..58b6468fa91 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc @@ -16,11 +16,6 @@ PaintRecordBuilder::PaintRecordBuilder( PaintController* paint_controller, paint_preview::PaintPreviewTracker* tracker) : paint_controller_(nullptr) { - GraphicsContext::DisabledMode disabled_mode = - GraphicsContext::kNothingDisabled; - if (containing_context && containing_context->ContextDisabled()) - disabled_mode = GraphicsContext::kFullyDisabled; - if (paint_controller) { paint_controller_ = paint_controller; } else { @@ -30,10 +25,10 @@ PaintRecordBuilder::PaintRecordBuilder( } paint_controller_->UpdateCurrentPaintChunkProperties( - base::nullopt, PropertyTreeState::Root()); + nullptr, PropertyTreeState::Root()); - context_ = std::make_unique<GraphicsContext>( - *paint_controller_, disabled_mode, metafile, tracker); + context_ = + std::make_unique<GraphicsContext>(*paint_controller_, metafile, tracker); if (containing_context) { context_->SetDarkMode(containing_context->dark_mode_settings()); context_->SetDeviceScaleFactor(containing_context->DeviceScaleFactor()); diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc index 690447f4d70..94372310848 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc @@ -12,9 +12,11 @@ const PropertyTreeState& PropertyTreeState::Uninitialized() { DEFINE_STATIC_REF(const TransformPaintPropertyNode, transform, TransformPaintPropertyNode::Create( TransformPaintPropertyNode::Root(), {})); - DEFINE_STATIC_REF(const ClipPaintPropertyNode, clip, - ClipPaintPropertyNode::Create(ClipPaintPropertyNode::Root(), - {transform})); + DEFINE_STATIC_REF( + const ClipPaintPropertyNode, clip, + ClipPaintPropertyNode::Create( + ClipPaintPropertyNode::Root(), + ClipPaintPropertyNode::State(transform, FloatRoundedRect()))); DEFINE_STATIC_REF(const EffectPaintPropertyNode, effect, EffectPaintPropertyNode::Create( EffectPaintPropertyNode::Root(), {transform})); @@ -49,6 +51,14 @@ String PropertyTreeState::ToTreeString() const { #endif +std::unique_ptr<JSONObject> PropertyTreeState::ToJSON() const { + std::unique_ptr<JSONObject> result = std::make_unique<JSONObject>(); + result->SetObject("transform", transform_->ToJSON()); + result->SetObject("clip", clip_->ToJSON()); + result->SetObject("effect", effect_->ToJSON()); + return result; +} + size_t PropertyTreeState::CacheMemoryUsageInBytes() const { return Clip().CacheMemoryUsageInBytes() + Transform().CacheMemoryUsageInBytes(); diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h b/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h index 5fa7473122c..d8ac00926b3 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h @@ -79,6 +79,7 @@ class PLATFORM_EXPORT PropertyTreeState { // Dumps the tree from this state up to the root as a string. String ToTreeString() const; #endif + std::unique_ptr<JSONObject> ToJSON() const; // Returns memory usage of the transform & clip caches of this state plus // ancestors. diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc index 1a65460c14d..b987497e0fd 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.cc @@ -78,47 +78,48 @@ static bool CompareRasterInvalidationInfo(const RasterInvalidationInfo& a, return a.reason < b.reason; } -void RasterInvalidationTracking::AsJSON(JSONObject* json) const { +void RasterInvalidationTracking::AsJSON(JSONObject* json, bool detailed) const { if (!invalidations_.IsEmpty()) { // Sort to make the output more readable and easier to see the differences // by a human. - auto sorted_invalidations = invalidations_; - std::sort(sorted_invalidations.begin(), sorted_invalidations.end(), - &CompareRasterInvalidationInfo); - auto paint_invalidations_json = std::make_unique<JSONArray>(); - for (auto& info : sorted_invalidations) { - auto info_json = std::make_unique<JSONObject>(); - info_json->SetString("object", info.client_debug_name); - if (!info.rect.IsEmpty()) { - if (info.rect == LayoutRect::InfiniteIntRect()) - info_json->SetString("rect", "infinite"); - else - info_json->SetArray("rect", RectAsJSONArray(info.rect)); + auto sorted = invalidations_; + std::sort(sorted.begin(), sorted.end(), &CompareRasterInvalidationInfo); + auto invalidations_json = std::make_unique<JSONArray>(); + IntRect last_rect; + for (auto* it = sorted.begin(); it != sorted.end(); it++) { + const auto& info = *it; + if (detailed) { + auto info_json = std::make_unique<JSONObject>(); + info_json->SetArray("rect", RectAsJSONArray(info.rect)); + info_json->SetString("object", info.client_debug_name); + info_json->SetString("reason", + PaintInvalidationReasonToString(info.reason)); + invalidations_json->PushObject(std::move(info_json)); + } else if (std::none_of(sorted.begin(), it, [&info](auto& previous) { + return previous.rect.Contains(info.rect); + })) { + invalidations_json->PushArray(RectAsJSONArray(info.rect)); + last_rect = info.rect; } - info_json->SetString("reason", - PaintInvalidationReasonToString(info.reason)); - paint_invalidations_json->PushObject(std::move(info_json)); } - json->SetArray("paintInvalidations", std::move(paint_invalidations_json)); + json->SetArray("invalidations", std::move(invalidations_json)); } if (!under_invalidations_.IsEmpty()) { - auto under_paint_invalidations_json = std::make_unique<JSONArray>(); - for (auto& under_paint_invalidation : under_invalidations_) { - auto under_paint_invalidation_json = std::make_unique<JSONObject>(); - under_paint_invalidation_json->SetDouble("x", under_paint_invalidation.x); - under_paint_invalidation_json->SetDouble("y", under_paint_invalidation.y); - under_paint_invalidation_json->SetString( + auto under_invalidations_json = std::make_unique<JSONArray>(); + for (auto& under_invalidation : under_invalidations_) { + auto under_invalidation_json = std::make_unique<JSONObject>(); + under_invalidation_json->SetDouble("x", under_invalidation.x); + under_invalidation_json->SetDouble("y", under_invalidation.y); + under_invalidation_json->SetString( "oldPixel", - Color(under_paint_invalidation.old_pixel).NameForLayoutTreeAsText()); - under_paint_invalidation_json->SetString( + Color(under_invalidation.old_pixel).NameForLayoutTreeAsText()); + under_invalidation_json->SetString( "newPixel", - Color(under_paint_invalidation.new_pixel).NameForLayoutTreeAsText()); - under_paint_invalidations_json->PushObject( - std::move(under_paint_invalidation_json)); + Color(under_invalidation.new_pixel).NameForLayoutTreeAsText()); + under_invalidations_json->PushObject(std::move(under_invalidation_json)); } - json->SetArray("underPaintInvalidations", - std::move(under_paint_invalidations_json)); + json->SetArray("underInvalidations", std::move(under_invalidations_json)); } } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h index 94bf0a526ce..29047835625 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h @@ -95,7 +95,7 @@ class PLATFORM_EXPORT RasterInvalidationTracking { sk_sp<PaintRecord> new_record, const IntRect& new_interest_rect); - void AsJSON(JSONObject*) const; + void AsJSON(JSONObject*, bool detailed) const; void AddToLayerDebugInfo(cc::LayerDebugInfo&) const; diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc index a7033cf5adb..54f8b52e774 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.cc @@ -48,7 +48,7 @@ void RasterInvalidator::SetTracksRasterInvalidations(bool should_track) { } } -const PaintChunk& RasterInvalidator::GetOldChunk(size_t index) const { +const PaintChunk& RasterInvalidator::GetOldChunk(wtf_size_t index) const { DCHECK(old_paint_artifact_); const auto& old_chunk_info = old_paint_chunks_info_[index]; const auto& old_chunk = @@ -60,13 +60,14 @@ const PaintChunk& RasterInvalidator::GetOldChunk(size_t index) const { return old_chunk; } -size_t RasterInvalidator::MatchNewChunkToOldChunk(const PaintChunk& new_chunk, - size_t old_index) const { - for (size_t i = old_index; i < old_paint_chunks_info_.size(); i++) { +wtf_size_t RasterInvalidator::MatchNewChunkToOldChunk( + const PaintChunk& new_chunk, + wtf_size_t old_index) const { + for (wtf_size_t i = old_index; i < old_paint_chunks_info_.size(); i++) { if (new_chunk.Matches(GetOldChunk(i))) return i; } - for (size_t i = 0; i < old_index; i++) { + for (wtf_size_t i = 0; i < old_index; i++) { if (new_chunk.Matches(GetOldChunk(i))) return i; } @@ -170,6 +171,7 @@ bool ShouldSkipForRasterInvalidation(const PaintArtifact& paint_artifact, // common cases that most of the chunks can be matched in-order, the complexity // is slightly larger than O(n). void RasterInvalidator::GenerateRasterInvalidations( + RasterInvalidationFunction function, const PaintArtifact& new_paint_artifact, const PaintChunkSubset& new_chunks, const PropertyTreeState& layer_state, @@ -179,8 +181,8 @@ void RasterInvalidator::GenerateRasterInvalidations( visual_rect_subpixel_offset); Vector<bool> old_chunks_matched; old_chunks_matched.resize(old_paint_chunks_info_.size()); - size_t old_index = 0; - size_t max_matched_old_index = 0; + wtf_size_t old_index = 0; + wtf_size_t max_matched_old_index = 0; for (auto it = new_chunks.begin(); it != new_chunks.end(); ++it) { const auto& new_chunk = *it; if (ShouldSkipForRasterInvalidation(new_paint_artifact, new_chunk)) @@ -189,23 +191,26 @@ void RasterInvalidator::GenerateRasterInvalidations( mapper.SwitchToChunk(new_chunk); auto& new_chunk_info = new_chunks_info.emplace_back(*this, mapper, it); - // Foreign layers take care of raster invalidation by themselves. - if (DisplayItem::IsForeignLayerType(new_chunk.id.type)) + // Foreign layers and GraphicsLayers take care of raster invalidation by + // themselves. + if (DisplayItem::IsGraphicsLayerWrapperType(new_chunk.id.type) || + DisplayItem::IsForeignLayerType(new_chunk.id.type)) { continue; + } if (!new_chunk.is_cacheable) { - AddRasterInvalidation(new_chunk_info.bounds_in_layer, new_chunk.id.client, - PaintInvalidationReason::kChunkUncacheable, - kClientIsNew); + AddRasterInvalidation( + function, new_chunk_info.bounds_in_layer, new_chunk.id.client, + PaintInvalidationReason::kChunkUncacheable, kClientIsNew); continue; } - size_t matched_old_index = MatchNewChunkToOldChunk(new_chunk, old_index); + auto matched_old_index = MatchNewChunkToOldChunk(new_chunk, old_index); if (matched_old_index == kNotFound) { // The new chunk doesn't match any old chunk. - AddRasterInvalidation(new_chunk_info.bounds_in_layer, new_chunk.id.client, - PaintInvalidationReason::kChunkAppeared, - kClientIsNew); + AddRasterInvalidation( + function, new_chunk_info.bounds_in_layer, new_chunk.id.client, + PaintInvalidationReason::kChunkAppeared, kClientIsNew); continue; } @@ -229,10 +234,10 @@ void RasterInvalidator::GenerateRasterInvalidations( // Invalidate both old and new bounds of the chunk if the chunk's paint // properties changed, or is moved backward and may expose area that was // previously covered by it. - AddRasterInvalidation(old_chunk_info.bounds_in_layer, new_chunk.id.client, - reason, kClientIsNew); + AddRasterInvalidation(function, old_chunk_info.bounds_in_layer, + new_chunk.id.client, reason, kClientIsNew); if (old_chunk_info.bounds_in_layer != new_chunk_info.bounds_in_layer) { - AddRasterInvalidation(new_chunk_info.bounds_in_layer, + AddRasterInvalidation(function, new_chunk_info.bounds_in_layer, new_chunk.id.client, reason, kClientIsNew); } // Ignore the display item raster invalidations because we have fully @@ -246,12 +251,12 @@ void RasterInvalidator::GenerateRasterInvalidations( old_chunk_info.chunk_to_layer_transform; if (reason == PaintInvalidationReason::kIncremental) { - IncrementallyInvalidateChunk(old_chunk_info, new_chunk_info, + IncrementallyInvalidateChunk(function, old_chunk_info, new_chunk_info, new_chunk.id.client); } if (&new_paint_artifact != old_paint_artifact_) { - DisplayItemRasterInvalidator(*this, *old_paint_artifact_, + DisplayItemRasterInvalidator(*this, function, *old_paint_artifact_, new_paint_artifact, old_chunk, new_chunk, mapper) .Generate(); @@ -265,7 +270,7 @@ void RasterInvalidator::GenerateRasterInvalidations( } // Invalidate remaining unmatched (disappeared or uncacheable) old chunks. - for (size_t i = 0; i < old_paint_chunks_info_.size(); ++i) { + for (wtf_size_t i = 0; i < old_paint_chunks_info_.size(); ++i) { if (old_chunks_matched[i]) continue; @@ -273,19 +278,20 @@ void RasterInvalidator::GenerateRasterInvalidations( auto reason = old_chunk.is_cacheable ? PaintInvalidationReason::kChunkDisappeared : PaintInvalidationReason::kChunkUncacheable; - AddRasterInvalidation(old_paint_chunks_info_[i].bounds_in_layer, + AddRasterInvalidation(function, old_paint_chunks_info_[i].bounds_in_layer, old_chunk.id.client, reason, kClientIsOld); } } void RasterInvalidator::IncrementallyInvalidateChunk( + RasterInvalidationFunction function, const PaintChunkInfo& old_chunk_info, const PaintChunkInfo& new_chunk_info, const DisplayItemClient& client) { SkRegion diff(old_chunk_info.bounds_in_layer); diff.op(new_chunk_info.bounds_in_layer, SkRegion::kXOR_Op); for (SkRegion::Iterator it(diff); !it.done(); it.next()) { - AddRasterInvalidation(IntRect(it.rect()), client, + AddRasterInvalidation(function, IntRect(it.rect()), client, PaintInvalidationReason::kIncremental, kClientIsNew); } } @@ -308,16 +314,19 @@ RasterInvalidationTracking& RasterInvalidator::EnsureTracking() { } void RasterInvalidator::Generate( + RasterInvalidationFunction raster_invalidation_function, scoped_refptr<const PaintArtifact> new_paint_artifact, const gfx::Rect& layer_bounds, const PropertyTreeState& layer_state, const FloatSize& visual_rect_subpixel_offset, const DisplayItemClient* layer_client) { - Generate(new_paint_artifact, new_paint_artifact->PaintChunks(), layer_bounds, - layer_state, visual_rect_subpixel_offset, layer_client); + Generate(raster_invalidation_function, new_paint_artifact, + new_paint_artifact->PaintChunks(), layer_bounds, layer_state, + visual_rect_subpixel_offset, layer_client); } void RasterInvalidator::Generate( + RasterInvalidationFunction raster_invalidation_function, scoped_refptr<const PaintArtifact> new_paint_artifact, const PaintChunkSubset& paint_chunks, const gfx::Rect& layer_bounds, @@ -352,7 +361,8 @@ void RasterInvalidator::Generate( layer_client ? *layer_client : paint_chunks[0].id.client); } } else { - GenerateRasterInvalidations(*new_paint_artifact, paint_chunks, layer_state, + GenerateRasterInvalidations(raster_invalidation_function, + *new_paint_artifact, paint_chunks, layer_state, visual_rect_subpixel_offset, new_chunks_info); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h index 55229e99ca1..35b7fabef0c 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h @@ -27,10 +27,7 @@ class PLATFORM_EXPORT RasterInvalidator { using RasterInvalidationFunction = base::RepeatingCallback<void(const IntRect&)>; - RasterInvalidator(RasterInvalidationFunction raster_invalidation_function) - : raster_invalidation_function_(std::move(raster_invalidation_function)) { - DCHECK(!raster_invalidation_function_.is_null()); - } + RasterInvalidator() = default; void SetTracksRasterInvalidations(bool); RasterInvalidationTracking* GetTracking() const { @@ -41,7 +38,8 @@ class PLATFORM_EXPORT RasterInvalidator { // Generate raster invalidations for all of the changed paint chunks and // display items in the paint artifact. - void Generate(scoped_refptr<const PaintArtifact>, + void Generate(RasterInvalidationFunction, + scoped_refptr<const PaintArtifact>, const gfx::Rect& layer_bounds, const PropertyTreeState& layer_state, const FloatSize& visual_rect_subpixel_offset = FloatSize(), @@ -49,7 +47,8 @@ class PLATFORM_EXPORT RasterInvalidator { // Generate raster invalidations for a subset of the paint chunks in the // paint artifact. - void Generate(scoped_refptr<const PaintArtifact>, + void Generate(RasterInvalidationFunction, + scoped_refptr<const PaintArtifact>, const PaintChunkSubset&, const gfx::Rect& layer_bounds, const PropertyTreeState& layer_state, @@ -77,7 +76,7 @@ class PLATFORM_EXPORT RasterInvalidator { id(chunk_it->id), #endif bounds_in_layer(invalidator.ClipByLayerBounds( - mapper.MapVisualRect(chunk_it->bounds))), + mapper.MapVisualRect(chunk_it->drawable_bounds))), chunk_to_layer_clip(mapper.ClipRect()), chunk_to_layer_transform(mapper.Transform()) { } @@ -85,7 +84,7 @@ class PLATFORM_EXPORT RasterInvalidator { // The index of the chunk in the PaintArtifact. It may be different from // the index of this PaintChunkInfo in paint_chunks_info_ when a subset of // the paint chunks is handled by the RasterInvalidator. - size_t index_in_paint_artifact; + wtf_size_t index_in_paint_artifact; #if DCHECK_IS_ON() PaintChunk::Id id; @@ -96,17 +95,19 @@ class PLATFORM_EXPORT RasterInvalidator { SkMatrix chunk_to_layer_transform; }; - void GenerateRasterInvalidations(const PaintArtifact&, + void GenerateRasterInvalidations(RasterInvalidationFunction, + const PaintArtifact&, const PaintChunkSubset&, const PropertyTreeState& layer_state, const FloatSize& visual_rect_subpixel_offset, Vector<PaintChunkInfo>& new_chunks_info); - ALWAYS_INLINE const PaintChunk& GetOldChunk(size_t index) const; - ALWAYS_INLINE size_t MatchNewChunkToOldChunk(const PaintChunk& new_chunk, - size_t old_index) const; + ALWAYS_INLINE const PaintChunk& GetOldChunk(wtf_size_t index) const; + ALWAYS_INLINE wtf_size_t MatchNewChunkToOldChunk(const PaintChunk& new_chunk, + wtf_size_t old_index) const; ALWAYS_INLINE void IncrementallyInvalidateChunk( + RasterInvalidationFunction, const PaintChunkInfo& old_chunk_info, const PaintChunkInfo& new_chunk_info, const DisplayItemClient&); @@ -115,13 +116,14 @@ class PLATFORM_EXPORT RasterInvalidator { // get DebugName() directly or should get from |tracking_info_ // ->old_client_debug_names|. enum ClientIsOldOrNew { kClientIsOld, kClientIsNew }; - void AddRasterInvalidation(const IntRect& rect, + void AddRasterInvalidation(RasterInvalidationFunction function, + const IntRect& rect, const DisplayItemClient& client, PaintInvalidationReason reason, ClientIsOldOrNew old_or_new) { if (rect.IsEmpty()) return; - raster_invalidation_function_.Run(rect); + function.Run(rect); if (tracking_info_) TrackRasterInvalidation(rect, client, reason, old_or_new); } @@ -146,7 +148,6 @@ class PLATFORM_EXPORT RasterInvalidator { void TrackImplicitFullLayerInvalidation(const DisplayItemClient&); - RasterInvalidationFunction raster_invalidation_function_; gfx::Rect layer_bounds_; Vector<PaintChunkInfo> old_paint_chunks_info_; scoped_refptr<const PaintArtifact> old_paint_artifact_; diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc index f4ad986d71a..d4598f77c81 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc @@ -6,6 +6,7 @@ #include <utility> #include "base/bind_helpers.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h" @@ -13,6 +14,8 @@ #include "third_party/blink/renderer/platform/testing/paint_test_configurations.h" #include "third_party/blink/renderer/platform/testing/test_paint_artifact.h" +using testing::ElementsAre; + namespace blink { static const IntRect kDefaultLayerBounds(-9999, -7777, 18888, 16666); @@ -20,7 +23,7 @@ static const IntRect kDefaultLayerBounds(-9999, -7777, 18888, 16666); class RasterInvalidatorTest : public testing::Test, public PaintTestConfigurations { public: - RasterInvalidatorTest() : invalidator_(base::DoNothing()) {} + RasterInvalidatorTest() = default; static PropertyTreeState DefaultPropertyTreeState() { return PropertyTreeState::Root(); @@ -47,58 +50,60 @@ class RasterInvalidatorTest : public testing::Test, return invalidator_.GetTracking()->Invalidations(); } - using MapFunction = base::RepeatingCallback<void(IntRect&)>; - static IntRect ChunkRectToLayer( - const IntRect& rect, - const IntPoint& layer_offset, - const MapFunction& mapper = base::DoNothing()) { - auto r = rect; - mapper.Run(r); - r.MoveBy(layer_offset); - return r; - } - RasterInvalidator invalidator_; }; INSTANTIATE_PAINT_TEST_SUITE_P(RasterInvalidatorTest); -#define EXPECT_CHUNK_INVALIDATION_CUSTOM( \ - invalidations, index, chunk, expected_reason, layer_offset, mapper) \ - do { \ - const auto& info = (invalidations)[index]; \ - EXPECT_EQ(ChunkRectToLayer((chunk).bounds, layer_offset, mapper), \ - info.rect); \ - EXPECT_EQ(&(chunk).id.client, info.client); \ - EXPECT_EQ(expected_reason, info.reason); \ - } while (false) - -#define EXPECT_CHUNK_INVALIDATION(invalidations, index, chunk, reason) \ - EXPECT_CHUNK_INVALIDATION_CUSTOM(invalidations, index, chunk, reason, \ - -kDefaultLayerBounds.Location(), \ - base::DoNothing()) - -#define EXPECT_INCREMENTAL_INVALIDATION(invalidations, index, chunk, \ - chunk_rect) \ - do { \ - const auto& info = (invalidations)[index]; \ - EXPECT_EQ(ChunkRectToLayer(chunk_rect, -kDefaultLayerBounds.Location()), \ - info.rect); \ - EXPECT_EQ(&(chunk).id.client, info.client); \ - EXPECT_EQ(PaintInvalidationReason::kIncremental, info.reason); \ - } while (false) +using MapFunction = base::RepeatingCallback<void(IntRect&)>; +static IntRect ChunkRectToLayer(const IntRect& rect, + const IntPoint& layer_offset, + const MapFunction& mapper = base::DoNothing()) { + auto r = rect; + mapper.Run(r); + r.MoveBy(layer_offset); + return r; +} + +static bool CheckChunkInvalidation( + const RasterInvalidationInfo& info, + const PaintChunk& chunk, + const IntRect& chunk_rect, + PaintInvalidationReason reason, + const IntPoint& layer_offset, + const MapFunction& mapper = base::DoNothing()) { + return ChunkRectToLayer(chunk_rect, layer_offset, mapper) == info.rect && + &chunk.id.client == info.client && reason == info.reason; +} + +MATCHER_P4(ChunkInvalidation, chunk, reason, layer_offset, mapper, "") { + return CheckChunkInvalidation(arg, *chunk, chunk->drawable_bounds, reason, + layer_offset, mapper); +} + +MATCHER_P2(ChunkInvalidation, chunk, reason, "") { + return CheckChunkInvalidation(arg, *chunk, chunk->drawable_bounds, reason, + -kDefaultLayerBounds.Location()); +} + +MATCHER_P2(IncrementalInvalidation, chunk, chunk_rect, "") { + return CheckChunkInvalidation(arg, *chunk, chunk_rect, + PaintInvalidationReason::kIncremental, + -kDefaultLayerBounds.Location()); +} TEST_P(RasterInvalidatorTest, ImplicitFullLayerInvalidation) { auto artifact = TestPaintArtifact().Chunk(0).Build(); invalidator_.SetTracksRasterInvalidations(true); - invalidator_.Generate(artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(1u, invalidations.size()); - EXPECT_EQ(IntRect(IntPoint(), kDefaultLayerBounds.Size()), - invalidations[0].rect); - EXPECT_EQ(PaintInvalidationReason::kFullLayer, invalidations[0].reason); + const auto& client = artifact->PaintChunks()[0].id.client; + EXPECT_THAT(TrackedRasterInvalidations(), + ElementsAre(RasterInvalidationInfo{ + &client, client.DebugName(), + IntRect(IntPoint(), kDefaultLayerBounds.Size()), + PaintInvalidationReason::kFullLayer})); FinishCycle(*artifact); invalidator_.SetTracksRasterInvalidations(false); } @@ -106,34 +111,35 @@ TEST_P(RasterInvalidatorTest, ImplicitFullLayerInvalidation) { TEST_P(RasterInvalidatorTest, LayerBounds) { auto artifact = TestPaintArtifact().Chunk(0).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); FinishCycle(*artifact); invalidator_.SetTracksRasterInvalidations(true); - invalidator_.Generate(artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); // No raster invalidations needed if layer origin doesn't change. EXPECT_TRUE(TrackedRasterInvalidations().IsEmpty()); auto new_layer_bounds = kDefaultLayerBounds; new_layer_bounds.Move(66, 77); - invalidator_.Generate(artifact, new_layer_bounds, DefaultPropertyTreeState()); + invalidator_.Generate(base::DoNothing(), artifact, new_layer_bounds, + DefaultPropertyTreeState()); // Change of layer origin causes change of chunk0's transform to layer. - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(2u, invalidations.size()); - EXPECT_CHUNK_INVALIDATION(invalidations, 0, artifact->PaintChunks()[0], - PaintInvalidationReason::kPaintProperty); - EXPECT_CHUNK_INVALIDATION_CUSTOM(invalidations, 1, artifact->PaintChunks()[0], - PaintInvalidationReason::kPaintProperty, - -new_layer_bounds.Location(), - base::DoNothing()); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre( + ChunkInvalidation(&artifact->PaintChunks()[0], + PaintInvalidationReason::kPaintProperty), + ChunkInvalidation(&artifact->PaintChunks()[0], + PaintInvalidationReason::kPaintProperty, + -new_layer_bounds.Location(), base::DoNothing()))); FinishCycle(*artifact); } TEST_P(RasterInvalidatorTest, ReorderChunks) { auto artifact = TestPaintArtifact().Chunk(0).Chunk(1).Chunk(2).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); FinishCycle(*artifact); @@ -145,23 +151,23 @@ TEST_P(RasterInvalidatorTest, ReorderChunks) { .Chunk(1) .Bounds(IntRect(11, 22, 33, 44)) .Build(); - invalidator_.Generate(new_artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), new_artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(2u, invalidations.size()); // Invalidated new chunk 2's old (as artifact->PaintChunks()[1]) and new // (as new_artifact->PaintChunks()[2]) bounds. - EXPECT_CHUNK_INVALIDATION(invalidations, 0, artifact->PaintChunks()[1], - PaintInvalidationReason::kChunkReordered); - EXPECT_CHUNK_INVALIDATION(invalidations, 1, new_artifact->PaintChunks()[2], - PaintInvalidationReason::kChunkReordered); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation(&artifact->PaintChunks()[1], + PaintInvalidationReason::kChunkReordered), + ChunkInvalidation(&new_artifact->PaintChunks()[2], + PaintInvalidationReason::kChunkReordered))); FinishCycle(*new_artifact); } TEST_P(RasterInvalidatorTest, ReorderChunkSubsequences) { auto artifact = TestPaintArtifact().Chunk(0).Chunk(1).Chunk(2).Chunk(3).Chunk(4).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); FinishCycle(*artifact); @@ -175,63 +181,98 @@ TEST_P(RasterInvalidatorTest, ReorderChunkSubsequences) { .Bounds(IntRect(11, 22, 33, 44)) .Chunk(2) .Build(); - invalidator_.Generate(new_artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), new_artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(3u, invalidations.size()); // Invalidated new chunk 3's old (as artifact->PaintChunks()[1] and new // (as new_artifact->PaintChunks()[3]) bounds. - EXPECT_CHUNK_INVALIDATION(invalidations, 0, artifact->PaintChunks()[1], - PaintInvalidationReason::kChunkReordered); - EXPECT_CHUNK_INVALIDATION(invalidations, 1, new_artifact->PaintChunks()[3], - PaintInvalidationReason::kChunkReordered); // Invalidated new chunk 4's new bounds. Didn't invalidate old bounds because // it's the same as the new bounds. - EXPECT_CHUNK_INVALIDATION(invalidations, 2, new_artifact->PaintChunks()[4], - PaintInvalidationReason::kChunkReordered); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation(&artifact->PaintChunks()[1], + PaintInvalidationReason::kChunkReordered), + ChunkInvalidation(&new_artifact->PaintChunks()[3], + PaintInvalidationReason::kChunkReordered), + ChunkInvalidation(&new_artifact->PaintChunks()[4], + PaintInvalidationReason::kChunkReordered))); FinishCycle(*new_artifact); } TEST_P(RasterInvalidatorTest, ChunkAppearAndDisappear) { auto artifact = TestPaintArtifact().Chunk(0).Chunk(1).Chunk(2).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); FinishCycle(*artifact); // Chunk 1 and 2 disappeared, 3 and 4 appeared. invalidator_.SetTracksRasterInvalidations(true); auto new_artifact = TestPaintArtifact().Chunk(0).Chunk(3).Chunk(4).Build(); - invalidator_.Generate(new_artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), new_artifact, kDefaultLayerBounds, + DefaultPropertyTreeState()); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre( + ChunkInvalidation(&new_artifact->PaintChunks()[1], + PaintInvalidationReason::kChunkAppeared), + ChunkInvalidation(&new_artifact->PaintChunks()[2], + PaintInvalidationReason::kChunkAppeared), + ChunkInvalidation(&artifact->PaintChunks()[1], + PaintInvalidationReason::kChunkDisappeared), + ChunkInvalidation(&artifact->PaintChunks()[2], + PaintInvalidationReason::kChunkDisappeared))); + FinishCycle(*new_artifact); +} + +TEST_P(RasterInvalidatorTest, InvalidateDrawableBounds) { + IntRect drawable_bounds(11, 22, 33, 44); + IntRect bounds(0, 0, 100, 100); + auto artifact = TestPaintArtifact() + .Chunk(0) + .Chunk(1) + .Bounds(bounds) + .DrawableBounds(drawable_bounds) + .Build(); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + DefaultPropertyTreeState()); + FinishCycle(*artifact); + + invalidator_.SetTracksRasterInvalidations(true); + auto new_artifact = TestPaintArtifact() + .Chunk(0) + .Chunk(2) + .Bounds(bounds) + .DrawableBounds(drawable_bounds) + .Build(); + invalidator_.Generate(base::DoNothing(), new_artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(4u, invalidations.size()); - EXPECT_CHUNK_INVALIDATION(invalidations, 0, new_artifact->PaintChunks()[1], - PaintInvalidationReason::kChunkAppeared); - EXPECT_CHUNK_INVALIDATION(invalidations, 1, new_artifact->PaintChunks()[2], - PaintInvalidationReason::kChunkAppeared); - EXPECT_CHUNK_INVALIDATION(invalidations, 2, artifact->PaintChunks()[1], - PaintInvalidationReason::kChunkDisappeared); - EXPECT_CHUNK_INVALIDATION(invalidations, 3, artifact->PaintChunks()[2], - PaintInvalidationReason::kChunkDisappeared); + // ChunkInvalidation uses the drawable_bounds. We expect raster invalidations + // based on drawable_bounds instead of bounds. + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre( + ChunkInvalidation(&new_artifact->PaintChunks()[1], + PaintInvalidationReason::kChunkAppeared), + ChunkInvalidation(&artifact->PaintChunks()[1], + PaintInvalidationReason::kChunkDisappeared))); FinishCycle(*new_artifact); } TEST_P(RasterInvalidatorTest, ChunkAppearAtEnd) { auto artifact = TestPaintArtifact().Chunk(0).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); FinishCycle(*artifact); invalidator_.SetTracksRasterInvalidations(true); auto new_artifact = TestPaintArtifact().Chunk(0).Chunk(1).Chunk(2).Build(); - invalidator_.Generate(new_artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), new_artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(2u, invalidations.size()); - EXPECT_CHUNK_INVALIDATION(invalidations, 0, new_artifact->PaintChunks()[1], - PaintInvalidationReason::kChunkAppeared); - EXPECT_CHUNK_INVALIDATION(invalidations, 1, new_artifact->PaintChunks()[2], - PaintInvalidationReason::kChunkAppeared); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation(&new_artifact->PaintChunks()[1], + PaintInvalidationReason::kChunkAppeared), + ChunkInvalidation(&new_artifact->PaintChunks()[2], + PaintInvalidationReason::kChunkAppeared))); FinishCycle(*new_artifact); } @@ -239,21 +280,22 @@ TEST_P(RasterInvalidatorTest, UncacheableChunks) { auto artifact = TestPaintArtifact().Chunk(0).Chunk(1).Uncacheable().Chunk(2).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); FinishCycle(*artifact); invalidator_.SetTracksRasterInvalidations(true); auto new_artifact = TestPaintArtifact().Chunk(0).Chunk(2).Chunk(1).Uncacheable().Build(); - invalidator_.Generate(new_artifact, kDefaultLayerBounds, + invalidator_.Generate(base::DoNothing(), new_artifact, kDefaultLayerBounds, DefaultPropertyTreeState()); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(2u, invalidations.size()); - EXPECT_CHUNK_INVALIDATION(invalidations, 0, new_artifact->PaintChunks()[2], - PaintInvalidationReason::kChunkUncacheable); - EXPECT_CHUNK_INVALIDATION(invalidations, 1, artifact->PaintChunks()[1], - PaintInvalidationReason::kChunkUncacheable); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre( + ChunkInvalidation(&new_artifact->PaintChunks()[2], + PaintInvalidationReason::kChunkUncacheable), + ChunkInvalidation(&artifact->PaintChunks()[1], + PaintInvalidationReason::kChunkUncacheable))); FinishCycle(*new_artifact); } @@ -275,7 +317,8 @@ TEST_P(RasterInvalidatorTest, ClipPropertyChangeRounded) { .Properties(t0(), *clip2, e0()) .Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); // Change both clip0 and clip2. @@ -288,13 +331,14 @@ TEST_P(RasterInvalidatorTest, ClipPropertyChangeRounded) { ClipPaintPropertyNode::State{&clip2->LocalTransformSpace(), new_clip_rect}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(1u, invalidations.size()); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); // Property change in the layer state should not trigger raster invalidation. // |clip2| change should trigger raster invalidation. - EXPECT_CHUNK_INVALIDATION(invalidations, 0, artifact->PaintChunks()[2], - PaintInvalidationReason::kPaintProperty); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation(&artifact->PaintChunks()[2], + PaintInvalidationReason::kPaintProperty))); invalidator_.SetTracksRasterInvalidations(false); FinishCycle(*artifact); @@ -309,11 +353,12 @@ TEST_P(RasterInvalidatorTest, ClipPropertyChangeRounded) { .Build(); invalidator_.SetTracksRasterInvalidations(true); - invalidator_.Generate(new_artifact1, kDefaultLayerBounds, layer_state); - const auto& invalidations1 = TrackedRasterInvalidations(); - ASSERT_EQ(1u, invalidations1.size()); - EXPECT_CHUNK_INVALIDATION(invalidations1, 0, new_artifact1->PaintChunks()[1], - PaintInvalidationReason::kPaintProperty); + invalidator_.Generate(base::DoNothing(), new_artifact1, kDefaultLayerBounds, + layer_state); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation(&new_artifact1->PaintChunks()[1], + PaintInvalidationReason::kPaintProperty))); invalidator_.SetTracksRasterInvalidations(false); FinishCycle(*new_artifact1); } @@ -334,7 +379,8 @@ TEST_P(RasterInvalidatorTest, ClipPropertyChangeSimple) { .Bounds(EnclosingIntRect(clip_rect.Rect())) .Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); // Change clip1 to bigger, which is still bound by clip0, resulting no actual @@ -345,7 +391,8 @@ TEST_P(RasterInvalidatorTest, ClipPropertyChangeSimple) { ClipPaintPropertyNode::State{&clip1->LocalTransformSpace(), new_clip_rect1}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); EXPECT_TRUE(TrackedRasterInvalidations().IsEmpty()); FinishCycle(*artifact); @@ -355,18 +402,19 @@ TEST_P(RasterInvalidatorTest, ClipPropertyChangeSimple) { ClipPaintPropertyNode::State{&clip1->LocalTransformSpace(), new_clip_rect2}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(4u, invalidations.size()); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); // |clip1| change should trigger incremental raster invalidation. - EXPECT_INCREMENTAL_INVALIDATION(invalidations, 0, artifact->PaintChunks()[1], - IntRect(-1000, -1000, 2000, 500)); - EXPECT_INCREMENTAL_INVALIDATION(invalidations, 1, artifact->PaintChunks()[1], - IntRect(-1000, -500, 500, 1000)); - EXPECT_INCREMENTAL_INVALIDATION(invalidations, 2, artifact->PaintChunks()[1], - IntRect(500, -500, 500, 1000)); - EXPECT_INCREMENTAL_INVALIDATION(invalidations, 3, artifact->PaintChunks()[1], - IntRect(-1000, 500, 2000, 500)); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(IncrementalInvalidation(&artifact->PaintChunks()[1], + IntRect(-1000, -1000, 2000, 500)), + IncrementalInvalidation(&artifact->PaintChunks()[1], + IntRect(-1000, -500, 500, 1000)), + IncrementalInvalidation(&artifact->PaintChunks()[1], + IntRect(500, -500, 500, 1000)), + IncrementalInvalidation(&artifact->PaintChunks()[1], + IntRect(-1000, 500, 2000, 500)))); invalidator_.SetTracksRasterInvalidations(false); FinishCycle(*artifact); @@ -377,12 +425,12 @@ TEST_P(RasterInvalidatorTest, ClipPropertyChangeSimple) { new_clip_rect3}); invalidator_.SetTracksRasterInvalidations(true); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); - const auto& invalidations1 = TrackedRasterInvalidations(); - ASSERT_EQ(1u, invalidations1.size()); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); // |clip1| change should trigger incremental raster invalidation. - EXPECT_INCREMENTAL_INVALIDATION(invalidations1, 0, artifact->PaintChunks()[1], - IntRect(500, -500, 500, 1000)); + EXPECT_THAT(TrackedRasterInvalidations(), + ElementsAre(IncrementalInvalidation( + &artifact->PaintChunks()[1], IntRect(500, -500, 500, 1000)))); invalidator_.SetTracksRasterInvalidations(false); FinishCycle(*artifact); } @@ -402,21 +450,22 @@ TEST_P(RasterInvalidatorTest, ClipPropertyChangeWithOutsetForRasterEffects) { .OutsetForRasterEffects(2) .Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); invalidator_.SetTracksRasterInvalidations(true); FloatRoundedRect new_clip_rect(-2000, -2000, 4000, 4000); clip->Update(c0(), ClipPaintPropertyNode::State{&t0(), new_clip_rect}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(1u, invalidations.size()); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); auto mapper = [](IntRect& r) { r.Inflate(2); }; - EXPECT_CHUNK_INVALIDATION_CUSTOM(invalidations, 0, artifact->PaintChunks()[0], - PaintInvalidationReason::kPaintProperty, - -kDefaultLayerBounds.Location(), - base::BindRepeating(mapper)); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation( + &artifact->PaintChunks()[0], PaintInvalidationReason::kPaintProperty, + -kDefaultLayerBounds.Location(), base::BindRepeating(mapper)))); invalidator_.SetTracksRasterInvalidations(false); FinishCycle(*artifact); } @@ -434,7 +483,8 @@ TEST_P(RasterInvalidatorTest, ClipLocalTransformSpaceChange) { auto artifact = TestPaintArtifact().Chunk(0).Properties(*t2, *c1, e0()).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); // Change both t1 and t2 but keep t1*t2 unchanged, to test change of @@ -443,11 +493,12 @@ TEST_P(RasterInvalidatorTest, ClipLocalTransformSpaceChange) { t1->Update(t0(), TransformPaintPropertyNode::State{FloatSize(-10, -20)}); t2->Update(*t1, TransformPaintPropertyNode::State{FloatSize(10, 20)}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(1u, invalidations.size()); - EXPECT_CHUNK_INVALIDATION(invalidations, 0, artifact->PaintChunks()[0], - PaintInvalidationReason::kPaintProperty); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation(&artifact->PaintChunks()[0], + PaintInvalidationReason::kPaintProperty))); invalidator_.SetTracksRasterInvalidations(false); } @@ -468,7 +519,8 @@ TEST_P(RasterInvalidatorTest, ClipLocalTransformSpaceChangeNoInvalidation) { auto artifact = TestPaintArtifact().Chunk(0).Properties(*t2, *c1, e0()).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); // Change both t1 and t2 but keep t1*t2 unchanged. @@ -476,7 +528,8 @@ TEST_P(RasterInvalidatorTest, ClipLocalTransformSpaceChangeNoInvalidation) { t1->Update(t0(), TransformPaintPropertyNode::State{FloatSize(-10, -20)}); t2->Update(*t1, TransformPaintPropertyNode::State{FloatSize(10, 20)}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); EXPECT_TRUE(TrackedRasterInvalidations().IsEmpty()); FinishCycle(*artifact); } @@ -494,7 +547,8 @@ TEST_P(RasterInvalidatorTest, TransformPropertyChange) { .Properties(*transform1, c0(), e0()) .Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); // Change layer_transform should not cause raster invalidation in the layer. @@ -503,7 +557,8 @@ TEST_P(RasterInvalidatorTest, TransformPropertyChange) { *layer_transform->Parent(), TransformPaintPropertyNode::State{TransformationMatrix().Scale(10)}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); EXPECT_TRUE(TrackedRasterInvalidations().IsEmpty()); FinishCycle(*artifact); @@ -516,7 +571,8 @@ TEST_P(RasterInvalidatorTest, TransformPropertyChange) { transform0->Update(*new_layer_transform, TransformPaintPropertyNode::State{ transform0->Translation2D()}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); EXPECT_TRUE(TrackedRasterInvalidations().IsEmpty()); FinishCycle(*artifact); @@ -526,7 +582,8 @@ TEST_P(RasterInvalidatorTest, TransformPropertyChange) { transform0->Update(layer_state.Transform(), TransformPaintPropertyNode::State{ transform0->Translation2D()}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); EXPECT_TRUE(TrackedRasterInvalidations().IsEmpty()); FinishCycle(*artifact); @@ -540,19 +597,20 @@ TEST_P(RasterInvalidatorTest, TransformPropertyChange) { TransformPaintPropertyNode::State{ transform1->Translation2D() + FloatSize(-20, -30)}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(2u, invalidations.size()); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); auto mapper0 = [](IntRect& r) { r.Move(10, 20); }; - EXPECT_CHUNK_INVALIDATION_CUSTOM(invalidations, 0, artifact->PaintChunks()[0], - PaintInvalidationReason::kPaintProperty, - -kDefaultLayerBounds.Location(), - base::BindRepeating(mapper0)); auto mapper1 = [](IntRect& r) { r.Move(30, 50); }; - EXPECT_CHUNK_INVALIDATION_CUSTOM(invalidations, 1, artifact->PaintChunks()[0], - PaintInvalidationReason::kPaintProperty, - -kDefaultLayerBounds.Location(), - base::BindRepeating(mapper1)); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation(&artifact->PaintChunks()[0], + PaintInvalidationReason::kPaintProperty, + -kDefaultLayerBounds.Location(), + base::BindRepeating(mapper0)), + ChunkInvalidation(&artifact->PaintChunks()[0], + PaintInvalidationReason::kPaintProperty, + -kDefaultLayerBounds.Location(), + base::BindRepeating(mapper1)))); invalidator_.SetTracksRasterInvalidations(false); FinishCycle(*artifact); } @@ -567,7 +625,8 @@ TEST_P(RasterInvalidatorTest, TransformPropertyTinyChange) { .Properties(*chunk_transform, c0(), e0()) .Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); // Change chunk_transform by tiny difference, which should be ignored. @@ -579,7 +638,8 @@ TEST_P(RasterInvalidatorTest, TransformPropertyTinyChange) { .Scale(1.0000001) .Rotate(0.0000001)}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); EXPECT_TRUE(TrackedRasterInvalidations().IsEmpty()); FinishCycle(*artifact); @@ -593,7 +653,8 @@ TEST_P(RasterInvalidatorTest, TransformPropertyTinyChange) { .Translate(0.0000001, -0.0000001) .Scale(1.0000001) .Rotate(0.0000001)}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); invalidated = !TrackedRasterInvalidations().IsEmpty(); FinishCycle(*artifact); } @@ -613,7 +674,8 @@ TEST_P(RasterInvalidatorTest, TransformPropertyTinyChangeScale) { .Bounds(chunk_bounds) .Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); // Scale change from 1e-6 to 2e-6 should be treated as significant. @@ -622,7 +684,8 @@ TEST_P(RasterInvalidatorTest, TransformPropertyTinyChangeScale) { layer_state.Transform(), TransformPaintPropertyNode::State{TransformationMatrix().Scale(2e-6)}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); EXPECT_FALSE(TrackedRasterInvalidations().IsEmpty()); invalidator_.SetTracksRasterInvalidations(false); FinishCycle(*artifact); @@ -633,7 +696,8 @@ TEST_P(RasterInvalidatorTest, TransformPropertyTinyChangeScale) { TransformPaintPropertyNode::State{ TransformationMatrix().Scale(2e-6 + 1e-15)}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); EXPECT_TRUE(TrackedRasterInvalidations().IsEmpty()); invalidator_.SetTracksRasterInvalidations(false); FinishCycle(*artifact); @@ -650,7 +714,8 @@ TEST_P(RasterInvalidatorTest, EffectLocalTransformSpaceChange) { auto artifact = TestPaintArtifact().Chunk(0).Properties(*t2, c0(), *e1).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); // Change both t1 and t2 but keep t1*t2 unchanged, to test change of @@ -659,14 +724,14 @@ TEST_P(RasterInvalidatorTest, EffectLocalTransformSpaceChange) { t1->Update(t0(), TransformPaintPropertyNode::State{FloatSize(-10, -20)}); t2->Update(*t1, TransformPaintPropertyNode::State{FloatSize(10, 20)}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(1u, invalidations.size()); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); auto mapper = [](IntRect& r) { r.Inflate(60); }; - EXPECT_CHUNK_INVALIDATION_CUSTOM(invalidations, 0, artifact->PaintChunks()[0], - PaintInvalidationReason::kPaintProperty, - -kDefaultLayerBounds.Location(), - base::BindRepeating(mapper)); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation( + &artifact->PaintChunks()[0], PaintInvalidationReason::kPaintProperty, + -kDefaultLayerBounds.Location(), base::BindRepeating(mapper)))); invalidator_.SetTracksRasterInvalidations(false); FinishCycle(*artifact); } @@ -686,7 +751,8 @@ TEST_P(RasterInvalidatorTest, EffectLocalTransformSpaceChangeNoInvalidation) { auto artifact = TestPaintArtifact().Chunk(0).Properties(*t2, c0(), *e1).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); // Change both t1 and t2 but keep t1*t2 unchanged. @@ -694,7 +760,8 @@ TEST_P(RasterInvalidatorTest, EffectLocalTransformSpaceChangeNoInvalidation) { t1->Update(t0(), TransformPaintPropertyNode::State{FloatSize(-10, -20)}); t2->Update(*t1, TransformPaintPropertyNode::State{FloatSize(10, 20)}); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); EXPECT_TRUE(TrackedRasterInvalidations().IsEmpty()); FinishCycle(*artifact); } @@ -711,7 +778,8 @@ TEST_P(RasterInvalidatorTest, AliasEffectParentChanges) { PropertyTreeState chunk_state(t0(), c0(), *alias_effect); auto artifact = TestPaintArtifact().Chunk(0).Properties(chunk_state).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); invalidator_.SetTracksRasterInvalidations(true); @@ -721,11 +789,12 @@ TEST_P(RasterInvalidatorTest, AliasEffectParentChanges) { // We expect to get invalidations since the effect unaliased effect is // actually different now. - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(1u, invalidations.size()); - EXPECT_CHUNK_INVALIDATION(invalidations, 0, artifact->PaintChunks()[0], - PaintInvalidationReason::kPaintProperty); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation(&artifact->PaintChunks()[0], + PaintInvalidationReason::kPaintProperty))); FinishCycle(*artifact); } @@ -742,7 +811,8 @@ TEST_P(RasterInvalidatorTest, NestedAliasEffectParentChanges) { PropertyTreeState chunk_state(t0(), c0(), *alias_effect_2); auto artifact = TestPaintArtifact().Chunk(0).Properties(chunk_state).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); invalidator_.SetTracksRasterInvalidations(true); @@ -753,11 +823,12 @@ TEST_P(RasterInvalidatorTest, NestedAliasEffectParentChanges) { // We expect to get invalidations since the effect unaliased effect is // actually different now. - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(1u, invalidations.size()); - EXPECT_CHUNK_INVALIDATION(invalidations, 0, artifact->PaintChunks()[0], - PaintInvalidationReason::kPaintProperty); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation(&artifact->PaintChunks()[0], + PaintInvalidationReason::kPaintProperty))); FinishCycle(*artifact); } @@ -775,7 +846,8 @@ TEST_P(RasterInvalidatorTest, EffectWithAliasTransformWhoseParentChanges) { PropertyTreeState chunk_state(t0(), c0(), *e1); auto artifact = TestPaintArtifact().Chunk(0).Properties(chunk_state).Build(); - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); FinishCycle(*artifact); invalidator_.SetTracksRasterInvalidations(true); @@ -785,11 +857,12 @@ TEST_P(RasterInvalidatorTest, EffectWithAliasTransformWhoseParentChanges) { // We expect to get invalidations since the effect unaliased effect is // actually different now. - invalidator_.Generate(artifact, kDefaultLayerBounds, layer_state); - const auto& invalidations = TrackedRasterInvalidations(); - ASSERT_EQ(1u, invalidations.size()); - EXPECT_CHUNK_INVALIDATION(invalidations, 0, artifact->PaintChunks()[0], - PaintInvalidationReason::kPaintProperty); + invalidator_.Generate(base::DoNothing(), artifact, kDefaultLayerBounds, + layer_state); + EXPECT_THAT( + TrackedRasterInvalidations(), + ElementsAre(ChunkInvalidation(&artifact->PaintChunks()[0], + PaintInvalidationReason::kPaintProperty))); FinishCycle(*artifact); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h index 334ca46377b..23ff52e3096 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_display_item_fragment.h @@ -13,7 +13,7 @@ namespace blink { class ScopedDisplayItemFragment final { - DISALLOW_NEW(); + STACK_ALLOCATED(); public: ScopedDisplayItemFragment(GraphicsContext& context, unsigned fragment) diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_hint.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_hint.h new file mode 100644 index 00000000000..5d4e7190e26 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_hint.h @@ -0,0 +1,74 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCOPED_PAINT_CHUNK_HINT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCOPED_PAINT_CHUNK_HINT_H_ + +#include "third_party/blink/renderer/platform/graphics/paint/display_item.h" +#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" +#include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h" + +namespace blink { + +// Hints for new paint chunks for a scope during paint. Will create new paint +// chunks if we create any display items in the paint scope and +// - paint chunk properties are different from the current chunk, or +// - If the client's visual rect isn't fully contained by the current chunk's +// bounds. This is a heuristic to create only paint chunks that are meaningful +// to CompositeAfterPaint layerization, i.e. avoid unnecessary forced paint +// chunks that will definitely be merged into the previous paint chunk. +// Hinted paint chunks are never required for correctness and rendering should +// be the same without this class. These hints are for performance: by causing +// additional paint chunks (while avoiding some unnecessary ones) with explicit +// ids, we can improve raster invalidation and layerization. +class ScopedPaintChunkHint { + STACK_ALLOCATED(); + + public: + ScopedPaintChunkHint(PaintController& paint_controller, + const DisplayItemClient& client, + DisplayItem::Type type) + : ScopedPaintChunkHint(paint_controller, + paint_controller.CurrentPaintChunkProperties(), + client, + type) {} + + ScopedPaintChunkHint(PaintController& paint_controller, + const PropertyTreeState& properties, + const DisplayItemClient& client, + DisplayItem::Type type) + : paint_controller_(paint_controller), + previous_num_chunks_(paint_controller_.NumNewChunks()), + previous_force_new_chunk_(paint_controller_.WillForceNewChunk()) { + if (!previous_force_new_chunk_ && previous_num_chunks_ && + !paint_controller_.LastChunkBounds().Contains(client.VisualRect())) + paint_controller_.SetForceNewChunk(true); + // This is after SetForceNewChunk(true) so that the possible new chunk will + // use the specified id. + paint_chunk_properties_.emplace(paint_controller, properties, client, type); + } + + ~ScopedPaintChunkHint() { + paint_chunk_properties_ = base::nullopt; + if (!HasCreatedPaintChunk()) + paint_controller_.SetForceNewChunk(previous_force_new_chunk_); + } + + bool HasCreatedPaintChunk() const { + DCHECK_GE(paint_controller_.NumNewChunks(), previous_num_chunks_); + return paint_controller_.NumNewChunks() > previous_num_chunks_; + } + + private: + PaintController& paint_controller_; + // This is actually always emplaced, but is wrapped in base::Optional<> to + // control its lifetime related to SetForceNewChunk(). + base::Optional<ScopedPaintChunkProperties> paint_chunk_properties_; + wtf_size_t previous_num_chunks_; + bool previous_force_new_chunk_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCOPED_PAINT_CHUNK_HINT_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h index 0b0b87f5456..4df836d70c6 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h @@ -6,7 +6,6 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCOPED_PAINT_CHUNK_PROPERTIES_H_ #include "base/macros.h" -#include "base/optional.h" #include "third_party/blink/renderer/platform/graphics/paint/display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" @@ -16,7 +15,7 @@ namespace blink { class ScopedPaintChunkProperties { - DISALLOW_NEW(); + STACK_ALLOCATED(); public: // Use new PropertyTreeState for the scope. @@ -26,8 +25,8 @@ class ScopedPaintChunkProperties { DisplayItem::Type type) : paint_controller_(paint_controller), previous_properties_(paint_controller.CurrentPaintChunkProperties()) { - paint_controller_.UpdateCurrentPaintChunkProperties( - PaintChunk::Id(client, type), properties); + PaintChunk::Id id(client, type); + paint_controller_.UpdateCurrentPaintChunkProperties(&id, properties); } // Use new transform state, and keep the current other properties. @@ -69,7 +68,7 @@ class ScopedPaintChunkProperties { // ScopedPaintChunkProperties. The painter should create another scope of // paint properties with new id, or the new chunk will use the id of the // first display item as its id. - paint_controller_.UpdateCurrentPaintChunkProperties(base::nullopt, + paint_controller_.UpdateCurrentPaintChunkProperties(nullptr, previous_properties_); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.cc deleted file mode 100644 index 3c8cd82d14a..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.cc +++ /dev/null @@ -1,80 +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 "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h" - -#include "third_party/blink/renderer/platform/graphics/graphics_context.h" -#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" -#include "third_party/blink/renderer/platform/wtf/assertions.h" - -namespace blink { - -ScrollHitTestDisplayItem::ScrollHitTestDisplayItem( - const DisplayItemClient& client, - DisplayItem::Type type, - const TransformPaintPropertyNode* scroll_offset_node, - const IntRect& scroll_container_bounds) - : DisplayItem(client, type, sizeof(*this)), - scroll_offset_node_(scroll_offset_node), - scroll_container_bounds_(scroll_container_bounds) { -#if DCHECK_IS_ON() - if (type == DisplayItem::Type::kResizerScrollHitTest || - type == DisplayItem::Type::kPluginScrollHitTest) { - // Resizer and plugin scroll hit tests are only used to prevent composited - // scrolling and should not have a scroll offset node. - DCHECK(!scroll_offset_node); - } else if (type == DisplayItem::Type::kScrollHitTest) { - DCHECK(scroll_offset_node); - // The scroll offset transform node should have an associated scroll node. - DCHECK(scroll_offset_node_->ScrollNode()); - } else { - NOTREACHED(); - } -#endif -} - -ScrollHitTestDisplayItem::~ScrollHitTestDisplayItem() = default; - -bool ScrollHitTestDisplayItem::Equals(const DisplayItem& other) const { - return DisplayItem::Equals(other) && - scroll_offset_node() == - static_cast<const ScrollHitTestDisplayItem&>(other) - .scroll_offset_node() && - scroll_container_bounds() == - static_cast<const ScrollHitTestDisplayItem&>(other) - .scroll_container_bounds(); -} - -#if DCHECK_IS_ON() -void ScrollHitTestDisplayItem::PropertiesAsJSON(JSONObject& json) const { - DisplayItem::PropertiesAsJSON(json); - json.SetString("scrollOffsetNode", String::Format("%p", scroll_offset_node_)); - json.SetString("scrollContainerBounds", scroll_container_bounds_.ToString()); -} -#endif - -void ScrollHitTestDisplayItem::Record( - GraphicsContext& context, - const DisplayItemClient& client, - DisplayItem::Type type, - const TransformPaintPropertyNode* scroll_offset_node, - const IntRect& scroll_container_bounds) { - PaintController& paint_controller = context.GetPaintController(); - - // The scroll hit test should be in the non-scrolled transform space and - // therefore should not be scrolled by the associated scroll offset. - DCHECK_NE(&paint_controller.CurrentPaintChunkProperties().Transform(), - scroll_offset_node); - - if (paint_controller.DisplayItemConstructionIsDisabled()) - return; - - if (paint_controller.UseCachedItemIfPossible(client, type)) - return; - - paint_controller.CreateAndAppend<ScrollHitTestDisplayItem>( - client, type, scroll_offset_node, scroll_container_bounds); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h deleted file mode 100644 index b2bfbe00b4d..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h +++ /dev/null @@ -1,88 +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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_HIT_TEST_DISPLAY_ITEM_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_HIT_TEST_DISPLAY_ITEM_H_ - -#include "base/memory/ref_counted.h" -#include "third_party/blink/renderer/platform/graphics/paint/display_item.h" -#include "third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h" -#include "third_party/blink/renderer/platform/platform_export.h" - -namespace blink { - -class GraphicsContext; - -// Display item for marking a region as scrollable. This should be emitted in -// the non-scrolling paint property tree state (in other words, this item should -// not scroll). A display item is needed because hit testing is in paint order. -// -// This serves three purposes: -// 1. Creating non-fast scrollable regions for non-composited scrollers. -// Scrollable areas create a non-fast scrollable region in the -// non-scrolling paint property tree state. Pre-CompositeAfterPaint, we skip -// painting these for composited scrollers. With CompositeAfterPaint, we -// paint the non-fast item and later ignore it if the scroller was composited -// (see: PaintArtifactCompositor::UpdateNonFastScrollableRegions). -// -// 2. Creating non-fast scrollable regions for plugins and resize handles. -// Plugins that have blocking event handlers and resize handles both need to -// prevent composited scrolling. A different display item type is used -// (kPluginScrollHitTest and kResizerScrollHitTest) to disambiguate multiple -// scroll hit test display items (e.g., if a scroller is non-composited and -// has a resizer). -// -// 3. Creating cc::Layers marked as being scrollable. -// (when CompositeAfterPaint is enabled). -// A single cc::Layer must be marked as being "scrollable", and this display -// item is used by PaintArtifactCompositor to create the scrollable cc::Layer. -class PLATFORM_EXPORT ScrollHitTestDisplayItem final : public DisplayItem { - public: - ScrollHitTestDisplayItem(const DisplayItemClient&, - DisplayItem::Type, - const TransformPaintPropertyNode* scroll_offset_node, - const IntRect& scroll_container_bounds); - ~ScrollHitTestDisplayItem() override; - - const TransformPaintPropertyNode* scroll_offset_node() const { - return scroll_offset_node_; - } - - const IntRect& scroll_container_bounds() const { - return scroll_container_bounds_; - } - - // DisplayItem - bool Equals(const DisplayItem&) const override; -#if DCHECK_IS_ON() - void PropertiesAsJSON(JSONObject&) const override; -#endif - - // Create and append a ScrollHitTestDisplayItem onto the context. This is - // similar to a recorder class (e.g., DrawingRecorder) but just emits a single - // item. If no |scroll_offset_node| is passed in, this display item will only - // be used for creating a non-fast-scrollable-region. - static void Record(GraphicsContext&, - const DisplayItemClient&, - DisplayItem::Type, - const TransformPaintPropertyNode* scroll_offset_node, - const IntRect& scroll_container_bounds); - - const ScrollPaintPropertyNode* scroll_node() const { - return scroll_offset_node_->ScrollNode(); - } - - private: - // The scroll offset transform node if this ScrollHitTestDisplayItem could be - // used for composited scrolling, or null if it is only used to prevent - // composited scrolling. - const TransformPaintPropertyNode* scroll_offset_node_; - // The bounds of the scroll container, including scrollbars. We cannot use - // scroll_node().container_rect() because it does not include scrollbars. - const IntRect scroll_container_bounds_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_SCROLL_HIT_TEST_DISPLAY_ITEM_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc index 4aa3435f851..a91ac0fa9d9 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.cc @@ -52,10 +52,6 @@ std::unique_ptr<JSONObject> ScrollPaintPropertyNode::ToJSON() const { state_.main_thread_scrolling_reasons) .c_str()); } - if (state_.scrolls_inner_viewport) - json->SetString("scrollsInnerViewport", "true"); - if (state_.scrolls_outer_viewport) - json->SetString("scrollsOuterViewport", "true"); if (state_.max_scroll_offset_affected_by_page_scale) json->SetString("maxScrollOffsetAffectedByPageScale", "true"); if (state_.compositor_element_id) { diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h index c1ebe1b0f61..75e7b5c3d4b 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h @@ -43,8 +43,6 @@ class PLATFORM_EXPORT ScrollPaintPropertyNode IntSize contents_size; bool user_scrollable_horizontal = false; bool user_scrollable_vertical = false; - bool scrolls_inner_viewport = false; - bool scrolls_outer_viewport = false; // This bit tells the compositor whether the inner viewport should be // scrolled using the full viewport mechanism (overscroll, top control @@ -69,8 +67,6 @@ class PLATFORM_EXPORT ScrollPaintPropertyNode contents_size != other.contents_size || user_scrollable_horizontal != other.user_scrollable_horizontal || user_scrollable_vertical != other.user_scrollable_vertical || - scrolls_inner_viewport != other.scrolls_inner_viewport || - scrolls_outer_viewport != other.scrolls_outer_viewport || prevent_viewport_scrolling_from_inner != other.prevent_viewport_scrolling_from_inner || max_scroll_offset_affected_by_page_scale != @@ -145,8 +141,6 @@ class PLATFORM_EXPORT ScrollPaintPropertyNode bool UserScrollableVertical() const { return state_.user_scrollable_vertical; } - bool ScrollsInnerViewport() const { return state_.scrolls_inner_viewport; } - bool ScrollsOuterViewport() const { return state_.scrolls_outer_viewport; } bool PreventViewportScrollingFromInner() const { return state_.prevent_viewport_scrolling_from_inner; } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.cc index 72d3fb4c053..eeb5932afc5 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.cc @@ -34,6 +34,9 @@ ScrollbarDisplayItem::ScrollbarDisplayItem( } sk_sp<const PaintRecord> ScrollbarDisplayItem::Paint() const { + // We are painting a non-composited scrollbar, so we don't need layer_. + layer_ = nullptr; + if (record_) { DCHECK(!scrollbar_->NeedsRepaintPart(cc::TRACK_BUTTONS_TICKMARKS)); DCHECK(!scrollbar_->NeedsRepaintPart(cc::THUMB)); @@ -52,7 +55,27 @@ sk_sp<const PaintRecord> ScrollbarDisplayItem::Paint() const { return record_; } -scoped_refptr<cc::Layer> ScrollbarDisplayItem::CreateLayer() const { +scoped_refptr<cc::ScrollbarLayerBase> ScrollbarDisplayItem::GetLayer() const { + // This function is called when the scrollbar is composited. We don't need + // record_ which is for non-composited scrollbars. + record_ = nullptr; + + if (!layer_ || layer_->is_left_side_vertical_scrollbar() != + scrollbar_->IsLeftSideVerticalScrollbar()) + layer_ = CreateLayer(); + + layer_->SetOffsetToTransformParent( + gfx::Vector2dF(FloatPoint(rect_.Location()))); + layer_->SetBounds(gfx::Size(rect_.Size())); + + if (scrollbar_->NeedsRepaintPart(cc::THUMB) || + scrollbar_->NeedsRepaintPart(cc::TRACK_BUTTONS_TICKMARKS)) + layer_->SetNeedsDisplay(); + return layer_; +} + +scoped_refptr<cc::ScrollbarLayerBase> ScrollbarDisplayItem::CreateLayer() + const { scoped_refptr<cc::ScrollbarLayerBase> layer; if (scrollbar_->IsSolidColor()) { DCHECK(scrollbar_->IsOverlay()); @@ -74,10 +97,10 @@ scoped_refptr<cc::Layer> ScrollbarDisplayItem::CreateLayer() const { layer->SetIsDrawable(true); layer->SetElementId(element_id_); - if (scroll_translation_) { - layer->SetScrollElementId( - scroll_translation_->ScrollNode()->GetCompositorElementId()); - } + layer->SetScrollElementId( + scroll_translation_ + ? scroll_translation_->ScrollNode()->GetCompositorElementId() + : CompositorElementId()); return layer; } @@ -114,9 +137,6 @@ void ScrollbarDisplayItem::Record( const TransformPaintPropertyNode* scroll_translation, CompositorElementId element_id) { PaintController& paint_controller = context.GetPaintController(); - if (paint_controller.DisplayItemConstructionIsDisabled()) - return; - // Must check PaintController::UseCachedItemIfPossible before this function. DCHECK(RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() || !paint_controller.UseCachedItemIfPossible(client, type)); diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.h b/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.h index c8e214c8007..1ddb96941c8 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.h @@ -12,7 +12,7 @@ #include "third_party/skia/include/core/SkRefCnt.h" namespace cc { -class Layer; +class ScrollbarLayerBase; } namespace blink { @@ -35,8 +35,6 @@ class PLATFORM_EXPORT ScrollbarDisplayItem final : public DisplayItem { const TransformPaintPropertyNode* scroll_translation, CompositorElementId element_id); - cc::Scrollbar* GetScrollbar() const { return scrollbar_.get(); } - const IntRect& GetRect() const { return rect_; } const TransformPaintPropertyNode* ScrollTranslation() const { return scroll_translation_; } @@ -46,8 +44,8 @@ class PLATFORM_EXPORT ScrollbarDisplayItem final : public DisplayItem { // scrollbar. sk_sp<const PaintRecord> Paint() const; - // Creates cc layer for composited scrollbar. - scoped_refptr<cc::Layer> CreateLayer() const; + // Create or reuse the cc scrollbar layer, for composited scrollbar. + scoped_refptr<cc::ScrollbarLayerBase> GetLayer() const; // DisplayItem bool Equals(const DisplayItem&) const override; @@ -67,12 +65,16 @@ class PLATFORM_EXPORT ScrollbarDisplayItem final : public DisplayItem { CompositorElementId element_id); private: + scoped_refptr<cc::ScrollbarLayerBase> CreateLayer() const; + scoped_refptr<cc::Scrollbar> scrollbar_; IntRect rect_; const TransformPaintPropertyNode* scroll_translation_; CompositorElementId element_id_; // This is lazily created for non-composited scrollbar. mutable sk_sp<const PaintRecord> record_; + // This is lazily created for composited scrollbar. + mutable scoped_refptr<cc::ScrollbarLayerBase> layer_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item_test.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item_test.cc new file mode 100644 index 00000000000..18148c28b3e --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item_test.cc @@ -0,0 +1,155 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by node BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.h" + +#include "cc/layers/painted_overlay_scrollbar_layer.h" +#include "cc/layers/painted_scrollbar_layer.h" +#include "cc/layers/solid_color_scrollbar_layer.h" +#include "cc/test/fake_scrollbar.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/graphics/paint/scroll_paint_property_node.h" +#include "third_party/blink/renderer/platform/testing/fake_display_item_client.h" +#include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h" + +namespace blink { + +CompositorElementId ScrollbarElementId(const cc::Scrollbar& scrollbar) { + return CompositorElementIdFromUniqueObjectId( + 13579, scrollbar.Orientation() == cc::HORIZONTAL + ? CompositorElementIdNamespace::kHorizontalScrollbar + : CompositorElementIdNamespace::kVerticalScrollbar); +} + +CompositorElementId ScrollElementId() { + return CompositorElementIdFromUniqueObjectId( + 24680, CompositorElementIdNamespace::kScroll); +} + +scoped_refptr<TransformPaintPropertyNode> CreateScrollTranslation() { + ScrollPaintPropertyNode::State state{IntRect(0, 0, 100, 100), + IntSize(1000, 1000)}; + state.compositor_element_id = ScrollElementId(); + auto scroll = ScrollPaintPropertyNode::Create(ScrollPaintPropertyNode::Root(), + std::move(state)); + return CreateScrollTranslation(t0(), 0, 0, *scroll); +} + +TEST(ScrollbarDisplayItemTest, HorizontalSolidColorScrollbar) { + auto scrollbar = base::MakeRefCounted<cc::FakeScrollbar>(); + scrollbar->set_orientation(cc::HORIZONTAL); + scrollbar->set_is_solid_color(true); + scrollbar->set_is_overlay(true); + scrollbar->set_track_rect(gfx::Rect(2, 90, 96, 10)); + scrollbar->set_thumb_size(gfx::Size(30, 7)); + + FakeDisplayItemClient client; + IntRect scrollbar_rect(0, 90, 100, 10); + auto scroll_translation = CreateScrollTranslation(); + auto element_id = ScrollbarElementId(*scrollbar); + ScrollbarDisplayItem display_item(client, DisplayItem::kScrollbarHorizontal, + scrollbar, scrollbar_rect, + scroll_translation.get(), element_id); + auto layer = display_item.GetLayer(); + ASSERT_EQ(cc::ScrollbarLayerBase::kSolidColor, + layer->ScrollbarLayerTypeForTesting()); + auto* scrollbar_layer = + static_cast<cc::SolidColorScrollbarLayer*>(layer.get()); + EXPECT_EQ(cc::HORIZONTAL, scrollbar_layer->orientation()); + EXPECT_EQ(7, scrollbar_layer->thumb_thickness()); + EXPECT_EQ(2, scrollbar_layer->track_start()); + EXPECT_EQ(element_id, scrollbar_layer->element_id()); + EXPECT_EQ(ScrollElementId(), scrollbar_layer->scroll_element_id()); + + EXPECT_EQ(layer.get(), display_item.GetLayer().get()); +} + +TEST(ScrollbarDisplayItemTest, VerticalSolidColorScrollbar) { + auto scrollbar = base::MakeRefCounted<cc::FakeScrollbar>(); + scrollbar->set_orientation(cc::VERTICAL); + scrollbar->set_is_solid_color(true); + scrollbar->set_is_overlay(true); + scrollbar->set_track_rect(gfx::Rect(90, 2, 10, 96)); + scrollbar->set_thumb_size(gfx::Size(7, 30)); + + FakeDisplayItemClient client; + IntRect scrollbar_rect(90, 0, 10, 100); + auto scroll_translation = CreateScrollTranslation(); + auto element_id = ScrollbarElementId(*scrollbar); + ScrollbarDisplayItem display_item(client, DisplayItem::kScrollbarHorizontal, + scrollbar, scrollbar_rect, + scroll_translation.get(), element_id); + auto layer = display_item.GetLayer(); + ASSERT_EQ(cc::ScrollbarLayerBase::kSolidColor, + layer->ScrollbarLayerTypeForTesting()); + auto* scrollbar_layer = + static_cast<cc::SolidColorScrollbarLayer*>(layer.get()); + EXPECT_EQ(cc::VERTICAL, scrollbar_layer->orientation()); + EXPECT_EQ(7, scrollbar_layer->thumb_thickness()); + EXPECT_EQ(2, scrollbar_layer->track_start()); + EXPECT_EQ(element_id, scrollbar_layer->element_id()); + EXPECT_EQ(ScrollElementId(), scrollbar_layer->scroll_element_id()); + + EXPECT_EQ(layer.get(), display_item.GetLayer().get()); +} + +TEST(ScrollbarDisplayItemTest, PaintedColorScrollbar) { + auto scrollbar = base::MakeRefCounted<cc::FakeScrollbar>(); + + FakeDisplayItemClient client; + IntRect scrollbar_rect(0, 90, 100, 10); + auto scroll_translation = CreateScrollTranslation(); + auto element_id = ScrollbarElementId(*scrollbar); + ScrollbarDisplayItem display_item(client, DisplayItem::kScrollbarHorizontal, + scrollbar, scrollbar_rect, + scroll_translation.get(), element_id); + auto layer = display_item.GetLayer(); + ASSERT_EQ(cc::ScrollbarLayerBase::kPainted, + layer->ScrollbarLayerTypeForTesting()); + + EXPECT_EQ(layer.get(), display_item.GetLayer().get()); +} + +TEST(ScrollbarDisplayItemTest, PaintedColorScrollbarOverlayNonNinePatch) { + auto scrollbar = base::MakeRefCounted<cc::FakeScrollbar>(); + scrollbar->set_has_thumb(true); + scrollbar->set_is_overlay(true); + + FakeDisplayItemClient client; + IntRect scrollbar_rect(0, 90, 100, 10); + auto scroll_translation = CreateScrollTranslation(); + auto element_id = ScrollbarElementId(*scrollbar); + ScrollbarDisplayItem display_item(client, DisplayItem::kScrollbarHorizontal, + scrollbar, scrollbar_rect, + scroll_translation.get(), element_id); + auto layer = display_item.GetLayer(); + // We should create PaintedScrollbarLayer instead of + // PaintedOverlayScrollbarLayer for non-nine-patch overlay scrollbars. + ASSERT_EQ(cc::ScrollbarLayerBase::kPainted, + layer->ScrollbarLayerTypeForTesting()); + + EXPECT_EQ(layer.get(), display_item.GetLayer().get()); +} + +TEST(ScrollbarDisplayItemTest, PaintedColorScrollbarOverlayNinePatch) { + auto scrollbar = base::MakeRefCounted<cc::FakeScrollbar>(); + scrollbar->set_has_thumb(true); + scrollbar->set_is_overlay(true); + scrollbar->set_uses_nine_patch_thumb_resource(true); + + FakeDisplayItemClient client; + IntRect scrollbar_rect(0, 90, 100, 10); + auto scroll_translation = CreateScrollTranslation(); + auto element_id = ScrollbarElementId(*scrollbar); + ScrollbarDisplayItem display_item(client, DisplayItem::kScrollbarHorizontal, + scrollbar, scrollbar_rect, + scroll_translation.get(), element_id); + auto layer = display_item.GetLayer(); + ASSERT_EQ(cc::ScrollbarLayerBase::kPaintedOverlay, + layer->ScrollbarLayerTypeForTesting()); + + EXPECT_EQ(layer.get(), display_item.GetLayer().get()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h b/chromium/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h index e2e6288cce1..1e1666c9e92 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/subsequence_recorder.h @@ -21,13 +21,13 @@ class PaintController; // of DisplayItems, and supports caching via a CachedDisplayItem with the // CachedSubsequence DisplayItem type. // -// Also note that useCachedSubsequenceIfPossible is not sufficient to determine +// Also note that UseCachedSubsequenceIfPossible is not sufficient to determine // whether a CachedSubsequence can be used. In particular, the client is // responsible for checking that none of the DisplayItemClients that contribute // to the subsequence have been invalidated. // class SubsequenceRecorder final { - DISALLOW_NEW(); + STACK_ALLOCATED(); public: static bool UseCachedSubsequenceIfPossible(GraphicsContext& context, @@ -39,19 +39,15 @@ class SubsequenceRecorder final { : paint_controller_(context.GetPaintController()), client_(client), start_(0) { - if (!paint_controller_.DisplayItemConstructionIsDisabled()) - start_ = paint_controller_.BeginSubsequence(); + start_ = paint_controller_.BeginSubsequence(); } - ~SubsequenceRecorder() { - if (!paint_controller_.DisplayItemConstructionIsDisabled()) - paint_controller_.EndSubsequence(client_, start_); - } + ~SubsequenceRecorder() { paint_controller_.EndSubsequence(client_, start_); } private: PaintController& paint_controller_; const DisplayItemClient& client_; - size_t start_; + wtf_size_t start_; DISALLOW_COPY_AND_ASSIGN(SubsequenceRecorder); }; diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h index 802a8acba3e..b44b2c24da8 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint/transform_paint_property_node.h @@ -59,12 +59,8 @@ class PLATFORM_EXPORT TransformPaintPropertyNode // the transform is identity or a 2d translation, the translation_2d version // should be used instead. TransformAndOrigin(const TransformationMatrix& matrix, - const FloatPoint3D& origin = FloatPoint3D(), - bool disable_optimization = false) { - if (!disable_optimization && matrix.IsIdentityOr2DTranslation()) - translation_2d_ = matrix.To2DTranslation(); - else - matrix_and_origin_.reset(new MatrixAndOrigin{matrix, origin}); + const FloatPoint3D& origin = FloatPoint3D()) { + matrix_and_origin_.reset(new MatrixAndOrigin{matrix, origin}); } bool IsIdentityOr2DTranslation() const { return !matrix_and_origin_; } @@ -381,6 +377,11 @@ class PLATFORM_EXPORT TransformPaintPropertyNode return state_.direct_compositing_reasons & CompositingReason::kRootScroller; } + bool RequiresCompositingForWillChangeTransform() const { + return state_.direct_compositing_reasons & + CompositingReason::kWillChangeTransform; + } + const CompositorElementId& GetCompositorElementId() const { return state_.compositor_element_id; } @@ -421,6 +422,7 @@ class PLATFORM_EXPORT TransformPaintPropertyNode // The scroll compositor element id should be stored on the scroll node. DCHECK(!state_.compositor_element_id); } + DCHECK(!HasActiveTransformAnimation() || !IsIdentityOr2DTranslation()); #endif } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.cc b/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.cc index 45dbceb5bad..084304ea8cc 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.cc @@ -20,18 +20,16 @@ void PaintGeneratedImage::Draw(cc::PaintCanvas* canvas, ImageDecodingMode) { PaintCanvasAutoRestore ar(canvas, true); canvas->clipRect(dest_rect); - canvas->translate(dest_rect.X(), dest_rect.Y()); - if (dest_rect.Size() != src_rect.Size()) - canvas->scale(dest_rect.Width() / src_rect.Width(), - dest_rect.Height() / src_rect.Height()); - canvas->translate(-src_rect.X(), -src_rect.Y()); + canvas->concat(SkMatrix::MakeRectToRect(src_rect, dest_rect, + SkMatrix::kFill_ScaleToFit)); SkRect bounds = src_rect; canvas->saveLayer(&bounds, &flags); canvas->drawPicture(record_); } void PaintGeneratedImage::DrawTile(GraphicsContext& context, - const FloatRect& src_rect) { + const FloatRect& src_rect, + RespectImageOrientationEnum) { context.DrawRecord(record_); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.h b/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.h index 06fb89c4dd4..6978106580e 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.h +++ b/chromium/third_party/blink/renderer/platform/graphics/paint_generated_image.h @@ -28,7 +28,9 @@ class PLATFORM_EXPORT PaintGeneratedImage : public GeneratedImage { RespectImageOrientationEnum, ImageClampingMode, ImageDecodingMode) override; - void DrawTile(GraphicsContext&, const FloatRect&) final; + void DrawTile(GraphicsContext&, + const FloatRect&, + RespectImageOrientationEnum) final; PaintGeneratedImage(sk_sp<PaintRecord> record, const FloatSize& size) : GeneratedImage(size), record_(std::move(record)) {} diff --git a/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc index 43a015c670f..ab027f02901 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.cc @@ -177,7 +177,6 @@ class PlaceholderImage::SharedFont : public RefCounted<SharedFont> { explicit SharedFont(float scale_factor) : font_(CreatePlaceholderFontDescription(scale_factor)), scale_factor_(scale_factor) { - font_.Update(nullptr); } ~SharedFont() { @@ -191,7 +190,6 @@ class PlaceholderImage::SharedFont : public RefCounted<SharedFont> { scale_factor_ = scale_factor; font_ = Font(CreatePlaceholderFontDescription(scale_factor_)); - font_.Update(nullptr); } const Font& font() const { return font_; } @@ -252,8 +250,8 @@ PaintImage PlaceholderImage::PaintImageForCurrentFrame() { PaintRecorder paint_recorder; Draw(paint_recorder.beginRecording(FloatRect(dest_rect)), PaintFlags(), - FloatRect(dest_rect), FloatRect(dest_rect), - kDoNotRespectImageOrientation, kClampImageToSourceRect, kSyncDecode); + FloatRect(dest_rect), FloatRect(dest_rect), kRespectImageOrientation, + kClampImageToSourceRect, kSyncDecode); paint_record_for_current_frame_ = paint_recorder.finishRecordingAsPicture(); paint_record_content_id_ = PaintImage::GetNextContentId(); @@ -359,13 +357,15 @@ void PlaceholderImage::Draw(cc::PaintCanvas* canvas, Font::kUseFallbackIfFontNotReady, 1.0f, flags); } -void PlaceholderImage::DrawPattern(GraphicsContext& context, - const FloatRect& src_rect, - const FloatSize& scale, - const FloatPoint& phase, - SkBlendMode mode, - const FloatRect& dest_rect, - const FloatSize& repeat_spacing) { +void PlaceholderImage::DrawPattern( + GraphicsContext& context, + const FloatRect& src_rect, + const FloatSize& scale, + const FloatPoint& phase, + SkBlendMode mode, + const FloatRect& dest_rect, + const FloatSize& repeat_spacing, + RespectImageOrientationEnum respect_orientation) { DCHECK(context.Canvas()); PaintFlags flags = context.FillFlags(); @@ -374,9 +374,8 @@ void PlaceholderImage::DrawPattern(GraphicsContext& context, // Ignore the pattern specifications and just draw a single placeholder image // over the whole |dest_rect|. This is done in order to prevent repeated icons // from cluttering tiled background images. - Draw(context.Canvas(), flags, dest_rect, src_rect, - kDoNotRespectImageOrientation, kClampImageToSourceRect, - kUnspecifiedDecode); + Draw(context.Canvas(), flags, dest_rect, src_rect, respect_orientation, + kClampImageToSourceRect, kUnspecifiedDecode); } void PlaceholderImage::DestroyDecodedData() { diff --git a/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.h b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.h index e6a2647bbba..e1e706801b4 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.h +++ b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image.h @@ -83,7 +83,8 @@ class PLATFORM_EXPORT PlaceholderImage final : public Image { const FloatPoint& phase, SkBlendMode, const FloatRect& dest_rect, - const FloatSize& repeat_spacing) override; + const FloatSize& repeat_spacing, + RespectImageOrientationEnum) override; // SetData does nothing, and the passed in buffer is ignored. SizeAvailability SetData(scoped_refptr<SharedBuffer>, bool) override; diff --git a/chromium/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc index 81fb9df438a..eb5c0622151 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/placeholder_image_test.cc @@ -75,9 +75,8 @@ void DrawImageExpectingGrayBoxOnly(PlaceholderImage& image, EXPECT_CALL(canvas, drawTextBlob(_, _, _, _)).Times(0); image.Draw(&canvas, PaintFlags(), dest_rect, - FloatRect(0.0f, 0.0f, 100.0f, 100.0f), - kDoNotRespectImageOrientation, Image::kClampImageToSourceRect, - Image::kUnspecifiedDecode); + FloatRect(0.0f, 0.0f, 100.0f, 100.0f), kRespectImageOrientation, + Image::kClampImageToSourceRect, Image::kUnspecifiedDecode); } void DrawImageExpectingIconOnly(PlaceholderImage& image, @@ -134,7 +133,6 @@ float GetExpectedPlaceholderTextWidth(const StringView& text, description.SetWeight(FontSelectionValue(500)); Font font(description); - font.Update(nullptr); return font.Width(TextRun(text)); } diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc b/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc index 1e8ae93ee66..a254d06c0cf 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.cc @@ -38,6 +38,7 @@ #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h" #include "third_party/skia/include/effects/SkCornerPathEffect.h" #include "third_party/skia/include/third_party/skcms/skcms.h" +#include "ui/base/ui_base_features.h" #include <algorithm> #include <cmath> @@ -374,13 +375,19 @@ template <typename PrimitiveType> void DrawPlatformFocusRing(const PrimitiveType& primitive, cc::PaintCanvas* canvas, SkColor color, - float width) { + float width, + float border_radius) { PaintFlags flags; flags.setAntiAlias(true); flags.setStyle(PaintFlags::kStroke_Style); flags.setColor(color); flags.setStrokeWidth(width); + if (::features::IsFormControlsRefreshEnabled()) { + DrawFocusRingPrimitive(primitive, canvas, flags, border_radius); + return; + } + #if defined(OS_MACOSX) flags.setAlpha(64); const float corner_radius = (width - 1) * 0.5f; @@ -398,14 +405,18 @@ void DrawPlatformFocusRing(const PrimitiveType& primitive, #endif } -template void PLATFORM_EXPORT DrawPlatformFocusRing<SkRect>(const SkRect&, - cc::PaintCanvas*, - SkColor, - float width); -template void PLATFORM_EXPORT DrawPlatformFocusRing<SkPath>(const SkPath&, - cc::PaintCanvas*, - SkColor, - float width); +template void PLATFORM_EXPORT +DrawPlatformFocusRing<SkRect>(const SkRect&, + cc::PaintCanvas*, + SkColor, + float width, + float border_radius); +template void PLATFORM_EXPORT +DrawPlatformFocusRing<SkPath>(const SkPath&, + cc::PaintCanvas*, + SkColor, + float width, + float border_radius); sk_sp<SkData> TryAllocateSkData(size_t size) { void* buffer = WTF::Partitions::BufferPartition()->AllocFlags( diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.h b/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.h index b5f81ee0f40..b35b6ce5449 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.h +++ b/chromium/third_party/blink/renderer/platform/graphics/skia/skia_utils.h @@ -144,7 +144,8 @@ template <typename PrimitiveType> void DrawPlatformFocusRing(const PrimitiveType&, cc::PaintCanvas*, SkColor, - float width); + float width, + float border_radius); // TODO(fmalita): remove in favor of direct SrcRectConstraint use. inline cc::PaintCanvas::SrcRectConstraint diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc b/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc deleted file mode 100644 index f59034399e6..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.cc +++ /dev/null @@ -1,149 +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 "third_party/blink/renderer/platform/graphics/skia_texture_holder.h" - -#include "gpu/command_buffer/client/gles2_interface.h" -#include "third_party/blink/public/platform/platform.h" -#include "third_party/blink/renderer/platform/graphics/canvas_color_params.h" -#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" -#include "third_party/blink/renderer/platform/graphics/mailbox_texture_holder.h" -#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" -#include "third_party/khronos/GLES2/gl2ext.h" -#include "third_party/skia/include/gpu/GrBackendSurface.h" -#include "third_party/skia/include/gpu/GrContext.h" - -namespace blink { - -namespace { -bool IsSkImageOriginTopLeft(sk_sp<SkImage> image) { - GrSurfaceOrigin origin; - image->getBackendTexture(false, &origin); - return origin == kTopLeft_GrSurfaceOrigin; -} - -struct ReleaseContext { - scoped_refptr<TextureHolder::MailboxRef> mailbox_ref; - GLuint texture_id = 0u; - bool is_shared_image = false; - GrTexture* gr_texture = nullptr; - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper; -}; - -void ReleaseTexture(void* ctx) { - auto* release_ctx = static_cast<ReleaseContext*>(ctx); - if (release_ctx->context_provider_wrapper) { - if (release_ctx->gr_texture) { - release_ctx->context_provider_wrapper->Utils()->RemoveCachedMailbox( - release_ctx->gr_texture); - } - - if (release_ctx->texture_id) { - auto* gl = - release_ctx->context_provider_wrapper->ContextProvider()->ContextGL(); - if (release_ctx->is_shared_image) - gl->EndSharedImageAccessDirectCHROMIUM(release_ctx->texture_id); - gl->DeleteTextures(1u, &release_ctx->texture_id); - } - } - - delete release_ctx; -} - -} // namespace - -SkiaTextureHolder::SkiaTextureHolder( - sk_sp<SkImage> image, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&& - context_provider_wrapper) - : TextureHolder(std::move(context_provider_wrapper), - base::MakeRefCounted<MailboxRef>(nullptr), - IsSkImageOriginTopLeft(image)), - image_(std::move(image)) {} - -SkiaTextureHolder::SkiaTextureHolder( - const MailboxTextureHolder* mailbox_texture_holder, - GLuint shared_image_texture_id) - : TextureHolder(SharedGpuContext::ContextProviderWrapper(), - mailbox_texture_holder->mailbox_ref(), - mailbox_texture_holder->IsOriginTopLeft()) { - const gpu::Mailbox mailbox = mailbox_texture_holder->GetMailbox(); - DCHECK(!shared_image_texture_id || mailbox.IsSharedImage()); - DCHECK(!shared_image_texture_id || !mailbox_texture_holder->IsCrossThread()); - - const gpu::SyncToken sync_token = mailbox_texture_holder->GetSyncToken(); - const auto& sk_image_info = mailbox_texture_holder->sk_image_info(); - GLenum texture_target = mailbox_texture_holder->texture_target(); - - if (!ContextProvider()) - return; - - gpu::gles2::GLES2Interface* shared_gl = ContextProvider()->ContextGL(); - GrContext* shared_gr_context = ContextProvider()->GetGrContext(); - DCHECK(shared_gl && - shared_gr_context); // context isValid already checked in callers - - GLuint shared_context_texture_id = 0u; - bool should_delete_texture_on_release = true; - - if (shared_image_texture_id) { - shared_context_texture_id = shared_image_texture_id; - should_delete_texture_on_release = false; - } else { - shared_gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData()); - if (mailbox.IsSharedImage()) { - shared_context_texture_id = - shared_gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name); - shared_gl->BeginSharedImageAccessDirectCHROMIUM( - shared_context_texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM); - } else { - shared_context_texture_id = - shared_gl->CreateAndConsumeTextureCHROMIUM(mailbox.name); - } - } - - GrGLTextureInfo texture_info; - texture_info.fTarget = texture_target; - texture_info.fID = shared_context_texture_id; - texture_info.fFormat = - CanvasColorParams(sk_image_info).GLSizedInternalFormat(); - GrBackendTexture backend_texture(sk_image_info.width(), - sk_image_info.height(), GrMipMapped::kNo, - texture_info); - - GrSurfaceOrigin origin = IsOriginTopLeft() ? kTopLeft_GrSurfaceOrigin - : kBottomLeft_GrSurfaceOrigin; - - auto* release_ctx = new ReleaseContext; - release_ctx->mailbox_ref = mailbox_ref(); - if (should_delete_texture_on_release) - release_ctx->texture_id = shared_context_texture_id; - release_ctx->is_shared_image = mailbox.IsSharedImage(); - release_ctx->context_provider_wrapper = ContextProviderWrapper(); - - image_ = SkImage::MakeFromTexture( - shared_gr_context, backend_texture, origin, sk_image_info.colorType(), - sk_image_info.alphaType(), sk_image_info.refColorSpace(), &ReleaseTexture, - release_ctx); - if (image_) { - release_ctx->gr_texture = image_->getTexture(); - DCHECK(release_ctx->gr_texture); - ContextProviderWrapper()->Utils()->RegisterMailbox(image_->getTexture(), - mailbox); - } else { - ReleaseTexture(release_ctx); - } -} - -SkiaTextureHolder::~SkiaTextureHolder() { - // Object must be destroyed on the same thread where it was created. - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); -} - -bool SkiaTextureHolder::IsValid() const { - return !!image_ && !!ContextProviderWrapper() && - image_->isValid(ContextProvider()->GetGrContext()); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.h b/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.h deleted file mode 100644 index 4aa252996c0..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/skia_texture_holder.h +++ /dev/null @@ -1,60 +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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_TEXTURE_HOLDER_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_TEXTURE_HOLDER_H_ - -#include "base/memory/weak_ptr.h" -#include "base/threading/thread_checker.h" -#include "gpu/GLES2/gl2extchromium.h" -#include "third_party/blink/renderer/platform/graphics/texture_holder.h" -#include "third_party/blink/renderer/platform/platform_export.h" - -namespace blink { -class MailboxTextureHolder; -class WebGraphicsContext3DProviderWrapper; - -class PLATFORM_EXPORT SkiaTextureHolder final : public TextureHolder { - public: - ~SkiaTextureHolder() override; - - // TextureHolder impl. - IntSize Size() const final { - if (image_) - return IntSize(image_->width(), image_->height()); - return IntSize(); - } - bool IsValid() const final; - bool CurrentFrameKnownToBeOpaque() const final { - return image_ && image_->isOpaque(); - } - - const sk_sp<SkImage>& GetSkImage() const { return image_; } - - // When creating a AcceleratedStaticBitmap from a texture-backed SkImage, this - // function will be called to create a TextureHolder object. - SkiaTextureHolder(sk_sp<SkImage>, - base::WeakPtr<WebGraphicsContext3DProviderWrapper>&&); - - // This function consumes the mailbox in the input parameter and turn it into - // a texture-backed SkImage. - // |shared_image_texture_id| is an optional texture bound to the - // |texture_holder|'s mailbox and imported into its context. Note that if a - // texture is provided it must: - // 1) Be associated with a shared image mailbox. - // 2) Stay alive and have a read lock on the shared image until the - // |MailboxRef| is destroyed. - SkiaTextureHolder(const MailboxTextureHolder* texture_holder, - GLuint shared_image_texture_id); - - private: - // The image_ should always be texture-backed. - sk_sp<SkImage> image_; - - THREAD_CHECKER(thread_checker_); -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SKIA_TEXTURE_HOLDER_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.cc b/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.cc index 6605e4258a3..f1f66e3a6d3 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.cc @@ -56,10 +56,6 @@ constexpr SquashingDisallowedReasonStringMap "squashingNearestFixedPositionMismatch", "Cannot be squashed because this layer has a different nearest fixed " "position layer than the squashing layer"}, - {SquashingDisallowedReason::kScrollChildWithCompositedDescendants, - "scrollChildWithCompositedDescendants", - "Squashing a scroll child with composited descendants is not " - "supported."}, {SquashingDisallowedReason::kSquashingLayerIsAnimating, "squashingLayerIsAnimating", "Cannot squash into a layer that is animating."}, diff --git a/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h b/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h index 21357fae5cf..46b6e780942 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h +++ b/chromium/third_party/blink/renderer/platform/graphics/squashing_disallowed_reasons.h @@ -24,7 +24,6 @@ using SquashingDisallowedReasons = unsigned; V(SquashingLayoutEmbeddedContentIsDisallowed) \ V(SquashingBlendingIsDisallowed) \ V(NearestFixedPositionMismatch) \ - V(ScrollChildWithCompositedDescendants) \ V(SquashingLayerIsAnimating) \ V(RenderingContextMismatch) \ V(FragmentedContent) \ diff --git a/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc b/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc index ae6cb2afc46..fce932e5648 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc @@ -20,53 +20,64 @@ namespace blink { -scoped_refptr<StaticBitmapImage> StaticBitmapImage::Create(PaintImage image) { +scoped_refptr<StaticBitmapImage> StaticBitmapImage::Create( + PaintImage image, + ImageOrientation orientation) { DCHECK(!image.GetSkImage()->isTextureBacked()); - return UnacceleratedStaticBitmapImage::Create(std::move(image)); + return UnacceleratedStaticBitmapImage::Create(std::move(image), orientation); } scoped_refptr<StaticBitmapImage> StaticBitmapImage::Create( sk_sp<SkData> data, - const SkImageInfo& info) { + const SkImageInfo& info, + ImageOrientation orientation) { return UnacceleratedStaticBitmapImage::Create( - SkImage::MakeRasterData(info, std::move(data), info.minRowBytes())); + SkImage::MakeRasterData(info, std::move(data), info.minRowBytes()), + orientation); +} + +IntSize StaticBitmapImage::SizeRespectingOrientation() const { + if (orientation_.UsesWidthAsHeight()) + return Size().TransposedSize(); + else + return Size(); } -void StaticBitmapImage::DrawHelper(cc::PaintCanvas* canvas, - const PaintFlags& flags, - const FloatRect& dst_rect, - const FloatRect& src_rect, - ImageClampingMode clamp_mode, - const PaintImage& image) { +void StaticBitmapImage::DrawHelper( + cc::PaintCanvas* canvas, + const PaintFlags& flags, + const FloatRect& dst_rect, + const FloatRect& src_rect, + ImageClampingMode clamp_mode, + RespectImageOrientationEnum respect_orientation, + const PaintImage& image) { FloatRect adjusted_src_rect = src_rect; adjusted_src_rect.Intersect(SkRect::MakeWH(image.width(), image.height())); if (dst_rect.IsEmpty() || adjusted_src_rect.IsEmpty()) return; // Nothing to draw. - canvas->drawImageRect(image, adjusted_src_rect, dst_rect, &flags, - WebCoreClampingModeToSkiaRectConstraint(clamp_mode)); -} + cc::PaintCanvasAutoRestore auto_restore(canvas, false); + FloatRect adjusted_dst_rect = dst_rect; + if (respect_orientation && orientation_ != kDefaultImageOrientation) { + canvas->save(); -scoped_refptr<StaticBitmapImage> StaticBitmapImage::ConvertToColorSpace( - sk_sp<SkColorSpace> color_space, - SkColorType color_type) { - DCHECK(color_space); - sk_sp<SkImage> skia_image = PaintImageForCurrentFrame().GetSkImage(); - - // If we don't need to change the color type, use SkImage::makeColorSpace() - if (skia_image->colorType() == color_type) { - skia_image = skia_image->makeColorSpace(color_space); - } else { - skia_image = - skia_image->makeColorTypeAndColorSpace(color_type, color_space); - } + // ImageOrientation expects the origin to be at (0, 0) + canvas->translate(adjusted_dst_rect.X(), adjusted_dst_rect.Y()); + adjusted_dst_rect.SetLocation(FloatPoint()); - if (skia_image->isTextureBacked()) { - return AcceleratedStaticBitmapImage::CreateFromSkImage( - skia_image, ContextProviderWrapper()); + canvas->concat(AffineTransformToSkMatrix( + orientation_.TransformFromDefault(adjusted_dst_rect.Size()))); + + if (orientation_.UsesWidthAsHeight()) { + adjusted_dst_rect = + FloatRect(adjusted_dst_rect.X(), adjusted_dst_rect.Y(), + adjusted_dst_rect.Height(), adjusted_dst_rect.Width()); + } } - return UnacceleratedStaticBitmapImage::Create(skia_image); + + canvas->drawImageRect(image, adjusted_src_rect, adjusted_dst_rect, &flags, + WebCoreClampingModeToSkiaRectConstraint(clamp_mode)); } base::CheckedNumeric<size_t> StaticBitmapImage::GetSizeInBytes( @@ -120,9 +131,4 @@ bool StaticBitmapImage::CopyToByteArray( return true; } -const gpu::SyncToken& StaticBitmapImage::GetSyncToken() const { - static const gpu::SyncToken sync_token; - return sync_token; -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.h b/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.h index 7366bdebcdc..f14b5b86cc3 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.h +++ b/chromium/third_party/blink/renderer/platform/graphics/static_bitmap_image.h @@ -6,11 +6,11 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_STATIC_BITMAP_IMAGE_H_ #include "base/memory/weak_ptr.h" -#include "gpu/command_buffer/common/mailbox.h" -#include "gpu/command_buffer/common/sync_token.h" +#include "gpu/command_buffer/common/mailbox_holder.h" #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h" #include "third_party/blink/renderer/platform/graphics/graphics_types.h" #include "third_party/blink/renderer/platform/graphics/image.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/skia/include/core/SkRefCnt.h" @@ -22,23 +22,29 @@ class GLES2Interface; namespace blink { -class WebGraphicsContext3DProviderWrapper; - class PLATFORM_EXPORT StaticBitmapImage : public Image { public: - static scoped_refptr<StaticBitmapImage> Create(PaintImage); - static scoped_refptr<StaticBitmapImage> Create(sk_sp<SkData> data, - const SkImageInfo&); + // The ImageOrientation should be derived from the source of the image data. + static scoped_refptr<StaticBitmapImage> Create( + PaintImage, + ImageOrientation = kDefaultImageOrientation); + static scoped_refptr<StaticBitmapImage> Create( + sk_sp<SkData> data, + const SkImageInfo&, + ImageOrientation = kDefaultImageOrientation); + + StaticBitmapImage(ImageOrientation orientation) : orientation_(orientation) {} bool IsStaticBitmapImage() const override { return true; } // Methods overridden by all sub-classes ~StaticBitmapImage() override = default; - // Creates a gpu copy of the image using the given ContextProvider. Should - // not be called if IsTextureBacked() is already true. May return null if the - // conversion failed (for instance if the context had an error). - virtual scoped_refptr<StaticBitmapImage> MakeAccelerated( - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_wrapper) = 0; + + IntSize SizeRespectingOrientation() const override; + + virtual scoped_refptr<StaticBitmapImage> ConvertToColorSpace( + sk_sp<SkColorSpace>, + SkColorType = kN32_SkColorType) = 0; // Methods have common implementation for all sub-classes bool CurrentFrameIsComplete() override { return true; } @@ -46,9 +52,9 @@ class PLATFORM_EXPORT StaticBitmapImage : public Image { // Methods that have a default implementation, and overridden by only one // sub-class - virtual bool HasMailbox() const { return false; } virtual bool IsValid() const { return true; } virtual void Transfer() {} + virtual bool IsOriginTopLeft() const { return true; } // Creates a non-gpu copy of the image, or returns this if image is already // non-gpu. @@ -68,37 +74,27 @@ class PLATFORM_EXPORT StaticBitmapImage : public Image { return false; } - // EnsureMailbox modifies the internal state of an accelerated static bitmap - // image to make sure that it is represented by a Mailbox. This must be - // called whenever the image is intende to be used in a differen GPU context. - // It is important to use the correct MailboxSyncMode in order to achieve - // optimal performance without compromising security or causeing graphics - // glitches. Here is how to select the aprropriate mode: - // - // Case 1: Passing to a gpu context that is on a separate channel. - // Note: a context in a separate process is necessarily on another channel. - // Use kVerifiedSyncToken. Or use kUnverifiedSyncToken with a later call - // to VerifySyncTokensCHROMIUM() - // Case 2: Passing to a gpu context that is on the same channel but not the - // same stream. - // Use kUnverifiedSyncToken - // Case 3: Passing to a gpu context on the same stream. - // Use kOrderingBarrier - virtual void EnsureMailbox(MailboxSyncMode, GLenum filter) { NOTREACHED(); } - virtual const gpu::Mailbox& GetMailbox() const { + virtual void EnsureSyncTokenVerified() { NOTREACHED(); } + virtual gpu::MailboxHolder GetMailboxHolder() const { NOTREACHED(); - static const gpu::Mailbox mailbox; - return mailbox; + return gpu::MailboxHolder(); } - virtual const gpu::SyncToken& GetSyncToken() const; + virtual void UpdateSyncToken(const gpu::SyncToken&) { NOTREACHED(); } virtual bool IsPremultiplied() const { return true; } // Methods have exactly the same implementation for all sub-classes bool OriginClean() const { return is_origin_clean_; } void SetOriginClean(bool flag) { is_origin_clean_ = flag; } - scoped_refptr<StaticBitmapImage> ConvertToColorSpace( - sk_sp<SkColorSpace>, - SkColorType = kN32_SkColorType); + + // StaticBitmapImage needs to store the orientation of the image itself, + // because the underlying representations do not. If the bitmap represents + // a non-default orientation it must be explicitly given in the constructor. + ImageOrientation CurrentFrameOrientation() const override { + return orientation_; + } + bool HasDefaultOrientation() const override { + return orientation_ == kDefaultImageOrientation; + } static base::CheckedNumeric<size_t> GetSizeInBytes( const IntRect& rect, @@ -119,8 +115,15 @@ class PLATFORM_EXPORT StaticBitmapImage : public Image { const FloatRect&, const FloatRect&, ImageClampingMode, + RespectImageOrientationEnum, const PaintImage&); + // The image orientation is stored here because it is only available when the + // static image is created and the underlying representations do not store + // the information. The property is set at construction based on the source of + // the image data. + ImageOrientation orientation_ = kDefaultImageOrientation; + // The following property is here because the SkImage API doesn't expose the // info. It is applied to both UnacceleratedStaticBitmapImage and // AcceleratedStaticBitmapImage. To change this property, the call site would @@ -128,7 +131,12 @@ class PLATFORM_EXPORT StaticBitmapImage : public Image { bool is_origin_clean_ = true; }; -DEFINE_IMAGE_TYPE_CASTS(StaticBitmapImage); +template <> +struct DowncastTraits<StaticBitmapImage> { + static bool AllowFrom(const Image& image) { + return image.IsStaticBitmapImage(); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc b/chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc index 4f3d1cc74ec..5b851f0e695 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc @@ -14,8 +14,8 @@ #include "components/viz/common/surfaces/surface_info.h" #include "media/base/media_switches.h" #include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/mojo/mojo_helper.h" #include "third_party/blink/renderer/platform/wtf/functional.h" @@ -33,7 +33,7 @@ SurfaceLayerBridge::SurfaceLayerBridge( frame_sink_id_(Platform::Current()->GenerateFrameSinkId()), parent_frame_sink_id_(parent_frame_sink_id) { mojo::Remote<mojom::blink::EmbeddedFrameSinkProvider> provider; - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( provider.BindNewPipeAndPassReceiver()); // TODO(xlai): Ensure OffscreenCanvas commit() is still functional when a // frame-less HTML canvas's document is reparenting under another frame. diff --git a/chromium/third_party/blink/renderer/platform/graphics/texture_holder.h b/chromium/third_party/blink/renderer/platform/graphics/texture_holder.h deleted file mode 100644 index a1ba07e2056..00000000000 --- a/chromium/third_party/blink/renderer/platform/graphics/texture_holder.h +++ /dev/null @@ -1,85 +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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEXTURE_HOLDER_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEXTURE_HOLDER_H_ - -#include "base/memory/weak_ptr.h" -#include "components/viz/common/resources/single_release_callback.h" -#include "gpu/command_buffer/common/mailbox.h" -#include "gpu/command_buffer/common/sync_token.h" -#include "third_party/blink/renderer/platform/geometry/int_size.h" -#include "third_party/blink/renderer/platform/graphics/graphics_types.h" -#include "third_party/blink/renderer/platform/graphics/image.h" -#include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h" -#include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h" -#include "third_party/skia/include/core/SkImage.h" -#include "third_party/skia/include/core/SkRefCnt.h" - -namespace blink { - -class PLATFORM_EXPORT TextureHolder { - public: - class MailboxRef : public ThreadSafeRefCounted<MailboxRef> { - public: - explicit MailboxRef( - std::unique_ptr<viz::SingleReleaseCallback> release_callback) - : release_callback_(std::move(release_callback)) {} - ~MailboxRef() { - if (release_callback_) - release_callback_->Run(sync_token_, false /* resource_lost */); - } - - void set_sync_token(gpu::SyncToken token) { sync_token_ = token; } - const gpu::SyncToken& sync_token() const { return sync_token_; } - - private: - gpu::SyncToken sync_token_; - std::unique_ptr<viz::SingleReleaseCallback> release_callback_; - }; - - virtual ~TextureHolder() = default; - - // Methods overridden by all sub-classes - virtual IntSize Size() const = 0; - virtual bool CurrentFrameKnownToBeOpaque() const = 0; - virtual bool IsValid() const = 0; - - // Methods that have exactly the same impelmentation for all sub-classes - base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper() - const { - return context_provider_wrapper_; - } - - WebGraphicsContext3DProvider* ContextProvider() const { - return context_provider_wrapper_ - ? context_provider_wrapper_->ContextProvider() - : nullptr; - } - - bool IsOriginTopLeft() const { return is_origin_top_left_; } - - const scoped_refptr<MailboxRef>& mailbox_ref() const { return mailbox_ref_; } - - protected: - TextureHolder(base::WeakPtr<WebGraphicsContext3DProviderWrapper>&& - context_provider_wrapper, - scoped_refptr<MailboxRef> mailbox_ref, - bool is_origin_top_left) - : context_provider_wrapper_(std::move(context_provider_wrapper)), - mailbox_ref_(std::move(mailbox_ref)), - is_origin_top_left_(is_origin_top_left) { - DCHECK(mailbox_ref_); - } - - private: - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_; - scoped_refptr<MailboxRef> mailbox_ref_; - bool is_origin_top_left_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEXTURE_HOLDER_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/touch_action_rect.cc b/chromium/third_party/blink/renderer/platform/graphics/touch_action_rect.cc new file mode 100644 index 00000000000..1b18032e09f --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/touch_action_rect.cc @@ -0,0 +1,21 @@ +// 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 "third_party/blink/renderer/platform/graphics/touch_action_rect.h" + +#include "cc/base/region.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +String TouchActionRect::ToString() const { + return rect.ToString() + " " + cc::TouchActionToString(allowed_touch_action); +} + +std::ostream& operator<<(std::ostream& os, + const TouchActionRect& hit_test_rect) { + return os << hit_test_rect.ToString(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/touch_action_rect.h b/chromium/third_party/blink/renderer/platform/graphics/touch_action_rect.h new file mode 100644 index 00000000000..578b2bbbe10 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/touch_action_rect.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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TOUCH_ACTION_RECT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TOUCH_ACTION_RECT_H_ + +#include "third_party/blink/renderer/platform/geometry/int_rect.h" +#include "third_party/blink/renderer/platform/graphics/touch_action.h" +#include "third_party/blink/renderer/platform/platform_export.h" + +namespace blink { + +struct PLATFORM_EXPORT TouchActionRect { + IntRect rect; + TouchAction allowed_touch_action = TouchAction::kNone; + + bool operator==(const TouchActionRect& rhs) const { + return rect == rhs.rect && allowed_touch_action == rhs.allowed_touch_action; + } + + bool operator!=(const TouchActionRect& rhs) const { return !(*this == rhs); } + + String ToString() const; +}; + +PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const TouchActionRect&); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TOUCH_ACTION_RECT_H_ diff --git a/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc b/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc index 01253176e58..5a0e96f8d48 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.cc @@ -17,13 +17,17 @@ namespace blink { scoped_refptr<UnacceleratedStaticBitmapImage> -UnacceleratedStaticBitmapImage::Create(sk_sp<SkImage> image) { +UnacceleratedStaticBitmapImage::Create(sk_sp<SkImage> image, + ImageOrientation orientation) { DCHECK(!image->isTextureBacked()); - return base::AdoptRef(new UnacceleratedStaticBitmapImage(std::move(image))); + return base::AdoptRef( + new UnacceleratedStaticBitmapImage(std::move(image), orientation)); } UnacceleratedStaticBitmapImage::UnacceleratedStaticBitmapImage( - sk_sp<SkImage> image) { + sk_sp<SkImage> image, + ImageOrientation orientation) + : StaticBitmapImage(orientation) { CHECK(image); DCHECK(!image->isLazyGenerated()); paint_image_ = @@ -33,12 +37,16 @@ UnacceleratedStaticBitmapImage::UnacceleratedStaticBitmapImage( } scoped_refptr<UnacceleratedStaticBitmapImage> -UnacceleratedStaticBitmapImage::Create(PaintImage image) { - return base::AdoptRef(new UnacceleratedStaticBitmapImage(std::move(image))); +UnacceleratedStaticBitmapImage::Create(PaintImage image, + ImageOrientation orientation) { + return base::AdoptRef( + new UnacceleratedStaticBitmapImage(std::move(image), orientation)); } -UnacceleratedStaticBitmapImage::UnacceleratedStaticBitmapImage(PaintImage image) - : paint_image_(std::move(image)) { +UnacceleratedStaticBitmapImage::UnacceleratedStaticBitmapImage( + PaintImage image, + ImageOrientation orientation) + : StaticBitmapImage(orientation), paint_image_(std::move(image)) { CHECK(paint_image_.GetSkImage()); } @@ -66,40 +74,21 @@ bool UnacceleratedStaticBitmapImage::IsPremultiplied() const { SkAlphaType::kPremul_SkAlphaType; } -scoped_refptr<StaticBitmapImage> -UnacceleratedStaticBitmapImage::MakeAccelerated( - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_wrapper) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - if (!context_wrapper) - return nullptr; // Can happen if the context is lost. - - GrContext* grcontext = context_wrapper->ContextProvider()->GetGrContext(); - if (!grcontext) - return nullptr; // Can happen if the context is lost. - - sk_sp<SkImage> sk_image = paint_image_.GetSkImage(); - sk_sp<SkImage> gpu_skimage = sk_image->makeTextureImage(grcontext); - if (!gpu_skimage) - return nullptr; - - return AcceleratedStaticBitmapImage::CreateFromSkImage( - std::move(gpu_skimage), std::move(context_wrapper)); -} - bool UnacceleratedStaticBitmapImage::CurrentFrameKnownToBeOpaque() { return paint_image_.GetSkImage()->isOpaque(); } -void UnacceleratedStaticBitmapImage::Draw(cc::PaintCanvas* canvas, - const cc::PaintFlags& flags, - const FloatRect& dst_rect, - const FloatRect& src_rect, - RespectImageOrientationEnum, - ImageClampingMode clamp_mode, - ImageDecodingMode) { +void UnacceleratedStaticBitmapImage::Draw( + cc::PaintCanvas* canvas, + const cc::PaintFlags& flags, + const FloatRect& dst_rect, + const FloatRect& src_rect, + RespectImageOrientationEnum should_respect_image_orientation, + ImageClampingMode clamp_mode, + ImageDecodingMode) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); StaticBitmapImage::DrawHelper(canvas, flags, dst_rect, src_rect, clamp_mode, + should_respect_image_orientation, PaintImageForCurrentFrame()); } @@ -114,4 +103,21 @@ void UnacceleratedStaticBitmapImage::Transfer() { original_skia_image_task_runner_ = Thread::Current()->GetTaskRunner(); } +scoped_refptr<StaticBitmapImage> +UnacceleratedStaticBitmapImage::ConvertToColorSpace( + sk_sp<SkColorSpace> color_space, + SkColorType color_type) { + DCHECK(color_space); + + sk_sp<SkImage> skia_image = PaintImageForCurrentFrame().GetSkImage(); + // If we don't need to change the color type, use SkImage::makeColorSpace() + if (skia_image->colorType() == color_type) { + skia_image = skia_image->makeColorSpace(color_space); + } else { + skia_image = + skia_image->makeColorTypeAndColorSpace(color_type, color_space); + } + return UnacceleratedStaticBitmapImage::Create(skia_image, orientation_); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h b/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h index d5ec71b3c44..d7c50181397 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h +++ b/chromium/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h @@ -17,15 +17,20 @@ class PLATFORM_EXPORT UnacceleratedStaticBitmapImage final : public StaticBitmapImage { public: ~UnacceleratedStaticBitmapImage() override; - static scoped_refptr<UnacceleratedStaticBitmapImage> Create(sk_sp<SkImage>); - static scoped_refptr<UnacceleratedStaticBitmapImage> Create(PaintImage); + + // The ImageOrientation should be derived from the source of the image data. + static scoped_refptr<UnacceleratedStaticBitmapImage> Create( + sk_sp<SkImage>, + ImageOrientation orientation = kDefaultImageOrientation); + static scoped_refptr<UnacceleratedStaticBitmapImage> Create( + PaintImage, + ImageOrientation orientation = kDefaultImageOrientation); bool CurrentFrameKnownToBeOpaque() override; IntSize Size() const override; bool IsPremultiplied() const override; - scoped_refptr<StaticBitmapImage> MakeAccelerated( - base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_wrapper) - override; + scoped_refptr<StaticBitmapImage> ConvertToColorSpace(sk_sp<SkColorSpace>, + SkColorType) override; void Draw(cc::PaintCanvas*, const cc::PaintFlags&, @@ -40,8 +45,8 @@ class PLATFORM_EXPORT UnacceleratedStaticBitmapImage final void Transfer() final; private: - UnacceleratedStaticBitmapImage(sk_sp<SkImage>); - UnacceleratedStaticBitmapImage(PaintImage); + UnacceleratedStaticBitmapImage(sk_sp<SkImage>, ImageOrientation); + UnacceleratedStaticBitmapImage(PaintImage, ImageOrientation); PaintImage paint_image_; THREAD_CHECKER(thread_checker_); diff --git a/chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc b/chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc index 38028ad0d5b..7fe9c308b58 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc @@ -36,8 +36,7 @@ void VideoFrameResourceProvider::Initialize( viz::RasterContextProvider* media_context_provider, viz::SharedBitmapReporter* shared_bitmap_reporter) { context_provider_ = media_context_provider; - resource_provider_ = std::make_unique<viz::ClientResourceProvider>( - /*delegated_sync_points_required=*/true); + resource_provider_ = std::make_unique<viz::ClientResourceProvider>(); int max_texture_size; if (context_provider_) { diff --git a/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc index 1f551556454..e0cb8886ddd 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc @@ -10,6 +10,7 @@ #include "base/metrics/histogram_macros.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/trace_event/trace_event.h" +#include "cc/metrics/video_playback_roughness_reporter.h" #include "components/viz/common/resources/resource_id.h" #include "components/viz/common/resources/returned_resource.h" #include "media/base/video_frame.h" @@ -18,8 +19,8 @@ #include "services/viz/public/cpp/gpu/context_provider_command_buffer.h" #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom-blink.h" #include "services/viz/public/mojom/hit_test/hit_test_region_list.mojom-blink.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/frame_sinks/embedded_frame_sink.mojom-blink.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource.h" #include "ui/gfx/presentation_feedback.h" @@ -28,10 +29,13 @@ namespace blink { VideoFrameSubmitter::VideoFrameSubmitter( WebContextProviderCallback context_provider_callback, + cc::PlaybackRoughnessReportingCallback roughness_reporting_callback, std::unique_ptr<VideoFrameResourceProvider> resource_provider) : context_provider_callback_(context_provider_callback), resource_provider_(std::move(resource_provider)), rotation_(media::VIDEO_ROTATION_0), + roughness_reporter_(std::make_unique<cc::VideoPlaybackRoughnessReporter>( + std::move(roughness_reporting_callback))), frame_trackers_(false, nullptr) { DETACH_FROM_THREAD(thread_checker_); } @@ -72,6 +76,7 @@ void VideoFrameSubmitter::StopRendering() { is_rendering_ = false; frame_trackers_.StopSequence(cc::FrameSequenceTrackerType::kVideo); + roughness_reporter_->Reset(); UpdateSubmissionState(); } @@ -182,31 +187,39 @@ void VideoFrameSubmitter::OnBeginFrame( if (viz::FrameTokenGT(pair.key, *next_frame_token_)) continue; - if (base::Contains(frame_token_to_timestamp_map_, pair.key) && - !(pair.value->presentation_feedback->flags & - gfx::PresentationFeedback::kFailure)) { - if (!ignorable_submitted_frames_.contains(pair.key)) { - frame_trackers_.NotifyFramePresented( - pair.key, gfx::PresentationFeedback( - pair.value->presentation_feedback->timestamp, - pair.value->presentation_feedback->interval, - pair.value->presentation_feedback->flags)); - } - UMA_HISTOGRAM_TIMES("Media.VideoFrameSubmitter", - pair.value->presentation_feedback->timestamp - - frame_token_to_timestamp_map_[pair.key]); - frame_token_to_timestamp_map_.erase(pair.key); +#ifdef OS_LINUX + // TODO: On Linux failure flag is unreliable, and perfectly rendered frames + // are reported as failures all the time. + bool presentation_failure = false; +#else + bool presentation_failure = !!(pair.value->presentation_feedback->flags & + gfx::PresentationFeedback::kFailure); +#endif + if (!presentation_failure && + !ignorable_submitted_frames_.contains(pair.key)) { + frame_trackers_.NotifyFramePresented( + pair.key, gfx::PresentationFeedback( + pair.value->presentation_feedback->timestamp, + pair.value->presentation_feedback->interval, + pair.value->presentation_feedback->flags)); + roughness_reporter_->FramePresented( + pair.key, pair.value->presentation_feedback->timestamp); } ignorable_submitted_frames_.erase(pair.key); - - TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0( - "media", "VideoFrameSubmitter", pair.key, + TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( + "media", "VideoFrameSubmitter", TRACE_ID_LOCAL(pair.key), pair.value->presentation_feedback->timestamp); } - frame_trackers_.NotifyBeginImplFrame(args); + base::ScopedClosureRunner end_frame( + base::BindOnce(&cc::FrameSequenceTrackerCollection::NotifyFrameEnd, + base::Unretained(&frame_trackers_), args, args)); + base::ScopedClosureRunner roughness_processing( + base::BindOnce(&cc::VideoPlaybackRoughnessReporter::ProcessFrameWindow, + base::Unretained(roughness_reporter_.get()))); + // Don't call UpdateCurrentFrame() for MISSED BeginFrames. Also don't call it // after StopRendering() has been called (forbidden by API contract). viz::BeginFrameAck current_begin_frame_ack(args, false); @@ -274,6 +287,8 @@ void VideoFrameSubmitter::OnReceivedContextProvider( DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (!use_gpu_compositing) { resource_provider_->Initialize(nullptr, this); + if (frame_sink_id_.is_valid()) + StartSubmitting(); return; } @@ -317,7 +332,7 @@ void VideoFrameSubmitter::StartSubmitting() { DCHECK(frame_sink_id_.is_valid()); mojo::Remote<mojom::blink::EmbeddedFrameSinkProvider> provider; - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( provider.BindNewPipeAndPassReceiver()); provider->CreateCompositorFrameSink( @@ -437,8 +452,15 @@ bool VideoFrameSubmitter::SubmitFrame( frame_size_ = frame_size; } - auto compositor_frame = - CreateCompositorFrame(begin_frame_ack, std::move(video_frame)); + auto frame_token = ++next_frame_token_; + auto source_id = begin_frame_ack.frame_id.source_id; + if (source_id != viz::BeginFrameArgs::kManualSourceId) { + // Roughness reporter only cares about true video frames. + roughness_reporter_->FrameSubmitted(frame_token, *video_frame.get(), + last_begin_frame_args_.interval); + } + auto compositor_frame = CreateCompositorFrame(frame_token, begin_frame_ack, + std::move(video_frame)); WebVector<viz::ResourceId> resources; const auto& quad_list = compositor_frame.render_pass_list.back()->quad_list; @@ -453,7 +475,6 @@ bool VideoFrameSubmitter::SubmitFrame( // We can pass nullptr for the HitTestData as the CompositorFram will not // contain any SurfaceDrawQuads. - auto frame_token = compositor_frame.metadata.frame_token; compositor_frame_sink_->SubmitCompositorFrame( child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation() .local_surface_id(), @@ -479,9 +500,10 @@ void VideoFrameSubmitter::SubmitEmptyFrame() { last_frame_id_.reset(); auto begin_frame_ack = viz::BeginFrameAck::CreateManualAckWithDamage(); - auto compositor_frame = CreateCompositorFrame(begin_frame_ack, nullptr); + auto frame_token = ++next_frame_token_; + auto compositor_frame = + CreateCompositorFrame(frame_token, begin_frame_ack, nullptr); - auto frame_token = compositor_frame.metadata.frame_token; compositor_frame_sink_->SubmitCompositorFrame( child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation() .local_surface_id(), @@ -520,13 +542,14 @@ bool VideoFrameSubmitter::ShouldSubmit() const { } viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame( + uint32_t frame_token, const viz::BeginFrameAck& begin_frame_ack, scoped_refptr<media::VideoFrame> video_frame) { DCHECK(!frame_size_.IsEmpty()); viz::CompositorFrame compositor_frame; compositor_frame.metadata.begin_frame_ack = begin_frame_ack; - compositor_frame.metadata.frame_token = ++next_frame_token_; + compositor_frame.metadata.frame_token = frame_token; compositor_frame.metadata.preferred_frame_interval = video_frame_provider_ ? video_frame_provider_->GetPreferredRenderInterval() @@ -535,22 +558,23 @@ viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame( base::TimeTicks value; if (video_frame && video_frame->metadata()->GetTimeTicks( media::VideoFrameMetadata::DECODE_END_TIME, &value)) { - TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0("media", "VideoFrameSubmitter", - *next_frame_token_, value); - TRACE_EVENT_ASYNC_STEP_PAST0("media", "VideoFrameSubmitter", - *next_frame_token_, "Pre-submit buffering"); - - frame_token_to_timestamp_map_[*next_frame_token_] = value; + TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( + "media", "VideoFrameSubmitter", TRACE_ID_LOCAL(frame_token), value); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( + "media", "Pre-submit buffering", TRACE_ID_LOCAL(frame_token), value); + TRACE_EVENT_NESTABLE_ASYNC_END0("media", "Pre-submit buffering", + TRACE_ID_LOCAL(frame_token)); - if (begin_frame_ack.source_id == viz::BeginFrameArgs::kManualSourceId) - ignorable_submitted_frames_.insert(*next_frame_token_); + if (begin_frame_ack.frame_id.source_id == + viz::BeginFrameArgs::kManualSourceId) + ignorable_submitted_frames_.insert(frame_token); UMA_HISTOGRAM_TIMES("Media.VideoFrameSubmitter.PreSubmitBuffering", base::TimeTicks::Now() - value); } else { - TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1( - "media", "VideoFrameSubmitter", *next_frame_token_, - base::TimeTicks::Now(), "empty video frame?", !video_frame); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("media", "VideoFrameSubmitter", + TRACE_ID_LOCAL(frame_token), + "empty video frame?", !video_frame); } // We don't assume that the ack is marked as having damage. However, we're @@ -567,6 +591,8 @@ viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame( gfx::Transform()); if (video_frame) { + compositor_frame.metadata.content_color_usage = + video_frame->ColorSpace().GetContentColorUsage(); const bool is_opaque = media::IsOpaque(video_frame->format()); resource_provider_->AppendQuads(render_pass.get(), std::move(video_frame), rotation_, is_opaque); diff --git a/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.h b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.h index 0953c624df4..90207762377 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.h +++ b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter.h @@ -14,6 +14,7 @@ #include "base/time/time.h" #include "base/timer/timer.h" #include "cc/metrics/frame_sequence_tracker.h" +#include "cc/metrics/video_playback_roughness_reporter.h" #include "components/viz/client/shared_bitmap_reporter.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/common/resources/shared_bitmap.h" @@ -44,6 +45,7 @@ class PLATFORM_EXPORT VideoFrameSubmitter public viz::mojom::blink::CompositorFrameSinkClient { public: VideoFrameSubmitter(WebContextProviderCallback, + cc::PlaybackRoughnessReportingCallback, std::unique_ptr<VideoFrameResourceProvider>); ~VideoFrameSubmitter() override; @@ -127,6 +129,7 @@ class PLATFORM_EXPORT VideoFrameSubmitter // Helper method for creating viz::CompositorFrame. If |video_frame| is null // then the frame will be empty. viz::CompositorFrame CreateCompositorFrame( + uint32_t frame_token, const viz::BeginFrameAck& begin_frame_ack, scoped_refptr<media::VideoFrame> video_frame); @@ -172,9 +175,7 @@ class PLATFORM_EXPORT VideoFrameSubmitter viz::FrameTokenGenerator next_frame_token_; - // Timestamps indexed by frame token for histogram purposes. - using FrameTokenType = decltype(*std::declval<viz::FrameTokenGenerator>()); - base::flat_map<FrameTokenType, base::TimeTicks> frame_token_to_timestamp_map_; + std::unique_ptr<cc::VideoPlaybackRoughnessReporter> roughness_reporter_; base::OneShotTimer empty_frame_timer_; diff --git a/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc index 742b9d0cc98..8a772fda9b0 100644 --- a/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc +++ b/chromium/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc @@ -12,10 +12,12 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/memory/read_only_shared_memory_region.h" +#include "base/test/bind_test_util.h" #include "base/test/simple_test_tick_clock.h" #include "base/test/task_environment.h" #include "base/time/time.h" #include "cc/layers/video_frame_provider.h" +#include "cc/metrics/video_playback_roughness_reporter.h" #include "cc/test/layer_test_common.h" #include "cc/trees/layer_tree_settings.h" #include "cc/trees/task_runner_provider.h" @@ -37,6 +39,7 @@ using testing::_; using testing::AnyNumber; +using testing::Invoke; using testing::Return; using testing::StrictMock; @@ -169,11 +172,13 @@ class VideoFrameSubmitterTest : public testing::Test { task_environment_.RunUntilIdle(); } - void MakeSubmitter() { + void MakeSubmitter() { MakeSubmitter(base::DoNothing()); } + + void MakeSubmitter(cc::PlaybackRoughnessReportingCallback reporting_cb) { resource_provider_ = new StrictMock<MockVideoFrameResourceProvider>( context_provider_.get(), nullptr); submitter_ = std::make_unique<VideoFrameSubmitter>( - base::DoNothing(), + base::DoNothing(), reporting_cb, base::WrapUnique<MockVideoFrameResourceProvider>(resource_provider_)); submitter_->Initialize(video_frame_provider_.get()); @@ -307,8 +312,8 @@ TEST_F(VideoFrameSubmitterTest, StopRenderingSkipsUpdateCurrentFrame) { // No frames should be produced after StopRendering(). EXPECT_CALL(*sink_, DidNotProduceFrame(_)); - begin_frame_source_->CreateBeginFrameArgs(BEGINFRAME_FROM_HERE, - now_src_.get()); + args = begin_frame_source_->CreateBeginFrameArgs(BEGINFRAME_FROM_HERE, + now_src_.get()); submitter_->OnBeginFrame(args, {}); task_environment_.RunUntilIdle(); } @@ -498,6 +503,8 @@ TEST_F(VideoFrameSubmitterTest, RotationInformationPassedToResourceProvider) { EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _)); EXPECT_CALL(*resource_provider_, ReleaseFrameResources()); + args = begin_frame_source_->CreateBeginFrameArgs(BEGINFRAME_FROM_HERE, + now_src_.get()); submitter_->OnBeginFrame(args, {}); task_environment_.RunUntilIdle(); } @@ -640,6 +647,27 @@ TEST_F(VideoFrameSubmitterTest, RecreateCompositorFrameSinkAfterContextLost) { task_environment_.RunUntilIdle(); } +// Test that after context is lost, the CompositorFrameSink is recreated but the +// SurfaceEmbedder isn't even with software compositing. +TEST_F(VideoFrameSubmitterTest, + RecreateCompositorFrameSinkAfterContextLostSoftwareCompositing) { + MockEmbeddedFrameSinkProvider mock_embedded_frame_sink_provider; + mojo::Receiver<mojom::blink::EmbeddedFrameSinkProvider> + embedded_frame_sink_provider_binding(&mock_embedded_frame_sink_provider); + auto override = + mock_embedded_frame_sink_provider.CreateScopedOverrideMojoInterface( + &embedded_frame_sink_provider_binding); + + EXPECT_CALL(*resource_provider_, Initialize(_, _)); + EXPECT_CALL(mock_embedded_frame_sink_provider, ConnectToEmbedder(_, _)) + .Times(0); + EXPECT_CALL(mock_embedded_frame_sink_provider, CreateCompositorFrameSink_(_)) + .Times(1); + submitter_->OnContextLost(); + OnReceivedContextProvider(false, nullptr); + task_environment_.RunUntilIdle(); +} + // This test simulates a race condition in which the |video_frame_provider_| is // destroyed before OnReceivedContextProvider returns. TEST_F(VideoFrameSubmitterTest, StopUsingProviderDuringContextLost) { @@ -662,7 +690,7 @@ TEST_F(VideoFrameSubmitterTest, StopUsingProviderDuringContextLost) { } // Test the behaviour of the ChildLocalSurfaceIdAllocator instance. It checks -// that the LocalSurfaceId is propoerly set at creation and updated when the +// that the LocalSurfaceId is properly set at creation and updated when the // video frames change. TEST_F(VideoFrameSubmitterTest, FrameSizeChangeUpdatesLocalSurfaceId) { { @@ -893,6 +921,8 @@ TEST_F(VideoFrameSubmitterTest, NoDuplicateFramesOnBeginFrame) { .WillOnce(Return(true)); EXPECT_CALL(*video_frame_provider_, GetCurrentFrame()).WillOnce(Return(vf)); EXPECT_CALL(*sink_, DidNotProduceFrame(_)); + args = begin_frame_source_->CreateBeginFrameArgs(BEGINFRAME_FROM_HERE, + now_src_.get()); submitter_->OnBeginFrame(args, {}); task_environment_.RunUntilIdle(); } @@ -928,4 +958,66 @@ TEST_F(VideoFrameSubmitterTest, ZeroSizedFramesAreNotSubmitted) { task_environment_.RunUntilIdle(); } +// Check that given enough frames with wallclock duration and enough +// presentation feedback data, VideoFrameSubmitter will call the video roughness +// reporting callback. +TEST_F(VideoFrameSubmitterTest, ProcessTimingDetails) { + int fps = 24; + int reports = 0; + base::TimeDelta frame_duration = base::TimeDelta::FromSecondsD(1.0 / fps); + int frames_to_run = + (fps / 2) * + (cc::VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit + 1); + WTF::HashMap<uint32_t, viz::mojom::blink::FrameTimingDetailsPtr> + timing_details; + + MakeSubmitter( + base::BindLambdaForTesting([&](int frames, base::TimeDelta duration, + double roughness) { reports++; })); + EXPECT_CALL(*sink_, SetNeedsBeginFrame(true)); + submitter_->StartRendering(); + task_environment_.RunUntilIdle(); + EXPECT_TRUE(IsRendering()); + + auto sink_submit = [&](const viz::LocalSurfaceId&, + viz::CompositorFrame* frame) { + auto token = frame->metadata.frame_token; + viz::mojom::blink::FrameTimingDetailsPtr details = + viz::mojom::blink::FrameTimingDetails::New(); + details->presentation_feedback = + gfx::mojom::blink::PresentationFeedback::New(); + details->presentation_feedback->timestamp = + base::TimeTicks() + frame_duration * token; + timing_details.clear(); + timing_details.Set(token, std::move(details)); + }; + + EXPECT_CALL(*video_frame_provider_, UpdateCurrentFrame) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*video_frame_provider_, PutCurrentFrame).Times(AnyNumber()); + EXPECT_CALL(*sink_, DoSubmitCompositorFrame) + .WillRepeatedly(Invoke(sink_submit)); + EXPECT_CALL(*resource_provider_, AppendQuads).Times(AnyNumber()); + EXPECT_CALL(*resource_provider_, PrepareSendToParent).Times(AnyNumber()); + EXPECT_CALL(*resource_provider_, ReleaseFrameResources).Times(AnyNumber()); + + for (int i = 0; i < frames_to_run; i++) { + auto frame = media::VideoFrame::CreateFrame( + media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)), + gfx::Size(8, 8), i * frame_duration); + frame->metadata()->SetTimeDelta( + media::VideoFrameMetadata::WALLCLOCK_FRAME_DURATION, frame_duration); + EXPECT_CALL(*video_frame_provider_, GetCurrentFrame()) + .WillRepeatedly(Return(frame)); + + auto args = begin_frame_source_->CreateBeginFrameArgs(BEGINFRAME_FROM_HERE, + now_src_.get()); + submitter_->OnBeginFrame(args, std::move(timing_details)); + task_environment_.RunUntilIdle(); + AckSubmittedFrame(); + } + submitter_->StopRendering(); + EXPECT_EQ(reports, 1); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/BUILD.gn b/chromium/third_party/blink/renderer/platform/heap/BUILD.gn index c21068565d3..d868c461141 100644 --- a/chromium/third_party/blink/renderer/platform/heap/BUILD.gn +++ b/chromium/third_party/blink/renderer/platform/heap/BUILD.gn @@ -3,20 +3,39 @@ # found in the LICENSE file. import("//build/buildflag_header.gni") +import("//build/config/compiler/compiler.gni") import("//build/config/jumbo.gni") +import("//build/config/sanitizers/sanitizers.gni") import("//testing/test.gni") +import("//third_party/blink/public/public_features.gni") import("//third_party/blink/renderer/platform/platform.gni") -declare_args() { - # Enables heap verification. - enable_blink_heap_verification = false -} - buildflag_header("blink_heap_buildflags") { header = "heap_buildflags.h" header_dir = "third_party/blink/renderer/platform/heap" - flags = [ "BLINK_HEAP_VERIFICATION=$enable_blink_heap_verification" ] + flags = [ + "BLINK_HEAP_VERIFICATION=$enable_blink_heap_verification", + "BLINK_HEAP_YOUNG_GENERATION=$enable_blink_heap_young_generation", + ] +} + +jumbo_source_set("heap_unsanitized") { + if (is_asan) { + configs -= [ "//build/config/sanitizers:default_sanitizer_flags" ] + } + configs += + [ "//third_party/blink/renderer/platform:blink_platform_implementation" ] + + sources = [ + "unsanitized_atomic.cc", + "unsanitized_atomic.h", + ] + + deps = [ + "//base", + "//third_party/blink/renderer/platform/wtf", + ] } blink_platform_sources("heap") { @@ -28,6 +47,9 @@ blink_platform_sources("heap") { "blink_gc_memory_dump_provider.h", "cancelable_task_scheduler.cc", "cancelable_task_scheduler.h", + "collection_support/heap_hash_table_backing.h", + "collection_support/heap_linked_stack.h", + "collection_support/heap_vector_backing.h", "disallow_new_wrapper.h", "finalizer_traits.h", "garbage_collected.h", @@ -41,7 +63,6 @@ blink_platform_sources("heap") { "heap_allocator.h", "heap_compact.cc", "heap_compact.h", - "heap_linked_stack.h", "heap_page.cc", "heap_page.h", "heap_stats_collector.cc", @@ -83,10 +104,16 @@ blink_platform_sources("heap") { ":blink_heap_buildflags", "//base", "//third_party/blink/renderer/platform:make_platform_generated", + "//third_party/blink/renderer/platform/heap:heap_unsanitized", "//third_party/blink/renderer/platform/heap/asm", "//third_party/icu", "//v8", ] + + if (!is_debug && !optimize_for_size) { + configs -= [ "//build/config/compiler:default_optimization" ] + configs += [ "//build/config/compiler:optimize_max" ] + } } jumbo_source_set("test_support") { @@ -98,6 +125,7 @@ jumbo_source_set("test_support") { ] deps = [ + ":blink_heap_buildflags", "//testing/gtest", "//third_party/blink/public/mojom:mojom_platform_blink_headers", "//third_party/blink/renderer/platform:bindings_buildflags", @@ -105,9 +133,7 @@ jumbo_source_set("test_support") { } test("blink_heap_unittests") { - deps = [ - ":blink_heap_unittests_sources", - ] + deps = [ ":blink_heap_unittests_sources" ] if (is_android) { deps += [ "//base:base_java", @@ -124,6 +150,8 @@ jumbo_source_set("blink_heap_unittests_sources") { "../testing/run_all_tests.cc", "blink_gc_memory_dump_provider_test.cc", "cancelable_task_scheduler_test.cc", + "card_table_test.cc", + "collection_support/heap_linked_stack_test.cc", "concurrent_marking_test.cc", "gc_info_test.cc", "heap_compact_test.cc", @@ -142,6 +170,10 @@ jumbo_source_set("blink_heap_unittests_sources") { "write_barrier_perftest.cc", ] + if (enable_blink_heap_young_generation) { + sources += [ "minor_gc_test.cc" ] + } + configs += [ "//third_party/blink/renderer/platform/wtf:wtf_config", "//third_party/blink/renderer:config", diff --git a/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md b/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md index f1415c771a5..eb532cd733c 100644 --- a/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md +++ b/chromium/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md @@ -201,7 +201,7 @@ Especially, avoid defining a pre-finalizer in a class that can be allocated a lo ### STACK_ALLOCATED -Class level annotation that should be used if the object is only stack allocated; it disallows use of `operator new`. Any fields holding garbage-collected objects should use `Member<T>` references, but you do not need to define a `Trace()` method as they are on the stack, and automatically traced and kept alive should a conservative GC be required. +Class level annotation that should be used if the object is only stack allocated; it disallows use of `operator new`. Any fields holding garbage-collected objects should use regular pointers or references and you do not need to define a `Trace()` method as they are on the stack, and automatically traced and kept alive should a conservative GC be required. Classes with this annotation do not need a `Trace()` method and must not inherit a on-heap garbage collected class. @@ -275,7 +275,7 @@ You need to trace every `Member<T>` and `WeakMember<T>` in your class. See [Trac Unlike 'Member<T>', 'UntracedMember<T>' will not keep an object alive. However, unlike 'WeakMember<T>', the reference will not be cleared (i.e. set to 'nullptr') if the referenced object dies. Furthermore, class fields of type 'UntracedMember<T>' should not be traced by the class' tracing method. -Users should use 'UntracedMember<T>' when implementing [custom weakness semantics](#Custom weak callbacks). +Users should use 'UntracedMember<T>' when implementing [custom weakness semantics](#Custom-weak-callbacks). ### Persistent, WeakPersistent, CrossThreadPersistent, CrossThreadWeakPersistent diff --git a/chromium/third_party/blink/renderer/platform/heap/DEPS b/chromium/third_party/blink/renderer/platform/heap/DEPS index eda4c2ea500..5814f38e1e9 100644 --- a/chromium/third_party/blink/renderer/platform/heap/DEPS +++ b/chromium/third_party/blink/renderer/platform/heap/DEPS @@ -12,8 +12,9 @@ include_rules = [ "+base/strings/string_number_conversions.h", "+base/synchronization/lock.h", "+base/task_runner.h", + "+base/task/post_job.h", "+base/template_util.h", - "+testing/perf/perf_test.h", + "+testing/perf/perf_result_reporter.h", "+third_party/blink/renderer/platform/bindings", "+third_party/blink/renderer/platform/instrumentation/histogram.h", diff --git a/chromium/third_party/blink/renderer/platform/heap/OWNERS b/chromium/third_party/blink/renderer/platform/heap/OWNERS index bca3233e2e4..dafb539f11d 100644 --- a/chromium/third_party/blink/renderer/platform/heap/OWNERS +++ b/chromium/third_party/blink/renderer/platform/heap/OWNERS @@ -2,6 +2,7 @@ bikineev@chromium.org haraken@chromium.org kouhei@chromium.org mlippautz@chromium.org +omerkatz@chromium.org # TEAM: oilpan-reviews@chromium.org # COMPONENT: Blink>MemoryAllocator>GarbageCollection diff --git a/chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn b/chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn index 8aa2cbe249d..fe44daf27a5 100644 --- a/chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn +++ b/chromium/third_party/blink/renderer/platform/heap/asm/BUILD.gn @@ -8,9 +8,7 @@ if (current_cpu == "x86" || current_cpu == "x64") { nasm_assemble("asm") { assert(current_cpu == "x86" || current_cpu == "x64") - sources = [ - "SaveRegisters_x86.asm", - ] + sources = [ "SaveRegisters_x86.asm" ] defines = [] if (is_mac) { @@ -31,25 +29,15 @@ if (current_cpu == "x86" || current_cpu == "x64") { } else { # current_cpu == "x86" || current_cpu == "x64" source_set("asm") { if (current_cpu == "arm") { - sources = [ - "SaveRegisters_arm.S", - ] + sources = [ "SaveRegisters_arm.S" ] } else if (current_cpu == "arm64") { - sources = [ - "SaveRegisters_arm64.S", - ] + sources = [ "SaveRegisters_arm64.S" ] } else if (current_cpu == "mipsel") { - sources = [ - "SaveRegisters_mips.S", - ] + sources = [ "SaveRegisters_mips.S" ] } else if (current_cpu == "mips64el") { - sources = [ - "SaveRegisters_mips64.S", - ] + sources = [ "SaveRegisters_mips64.S" ] } else if (current_cpu == "ppc64") { - sources = [ - "SaveRegisters_ppc64.S", - ] + sources = [ "SaveRegisters_ppc64.S" ] } if (current_cpu == "arm") { diff --git a/chromium/third_party/blink/renderer/platform/heap/blink_gc.h b/chromium/third_party/blink/renderer/platform/heap/blink_gc.h index a1ba0e53a23..ccc1bfb0443 100644 --- a/chromium/third_party/blink/renderer/platform/heap/blink_gc.h +++ b/chromium/third_party/blink/renderer/platform/heap/blink_gc.h @@ -19,17 +19,18 @@ class MarkingVisitor; class Visitor; using Address = uint8_t*; +using ConstAddress = const uint8_t*; using FinalizationCallback = void (*)(void*); -using VisitorCallback = void (*)(Visitor*, void*); -using MarkingVisitorCallback = void (*)(MarkingVisitor*, void*); +using VisitorCallback = void (*)(Visitor*, const void*); +using MarkingVisitorCallback = void (*)(MarkingVisitor*, const void*); using TraceCallback = VisitorCallback; -using WeakCallback = void (*)(const WeakCallbackInfo&, void*); +using WeakCallback = void (*)(const WeakCallbackInfo&, const void*); using EphemeronCallback = VisitorCallback; // Simple alias to avoid heap compaction type signatures turning into // a sea of generic |void*|s. -using MovableReference = void*; +using MovableReference = const void*; // Heap compaction supports registering callbacks that are to be invoked // when an object is moved during compaction. This is to support internal @@ -64,6 +65,11 @@ class PLATFORM_EXPORT BlinkGC final { STATIC_ONLY(BlinkGC); public: + // CollectionType represents generational collection. kMinor collects objects + // in the young generation (i.e. allocated since the previous collection + // cycle, since we use sticky bits), kMajor collects the entire heap. + enum class CollectionType { kMinor, kMajor }; + // When garbage collecting we need to know whether or not there // can be pointers to Blink GC managed objects on the stack for // each thread. When threads reach a safe point they record diff --git a/chromium/third_party/blink/renderer/platform/heap/cancelable_task_scheduler_test.cc b/chromium/third_party/blink/renderer/platform/heap/cancelable_task_scheduler_test.cc index 23df65f74ed..97f4c8bf38d 100644 --- a/chromium/third_party/blink/renderer/platform/heap/cancelable_task_scheduler_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/cancelable_task_scheduler_test.cc @@ -25,8 +25,6 @@ class ParallelTaskRunner : public base::TaskRunner { return true; } - bool RunsTasksInCurrentSequence() const override { return false; } - void RunUntilIdle() {} }; diff --git a/chromium/third_party/blink/renderer/platform/heap/card_table_test.cc b/chromium/third_party/blink/renderer/platform/heap/card_table_test.cc new file mode 100644 index 00000000000..a4620eadf0a --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/heap/card_table_test.cc @@ -0,0 +1,181 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <vector> + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/heap/heap_page.h" +#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" + +namespace blink { + +namespace { + +class BaseObject : public GarbageCollected<BaseObject> { + public: + size_t CardNumber() const; + void Trace(Visitor*) {} +}; + +} // namespace + +class CardTableTest : public TestSupportingGC { + public: + static constexpr size_t kCardSize = NormalPage::CardTable::kCardSize; + + CardTableTest() { ClearOutOldGarbage(); } + + static void CheckObjects(const std::vector<BaseObject*>& objects, + const NormalPage& page) { + page.IterateCardTable([&objects](HeapObjectHeader* header) { + const BaseObject* object = + reinterpret_cast<BaseObject*>(header->Payload()); + auto it = std::find(objects.begin(), objects.end(), object); + EXPECT_NE(it, objects.end()); + }); + } + + static void MarkCardForObject(BaseObject* object) { + NormalPage* page = static_cast<NormalPage*>(PageFromObject(object)); + page->MarkCard(reinterpret_cast<Address>(object)); + } + + static bool IsCardMarked(const NormalPage& page, size_t card_number) { + return page.card_table_.IsMarked(card_number); + } + + static size_t ObjectsInCard(const NormalPage& page, size_t card_number) { + const NormalPage::CardTable& cards = page.card_table_; + + size_t objects = 0; + Address card_begin = + RoundToBlinkPageStart(page.GetAddress()) + (card_number * kCardSize); + const Address card_end = card_begin + kCardSize; + if (card_number == cards.begin().index) { + // First card is misaligned due to padding. + card_begin = page.Payload(); + } + + page.ArenaForNormalPage()->MakeConsistentForGC(); + + page.IterateOnCard( + [card_begin, card_end, &objects](HeapObjectHeader* header) { + const Address header_address = reinterpret_cast<Address>(header); + if (header_address < card_begin) { + const Address next_header_address = header_address + header->size(); + EXPECT_GT(next_header_address, card_begin); + } else { + objects++; + EXPECT_LT(header_address, card_end); + } + }, + card_number); + + return objects; + } + + static size_t MarkedObjects(const NormalPage& page) { + size_t objects = 0; + page.ArenaForNormalPage()->MakeConsistentForGC(); + page.IterateCardTable([&objects](HeapObjectHeader*) { ++objects; }); + return objects; + } + + static void ClearCardTable(NormalPage& page) { page.card_table_.Clear(); } +}; + +namespace { + +size_t BaseObject::CardNumber() const { + return (reinterpret_cast<uintptr_t>(this) & kBlinkPageOffsetMask) / + CardTableTest::kCardSize; +} + +template <size_t Size> +class Object : public BaseObject { + private: + uint8_t array[Size]; +}; + +} // namespace + +TEST_F(CardTableTest, Empty) { + BaseObject* obj = MakeGarbageCollected<BaseObject>(); + EXPECT_EQ(0u, MarkedObjects(*static_cast<NormalPage*>(PageFromObject(obj)))); +} + +TEST_F(CardTableTest, SingleObjectOnFirstCard) { + BaseObject* obj = MakeGarbageCollected<BaseObject>(); + MarkCardForObject(obj); + + const NormalPage& page = *static_cast<NormalPage*>(PageFromObject(obj)); + const size_t card_number = obj->CardNumber(); + EXPECT_TRUE(IsCardMarked(page, card_number)); + + const size_t objects = ObjectsInCard(page, card_number); + EXPECT_EQ(1u, objects); +} + +TEST_F(CardTableTest, SingleObjectOnSecondCard) { + MakeGarbageCollected<Object<kCardSize>>(); + BaseObject* obj = MakeGarbageCollected<Object<kCardSize>>(); + MarkCardForObject(obj); + + const NormalPage& page = *static_cast<NormalPage*>(PageFromObject(obj)); + const size_t card_number = obj->CardNumber(); + EXPECT_TRUE(IsCardMarked(page, card_number)); + + const size_t objects = ObjectsInCard(page, card_number); + EXPECT_EQ(1u, objects); +} + +TEST_F(CardTableTest, TwoObjectsOnSecondCard) { + static constexpr size_t kHalfCardSize = kCardSize / 2; + MakeGarbageCollected<Object<kHalfCardSize>>(); + MakeGarbageCollected<Object<kHalfCardSize>>(); + // The card on which 'obj' resides is guaranteed to have two objects, either + // the previously allocated one or the following one. + BaseObject* obj = MakeGarbageCollected<Object<kHalfCardSize>>(); + MakeGarbageCollected<Object<kHalfCardSize>>(); + MarkCardForObject(obj); + + const NormalPage& page = *static_cast<NormalPage*>(PageFromObject(obj)); + const size_t card_number = obj->CardNumber(); + EXPECT_TRUE(IsCardMarked(page, card_number)); + + const size_t objects = ObjectsInCard(page, card_number); + EXPECT_EQ(2u, objects); +} + +TEST_F(CardTableTest, Clear) { + MakeGarbageCollected<Object<kCardSize>>(); + MakeGarbageCollected<Object<kCardSize / 2>>(); + BaseObject* obj = MakeGarbageCollected<Object<kCardSize / 2>>(); + MarkCardForObject(obj); + + NormalPage& page = *static_cast<NormalPage*>(PageFromObject(obj)); + ClearCardTable(page); + + const size_t card_number = obj->CardNumber(); + EXPECT_FALSE(IsCardMarked(page, card_number)); +} + +TEST_F(CardTableTest, MultipleObjects) { + std::vector<BaseObject*> objects; + BaseObject* obj = MakeGarbageCollected<Object<kCardSize>>(); + BasePage* const first_page = PageFromObject(obj); + BasePage* new_page = first_page; + + while (first_page == new_page) { + objects.push_back(obj); + MarkCardForObject(obj); + + obj = MakeGarbageCollected<Object<kCardSize>>(); + new_page = PageFromObject(obj); + } + + CheckObjects(objects, *static_cast<NormalPage*>(first_page)); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_hash_table_backing.h b/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_hash_table_backing.h new file mode 100644 index 00000000000..8b91764b10d --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_hash_table_backing.h @@ -0,0 +1,315 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_TABLE_BACKING_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_TABLE_BACKING_H_ + +#include "third_party/blink/renderer/platform/heap/heap_page.h" +#include "third_party/blink/renderer/platform/heap/threading_traits.h" +#include "third_party/blink/renderer/platform/heap/trace_traits.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +namespace blink { + +template <typename Table> +class HeapHashTableBacking { + DISALLOW_NEW(); + IS_GARBAGE_COLLECTED_TYPE(); + + public: + template <typename Backing> + static void* AllocateObject(size_t size); + + static void Finalize(void* pointer); + void FinalizeGarbageCollectedObject() { Finalize(this); } +}; + +template <typename Table> +struct ThreadingTrait<HeapHashTableBacking<Table>> { + STATIC_ONLY(ThreadingTrait); + using Key = typename Table::KeyType; + using Value = typename Table::ValueType; + static const ThreadAffinity kAffinity = + (ThreadingTrait<Key>::kAffinity == kMainThreadOnly) && + (ThreadingTrait<Value>::kAffinity == kMainThreadOnly) + ? kMainThreadOnly + : kAnyThread; +}; + +// static +template <typename Table> +template <typename Backing> +void* HeapHashTableBacking<Table>::AllocateObject(size_t size) { + ThreadState* state = + ThreadStateFor<ThreadingTrait<Backing>::kAffinity>::GetState(); + DCHECK(state->IsAllocationAllowed()); + const char* type_name = WTF_HEAP_PROFILER_TYPE_NAME(Backing); + return state->Heap().AllocateOnArenaIndex( + state, size, BlinkGC::kHashTableArenaIndex, GCInfoTrait<Backing>::Index(), + type_name); +} + +template <typename Table> +void HeapHashTableBacking<Table>::Finalize(void* pointer) { + using Value = typename Table::ValueType; + static_assert( + !std::is_trivially_destructible<Value>::value, + "Finalization of trivially destructible classes should not happen."); + HeapObjectHeader* header = HeapObjectHeader::FromPayload(pointer); + // Use the payload size as recorded by the heap to determine how many + // elements to finalize. + size_t length = header->PayloadSize() / sizeof(Value); + Value* table = reinterpret_cast<Value*>(pointer); + for (unsigned i = 0; i < length; ++i) { + if (!Table::IsEmptyOrDeletedBucket(table[i])) + table[i].~Value(); + } +} + +template <typename Table> +struct MakeGarbageCollectedTrait<HeapHashTableBacking<Table>> { + static HeapHashTableBacking<Table>* Call(size_t num_elements) { + CHECK_GT(num_elements, 0u); + void* memory = HeapHashTableBacking<Table>::template AllocateObject< + HeapHashTableBacking<Table>>(num_elements * + sizeof(typename Table::ValueType)); + HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory); + // Placement new as regular operator new() is deleted. + HeapHashTableBacking<Table>* object = + ::new (memory) HeapHashTableBacking<Table>(); + header->MarkFullyConstructed<HeapObjectHeader::AccessMode::kAtomic>(); + return object; + } +}; + +template <typename Table> +struct FinalizerTrait<HeapHashTableBacking<Table>> { + STATIC_ONLY(FinalizerTrait); + static const bool kNonTrivialFinalizer = + !std::is_trivially_destructible<typename Table::ValueType>::value; + static void Finalize(void* obj) { + internal::FinalizerTraitImpl<HeapHashTableBacking<Table>, + kNonTrivialFinalizer>::Finalize(obj); + } +}; + +// The trace trait for the heap hashtable backing is used when we find a +// direct pointer to the backing from the conservative stack scanner. This +// normally indicates that there is an ongoing iteration over the table, and so +// we disable weak processing of table entries. When the backing is found +// through the owning hash table we mark differently, in order to do weak +// processing. +template <typename Table> +struct TraceTrait<HeapHashTableBacking<Table>> { + STATIC_ONLY(TraceTrait); + using Backing = HeapHashTableBacking<Table>; + using ValueType = typename Table::ValueTraits::TraitType; + using Traits = typename Table::ValueTraits; + + public: + static TraceDescriptor GetTraceDescriptor(const void* self) { + return {self, Trace<WTF::kNoWeakHandling>}; + } + + static TraceDescriptor GetWeakTraceDescriptor(const void* self) { + return GetWeakTraceDescriptorImpl<ValueType>::GetWeakTraceDescriptor(self); + } + + template <WTF::WeakHandlingFlag WeakHandling = WTF::kNoWeakHandling> + static void Trace(Visitor* visitor, const void* self) { + if (visitor->ConcurrentTracingBailOut({self, &Trace})) + return; + + static_assert(WTF::IsTraceableInCollectionTrait<Traits>::value || + WTF::IsWeak<ValueType>::value, + "T should not be traced"); + WTF::TraceInCollectionTrait<WeakHandling, Backing, void>::Trace(visitor, + self); + } + + private: + template <typename ValueType> + struct GetWeakTraceDescriptorImpl { + static TraceDescriptor GetWeakTraceDescriptor(const void* backing) { + return {backing, nullptr}; + } + }; + + template <typename K, typename V> + struct GetWeakTraceDescriptorImpl<WTF::KeyValuePair<K, V>> { + static TraceDescriptor GetWeakTraceDescriptor(const void* backing) { + return GetWeakTraceDescriptorKVPImpl<K, V>::GetWeakTraceDescriptor( + backing); + } + + template <typename KeyType, + typename ValueType, + bool ephemeron_semantics = (WTF::IsWeak<KeyType>::value && + !WTF::IsWeak<ValueType>::value && + WTF::IsTraceable<ValueType>::value) || + (WTF::IsWeak<ValueType>::value && + !WTF::IsWeak<KeyType>::value && + WTF::IsTraceable<KeyType>::value)> + struct GetWeakTraceDescriptorKVPImpl { + static TraceDescriptor GetWeakTraceDescriptor(const void* backing) { + return {backing, nullptr}; + } + }; + + template <typename KeyType, typename ValueType> + struct GetWeakTraceDescriptorKVPImpl<KeyType, ValueType, true> { + static TraceDescriptor GetWeakTraceDescriptor(const void* backing) { + return {backing, Trace<WTF::kWeakHandling>}; + } + }; + }; +}; + +} // namespace blink + +namespace WTF { + +// This trace method is for tracing a HashTableBacking either through regular +// tracing (via the relevant TraceTraits) or when finding a HashTableBacking +// through conservative stack scanning (which will treat all references in the +// backing strongly). +template <WTF::WeakHandlingFlag WeakHandling, typename Table> +struct TraceHashTableBackingInCollectionTrait { + using Value = typename Table::ValueType; + using Traits = typename Table::ValueTraits; + + static bool Trace(blink::Visitor* visitor, const void* self) { + static_assert(IsTraceableInCollectionTrait<Traits>::value || + WTF::IsWeak<Value>::value, + "Table should not be traced"); + const Value* array = reinterpret_cast<const Value*>(self); + blink::HeapObjectHeader* header = + blink::HeapObjectHeader::FromPayload(self); + // Use the payload size as recorded by the heap to determine how many + // elements to trace. + size_t length = header->PayloadSize() / sizeof(Value); + const bool is_concurrent = visitor->IsConcurrent(); + for (size_t i = 0; i < length; ++i) { + // If tracing concurrently, use a concurrent-safe version of + // IsEmptyOrDeletedBucket (check performed on a local copy instead + // of directly on the bucket). + if (is_concurrent) { + if (!HashTableHelper<Value, typename Table::ExtractorType, + typename Table::KeyTraitsType>:: + IsEmptyOrDeletedBucketSafe(array[i])) { + blink::TraceCollectionIfEnabled<WeakHandling, Value, Traits>::Trace( + visitor, &array[i]); + } + } else { + if (!HashTableHelper<Value, typename Table::ExtractorType, + typename Table::KeyTraitsType>:: + IsEmptyOrDeletedBucket(array[i])) { + blink::TraceCollectionIfEnabled<WeakHandling, Value, Traits>::Trace( + visitor, &array[i]); + } + } + } + return false; + } +}; + +template <typename Table> +struct TraceInCollectionTrait<kNoWeakHandling, + blink::HeapHashTableBacking<Table>, + void> { + static bool Trace(blink::Visitor* visitor, const void* self) { + return TraceHashTableBackingInCollectionTrait<kNoWeakHandling, + Table>::Trace(visitor, self); + } +}; + +template <typename Table> +struct TraceInCollectionTrait<kWeakHandling, + blink::HeapHashTableBacking<Table>, + void> { + static bool Trace(blink::Visitor* visitor, const void* self) { + return TraceHashTableBackingInCollectionTrait<kWeakHandling, Table>::Trace( + visitor, self); + } +}; + +// Key value pairs, as used in HashMap. To disambiguate template choice we have +// to have two versions, first the one with no special weak handling, then the +// one with weak handling. +template <typename Key, typename Value, typename Traits> +struct TraceInCollectionTrait<kNoWeakHandling, + KeyValuePair<Key, Value>, + Traits> { + using EphemeronHelper = + blink::EphemeronKeyValuePair<Key, + Value, + typename Traits::KeyTraits, + typename Traits::ValueTraits>; + + static bool Trace(blink::Visitor* visitor, + const KeyValuePair<Key, Value>& self) { + if (WTF::IsWeak<Key>::value != WTF::IsWeak<Value>::value) { + // Strongification of Weak/Strong and Strong/Weak. + EphemeronHelper helper(&self.key, &self.value); + visitor->VisitEphemeronKeyValuePair( + helper.key, helper.value, + blink::TraceCollectionIfEnabled< + kNoWeakHandling, typename EphemeronHelper::KeyType, + typename EphemeronHelper::KeyTraits>::Trace, + blink::TraceCollectionIfEnabled< + kNoWeakHandling, typename EphemeronHelper::ValueType, + typename EphemeronHelper::ValueTraits>::Trace); + } else { + // Strongification of Strong/Strong or Weak/Weak. Order does not matter + // here. + blink::TraceCollectionIfEnabled< + kNoWeakHandling, Key, typename Traits::KeyTraits>::Trace(visitor, + &self.key); + blink::TraceCollectionIfEnabled< + kNoWeakHandling, Value, + typename Traits::ValueTraits>::Trace(visitor, &self.value); + } + return false; + } +}; + +template <typename Key, typename Value, typename Traits> +struct TraceInCollectionTrait<kWeakHandling, KeyValuePair<Key, Value>, Traits> { + using EphemeronHelper = + blink::EphemeronKeyValuePair<Key, + Value, + typename Traits::KeyTraits, + typename Traits::ValueTraits>; + + static bool IsAlive(const KeyValuePair<Key, Value>& self) { + // Needed for Weak/Weak, Strong/Weak (reverse ephemeron), and Weak/Strong + // (ephemeron). Order of invocation does not matter as tracing weak key or + // value does not have any side effects. + return blink::TraceCollectionIfEnabled< + WeakHandlingTrait<Key>::value, Key, + typename Traits::KeyTraits>::IsAlive(self.key) && + blink::TraceCollectionIfEnabled< + WeakHandlingTrait<Value>::value, Value, + typename Traits::ValueTraits>::IsAlive(self.value); + } + + static bool Trace(blink::Visitor* visitor, + const KeyValuePair<Key, Value>& self) { + EphemeronHelper helper(&self.key, &self.value); + return visitor->VisitEphemeronKeyValuePair( + helper.key, helper.value, + blink::TraceCollectionIfEnabled< + WeakHandlingTrait<typename EphemeronHelper::KeyType>::value, + typename EphemeronHelper::KeyType, + typename EphemeronHelper::KeyTraits>::Trace, + blink::TraceCollectionIfEnabled< + WeakHandlingTrait<typename EphemeronHelper::ValueType>::value, + typename EphemeronHelper::ValueType, + typename EphemeronHelper::ValueTraits>::Trace); + } +}; + +} // namespace WTF + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_TABLE_BACKING_H_ diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_linked_stack.h b/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h index b1ad5a367cd..c0b4e95350a 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_linked_stack.h +++ b/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h @@ -28,10 +28,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_LINKED_STACK_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_LINKED_STACK_H_ +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_LINKED_STACK_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_LINKED_STACK_H_ #include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/heap/heap_allocator.h" #include "third_party/blink/renderer/platform/heap/visitor.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -44,36 +45,40 @@ namespace blink { // (now removed: https://codereview.chromium.org/2761853003/). // See https://codereview.chromium.org/17314010 for the original use-case. template <typename T> -class HeapLinkedStack : public GarbageCollected<HeapLinkedStack<T>> { +class HeapLinkedStack final : public GarbageCollected<HeapLinkedStack<T>> { public: - HeapLinkedStack() : size_(0) {} + static void CheckType() { + static_assert(internal::IsMember<T>, + "HeapLinkedStack supports only Member."); + } - bool IsEmpty(); + HeapLinkedStack() { CheckType(); } - void Push(const T&); - const T& Peek(); - void Pop(); + inline size_t size() const; + inline bool IsEmpty() const; - size_t size(); + inline void Push(const T&); + inline const T& Peek() const; + inline void Pop(); - void Trace(blink::Visitor* visitor) { - for (Node* current = head_; current; current = current->next_) - visitor->Trace(current); - } + void Trace(Visitor* visitor) { visitor->Trace(head_); } private: - class Node : public GarbageCollected<Node> { + class Node final : public GarbageCollected<Node> { public: - Node(const T&, Node* next); + Node(const T&, Node*); - void Trace(blink::Visitor* visitor) { visitor->Trace(data_); } + void Trace(Visitor* visitor) { + visitor->Trace(data_); + visitor->Trace(next_); + } T data_; Member<Node> next_; }; Member<Node> head_; - size_t size_; + size_t size_ = 0; }; template <typename T> @@ -81,23 +86,23 @@ HeapLinkedStack<T>::Node::Node(const T& data, Node* next) : data_(data), next_(next) {} template <typename T> -inline bool HeapLinkedStack<T>::IsEmpty() { +bool HeapLinkedStack<T>::IsEmpty() const { return !head_; } template <typename T> -inline void HeapLinkedStack<T>::Push(const T& data) { +void HeapLinkedStack<T>::Push(const T& data) { head_ = MakeGarbageCollected<Node>(data, head_); ++size_; } template <typename T> -inline const T& HeapLinkedStack<T>::Peek() { +const T& HeapLinkedStack<T>::Peek() const { return head_->data_; } template <typename T> -inline void HeapLinkedStack<T>::Pop() { +void HeapLinkedStack<T>::Pop() { DCHECK(head_); DCHECK(size_); head_ = head_->next_; @@ -105,10 +110,10 @@ inline void HeapLinkedStack<T>::Pop() { } template <typename T> -inline size_t HeapLinkedStack<T>::size() { +size_t HeapLinkedStack<T>::size() const { return size_; } } // namespace blink -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_LINKED_STACK_H_ +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_LINKED_STACK_H_ diff --git a/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack_test.cc b/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack_test.cc new file mode 100644 index 00000000000..27b3f0dbf06 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack_test.cc @@ -0,0 +1,43 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h" +#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" + +namespace blink { + +namespace { +class HeapLinkedStackTest : public TestSupportingGC {}; +} // namespace + +TEST_F(HeapLinkedStackTest, PushPop) { + using Stack = HeapLinkedStack<Member<IntegerObject>>; + + ClearOutOldGarbage(); + IntegerObject::destructor_calls = 0; + + Stack* stack = MakeGarbageCollected<Stack>(); + + constexpr wtf_size_t kStackSize = 10; + + for (wtf_size_t i = 0; i < kStackSize; i++) + stack->Push(MakeGarbageCollected<IntegerObject>(i)); + + ConservativelyCollectGarbage(); + EXPECT_EQ(0, IntegerObject::destructor_calls); + EXPECT_EQ(kStackSize, stack->size()); + while (!stack->IsEmpty()) { + EXPECT_EQ(stack->size() - 1, static_cast<size_t>(stack->Peek()->Value())); + stack->Pop(); + } + + Persistent<Stack> holder = stack; + + PreciselyCollectGarbage(); + EXPECT_EQ(kStackSize, static_cast<size_t>(IntegerObject::destructor_calls)); + EXPECT_EQ(0u, holder->size()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_vector_backing.h b/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_vector_backing.h new file mode 100644 index 00000000000..eeba5dbc259 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/heap/collection_support/heap_vector_backing.h @@ -0,0 +1,203 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_VECTOR_BACKING_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_VECTOR_BACKING_H_ + +#include "base/logging.h" +#include "third_party/blink/renderer/platform/heap/finalizer_traits.h" +#include "third_party/blink/renderer/platform/heap/gc_info.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/heap/thread_state.h" +#include "third_party/blink/renderer/platform/heap/threading_traits.h" +#include "third_party/blink/renderer/platform/heap/trace_traits.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +template <typename T, typename Traits = WTF::VectorTraits<T>> +class HeapVectorBacking final { + DISALLOW_NEW(); + IS_GARBAGE_COLLECTED_TYPE(); + + public: + template <typename Backing> + static void* AllocateObject(size_t size) { + ThreadState* state = + ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState(); + DCHECK(state->IsAllocationAllowed()); + const char* type_name = WTF_HEAP_PROFILER_TYPE_NAME(Backing); + return state->Heap().AllocateOnArenaIndex( + state, size, BlinkGC::kVectorArenaIndex, GCInfoTrait<Backing>::Index(), + type_name); + } + + static void Finalize(void* pointer); + void FinalizeGarbageCollectedObject() { Finalize(this); } +}; + +template <typename T, typename Traits> +void HeapVectorBacking<T, Traits>::Finalize(void* pointer) { + static_assert(Traits::kNeedsDestruction, + "Only vector buffers with items requiring destruction should " + "be finalized"); + static_assert( + Traits::kCanClearUnusedSlotsWithMemset || std::is_polymorphic<T>::value, + "HeapVectorBacking doesn't support objects that cannot be cleared as " + "unused with memset or don't have a vtable"); + + static_assert( + !std::is_trivially_destructible<T>::value, + "Finalization of trivially destructible classes should not happen."); + HeapObjectHeader* header = HeapObjectHeader::FromPayload(pointer); + // Use the payload size as recorded by the heap to determine how many + // elements to finalize. + size_t length = header->PayloadSize() / sizeof(T); + char* payload = static_cast<char*>(pointer); +#ifdef ANNOTATE_CONTIGUOUS_CONTAINER + ANNOTATE_CHANGE_SIZE(payload, length * sizeof(T), 0, length * sizeof(T)); +#endif + // As commented above, HeapVectorBacking calls finalizers for unused slots + // (which are already zeroed out). + if (std::is_polymorphic<T>::value) { + for (unsigned i = 0; i < length; ++i) { + char* element = payload + i * sizeof(T); + if (blink::VTableInitialized(element)) + reinterpret_cast<T*>(element)->~T(); + } + } else { + T* buffer = reinterpret_cast<T*>(payload); + for (unsigned i = 0; i < length; ++i) + buffer[i].~T(); + } +} + +template <typename T> +struct MakeGarbageCollectedTrait<HeapVectorBacking<T>> { + static HeapVectorBacking<T>* Call(size_t num_elements) { + CHECK_GT(num_elements, 0u); + void* memory = + HeapVectorBacking<T>::template AllocateObject<HeapVectorBacking<T>>( + num_elements * sizeof(T)); + HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory); + // Placement new as regular operator new() is deleted. + HeapVectorBacking<T>* object = ::new (memory) HeapVectorBacking<T>(); + header->MarkFullyConstructed<HeapObjectHeader::AccessMode::kAtomic>(); + return object; + } +}; + +template <typename T, typename Traits> +struct FinalizerTrait<HeapVectorBacking<T, Traits>> { + STATIC_ONLY(FinalizerTrait); + static const bool kNonTrivialFinalizer = Traits::kNeedsDestruction; + static void Finalize(void* obj) { + internal::FinalizerTraitImpl<HeapVectorBacking<T, Traits>, + kNonTrivialFinalizer>::Finalize(obj); + } +}; + +template <typename T, typename Traits> +struct ThreadingTrait<HeapVectorBacking<T, Traits>> { + STATIC_ONLY(ThreadingTrait); + static const ThreadAffinity kAffinity = ThreadingTrait<T>::Affinity; +}; + +template <typename T, typename Traits> +struct TraceTrait<HeapVectorBacking<T, Traits>> { + STATIC_ONLY(TraceTrait); + using Backing = HeapVectorBacking<T, Traits>; + + public: + static TraceDescriptor GetTraceDescriptor(const void* self) { + return {self, TraceTrait<Backing>::Trace}; + } + + static void Trace(Visitor* visitor, const void* self) { + if (visitor->ConcurrentTracingBailOut({self, &Trace})) + return; + + static_assert(!WTF::IsWeak<T>::value, + "Weakness is not supported in HeapVector and HeapDeque"); + if (WTF::IsTraceableInCollectionTrait<Traits>::value) { + WTF::TraceInCollectionTrait<WTF::kNoWeakHandling, + HeapVectorBacking<T, Traits>, + void>::Trace(visitor, self); + } + } +}; + +} // namespace blink + +namespace WTF { + +// This trace method is used only for on-stack HeapVectors found in +// conservative scanning. On-heap HeapVectors are traced by Vector::trace. +template <typename T, typename Traits> +struct TraceInCollectionTrait<kNoWeakHandling, + blink::HeapVectorBacking<T, Traits>, + void> { + static bool Trace(blink::Visitor* visitor, const void* self) { + // HeapVectorBacking does not know the exact size of the vector + // and just knows the capacity of the vector. Due to the constraint, + // HeapVectorBacking can support only the following objects: + // + // - An object that has a vtable. In this case, HeapVectorBacking + // traces only slots that are not zeroed out. This is because if + // the object has a vtable, the zeroed slot means that it is + // an unused slot (Remember that the unused slots are guaranteed + // to be zeroed out by VectorUnusedSlotClearer). + // + // - An object that can be initialized with memset. In this case, + // HeapVectorBacking traces all slots including unused slots. + // This is fine because the fact that the object can be initialized + // with memset indicates that it is safe to treat the zerod slot + // as a valid object. + static_assert(!IsTraceableInCollectionTrait<Traits>::value || + Traits::kCanClearUnusedSlotsWithMemset || + std::is_polymorphic<T>::value, + "HeapVectorBacking doesn't support objects that cannot be " + "cleared as unused with memset."); + + // This trace method is instantiated for vectors where + // IsTraceableInCollectionTrait<Traits>::value is false, but the trace + // method should not be called. Thus we cannot static-assert + // IsTraceableInCollectionTrait<Traits>::value but should runtime-assert it. + DCHECK(IsTraceableInCollectionTrait<Traits>::value); + + const T* array = reinterpret_cast<const T*>(self); + blink::HeapObjectHeader* header = + blink::HeapObjectHeader::FromPayload(self); + // Use the payload size as recorded by the heap to determine how many + // elements to trace. + size_t length = header->PayloadSize() / sizeof(T); +#ifdef ANNOTATE_CONTIGUOUS_CONTAINER + // As commented above, HeapVectorBacking can trace unused slots + // (which are already zeroed out). + ANNOTATE_CHANGE_SIZE(array, length, 0, length); +#endif + if (std::is_polymorphic<T>::value) { + const char* pointer = reinterpret_cast<const char*>(array); + for (unsigned i = 0; i < length; ++i) { + const char* element = pointer + i * sizeof(T); + if (blink::VTableInitialized(element)) { + blink::TraceIfNeeded< + T, IsTraceableInCollectionTrait<Traits>::value>::Trace(visitor, + array[i]); + } + } + } else { + for (size_t i = 0; i < length; ++i) { + blink::TraceIfNeeded< + T, IsTraceableInCollectionTrait<Traits>::value>::Trace(visitor, + array[i]); + } + } + return false; + } +}; + +} // namespace WTF + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_VECTOR_BACKING_H_ diff --git a/chromium/third_party/blink/renderer/platform/heap/concurrent_marking_test.cc b/chromium/third_party/blink/renderer/platform/heap/concurrent_marking_test.cc index af57b2607c2..8f02fc2e91c 100644 --- a/chromium/third_party/blink/renderer/platform/heap/concurrent_marking_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/concurrent_marking_test.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#if defined(THREAD_SANITIZER) + #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" @@ -31,157 +33,294 @@ class CollectionWrapper : public GarbageCollected<CollectionWrapper<T>> { }; // ============================================================================= -// Tests that expose data races when modifying collections ================ +// Tests that expose data races when modifying collections ===================== // ============================================================================= -TEST_F(ConcurrentMarkingTest, AddToHashMap) { +template <typename C> +void AddToCollection() { + constexpr int kIterations = 100; IncrementalMarkingTestDriver driver(ThreadState::Current()); - using Map = HeapHashMap<Member<IntegerObject>, Member<IntegerObject>>; - Persistent<CollectionWrapper<Map>> persistent = - MakeGarbageCollected<CollectionWrapper<Map>>(); - Map* map = persistent->GetCollection(); + Persistent<CollectionWrapper<C>> persistent = + MakeGarbageCollected<CollectionWrapper<C>>(); + C* collection = persistent->GetCollection(); driver.Start(); - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < kIterations; ++i) { driver.SingleConcurrentStep(); - for (int j = 0; j < 100; ++j) { - int num = 100 * i + j; - map->insert(MakeGarbageCollected<IntegerObject>(num), - MakeGarbageCollected<IntegerObject>(num)); + for (int j = 0; j < kIterations; ++j) { + int num = kIterations * i + j; + collection->insert(MakeGarbageCollected<IntegerObject>(num)); } } driver.FinishSteps(); driver.FinishGC(); } -TEST_F(ConcurrentMarkingTest, RemoveFromHashMap) { +template <typename C, typename GetLocation> +void RemoveFromCollectionAtLocation(GetLocation location) { + constexpr int kIterations = 100; IncrementalMarkingTestDriver driver(ThreadState::Current()); - using Map = HeapHashMap<Member<IntegerObject>, Member<IntegerObject>>; - Persistent<CollectionWrapper<Map>> persistent = - MakeGarbageCollected<CollectionWrapper<Map>>(); - Map* map = persistent->GetCollection(); - for (int i = 0; i < 10000; ++i) { - map->insert(MakeGarbageCollected<IntegerObject>(i), - MakeGarbageCollected<IntegerObject>(i)); + Persistent<CollectionWrapper<C>> persistent = + MakeGarbageCollected<CollectionWrapper<C>>(); + C* collection = persistent->GetCollection(); + for (int i = 0; i < (kIterations * kIterations); ++i) { + collection->insert(MakeGarbageCollected<IntegerObject>(i)); } driver.Start(); - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < kIterations; ++i) { driver.SingleConcurrentStep(); - for (int j = 0; j < 100; ++j) { - map->erase(map->begin()); + for (int j = 0; j < kIterations; ++j) { + collection->erase(location(collection)); } } driver.FinishSteps(); driver.FinishGC(); } -TEST_F(ConcurrentMarkingTest, SwapHashMaps) { - IncrementalMarkingTestDriver driver(ThreadState::Current()); - using Map = HeapHashMap<Member<IntegerObject>, Member<IntegerObject>>; - Persistent<CollectionWrapper<Map>> persistent = - MakeGarbageCollected<CollectionWrapper<Map>>(); - Map* map = persistent->GetCollection(); - driver.Start(); - for (int i = 0; i < 100; ++i) { - Map new_map; - for (int j = 0; j < 10 * i; ++j) { - new_map.insert(MakeGarbageCollected<IntegerObject>(j), - MakeGarbageCollected<IntegerObject>(j)); - } - driver.SingleConcurrentStep(); - map->swap(new_map); - } - driver.FinishSteps(); - driver.FinishGC(); +template <typename C> +void RemoveFromBeginningOfCollection() { + RemoveFromCollectionAtLocation<C>( + [](C* collection) { return collection->begin(); }); } -TEST_F(ConcurrentMarkingTest, AddToHashSet) { - IncrementalMarkingTestDriver driver(ThreadState::Current()); - using Set = HeapHashSet<Member<IntegerObject>>; - Persistent<CollectionWrapper<Set>> persistent = - MakeGarbageCollected<CollectionWrapper<Set>>(); - Set* set = persistent->GetCollection(); - driver.Start(); - for (int i = 0; i < 100; ++i) { - driver.SingleConcurrentStep(); - for (int j = 0; j < 100; ++j) { - int num = 100 * i + j; - set->insert(MakeGarbageCollected<IntegerObject>(num)); +template <typename C> +void RemoveFromMiddleOfCollection() { + RemoveFromCollectionAtLocation<C>([](C* collection) { + auto iterator = collection->begin(); + // Move iterator to middle of collection. + for (size_t i = 0; i < collection->size() / 2; ++i) { + ++iterator; } - } - driver.FinishSteps(); - driver.FinishGC(); + return iterator; + }); +} + +template <typename C> +void RemoveFromEndOfCollection() { + RemoveFromCollectionAtLocation<C>([](C* collection) { + auto iterator = collection->end(); + return --iterator; + }); } -TEST_F(ConcurrentMarkingTest, RemoveFromHashSet) { +template <typename C> +void ClearCollection() { + constexpr int kIterations = 10; IncrementalMarkingTestDriver driver(ThreadState::Current()); - using Set = HeapHashSet<Member<IntegerObject>>; - Persistent<CollectionWrapper<Set>> persistent = - MakeGarbageCollected<CollectionWrapper<Set>>(); - Set* set = persistent->GetCollection(); - for (int i = 0; i < 10000; ++i) { - set->insert(MakeGarbageCollected<IntegerObject>(i)); - } + Persistent<CollectionWrapper<C>> persistent = + MakeGarbageCollected<CollectionWrapper<C>>(); + C* collection = persistent->GetCollection(); driver.Start(); - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < kIterations; ++i) { driver.SingleConcurrentStep(); - for (int j = 0; j < 100; ++j) { - set->erase(set->begin()); + for (int j = 0; j < kIterations; ++j) { + collection->insert(MakeGarbageCollected<IntegerObject>(i)); } + collection->clear(); } driver.FinishSteps(); driver.FinishGC(); } -TEST_F(ConcurrentMarkingTest, SwapHashSets) { +template <typename C> +void SwapCollections() { + constexpr int kIterations = 10; IncrementalMarkingTestDriver driver(ThreadState::Current()); - using Set = HeapHashSet<Member<IntegerObject>>; - Persistent<CollectionWrapper<Set>> persistent = - MakeGarbageCollected<CollectionWrapper<Set>>(); - Set* set = persistent->GetCollection(); + Persistent<CollectionWrapper<C>> persistent = + MakeGarbageCollected<CollectionWrapper<C>>(); + C* collection = persistent->GetCollection(); driver.Start(); - for (int i = 0; i < 100; ++i) { - Set new_set; - for (int j = 0; j < 10 * i; ++j) { - new_set.insert(MakeGarbageCollected<IntegerObject>(j)); + for (int i = 0; i < (kIterations * kIterations); ++i) { + C* new_collection = MakeGarbageCollected<C>(); + for (int j = 0; j < kIterations * i; ++j) { + new_collection->insert(MakeGarbageCollected<IntegerObject>(j)); } driver.SingleConcurrentStep(); - set->swap(new_set); + collection->swap(*new_collection); } driver.FinishSteps(); driver.FinishGC(); } -TEST_F(ConcurrentMarkingTest, AddToVector) { - IncrementalMarkingTestDriver driver(ThreadState::Current()); - using V = HeapVector<Member<IntegerObject>>; - Persistent<CollectionWrapper<V>> persistent = - MakeGarbageCollected<CollectionWrapper<V>>(); - V* vector = persistent->GetCollection(); - driver.Start(); - for (int i = 0; i < 100; ++i) { - driver.SingleConcurrentStep(); - for (int j = 0; j < 100; ++j) { - int num = 100 * i + j; - vector->push_back(MakeGarbageCollected<IntegerObject>(num)); - } +// HeapHashMap + +template <typename T> +class HeapHashMapAdapter : public HeapHashMap<T, T> { + public: + template <typename U> + ALWAYS_INLINE void insert(U* u) { + HeapHashMap<T, T>::insert(u, u); } - driver.FinishSteps(); - driver.FinishGC(); +}; + +TEST_F(ConcurrentMarkingTest, AddToHashMap) { + AddToCollection<HeapHashMapAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfHashMap) { + RemoveFromBeginningOfCollection<HeapHashMapAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfHashMap) { + RemoveFromMiddleOfCollection<HeapHashMapAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromEndOfHashMap) { + RemoveFromEndOfCollection<HeapHashMapAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, ClearHashMap) { + ClearCollection<HeapHashMapAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, SwapHashMap) { + SwapCollections<HeapHashMapAdapter<Member<IntegerObject>>>(); } -TEST_F(ConcurrentMarkingTest, RemoveFromVector) { +// HeapHashSet + +TEST_F(ConcurrentMarkingTest, AddToHashSet) { + AddToCollection<HeapHashSet<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfHashSet) { + RemoveFromBeginningOfCollection<HeapHashSet<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfHashSet) { + RemoveFromMiddleOfCollection<HeapHashSet<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromEndOfHashSet) { + RemoveFromEndOfCollection<HeapHashSet<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, ClearHashSet) { + ClearCollection<HeapHashSet<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, SwapHashSet) { + SwapCollections<HeapHashSet<Member<IntegerObject>>>(); +} + +// HeapLinkedHashSet +template <typename T> +class HeapLinkedHashSetAdapter : public HeapLinkedHashSet<T> { + public: + ALWAYS_INLINE void swap(HeapLinkedHashSetAdapter<T>& other) { + HeapLinkedHashSet<T>::Swap(other); + } +}; + +TEST_F(ConcurrentMarkingTest, AddToLinkedHashSet) { + AddToCollection<HeapLinkedHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfLinkedHashSet) { + RemoveFromBeginningOfCollection< + HeapLinkedHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfLinkedHashSet) { + RemoveFromMiddleOfCollection< + HeapLinkedHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromEndOfLinkedHashSet) { + RemoveFromEndOfCollection<HeapLinkedHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, ClearLinkedHashSet) { + ClearCollection<HeapLinkedHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, SwapLinkedHashSet) { + SwapCollections<HeapLinkedHashSetAdapter<Member<IntegerObject>>>(); +} + +// HeapNewLinkedHashSet +template <typename T> +class HeapNewLinkedHashSetAdapter : public HeapNewLinkedHashSet<T> { + public: + ALWAYS_INLINE void swap(HeapNewLinkedHashSetAdapter<T>& other) { + HeapNewLinkedHashSet<T>::Swap(other); + } +}; + +TEST_F(ConcurrentMarkingTest, AddToNewLinkedHashSet) { + AddToCollection<HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfNewLinkedHashSet) { + RemoveFromBeginningOfCollection< + HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfNewLinkedHashSet) { + RemoveFromMiddleOfCollection< + HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromEndOfNewLinkedHashSet) { + RemoveFromEndOfCollection< + HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, ClearNewLinkedHashSet) { + ClearCollection<HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, SwapNewLinkedHashSet) { + SwapCollections<HeapNewLinkedHashSetAdapter<Member<IntegerObject>>>(); +} + +// HeapListHashSet + +template <typename T> +class HeapListHashSetAdapter : public HeapListHashSet<T> { + public: + ALWAYS_INLINE void swap(HeapListHashSetAdapter<T>& other) { + HeapListHashSet<T>::Swap(other); + } +}; + +TEST_F(ConcurrentMarkingTest, AddToListHashSet) { + AddToCollection<HeapListHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfListHashSet) { + RemoveFromBeginningOfCollection< + HeapListHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfListHashSet) { + RemoveFromMiddleOfCollection<HeapListHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromEndOfListHashSet) { + RemoveFromEndOfCollection<HeapListHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, ClearListHashSet) { + ClearCollection<HeapListHashSetAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, SwapListHashSet) { + SwapCollections<HeapListHashSetAdapter<Member<IntegerObject>>>(); +} + +// HeapHashCountedSet + +TEST_F(ConcurrentMarkingTest, AddToHashCountedSet) { + AddToCollection<HeapHashCountedSet<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfHashCountedSet) { + RemoveFromBeginningOfCollection<HeapHashCountedSet<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfHashCountedSet) { + RemoveFromMiddleOfCollection<HeapHashCountedSet<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromEndOfHashCountedSet) { + RemoveFromEndOfCollection<HeapHashCountedSet<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, ClearHashCountedSet) { + ClearCollection<HeapHashCountedSet<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, SwapHashCountedSet) { + SwapCollections<HeapHashCountedSet<Member<IntegerObject>>>(); +} + +// HeapVector + +// Additional test for vectors and deques +template <typename V> +void PopFromCollection() { + constexpr int kIterations = 100; IncrementalMarkingTestDriver driver(ThreadState::Current()); - using V = HeapVector<Member<IntegerObject>>; Persistent<CollectionWrapper<V>> persistent = MakeGarbageCollected<CollectionWrapper<V>>(); V* vector = persistent->GetCollection(); - for (int i = 0; i < 10000; ++i) { - vector->push_back(MakeGarbageCollected<IntegerObject>(i)); + for (int i = 0; i < (kIterations * kIterations); ++i) { + vector->insert(MakeGarbageCollected<IntegerObject>(i)); } driver.Start(); - for (int i = 0; i < 100; ++i) { + for (int i = 0; i < kIterations; ++i) { driver.SingleConcurrentStep(); - for (int j = 0; j < 100; ++j) { + for (int j = 0; j < kIterations; ++j) { vector->pop_back(); } } @@ -189,24 +328,160 @@ TEST_F(ConcurrentMarkingTest, RemoveFromVector) { driver.FinishGC(); } -TEST_F(ConcurrentMarkingTest, SwapVectors) { - IncrementalMarkingTestDriver driver(ThreadState::Current()); - using V = HeapVector<Member<IntegerObject>>; - Persistent<CollectionWrapper<V>> persistent = - MakeGarbageCollected<CollectionWrapper<V>>(); - V* vector = persistent->GetCollection(); - driver.Start(); - for (int i = 0; i < 100; ++i) { - V new_vector; - for (int j = 0; j < 10 * i; ++j) { - new_vector.push_back(MakeGarbageCollected<IntegerObject>(j)); - } - driver.SingleConcurrentStep(); - vector->swap(new_vector); +#define TEST_VECTOR_COLLECTION(name, type) \ + TEST_F(ConcurrentMarkingTest, AddTo##name) { AddToCollection<type>(); } \ + TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOf##name) { \ + RemoveFromBeginningOfCollection<type>(); \ + } \ + TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOf##name) { \ + RemoveFromMiddleOfCollection<type>(); \ + } \ + TEST_F(ConcurrentMarkingTest, RemoveFromEndOf##name) { \ + RemoveFromEndOfCollection<type>(); \ + } \ + TEST_F(ConcurrentMarkingTest, Clear##name) { ClearCollection<type>(); } \ + TEST_F(ConcurrentMarkingTest, Swap##name) { SwapCollections<type>(); } \ + TEST_F(ConcurrentMarkingTest, PopFrom##name) { PopFromCollection<type>(); } + +template <typename T, wtf_size_t inlineCapacity = 0> +class HeapVectorAdapter : public HeapVector<T, inlineCapacity> { + using Base = HeapVector<T, inlineCapacity>; + + public: + template <typename U> + ALWAYS_INLINE void insert(U* u) { + Base::push_back(u); } - driver.FinishSteps(); - driver.FinishGC(); +}; + +TEST_F(ConcurrentMarkingTest, AddToVector) { + AddToCollection<HeapVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfVector) { + RemoveFromBeginningOfCollection<HeapVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfVector) { + RemoveFromMiddleOfCollection<HeapVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromEndOfVector) { + RemoveFromEndOfCollection<HeapVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, ClearVector) { + ClearCollection<HeapVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, SwapVector) { + SwapCollections<HeapVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, PopFromVector) { + PopFromCollection<HeapVectorAdapter<Member<IntegerObject>>>(); +} + +// HeapVector with inlined buffer + +template <typename T> +class HeapInlinedVectorAdapter : public HeapVectorAdapter<T, 100> {}; + +TEST_F(ConcurrentMarkingTest, AddToInlinedVector) { + AddToCollection<HeapInlinedVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfInlinedVector) { + RemoveFromBeginningOfCollection< + HeapInlinedVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfInlinedVector) { + RemoveFromMiddleOfCollection< + HeapInlinedVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromEndOfInlinedVector) { + RemoveFromEndOfCollection<HeapInlinedVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, ClearInlinedVector) { + ClearCollection<HeapInlinedVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, SwapInlinedVector) { + SwapCollections<HeapInlinedVectorAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, PopFromInlinedVector) { + PopFromCollection<HeapInlinedVectorAdapter<Member<IntegerObject>>>(); +} + +// HeapVector of std::pairs + +template <typename T> +class HeapVectorOfPairsAdapter : public HeapVector<std::pair<T, T>> { + using Base = HeapVector<std::pair<T, T>>; + + public: + template <typename U> + ALWAYS_INLINE void insert(U* u) { + Base::push_back(std::make_pair<T, T>(u, u)); + } +}; + +TEST_F(ConcurrentMarkingTest, AddToVectorOfPairs) { + AddToCollection<HeapVectorOfPairsAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfVectorOfPairs) { + RemoveFromBeginningOfCollection< + HeapVectorOfPairsAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfVectorOfPairs) { + RemoveFromMiddleOfCollection< + HeapVectorOfPairsAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromEndOfVectorOfPairs) { + RemoveFromEndOfCollection<HeapVectorOfPairsAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, ClearVectorOfPairs) { + ClearCollection<HeapVectorOfPairsAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, SwapVectorOfPairs) { + SwapCollections<HeapVectorOfPairsAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, PopFromVectorOfPairs) { + PopFromCollection<HeapVectorOfPairsAdapter<Member<IntegerObject>>>(); +} + +// HeapDeque + +template <typename T> +class HeapDequeAdapter : public HeapDeque<T> { + public: + template <typename U> + ALWAYS_INLINE void insert(U* u) { + HeapDeque<T>::push_back(u); + } + ALWAYS_INLINE void erase(typename HeapDeque<T>::iterator) { + HeapDeque<T>::pop_back(); + } + ALWAYS_INLINE void swap(HeapDequeAdapter<T>& other) { + HeapDeque<T>::Swap(other); + } +}; + +TEST_F(ConcurrentMarkingTest, AddToDeque) { + AddToCollection<HeapDequeAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromBeginningOfDeque) { + RemoveFromBeginningOfCollection<HeapDequeAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromMiddleOfDeque) { + RemoveFromMiddleOfCollection<HeapDequeAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, RemoveFromEndOfDeque) { + RemoveFromEndOfCollection<HeapDequeAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, ClearDeque) { + ClearCollection<HeapDequeAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, SwapDeque) { + SwapCollections<HeapDequeAdapter<Member<IntegerObject>>>(); +} +TEST_F(ConcurrentMarkingTest, PopFromDeque) { + PopFromCollection<HeapDequeAdapter<Member<IntegerObject>>>(); } } // namespace concurrent_marking_test } // namespace blink + +#endif // defined(THREAD_SANITIZER) diff --git a/chromium/third_party/blink/renderer/platform/heap/finalizer_traits.h b/chromium/third_party/blink/renderer/platform/heap/finalizer_traits.h index 03ff1d959fb..232c3b95b68 100644 --- a/chromium/third_party/blink/renderer/platform/heap/finalizer_traits.h +++ b/chromium/third_party/blink/renderer/platform/heap/finalizer_traits.h @@ -91,10 +91,6 @@ struct FinalizerTrait { }; class HeapAllocator; -template <typename T, typename Traits> -class HeapVectorBacking; -template <typename Table> -class HeapHashTableBacking; template <typename T, typename U, typename V> struct FinalizerTrait<LinkedHashSet<T, U, V, HeapAllocator>> { @@ -139,27 +135,6 @@ struct FinalizerTrait<Deque<T, inlineCapacity, HeapAllocator>> { } }; -template <typename Table> -struct FinalizerTrait<HeapHashTableBacking<Table>> { - STATIC_ONLY(FinalizerTrait); - static const bool kNonTrivialFinalizer = - !std::is_trivially_destructible<typename Table::ValueType>::value; - static void Finalize(void* obj) { - internal::FinalizerTraitImpl<HeapHashTableBacking<Table>, - kNonTrivialFinalizer>::Finalize(obj); - } -}; - -template <typename T, typename Traits> -struct FinalizerTrait<HeapVectorBacking<T, Traits>> { - STATIC_ONLY(FinalizerTrait); - static const bool kNonTrivialFinalizer = Traits::kNeedsDestruction; - static void Finalize(void* obj) { - internal::FinalizerTraitImpl<HeapVectorBacking<T, Traits>, - kNonTrivialFinalizer>::Finalize(obj); - } -}; - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_FINALIZER_TRAITS_H_ diff --git a/chromium/third_party/blink/renderer/platform/heap/garbage_collected.h b/chromium/third_party/blink/renderer/platform/heap/garbage_collected.h index d0f4d272e94..4f0495c4f3b 100644 --- a/chromium/third_party/blink/renderer/platform/heap/garbage_collected.h +++ b/chromium/third_party/blink/renderer/platform/heap/garbage_collected.h @@ -53,7 +53,7 @@ struct TraceDescriptor { public: // The adjusted base pointer of the object that should be traced. - void* base_object_payload; + const void* base_object_payload; // A callback for tracing the object. TraceCallback callback; }; diff --git a/chromium/third_party/blink/renderer/platform/heap/gc_info.cc b/chromium/third_party/blink/renderer/platform/heap/gc_info.cc index b6ea60e7f93..61b0f2e43b3 100644 --- a/chromium/third_party/blink/renderer/platform/heap/gc_info.cc +++ b/chromium/third_party/blink/renderer/platform/heap/gc_info.cc @@ -43,43 +43,46 @@ constexpr size_t MaxTableSize() { } // namespace GCInfoTable* GCInfoTable::global_table_ = nullptr; -constexpr uint32_t GCInfoTable::kMaxIndex; +constexpr GCInfoIndex GCInfoTable::kMaxIndex; +constexpr GCInfoIndex GCInfoTable::kMinIndex; void GCInfoTable::CreateGlobalTable() { DEFINE_STATIC_LOCAL(GCInfoTable, table, ()); global_table_ = &table; } -uint32_t GCInfoTable::EnsureGCInfoIndex( +GCInfoIndex GCInfoTable::EnsureGCInfoIndex( const GCInfo* gc_info, - std::atomic<uint32_t>* gc_info_index_slot) { + std::atomic<GCInfoIndex>* gc_info_index_slot) { DCHECK(gc_info); DCHECK(gc_info_index_slot); - // Ensuring a new index involves current index adjustment as well - // as potentially resizing the table, both operations that require - // a lock. + // Ensuring a new index involves current index adjustment as well as + // potentially resizing the table. For simplicity we use a lock. MutexLocker locker(table_mutex_); - // If more than one thread ends up allocating a slot for - // the same GCInfo, have later threads reuse the slot - // allocated by the first. - uint32_t gc_info_index = gc_info_index_slot->load(std::memory_order_acquire); + // If more than one thread ends up allocating a slot for the same GCInfo, have + // later threads reuse the slot allocated by the first. + GCInfoIndex gc_info_index = + gc_info_index_slot->load(std::memory_order_relaxed); if (gc_info_index) return gc_info_index; - gc_info_index = ++current_index_; - CHECK(gc_info_index < GCInfoTable::kMaxIndex); - if (current_index_ >= limit_) + if (current_index_ == limit_) Resize(); + gc_info_index = current_index_++; + CHECK_LT(gc_info_index, GCInfoTable::kMaxIndex); + table_[gc_info_index] = gc_info; gc_info_index_slot->store(gc_info_index, std::memory_order_release); return gc_info_index; } void GCInfoTable::Resize() { - const size_t new_limit = (limit_) ? 2 * limit_ : ComputeInitialTableLimit(); + const GCInfoIndex new_limit = + (limit_) ? 2 * limit_ : ComputeInitialTableLimit(); + CHECK_GT(new_limit, limit_); const size_t old_committed_size = limit_ * kEntrySize; const size_t new_committed_size = new_limit * kEntrySize; CHECK(table_); @@ -107,11 +110,10 @@ void GCInfoTable::Resize() { } #endif // DCHECK_IS_ON() - limit_ = static_cast<uint32_t>(new_limit); + limit_ = new_limit; } GCInfoTable::GCInfoTable() { - CHECK(!table_); table_ = reinterpret_cast<GCInfo const**>(base::AllocPages( nullptr, MaxTableSize(), base::kPageAllocationGranularity, base::PageInaccessible, base::PageTag::kBlinkGC)); @@ -119,16 +121,4 @@ GCInfoTable::GCInfoTable() { Resize(); } -#if DCHECK_IS_ON() -void AssertObjectHasGCInfo(const void* payload, size_t gc_info_index) { - HeapObjectHeader::CheckFromPayload(payload); -#if !defined(COMPONENT_BUILD) - // On component builds we cannot compare the gcInfos as they are statically - // defined in each of the components and hence will not match. - DCHECK_EQ(HeapObjectHeader::FromPayload(payload)->GcInfoIndex(), - gc_info_index); -#endif -} -#endif - } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/gc_info.h b/chromium/third_party/blink/renderer/platform/heap/gc_info.h index 36b53cc0c17..cf4d5a01c94 100644 --- a/chromium/third_party/blink/renderer/platform/heap/gc_info.h +++ b/chromium/third_party/blink/renderer/platform/heap/gc_info.h @@ -9,35 +9,26 @@ #include "base/gtest_prod_util.h" #include "third_party/blink/renderer/platform/heap/finalizer_traits.h" #include "third_party/blink/renderer/platform/heap/name_traits.h" -#include "third_party/blink/renderer/platform/heap/visitor.h" -#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" -#include "third_party/blink/renderer/platform/wtf/assertions.h" -#include "third_party/blink/renderer/platform/wtf/deque.h" -#include "third_party/blink/renderer/platform/wtf/hash_counted_set.h" -#include "third_party/blink/renderer/platform/wtf/hash_map.h" -#include "third_party/blink/renderer/platform/wtf/hash_set.h" -#include "third_party/blink/renderer/platform/wtf/hash_table.h" -#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h" -#include "third_party/blink/renderer/platform/wtf/list_hash_set.h" -#include "third_party/blink/renderer/platform/wtf/type_traits.h" -#include "third_party/blink/renderer/platform/wtf/vector.h" namespace blink { +template <typename T> +struct TraceTrait; + +using GCInfoIndex = uint32_t; + // GCInfo contains metadata for objects that are instantiated from classes that // inherit from GarbageCollected. -struct GCInfo { +struct PLATFORM_EXPORT GCInfo final { + static inline const GCInfo& From(GCInfoIndex); + const TraceCallback trace; const FinalizationCallback finalize; const NameCallback name; const bool has_v_table; }; -#if DCHECK_IS_ON() -PLATFORM_EXPORT void AssertObjectHasGCInfo(const void*, uint32_t gc_info_index); -#endif - -class PLATFORM_EXPORT GCInfoTable { +class PLATFORM_EXPORT GCInfoTable final { public: // At maximum |kMaxIndex - 1| indices are supported. // @@ -46,29 +37,36 @@ class PLATFORM_EXPORT GCInfoTable { // of the Oilpan GC Clang plugin, there appear to be at most about 6,000 // types. Thus 14 bits should be more than twice as many bits as we will ever // need. - static constexpr uint32_t kMaxIndex = 1 << 14; + static constexpr GCInfoIndex kMaxIndex = 1 << 14; + + // Minimum index returned. Values smaller |kMinIndex| may be used as + // sentinels. + static constexpr GCInfoIndex kMinIndex = 1; // Sets up a singleton table that can be acquired using Get(). static void CreateGlobalTable(); - static GCInfoTable& Get() { return *global_table_; } + static GCInfoTable* GetMutable() { return global_table_; } + static const GCInfoTable& Get() { return *global_table_; } - inline const GCInfo* GCInfoFromIndex(uint32_t index) { - DCHECK_GE(index, 1u); + const GCInfo& GCInfoFromIndex(GCInfoIndex index) const { + DCHECK_GE(index, kMinIndex); DCHECK_LT(index, kMaxIndex); DCHECK(table_); const GCInfo* info = table_[index]; DCHECK(info); - return info; + return *info; } - uint32_t EnsureGCInfoIndex(const GCInfo*, std::atomic<std::uint32_t>*); + GCInfoIndex EnsureGCInfoIndex(const GCInfo*, std::atomic<GCInfoIndex>*); - uint32_t GcInfoIndex() const { return current_index_; } + // Returns the number of recorded GCInfo objects, including |kMinIndex|. + GCInfoIndex NumberOfGCInfos() const { return current_index_; } private: FRIEND_TEST_ALL_PREFIXES(GCInfoTest, InitialEmpty); FRIEND_TEST_ALL_PREFIXES(GCInfoTest, ResizeToMaxIndex); + FRIEND_TEST_ALL_PREFIXES(GCInfoDeathTest, MoreThanMaxIndexInfos); // Singleton for each process. Retrieved through Get(). static GCInfoTable* global_table_; @@ -83,20 +81,25 @@ class PLATFORM_EXPORT GCInfoTable { // index into this table. const GCInfo** table_ = nullptr; - // GCInfo indices start from 1 for heap objects, with 0 being treated - // specially as the index for freelist entries and large heap objects. - uint32_t current_index_ = 0; + // Current index used when requiring a new GCInfo object. + GCInfoIndex current_index_ = kMinIndex; // The limit (exclusive) of the currently allocated table. - uint32_t limit_ = 0; + GCInfoIndex limit_ = 0; Mutex table_mutex_; }; +// static +const GCInfo& GCInfo::From(GCInfoIndex index) { + return GCInfoTable::Get().GCInfoFromIndex(index); +} + template <typename T> struct GCInfoTrait { STATIC_ONLY(GCInfoTrait); - static uint32_t Index() { + + static GCInfoIndex Index() { static_assert(sizeof(T), "T must be fully defined"); static const GCInfo kGcInfo = { TraceTrait<T>::Trace, @@ -105,11 +108,13 @@ struct GCInfoTrait { NameTrait<T>::GetName, std::is_polymorphic<T>::value}; // This is more complicated than using threadsafe initialization, but this // is instantiated many times (once for every GC type). - static std::atomic<std::uint32_t> gc_info_index{0}; - uint32_t index = gc_info_index.load(std::memory_order_acquire); - if (!index) - index = GCInfoTable::Get().EnsureGCInfoIndex(&kGcInfo, &gc_info_index); - DCHECK_GE(index, 1u); + static std::atomic<GCInfoIndex> gc_info_index{0}; + GCInfoIndex index = gc_info_index.load(std::memory_order_acquire); + if (!index) { + index = GCInfoTable::GetMutable()->EnsureGCInfoIndex(&kGcInfo, + &gc_info_index); + } + DCHECK_GE(index, GCInfoTable::kMinIndex); DCHECK_LT(index, GCInfoTable::kMaxIndex); return index; } @@ -118,49 +123,6 @@ struct GCInfoTrait { template <typename U> class GCInfoTrait<const U> : public GCInfoTrait<U> {}; -template <typename T, typename U, typename V, typename W, typename X> -class HeapHashMap; -template <typename T, typename U, typename V> -class HeapHashSet; -template <typename T, typename U, typename V> -class HeapLinkedHashSet; -template <typename T, wtf_size_t inlineCapacity, typename U> -class HeapListHashSet; -template <typename ValueArg, wtf_size_t inlineCapacity> -class HeapListHashSetAllocator; -template <typename T, wtf_size_t inlineCapacity> -class HeapVector; -template <typename T, wtf_size_t inlineCapacity> -class HeapDeque; -template <typename T, typename U, typename V> -class HeapHashCountedSet; - -template <typename T, typename U, typename V, typename W, typename X> -struct GCInfoTrait<HeapHashMap<T, U, V, W, X>> - : public GCInfoTrait<HashMap<T, U, V, W, X, HeapAllocator>> {}; -template <typename T, typename U, typename V> -struct GCInfoTrait<HeapHashSet<T, U, V>> - : public GCInfoTrait<HashSet<T, U, V, HeapAllocator>> {}; -template <typename T, typename U, typename V> -struct GCInfoTrait<HeapLinkedHashSet<T, U, V>> - : public GCInfoTrait<LinkedHashSet<T, U, V, HeapAllocator>> {}; -template <typename T, wtf_size_t inlineCapacity, typename U> -struct GCInfoTrait<HeapListHashSet<T, inlineCapacity, U>> - : public GCInfoTrait< - ListHashSet<T, - inlineCapacity, - U, - HeapListHashSetAllocator<T, inlineCapacity>>> {}; -template <typename T, wtf_size_t inlineCapacity> -struct GCInfoTrait<HeapVector<T, inlineCapacity>> - : public GCInfoTrait<Vector<T, inlineCapacity, HeapAllocator>> {}; -template <typename T, wtf_size_t inlineCapacity> -struct GCInfoTrait<HeapDeque<T, inlineCapacity>> - : public GCInfoTrait<Deque<T, inlineCapacity, HeapAllocator>> {}; -template <typename T, typename U, typename V> -struct GCInfoTrait<HeapHashCountedSet<T, U, V>> - : public GCInfoTrait<HashCountedSet<T, U, V, HeapAllocator>> {}; - } // namespace blink #endif diff --git a/chromium/third_party/blink/renderer/platform/heap/gc_info_test.cc b/chromium/third_party/blink/renderer/platform/heap/gc_info_test.cc index d6786ef34ab..0a4e667c914 100644 --- a/chromium/third_party/blink/renderer/platform/heap/gc_info_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/gc_info_test.cc @@ -10,20 +10,36 @@ namespace blink { TEST(GCInfoTest, InitialEmpty) { GCInfoTable table; - EXPECT_EQ(0u, table.GcInfoIndex()); + EXPECT_EQ(GCInfoTable::kMinIndex, table.NumberOfGCInfos()); } TEST(GCInfoTest, ResizeToMaxIndex) { GCInfoTable table; GCInfo info = {nullptr, nullptr, nullptr, false}; - std::atomic<std::uint32_t> slot{0}; - for (uint32_t i = 0; i < (GCInfoTable::kMaxIndex - 1); i++) { + std::atomic<GCInfoIndex> slot{0}; + for (GCInfoIndex i = GCInfoTable::kMinIndex; i < GCInfoTable::kMaxIndex; + i++) { slot = 0; - uint32_t index = table.EnsureGCInfoIndex(&info, &slot); + GCInfoIndex index = table.EnsureGCInfoIndex(&info, &slot); EXPECT_EQ(index, slot); EXPECT_LT(0u, slot); - EXPECT_EQ(&info, table.GCInfoFromIndex(slot)); + EXPECT_EQ(&info, &table.GCInfoFromIndex(index)); } } +TEST(GCInfoDeathTest, MoreThanMaxIndexInfos) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + GCInfoTable table; + GCInfo info = {nullptr, nullptr, nullptr, false}; + std::atomic<GCInfoIndex> slot{0}; + // Create GCInfoTable::kMaxIndex entries. + for (GCInfoIndex i = GCInfoTable::kMinIndex; i < GCInfoTable::kMaxIndex; + i++) { + slot = 0; + table.EnsureGCInfoIndex(&info, &slot); + } + slot = 0; + EXPECT_DEATH(table.EnsureGCInfoIndex(&info, &slot), ""); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/heap.cc b/chromium/third_party/blink/renderer/platform/heap/heap.cc index 6f4ce3f280d..b1c28d9798c 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap.cc +++ b/chromium/third_party/blink/renderer/platform/heap/heap.cc @@ -102,8 +102,9 @@ ThreadHeap::ThreadHeap(ThreadState* thread_state) main_thread_heap_ = this; for (int arena_index = 0; arena_index < BlinkGC::kLargeObjectArenaIndex; - arena_index++) + arena_index++) { arenas_[arena_index] = new NormalPageArena(thread_state_, arena_index); + } arenas_[BlinkGC::kLargeObjectArenaIndex] = new LargeObjectArena(thread_state_, BlinkGC::kLargeObjectArenaIndex); @@ -138,18 +139,47 @@ Address ThreadHeap::CheckAndMarkPointer(MarkingVisitor* visitor, return nullptr; } -void ThreadHeap::SetupWorklists() { +void ThreadHeap::VisitRememberedSets(MarkingVisitor* visitor) { + static_assert(BlinkGC::kLargeObjectArenaIndex + 1 == BlinkGC::kNumberOfArenas, + "LargeObject arena must be the last one."); + const auto visit_header = [visitor](HeapObjectHeader* header) { + // Process only old objects. + if (header->IsOld<HeapObjectHeader::AccessMode::kNonAtomic>()) { + // The design of young generation requires collections to be executed at + // the top level (with the guarantee that no objects are currently being + // in construction). This can be ensured by running young GCs from safe + // points or by reintroducing nested allocation scopes that avoid + // finalization. + DCHECK(header->IsMarked()); + DCHECK(!MarkingVisitor::IsInConstruction(header)); + const GCInfo& gc_info = GCInfo::From(header->GcInfoIndex()); + gc_info.trace(visitor, header->Payload()); + } + }; + for (size_t i = 0; i < BlinkGC::kLargeObjectArenaIndex; ++i) { + static_cast<NormalPageArena*>(arenas_[i]) + ->IterateAndClearCardTables(visit_header); + } + static_cast<LargeObjectArena*>(arenas_[BlinkGC::kLargeObjectArenaIndex]) + ->IterateAndClearRememberedPages(visit_header); +} + +void ThreadHeap::SetupWorklists(bool should_initialize_compaction_worklists) { marking_worklist_.reset(new MarkingWorklist()); write_barrier_worklist_.reset(new WriteBarrierWorklist()); not_fully_constructed_worklist_.reset(new NotFullyConstructedWorklist()); previously_not_fully_constructed_worklist_.reset( new NotFullyConstructedWorklist()); weak_callback_worklist_.reset(new WeakCallbackWorklist()); - movable_reference_worklist_.reset(new MovableReferenceWorklist()); weak_table_worklist_.reset(new WeakTableWorklist); - backing_store_callback_worklist_.reset(new BackingStoreCallbackWorklist()); v8_references_worklist_.reset(new V8ReferencesWorklist()); + not_safe_to_concurrently_trace_worklist_.reset( + new NotSafeToConcurrentlyTraceWorklist()); DCHECK(ephemeron_callbacks_.IsEmpty()); + if (should_initialize_compaction_worklists) { + movable_reference_worklist_.reset(new MovableReferenceWorklist()); + backing_store_callback_worklist_.reset(new BackingStoreCallbackWorklist()); + } } void ThreadHeap::DestroyMarkingWorklists(BlinkGC::StackState stack_state) { @@ -159,6 +189,7 @@ void ThreadHeap::DestroyMarkingWorklists(BlinkGC::StackState stack_state) { weak_callback_worklist_.reset(nullptr); weak_table_worklist_.reset(); v8_references_worklist_.reset(); + not_safe_to_concurrently_trace_worklist_.reset(); ephemeron_callbacks_.clear(); // The fixed point iteration may have found not-fully-constructed objects. @@ -201,8 +232,8 @@ HeapCompact* ThreadHeap::Compaction() { return compaction_.get(); } -bool ThreadHeap::ShouldRegisterMovingAddress(Address address) { - return Compaction()->ShouldRegisterMovingAddress(address); +bool ThreadHeap::ShouldRegisterMovingAddress() { + return Compaction()->ShouldRegisterMovingAddress(); } void ThreadHeap::FlushNotFullyConstructedObjects() { @@ -226,40 +257,8 @@ void ThreadHeap::MarkNotFullyConstructedObjects(MarkingVisitor* visitor) { while (not_fully_constructed_worklist_->Pop(WorklistTaskId::MutatorThread, &item)) { BasePage* const page = PageFromObject(item); - visitor->ConservativelyMarkAddress(page, reinterpret_cast<Address>(item)); - } -} - -void ThreadHeap::InvokeEphemeronCallbacks(MarkingVisitor* visitor) { - // Mark any strong pointers that have now become reachable in ephemeron maps. - ThreadHeapStatsCollector::Scope stats_scope( - stats_collector(), - ThreadHeapStatsCollector::kMarkInvokeEphemeronCallbacks); - - // We first reiterate over known callbacks from previous iterations. - for (auto& tuple : ephemeron_callbacks_) - tuple.value(visitor, tuple.key); - - DCHECK_EQ(WorklistTaskId::MutatorThread, visitor->task_id()); - - // Then we iterate over the new callbacks found by the marking visitor. - // Callbacks found by the concurrent marking will be flushed eventually - // and then invoked by the mutator thread (in the atomic pause at latest). - while ( - !weak_table_worklist_->IsLocalViewEmpty(WorklistTaskId::MutatorThread)) { - // Read ephemeron callbacks from worklist to ephemeron_callbacks_ hashmap. - WeakTableWorklist::View ephemerons_worklist(weak_table_worklist_.get(), - WorklistTaskId::MutatorThread); - WeakTableItem item; - while (ephemerons_worklist.Pop(&item)) { - auto result = - ephemeron_callbacks_.insert(item.base_object_payload, item.callback); - DCHECK(result.is_new_entry || - result.stored_value->value == item.callback); - if (result.is_new_entry) { - item.callback(visitor, item.base_object_payload); - } - } + visitor->ConservativelyMarkAddress(page, + reinterpret_cast<ConstAddress>(item)); } } @@ -288,6 +287,45 @@ bool DrainWorklistWithDeadline(base::TimeTicks deadline, } // namespace +bool ThreadHeap::InvokeEphemeronCallbacks(MarkingVisitor* visitor, + base::TimeTicks deadline) { + // Mark any strong pointers that have now become reachable in ephemeron maps. + ThreadHeapStatsCollector::Scope stats_scope( + stats_collector(), + ThreadHeapStatsCollector::kMarkInvokeEphemeronCallbacks); + + // We first reiterate over known callbacks from previous iterations. + constexpr size_t kDeadlineCheckInterval = 250; + size_t processed_callback_count = 0; + for (auto& tuple : ephemeron_callbacks_) { + tuple.value(visitor, tuple.key); + if (++processed_callback_count == kDeadlineCheckInterval) { + if (deadline <= base::TimeTicks::Now()) { + return false; + } + processed_callback_count = 0; + } + } + + DCHECK_EQ(WorklistTaskId::MutatorThread, visitor->task_id()); + + // Then we iterate over the new callbacks found by the marking visitor. + // Callbacks found by the concurrent marking will be flushed eventually + // and then invoked by the mutator thread (in the atomic pause at latest). + return DrainWorklistWithDeadline( + deadline, weak_table_worklist_.get(), + [this, visitor](const WeakTableItem& item) { + auto result = ephemeron_callbacks_.insert(item.base_object_payload, + item.callback); + DCHECK(result.is_new_entry || + result.stored_value->value == item.callback); + if (result.is_new_entry) { + item.callback(visitor, item.base_object_payload); + } + }, + WorklistTaskId::MutatorThread); +} + bool ThreadHeap::AdvanceMarking(MarkingVisitor* visitor, base::TimeTicks deadline) { DCHECK_EQ(WorklistTaskId::MutatorThread, visitor->task_id()); @@ -301,6 +339,39 @@ bool ThreadHeap::AdvanceMarking(MarkingVisitor* visitor, ThreadHeapStatsCollector::Scope stats_scope( stats_collector(), ThreadHeapStatsCollector::kMarkProcessWorklist); + // Start with mutator-thread-only worklists (not fully constructed). + // If time runs out, concurrent markers can take care of the rest. + + // Convert |previously_not_fully_constructed_worklist_| to + // |marking_worklist_|. This merely re-adds items with the proper + // callbacks. + finished = DrainWorklistWithDeadline( + deadline, previously_not_fully_constructed_worklist_.get(), + [visitor](NotFullyConstructedItem& item) { + visitor->DynamicallyMarkAddress( + reinterpret_cast<ConstAddress>(item)); + }, + WorklistTaskId::MutatorThread); + if (!finished) + break; + + { + ThreadHeapStatsCollector::EnabledScope bailout_scope( + stats_collector(), ThreadHeapStatsCollector::kMarkBailOutObjects); + finished = DrainWorklistWithDeadline( + deadline, not_safe_to_concurrently_trace_worklist_.get(), + [visitor](const MarkingItem& item) { + item.callback(visitor, item.base_object_payload); + }, + WorklistTaskId::MutatorThread); + if (!finished) + break; + } + + finished = FlushV8References(deadline); + if (!finished) + break; + finished = DrainWorklistWithDeadline( deadline, marking_worklist_.get(), [visitor](const MarkingItem& item) { @@ -318,66 +389,61 @@ bool ThreadHeap::AdvanceMarking(MarkingVisitor* visitor, deadline, write_barrier_worklist_.get(), [visitor](HeapObjectHeader* header) { DCHECK(!MarkingVisitor::IsInConstruction(header)); - GCInfoTable::Get() - .GCInfoFromIndex(header->GcInfoIndex()) - ->trace(visitor, header->Payload()); + GCInfo::From(header->GcInfoIndex()) + .trace(visitor, header->Payload()); visitor->AccountMarkedBytes(header); }, WorklistTaskId::MutatorThread); if (!finished) break; - - // Convert |previously_not_fully_constructed_worklist_| to - // |marking_worklist_|. This merely re-adds items with the proper - // callbacks. - finished = DrainWorklistWithDeadline( - deadline, previously_not_fully_constructed_worklist_.get(), - [visitor](const NotFullyConstructedItem& item) { - visitor->DynamicallyMarkAddress(reinterpret_cast<Address>(item)); - }, - WorklistTaskId::MutatorThread); - if (!finished) - break; } - InvokeEphemeronCallbacks(visitor); + finished = InvokeEphemeronCallbacks(visitor, deadline); + if (!finished) + break; // Rerun loop if ephemeron processing queued more objects for tracing. } while (!marking_worklist_->IsLocalViewEmpty(WorklistTaskId::MutatorThread)); - FlushV8References(); - return finished; } +bool ThreadHeap::HasWorkForConcurrentMarking() const { + return !marking_worklist_->IsGlobalPoolEmpty() || + !write_barrier_worklist_->IsGlobalPoolEmpty(); +} + bool ThreadHeap::AdvanceConcurrentMarking(ConcurrentMarkingVisitor* visitor, base::TimeTicks deadline) { - bool finished = false; - // Iteratively mark all objects that are reachable from the objects - // currently pushed onto the marking worklist. - finished = DrainWorklistWithDeadline( - deadline, marking_worklist_.get(), - [visitor](const MarkingItem& item) { - HeapObjectHeader* header = - HeapObjectHeader::FromPayload(item.base_object_payload); - DCHECK(!ConcurrentMarkingVisitor::IsInConstruction(header)); - item.callback(visitor, item.base_object_payload); - visitor->AccountMarkedBytes(header); - }, - visitor->task_id()); - if (!finished) - return false; - - finished = DrainWorklistWithDeadline( - deadline, write_barrier_worklist_.get(), - [visitor](HeapObjectHeader* header) { - DCHECK(!ConcurrentMarkingVisitor::IsInConstruction(header)); - GCInfoTable::Get() - .GCInfoFromIndex(header->GcInfoIndex()) - ->trace(visitor, header->Payload()); - visitor->AccountMarkedBytes(header); - }, - visitor->task_id()); + bool finished; + do { + // Iteratively mark all objects that are reachable from the objects + // currently pushed onto the marking worklist. + finished = DrainWorklistWithDeadline( + deadline, marking_worklist_.get(), + [visitor](const MarkingItem& item) { + HeapObjectHeader* header = + HeapObjectHeader::FromPayload(item.base_object_payload); + DCHECK(!ConcurrentMarkingVisitor::IsInConstruction(header)); + item.callback(visitor, item.base_object_payload); + visitor->AccountMarkedBytesSafe(header); + }, + visitor->task_id()); + if (!finished) + break; + + finished = DrainWorklistWithDeadline( + deadline, write_barrier_worklist_.get(), + [visitor](HeapObjectHeader* header) { + DCHECK(!ConcurrentMarkingVisitor::IsInConstruction(header)); + GCInfo::From(header->GcInfoIndex()).trace(visitor, header->Payload()); + visitor->AccountMarkedBytesSafe(header); + }, + visitor->task_id()); + if (!finished) + break; + } while (HasWorkForConcurrentMarking()); + return finished; } @@ -413,7 +479,7 @@ size_t ThreadHeap::ObjectPayloadSizeForTesting() { size_t object_payload_size = 0; thread_state_->SetGCPhase(ThreadState::GCPhase::kMarking); thread_state_->Heap().MakeConsistentForGC(); - thread_state_->Heap().PrepareForSweep(); + thread_state_->Heap().PrepareForSweep(BlinkGC::CollectionType::kMajor); for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i) object_payload_size += arenas_[i]->ObjectPayloadSizeForTesting(); MakeConsistentForMutator(); @@ -427,7 +493,7 @@ void ThreadHeap::ResetAllocationPointForTesting() { arenas_[i]->ResetAllocationPoint(); } -BasePage* ThreadHeap::LookupPageForAddress(Address address) { +BasePage* ThreadHeap::LookupPageForAddress(ConstAddress address) { if (PageMemoryRegion* region = region_tree_->Lookup(address)) { return region->PageFromAddress(address); } @@ -436,14 +502,23 @@ BasePage* ThreadHeap::LookupPageForAddress(Address address) { void ThreadHeap::MakeConsistentForGC() { DCHECK(thread_state_->InAtomicMarkingPause()); - for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i) - arenas_[i]->MakeConsistentForGC(); + for (BaseArena* arena : arenas_) { + arena->MakeConsistentForGC(); + } } void ThreadHeap::MakeConsistentForMutator() { DCHECK(thread_state_->InAtomicMarkingPause()); - for (int i = 0; i < BlinkGC::kNumberOfArenas; ++i) - arenas_[i]->MakeConsistentForMutator(); + for (BaseArena* arena : arenas_) { + arena->MakeConsistentForMutator(); + } +} + +void ThreadHeap::Unmark() { + DCHECK(thread_state_->InAtomicMarkingPause()); + for (BaseArena* arena : arenas_) { + arena->Unmark(); + } } void ThreadHeap::Compact() { @@ -468,11 +543,11 @@ void ThreadHeap::Compact() { Compaction()->Finish(); } -void ThreadHeap::PrepareForSweep() { +void ThreadHeap::PrepareForSweep(BlinkGC::CollectionType collection_type) { DCHECK(thread_state_->InAtomicMarkingPause()); DCHECK(thread_state_->CheckThread()); for (int i = 0; i < BlinkGC::kNumberOfArenas; i++) - arenas_[i]->PrepareForSweep(); + arenas_[i]->PrepareForSweep(collection_type); } void ThreadHeap::RemoveAllPages() { @@ -519,45 +594,53 @@ void ThreadHeap::CollectStatistics(ThreadState::Statistics* stats) { #undef SNAPSHOT_ARENA } -bool ThreadHeap::AdvanceSweep(SweepingType sweeping_type, - base::TimeTicks deadline) { +bool ThreadHeap::AdvanceLazySweep(base::TimeTicks deadline) { static constexpr base::TimeDelta slack = base::TimeDelta::FromSecondsD(0.001); - auto sweeping_function = sweeping_type == SweepingType::kMutator - ? &BaseArena::LazySweepWithDeadline - : &BaseArena::ConcurrentSweepWithDeadline; for (size_t i = 0; i < BlinkGC::kNumberOfArenas; i++) { // lazySweepWithDeadline() won't check the deadline until it sweeps // 10 pages. So we give a small slack for safety. const base::TimeDelta remaining_budget = deadline - slack - base::TimeTicks::Now(); if (remaining_budget <= base::TimeDelta() || - !(arenas_[i]->*sweeping_function)(deadline)) { + !arenas_[i]->LazySweepWithDeadline(deadline)) { return false; } } return true; } +bool ThreadHeap::AdvanceConcurrentSweep(base::JobDelegate* job) { + for (size_t i = 0; i < BlinkGC::kNumberOfArenas; i++) { + while (!arenas_[i]->ConcurrentSweepOnePage()) { + if (job->ShouldYield()) + return false; + } + } + return true; +} + // TODO(omerkatz): Temporary solution until concurrent marking is ready. see // https://crrev.com/c/1730054 for details. Eventually this will be removed. -void ThreadHeap::FlushV8References() { +bool ThreadHeap::FlushV8References(base::TimeTicks deadline) { if (!thread_state_->IsUnifiedGCMarkingInProgress()) - return; + return true; DCHECK(base::FeatureList::IsEnabled( blink::features::kBlinkHeapConcurrentMarking) || v8_references_worklist_->IsGlobalEmpty()); - V8ReferencesWorklist::View v8_references(v8_references_worklist_.get(), - WorklistTaskId::MutatorThread); - V8Reference reference; v8::EmbedderHeapTracer* controller = reinterpret_cast<v8::EmbedderHeapTracer*>( thread_state_->unified_heap_controller()); - while (v8_references.Pop(&reference)) { - controller->RegisterEmbedderReference( - reference->template Cast<v8::Data>().Get()); - } + return DrainWorklistWithDeadline( + deadline, v8_references_worklist_.get(), + [controller](const V8Reference& reference) { + if (!reference->Get().IsEmpty()) { + controller->RegisterEmbedderReference( + reference->template Cast<v8::Data>().Get()); + } + }, + WorklistTaskId::MutatorThread); } ThreadHeap* ThreadHeap::main_thread_heap_ = nullptr; diff --git a/chromium/third_party/blink/renderer/platform/heap/heap.h b/chromium/third_party/blink/renderer/platform/heap/heap.h index 5fa69d97c07..953dd7839f0 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap.h +++ b/chromium/third_party/blink/renderer/platform/heap/heap.h @@ -68,17 +68,17 @@ class ProcessHeapReporter; class RegionTree; using MarkingItem = TraceDescriptor; -using NotFullyConstructedItem = void*; +using NotFullyConstructedItem = const void*; using WeakTableItem = MarkingItem; struct BackingStoreCallbackItem { - void* backing; + const void* backing; MovingObjectCallback callback; }; struct CustomCallbackItem { WeakCallback callback; - void* parameter; + const void* parameter; }; using V8Reference = const TraceWrapperV8Reference<v8::Value>*; @@ -86,19 +86,21 @@ using V8Reference = const TraceWrapperV8Reference<v8::Value>*; // Segment size of 512 entries necessary to avoid throughput regressions. Since // the work list is currently a temporary object this is not a problem. using MarkingWorklist = Worklist<MarkingItem, 512 /* local entries */>; -using WriteBarrierWorklist = Worklist<HeapObjectHeader*, 256>; +using WriteBarrierWorklist = Worklist<HeapObjectHeader*, 64>; using NotFullyConstructedWorklist = Worklist<NotFullyConstructedItem, 16 /* local entries */>; using WeakCallbackWorklist = - Worklist<CustomCallbackItem, 256 /* local entries */>; + Worklist<CustomCallbackItem, 64 /* local entries */>; // Using large local segments here (sized 512 entries) to avoid throughput // regressions. using MovableReferenceWorklist = - Worklist<MovableReference*, 512 /* local entries */>; + Worklist<const MovableReference*, 256 /* local entries */>; using WeakTableWorklist = Worklist<WeakTableItem, 16 /* local entries */>; using BackingStoreCallbackWorklist = Worklist<BackingStoreCallbackItem, 16 /* local entries */>; using V8ReferencesWorklist = Worklist<V8Reference, 16 /* local entries */>; +using NotSafeToConcurrentlyTraceWorklist = + Worklist<MarkingItem, 64 /* local entries */>; class PLATFORM_EXPORT HeapAllocHooks { STATIC_ONLY(HeapAllocHooks); @@ -260,6 +262,10 @@ class PLATFORM_EXPORT ThreadHeap { return v8_references_worklist_.get(); } + NotSafeToConcurrentlyTraceWorklist* GetNotSafeToConcurrentlyTraceWorklist() + const { + return not_safe_to_concurrently_trace_worklist_.get(); + } // Register an ephemeron table for fixed-point iteration. void RegisterWeakTable(void* container_object, EphemeronCallback); @@ -268,7 +274,7 @@ class PLATFORM_EXPORT ThreadHeap { // Checks whether we need to register |addr| as a backing store or a slot // containing reference to it. - bool ShouldRegisterMovingAddress(Address addr); + bool ShouldRegisterMovingAddress(); RegionTree* GetRegionTree() { return region_tree_.get(); } @@ -302,6 +308,8 @@ class PLATFORM_EXPORT ThreadHeap { bool AdvanceMarking(MarkingVisitor*, base::TimeTicks deadline); void VerifyMarking(); + // Returns true if concurrent markers will have work to steal + bool HasWorkForConcurrentMarking() const; // Returns true if marker is done bool AdvanceConcurrentMarking(ConcurrentMarkingVisitor*, base::TimeTicks); @@ -309,6 +317,9 @@ class PLATFORM_EXPORT ThreadHeap { // thread heaps. If so marks the object pointed to as live. Address CheckAndMarkPointer(MarkingVisitor*, Address); + // Visits remembered sets. + void VisitRememberedSets(MarkingVisitor*); + size_t ObjectPayloadSizeForTesting(); void ResetAllocationPointForTesting(); @@ -317,7 +328,7 @@ class PLATFORM_EXPORT ThreadHeap { // This look-up uses the region search tree and a negative contains cache to // provide an efficient mapping from arbitrary addresses to the containing // heap-page if one exists. - BasePage* LookupPageForAddress(Address); + BasePage* LookupPageForAddress(ConstAddress); HeapCompact* Compaction(); @@ -341,12 +352,16 @@ class PLATFORM_EXPORT ThreadHeap { // the executions of mutators. void MakeConsistentForMutator(); + // Unmarks all objects in the entire heap. This is supposed to be called in + // the beginning of major GC. + void Unmark(); + void Compact(); - enum class SweepingType : uint8_t { kMutator, kConcurrent }; - bool AdvanceSweep(SweepingType sweeping_type, base::TimeTicks deadline); + bool AdvanceLazySweep(base::TimeTicks deadline); + bool AdvanceConcurrentSweep(base::JobDelegate*); - void PrepareForSweep(); + void PrepareForSweep(BlinkGC::CollectionType); void RemoveAllPages(); void InvokeFinalizersOnSweptPages(); void CompleteSweep(); @@ -374,16 +389,24 @@ class PLATFORM_EXPORT ThreadHeap { PageBloomFilter* page_bloom_filter() { return page_bloom_filter_.get(); } + bool IsInLastAllocatedRegion(Address address) const; + void SetLastAllocatedRegion(Address start, size_t length); + private: + struct LastAllocatedRegion { + Address start = nullptr; + size_t length = 0; + }; + static int ArenaIndexForObjectSize(size_t); - void SetupWorklists(); + void SetupWorklists(bool); void DestroyMarkingWorklists(BlinkGC::StackState); void DestroyCompactionWorklists(); - void InvokeEphemeronCallbacks(MarkingVisitor*); + bool InvokeEphemeronCallbacks(MarkingVisitor*, base::TimeTicks); - void FlushV8References(); + bool FlushV8References(base::TimeTicks); ThreadState* thread_state_; std::unique_ptr<ThreadHeapStatsCollector> heap_stats_collector_; @@ -436,12 +459,17 @@ class PLATFORM_EXPORT ThreadHeap { // to V8. std::unique_ptr<V8ReferencesWorklist> v8_references_worklist_; + std::unique_ptr<NotSafeToConcurrentlyTraceWorklist> + not_safe_to_concurrently_trace_worklist_; + // No duplicates allowed for ephemeron callbacks. Hence, we use a hashmap // with the key being the HashTable. - WTF::HashMap<void*, EphemeronCallback> ephemeron_callbacks_; + WTF::HashMap<const void*, EphemeronCallback> ephemeron_callbacks_; std::unique_ptr<HeapCompact> compaction_; + LastAllocatedRegion last_allocated_region_; + BaseArena* arenas_[BlinkGC::kNumberOfArenas]; static ThreadHeap* main_thread_heap_; @@ -457,31 +485,22 @@ template <typename T> class GarbageCollected { IS_GARBAGE_COLLECTED_TYPE(); - // For now direct allocation of arrays on the heap is not allowed. - void* operator new[](size_t size); - -#if defined(OS_WIN) && defined(COMPILER_MSVC) - // Due to some quirkiness in the MSVC compiler we have to provide - // the delete[] operator in the GarbageCollected subclasses as it - // is called when a class is exported in a DLL. - protected: - void operator delete[](void* p) { NOTREACHED(); } -#else - void operator delete[](void* p); -#endif - public: using ParentMostGarbageCollectedType = T; - void* operator new(size_t size) = delete; // Must use MakeGarbageCollected. + // Must use MakeGarbageCollected. + void* operator new(size_t) = delete; + void* operator new[](size_t) = delete; + // The garbage collector is taking care of reclaiming the object. Also, + // virtual destructor requires an unambiguous, accessible 'operator delete'. + void operator delete(void*) { NOTREACHED(); } + void operator delete[](void*) = delete; template <typename Derived> static void* AllocateObject(size_t size) { return ThreadHeap::Allocate<GCInfoFoldedType<Derived>>(size); } - void operator delete(void* p) { NOTREACHED(); } - protected: // This trait in theory can be moved to gc_info.h, but that would cause // significant memory bloat caused by huge number of ThreadHeap::Allocate<> @@ -513,61 +532,75 @@ class GarbageCollected { DISALLOW_COPY_AND_ASSIGN(GarbageCollected); }; -// Default MakeGarbageCollected: Constructs an instance of T, which is a garbage -// collected type. -template <typename T, typename... Args> -T* MakeGarbageCollected(Args&&... args) { - static_assert(WTF::IsGarbageCollectedType<T>::value, - "T needs to be a garbage collected object"); - static_assert(std::is_trivially_destructible<T>::value || - std::has_virtual_destructor<T>::value || - std::is_final<T>::value || - internal::IsGarbageCollectedContainer<T>::value || - internal::HasFinalizeGarbageCollectedObject<T>::value, - "Finalized GarbageCollected class should either have a virtual " - "destructor or be marked as final"); - static_assert(!IsGarbageCollectedMixin<T>::value || - sizeof(T) <= kLargeObjectSizeThreshold, - "GarbageCollectedMixin may not be a large object"); - void* memory = T::template AllocateObject<T>(sizeof(T)); - HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory); - // Placement new as regular operator new() is deleted. - T* object = ::new (memory) T(std::forward<Args>(args)...); - header->MarkFullyConstructed<HeapObjectHeader::AccessMode::kAtomic>(); - return object; -} - // Used for passing custom sizes to MakeGarbageCollected. struct AdditionalBytes { explicit AdditionalBytes(size_t bytes) : value(bytes) {} const size_t value; }; +template <typename T> +struct MakeGarbageCollectedTrait { + template <typename... Args> + static T* Call(Args&&... args) { + static_assert(WTF::IsGarbageCollectedType<T>::value, + "T needs to be a garbage collected object"); + static_assert( + std::is_trivially_destructible<T>::value || + std::has_virtual_destructor<T>::value || std::is_final<T>::value || + internal::IsGarbageCollectedContainer<T>::value || + internal::HasFinalizeGarbageCollectedObject<T>::value, + "Finalized GarbageCollected class should either have a virtual " + "destructor or be marked as final"); + static_assert(!IsGarbageCollectedMixin<T>::value || + sizeof(T) <= kLargeObjectSizeThreshold, + "GarbageCollectedMixin may not be a large object"); + void* memory = T::template AllocateObject<T>(sizeof(T)); + HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory); + // Placement new as regular operator new() is deleted. + T* object = ::new (memory) T(std::forward<Args>(args)...); + header->MarkFullyConstructed<HeapObjectHeader::AccessMode::kAtomic>(); + return object; + } + + template <typename... Args> + static T* Call(AdditionalBytes additional_bytes, Args&&... args) { + static_assert(WTF::IsGarbageCollectedType<T>::value, + "T needs to be a garbage collected object"); + static_assert( + std::is_trivially_destructible<T>::value || + std::has_virtual_destructor<T>::value || std::is_final<T>::value || + internal::IsGarbageCollectedContainer<T>::value || + internal::HasFinalizeGarbageCollectedObject<T>::value, + "Finalized GarbageCollected class should either have a virtual " + "destructor or be marked as final."); + const size_t size = sizeof(T) + additional_bytes.value; + if (IsGarbageCollectedMixin<T>::value) { + // Ban large mixin so we can use PageFromObject() on them. + CHECK_GE(kLargeObjectSizeThreshold, size) + << "GarbageCollectedMixin may not be a large object"; + } + void* memory = T::template AllocateObject<T>(size); + HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory); + // Placement new as regular operator new() is deleted. + T* object = ::new (memory) T(std::forward<Args>(args)...); + header->MarkFullyConstructed<HeapObjectHeader::AccessMode::kAtomic>(); + return object; + } +}; + +// Default MakeGarbageCollected: Constructs an instance of T, which is a garbage +// collected type. +template <typename T, typename... Args> +T* MakeGarbageCollected(Args&&... args) { + return MakeGarbageCollectedTrait<T>::Call(std::forward<Args>(args)...); +} + // Constructs an instance of T, which is a garbage collected type. This special // version takes size which enables constructing inline objects. template <typename T, typename... Args> T* MakeGarbageCollected(AdditionalBytes additional_bytes, Args&&... args) { - static_assert(WTF::IsGarbageCollectedType<T>::value, - "T needs to be a garbage collected object"); - static_assert(std::is_trivially_destructible<T>::value || - std::has_virtual_destructor<T>::value || - std::is_final<T>::value || - internal::IsGarbageCollectedContainer<T>::value || - internal::HasFinalizeGarbageCollectedObject<T>::value, - "Finalized GarbageCollected class should either have a virtual " - "destructor or be marked as final."); - const size_t size = sizeof(T) + additional_bytes.value; - if (IsGarbageCollectedMixin<T>::value) { - // Ban large mixin so we can use PageFromObject() on them. - CHECK_GE(kLargeObjectSizeThreshold, size) - << "GarbageCollectedMixin may not be a large object"; - } - void* memory = T::template AllocateObject<T>(size); - HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory); - // Placement new as regular operator new() is deleted. - T* object = ::new (memory) T(std::forward<Args>(args)...); - header->MarkFullyConstructed<HeapObjectHeader::AccessMode::kAtomic>(); - return object; + return MakeGarbageCollectedTrait<T>::Call(additional_bytes, + std::forward<Args>(args)...); } // Assigning class types to their arenas. @@ -620,9 +653,21 @@ Address ThreadHeap::Allocate(size_t size) { GCInfoTrait<T>::Index(), type_name); } +inline bool ThreadHeap::IsInLastAllocatedRegion(Address address) const { + return last_allocated_region_.start <= address && + address < + (last_allocated_region_.start + last_allocated_region_.length); +} + +inline void ThreadHeap::SetLastAllocatedRegion(Address start, size_t length) { + last_allocated_region_.start = start; + last_allocated_region_.length = length; +} + template <typename T> -void Visitor::HandleWeakCell(const WeakCallbackInfo&, void* object) { - WeakMember<T>* weak_member = reinterpret_cast<WeakMember<T>*>(object); +void Visitor::HandleWeakCell(const WeakCallbackInfo&, const void* object) { + WeakMember<T>* weak_member = + reinterpret_cast<WeakMember<T>*>(const_cast<void*>(object)); if (weak_member->Get()) { if (weak_member->IsHashTableDeletedValue()) { // This can happen when weak fields are deleted while incremental marking diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_allocator.h b/chromium/third_party/blink/renderer/platform/heap/heap_allocator.h index d41fe4e422c..867165c8079 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_allocator.h +++ b/chromium/third_party/blink/renderer/platform/heap/heap_allocator.h @@ -8,6 +8,8 @@ #include <type_traits> #include "build/build_config.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_table_backing.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector_backing.h" #include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/heap/heap_buildflags.h" #include "third_party/blink/renderer/platform/heap/marking_visitor.h" @@ -45,26 +47,6 @@ template <typename T> struct IsAllowedInContainer<T, typename T::IsDisallowedInContainerMarker> : std::false_type {}; -template <typename T, typename Traits = WTF::VectorTraits<T>> -class HeapVectorBacking { - DISALLOW_NEW(); - IS_GARBAGE_COLLECTED_TYPE(); - - public: - static void Finalize(void* pointer); - void FinalizeGarbageCollectedObject() { Finalize(this); } -}; - -template <typename Table> -class HeapHashTableBacking { - DISALLOW_NEW(); - IS_GARBAGE_COLLECTED_TYPE(); - - public: - static void Finalize(void* pointer); - void FinalizeGarbageCollectedObject() { Finalize(this); } -}; - // This is a static-only class used as a trait on collections to make them heap // allocated. However see also HeapListHashSetAllocator. class PLATFORM_EXPORT HeapAllocator { @@ -88,16 +70,8 @@ class PLATFORM_EXPORT HeapAllocator { } template <typename T> static T* AllocateVectorBacking(size_t size) { - ThreadState* state = - ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState(); - DCHECK(state->IsAllocationAllowed()); - uint32_t gc_info_index = GCInfoTrait<HeapVectorBacking<T>>::Index(); - const char* type_name = - WTF_HEAP_PROFILER_TYPE_NAME(HeapHashTableBacking<HeapVectorBacking<T>>); return reinterpret_cast<T*>( - MarkAsConstructed(state->Heap().AllocateOnArenaIndex( - state, ThreadHeap::AllocationSizeFromSize(size), - BlinkGC::kVectorArenaIndex, gc_info_index, type_name))); + MakeGarbageCollected<HeapVectorBacking<T>>(size / sizeof(T))); } static void FreeVectorBacking(void*); static bool ExpandVectorBacking(void*, size_t); @@ -107,16 +81,9 @@ class PLATFORM_EXPORT HeapAllocator { template <typename T, typename HashTable> static T* AllocateHashTableBacking(size_t size) { - uint32_t gc_info_index = - GCInfoTrait<HeapHashTableBacking<HashTable>>::Index(); - ThreadState* state = - ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState(); - const char* type_name = - WTF_HEAP_PROFILER_TYPE_NAME(HeapHashTableBacking<HashTable>); return reinterpret_cast<T*>( - MarkAsConstructed(state->Heap().AllocateOnArenaIndex( - state, size, BlinkGC::kHashTableArenaIndex, gc_info_index, - type_name))); + MakeGarbageCollected<HeapHashTableBacking<HashTable>>( + size / sizeof(typename HashTable::ValueType))); } template <typename T, typename HashTable> static T* AllocateZeroedHashTableBacking(size_t size) { @@ -125,19 +92,27 @@ class PLATFORM_EXPORT HeapAllocator { static void FreeHashTableBacking(void* address); static bool ExpandHashTableBacking(void*, size_t); - static void TraceMarkedBackingStore(void* address) { - MarkingVisitor::TraceMarkedBackingStore(address); + static void TraceBackingStoreIfMarked(const void* address) { + // Trace backing store elements only if backing store was marked. The + // sweeper may be active on the backing store which requires atomic mark bit + // access. A precise filter is performed in + // MarkingVisitor::TraceMarkedBackingStore. + if (HeapObjectHeader::FromPayload(address) + ->IsMarked<HeapObjectHeader::AccessMode::kAtomic>()) { + MarkingVisitor::TraceMarkedBackingStore(address); + } } - static void BackingWriteBarrier(void* address) { - MarkingVisitor::WriteBarrier(address); + template <typename T> + static void BackingWriteBarrier(T** slot) { + MarkingVisitor::WriteBarrier(slot); } - template <typename HashTable> - static void BackingWriteBarrierForHashTable(void* address) { - if (MarkingVisitor::WriteBarrier(address)) { + template <typename HashTable, typename T> + static void BackingWriteBarrierForHashTable(T** slot) { + if (MarkingVisitor::WriteBarrier(slot)) { AddMovingCallback<HashTable>( - static_cast<typename HashTable::ValueType*>(address)); + static_cast<typename HashTable::ValueType*>(*slot)); } } @@ -167,13 +142,18 @@ class PLATFORM_EXPORT HeapAllocator { return ThreadState::Current()->SweepForbidden(); } + static bool IsIncrementalMarking() { + return ThreadState::IsAnyIncrementalMarking() && + ThreadState::Current()->IsIncrementalMarking(); + } + template <typename T> static bool IsHeapObjectAlive(T* object) { return ThreadHeap::IsHeapObjectAlive(object); } template <typename T, typename Traits> - static void Trace(blink::Visitor* visitor, T& t) { + static void Trace(Visitor* visitor, const T& t) { TraceCollectionIfEnabled<WTF::WeakHandlingTrait<T>::value, T, Traits>::Trace(visitor, &t); } @@ -195,84 +175,106 @@ class PLATFORM_EXPORT HeapAllocator { template <typename T, typename Traits> static void NotifyNewObject(T* object) { +#if BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) + ThreadState* const thread_state = ThreadState::Current(); + if (!thread_state->IsIncrementalMarking()) { + MarkingVisitor::GenerationalBarrier(reinterpret_cast<Address>(object), + thread_state); + return; + } +#else if (!ThreadState::IsAnyIncrementalMarking()) return; // The object may have been in-place constructed as part of a large object. // It is not safe to retrieve the page from the object here. ThreadState* const thread_state = ThreadState::Current(); - if (thread_state->IsIncrementalMarking()) { - // Eagerly trace the object ensuring that the object and all its children - // are discovered by the marker. - ThreadState::NoAllocationScope no_allocation_scope(thread_state); - DCHECK(thread_state->CurrentVisitor()); - // No weak handling for write barriers. Modifying weakly reachable objects - // strongifies them for the current cycle. - DCHECK(!Traits::kCanHaveDeletedValue || !Traits::IsDeletedValue(*object)); - TraceCollectionIfEnabled<WTF::kNoWeakHandling, T, Traits>::Trace( - thread_state->CurrentVisitor(), object); + if (!thread_state->IsIncrementalMarking()) { + return; } +#endif // BLINK_HEAP_YOUNG_GENERATION + // Eagerly trace the object ensuring that the object and all its children + // are discovered by the marker. + ThreadState::NoAllocationScope no_allocation_scope(thread_state); + DCHECK(thread_state->CurrentVisitor()); + // No weak handling for write barriers. Modifying weakly reachable objects + // strongifies them for the current cycle. + DCHECK(!Traits::kCanHaveDeletedValue || !Traits::IsDeletedValue(*object)); + TraceCollectionIfEnabled<WTF::kNoWeakHandling, T, Traits>::Trace( + thread_state->CurrentVisitor(), object); } template <typename T, typename Traits> static void NotifyNewObjects(T* array, size_t len) { +#if BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) + ThreadState* const thread_state = ThreadState::Current(); + if (!thread_state->IsIncrementalMarking()) { + MarkingVisitor::GenerationalBarrier(reinterpret_cast<Address>(array), + thread_state); + return; + } +#else if (!ThreadState::IsAnyIncrementalMarking()) return; // The object may have been in-place constructed as part of a large object. // It is not safe to retrieve the page from the object here. ThreadState* const thread_state = ThreadState::Current(); - if (thread_state->IsIncrementalMarking()) { - // See |NotifyNewObject| for details. - ThreadState::NoAllocationScope no_allocation_scope(thread_state); - DCHECK(thread_state->CurrentVisitor()); - // No weak handling for write barriers. Modifying weakly reachable objects - // strongifies them for the current cycle. - while (len-- > 0) { - DCHECK(!Traits::kCanHaveDeletedValue || - !Traits::IsDeletedValue(*array)); - TraceCollectionIfEnabled<WTF::kNoWeakHandling, T, Traits>::Trace( - thread_state->CurrentVisitor(), array); - array++; - } + if (!thread_state->IsIncrementalMarking()) { + return; + } +#endif // BLINK_HEAP_YOUNG_GENERATION + // See |NotifyNewObject| for details. + ThreadState::NoAllocationScope no_allocation_scope(thread_state); + DCHECK(thread_state->CurrentVisitor()); + // No weak handling for write barriers. Modifying weakly reachable objects + // strongifies them for the current cycle. + while (len-- > 0) { + DCHECK(!Traits::kCanHaveDeletedValue || !Traits::IsDeletedValue(*array)); + TraceCollectionIfEnabled<WTF::kNoWeakHandling, T, Traits>::Trace( + thread_state->CurrentVisitor(), array); + array++; } } template <typename T> static void TraceVectorBacking(Visitor* visitor, - T* backing, - T** backing_slot) { + const T* backing, + const T* const* backing_slot) { visitor->TraceBackingStoreStrongly( - reinterpret_cast<HeapVectorBacking<T>*>(backing), - reinterpret_cast<HeapVectorBacking<T>**>(backing_slot)); + reinterpret_cast<const HeapVectorBacking<T>*>(backing), + reinterpret_cast<const HeapVectorBacking<T>* const*>(backing_slot)); } template <typename T, typename HashTable> static void TraceHashTableBackingStrongly(Visitor* visitor, - T* backing, - T** backing_slot) { + const T* backing, + const T* const* backing_slot) { visitor->TraceBackingStoreStrongly( - reinterpret_cast<HeapHashTableBacking<HashTable>*>(backing), - reinterpret_cast<HeapHashTableBacking<HashTable>**>(backing_slot)); + reinterpret_cast<const HeapHashTableBacking<HashTable>*>(backing), + reinterpret_cast<const HeapHashTableBacking<HashTable>* const*>( + backing_slot)); } template <typename T, typename HashTable> static void TraceHashTableBackingWeakly(Visitor* visitor, - T* backing, - T** backing_slot, + const T* backing, + const T* const* backing_slot, WeakCallback callback, - void* parameter) { + const void* parameter) { visitor->TraceBackingStoreWeakly<HashTable>( - reinterpret_cast<HeapHashTableBacking<HashTable>*>(backing), - reinterpret_cast<HeapHashTableBacking<HashTable>**>(backing_slot), + reinterpret_cast<const HeapHashTableBacking<HashTable>*>(backing), + reinterpret_cast<const HeapHashTableBacking<HashTable>* const*>( + backing_slot), callback, parameter); } template <typename T, typename HashTable> static void TraceHashTableBackingOnly(Visitor* visitor, - T* backing, - T** backing_slot) { + const T* backing, + const T* const* backing_slot) { visitor->TraceBackingStoreOnly( - reinterpret_cast<HeapHashTableBacking<HashTable>*>(backing), - reinterpret_cast<HeapHashTableBacking<HashTable>**>(backing_slot)); + reinterpret_cast<const HeapHashTableBacking<HashTable>*>(backing), + reinterpret_cast<const HeapHashTableBacking<HashTable>* const*>( + backing_slot)); } private: @@ -318,7 +320,8 @@ class PLATFORM_EXPORT HeapAllocator { }; template <typename VisitorDispatcher, typename Value> -static void TraceListHashSetValue(VisitorDispatcher visitor, Value& value) { +static void TraceListHashSetValue(VisitorDispatcher visitor, + const Value& value) { // We use the default hash traits for the value in the node, because // ListHashSet does not let you specify any specific ones. // We don't allow ListHashSet of WeakMember, so we set that one false @@ -376,64 +379,22 @@ class HeapListHashSetAllocator : public HeapAllocator { } template <typename VisitorDispatcher> - static void TraceValue(VisitorDispatcher visitor, Node* node) { + static void TraceValue(VisitorDispatcher visitor, const Node* node) { TraceListHashSetValue(visitor, node->value_); } }; -template <typename T, typename Traits> -void HeapVectorBacking<T, Traits>::Finalize(void* pointer) { - static_assert(Traits::kNeedsDestruction, - "Only vector buffers with items requiring destruction should " - "be finalized"); - // See the comment in HeapVectorBacking::trace. - static_assert( - Traits::kCanClearUnusedSlotsWithMemset || std::is_polymorphic<T>::value, - "HeapVectorBacking doesn't support objects that cannot be cleared as " - "unused with memset or don't have a vtable"); - - static_assert( - !std::is_trivially_destructible<T>::value, - "Finalization of trivially destructible classes should not happen."); - HeapObjectHeader* header = HeapObjectHeader::FromPayload(pointer); - // Use the payload size as recorded by the heap to determine how many - // elements to finalize. - size_t length = header->PayloadSize() / sizeof(T); - char* payload = static_cast<char*>(pointer); -#ifdef ANNOTATE_CONTIGUOUS_CONTAINER - ANNOTATE_CHANGE_SIZE(payload, length * sizeof(T), 0, length * sizeof(T)); -#endif - // As commented above, HeapVectorBacking calls finalizers for unused slots - // (which are already zeroed out). - if (std::is_polymorphic<T>::value) { - for (unsigned i = 0; i < length; ++i) { - char* element = payload + i * sizeof(T); - if (blink::VTableInitialized(element)) - reinterpret_cast<T*>(element)->~T(); - } - } else { - T* buffer = reinterpret_cast<T*>(payload); - for (unsigned i = 0; i < length; ++i) - buffer[i].~T(); - } -} +namespace internal { -template <typename Table> -void HeapHashTableBacking<Table>::Finalize(void* pointer) { - using Value = typename Table::ValueType; - static_assert( - !std::is_trivially_destructible<Value>::value, - "Finalization of trivially destructible classes should not happen."); - HeapObjectHeader* header = HeapObjectHeader::FromPayload(pointer); - // Use the payload size as recorded by the heap to determine how many - // elements to finalize. - size_t length = header->PayloadSize() / sizeof(Value); - Value* table = reinterpret_cast<Value*>(pointer); - for (unsigned i = 0; i < length; ++i) { - if (!Table::IsEmptyOrDeletedBucket(table[i])) - table[i].~Value(); - } -} +template <typename T> +constexpr bool IsMember = WTF::IsSubclassOfTemplate<T, Member>::value; + +template <typename T> +constexpr bool IsMemberOrWeakMemberType = + WTF::IsSubclassOfTemplate<T, Member>::value || + WTF::IsSubclassOfTemplate<T, WeakMember>::value; + +} // namespace internal template <typename KeyArg, typename MappedArg, @@ -475,6 +436,10 @@ class HeapHashMap : public HashMap<KeyArg, HeapHashMap() { CheckType(); } }; +template <typename T, typename U, typename V, typename W, typename X> +struct GCInfoTrait<HeapHashMap<T, U, V, W, X>> + : public GCInfoTrait<HashMap<T, U, V, W, X, HeapAllocator>> {}; + template <typename ValueArg, typename HashArg = typename DefaultHash<ValueArg>::Hash, typename TraitsArg = HashTraits<ValueArg>> @@ -484,6 +449,8 @@ class HeapHashSet DISALLOW_NEW(); static void CheckType() { + static_assert(internal::IsMemberOrWeakMemberType<ValueArg>, + "HeapHashSet supports only Member and WeakMember."); static_assert(std::is_trivially_destructible<HeapHashSet>::value, "HeapHashSet must be trivially destructible."); static_assert( @@ -504,6 +471,10 @@ class HeapHashSet HeapHashSet() { CheckType(); } }; +template <typename T, typename U, typename V> +struct GCInfoTrait<HeapHashSet<T, U, V>> + : public GCInfoTrait<HashSet<T, U, V, HeapAllocator>> {}; + template <typename ValueArg, typename HashArg = typename DefaultHash<ValueArg>::Hash, typename TraitsArg = HashTraits<ValueArg>> @@ -516,6 +487,8 @@ class HeapLinkedHashSet DISALLOW_IN_CONTAINER(); static void CheckType() { + static_assert(internal::IsMemberOrWeakMemberType<ValueArg>, + "HeapLinkedHashSet supports only Member and WeakMember."); static_assert( IsAllowedInContainer<ValueArg>::value, "Not allowed to directly nest type. Use Member<> indirection instead."); @@ -534,6 +507,45 @@ class HeapLinkedHashSet HeapLinkedHashSet() { CheckType(); } }; +template <typename T, typename U, typename V> +struct GCInfoTrait<HeapLinkedHashSet<T, U, V>> + : public GCInfoTrait<LinkedHashSet<T, U, V, HeapAllocator>> {}; + +// This class is still experimental. Do not use this class. +template <typename ValueArg> +class HeapNewLinkedHashSet : public NewLinkedHashSet<ValueArg, HeapAllocator> { + IS_GARBAGE_COLLECTED_CONTAINER_TYPE(); + DISALLOW_NEW(); + + static void CheckType() { + // TODO(keinakashima): support WeakMember<T> + static_assert(internal::IsMember<ValueArg>, + "HeapNewLinkedHashSet supports only Member."); + // If not trivially destructible, we have to add a destructor which will + // hinder performance. + static_assert(std::is_trivially_destructible<HeapNewLinkedHashSet>::value, + "HeapNewLinkedHashSet must be trivially destructible."); + static_assert( + IsAllowedInContainer<ValueArg>::value, + "Not allowed to directly nest type. Use Member<> indirection instead."); + static_assert(WTF::IsTraceable<ValueArg>::value, + "For sets without traceable elements, use NewLinkedHashSet<> " + "instead of HeapNewLinkedHashSet<>."); + } + + public: + template <typename> + static void* AllocateObject(size_t size) { + return ThreadHeap::Allocate<HeapNewLinkedHashSet<ValueArg>>(size); + } + + HeapNewLinkedHashSet() { CheckType(); } +}; + +template <typename T> +struct GCInfoTrait<HeapNewLinkedHashSet<T>> + : public GCInfoTrait<NewLinkedHashSet<T, HeapAllocator>> {}; + template <typename ValueArg, wtf_size_t inlineCapacity = 0, // The inlineCapacity is just a dummy to @@ -548,6 +560,8 @@ class HeapListHashSet DISALLOW_NEW(); static void CheckType() { + static_assert(internal::IsMemberOrWeakMemberType<ValueArg>, + "HeapListHashSet supports only Member and WeakMember."); static_assert(std::is_trivially_destructible<HeapListHashSet>::value, "HeapListHashSet must be trivially destructible."); static_assert( @@ -568,6 +582,14 @@ class HeapListHashSet HeapListHashSet() { CheckType(); } }; +template <typename T, wtf_size_t inlineCapacity, typename U> +struct GCInfoTrait<HeapListHashSet<T, inlineCapacity, U>> + : public GCInfoTrait< + ListHashSet<T, + inlineCapacity, + U, + HeapListHashSetAllocator<T, inlineCapacity>>> {}; + template <typename Value, typename HashFunctions = typename DefaultHash<Value>::Hash, typename Traits = HashTraits<Value>> @@ -577,6 +599,8 @@ class HeapHashCountedSet DISALLOW_NEW(); static void CheckType() { + static_assert(internal::IsMemberOrWeakMemberType<Value>, + "HeapHashCountedSet supports only Member and WeakMember."); static_assert(std::is_trivially_destructible<HeapHashCountedSet>::value, "HeapHashCountedSet must be trivially destructible."); static_assert( @@ -597,6 +621,10 @@ class HeapHashCountedSet HeapHashCountedSet() { CheckType(); } }; +template <typename T, typename U, typename V> +struct GCInfoTrait<HeapHashCountedSet<T, U, V>> + : public GCInfoTrait<HashCountedSet<T, U, V, HeapAllocator>> {}; + template <typename T, wtf_size_t inlineCapacity = 0> class HeapVector : public Vector<T, inlineCapacity, HeapAllocator> { IS_GARBAGE_COLLECTED_CONTAINER_TYPE(); @@ -612,6 +640,8 @@ class HeapVector : public Vector<T, inlineCapacity, HeapAllocator> { static_assert(WTF::IsTraceable<T>::value, "For vectors without traceable elements, use Vector<> " "instead of HeapVector<>."); + static_assert(!WTF::IsWeak<T>::value, + "Weak types are not allowed in HeapVector."); } public: @@ -649,15 +679,19 @@ class HeapVector : public Vector<T, inlineCapacity, HeapAllocator> { } }; -template <typename T, wtf_size_t inlineCapacity = 0> -class HeapDeque : public Deque<T, inlineCapacity, HeapAllocator> { +template <typename T, wtf_size_t inlineCapacity> +struct GCInfoTrait<HeapVector<T, inlineCapacity>> + : public GCInfoTrait<Vector<T, inlineCapacity, HeapAllocator>> {}; + +template <typename T> +class HeapDeque : public Deque<T, 0, HeapAllocator> { IS_GARBAGE_COLLECTED_CONTAINER_TYPE(); DISALLOW_NEW(); static void CheckType() { - static_assert( - std::is_trivially_destructible<HeapDeque>::value || inlineCapacity, - "HeapDeque must be trivially destructible."); + static_assert(internal::IsMember<T>, "HeapDeque supports only Member."); + static_assert(std::is_trivially_destructible<HeapDeque>::value, + "HeapDeque must be trivially destructible."); static_assert( IsAllowedInContainer<T>::value, "Not allowed to directly nest type. Use Member<> indirection instead."); @@ -669,37 +703,33 @@ class HeapDeque : public Deque<T, inlineCapacity, HeapAllocator> { public: template <typename> static void* AllocateObject(size_t size) { - // On-heap HeapDeques generally should not have inline capacity, but it is - // hard to avoid when using a type alias. Hence we only disallow the - // VectorTraits<T>::kNeedsDestruction case for now. - static_assert(inlineCapacity == 0 || !VectorTraits<T>::kNeedsDestruction, - "on-heap HeapDeque<> should not have an inline capacity"); - return ThreadHeap::Allocate<HeapDeque<T, inlineCapacity>>(size); + return ThreadHeap::Allocate<HeapDeque<T>>(size); } HeapDeque() { CheckType(); } - explicit HeapDeque(wtf_size_t size) - : Deque<T, inlineCapacity, HeapAllocator>(size) { + explicit HeapDeque(wtf_size_t size) : Deque<T, 0, HeapAllocator>(size) { CheckType(); } HeapDeque(wtf_size_t size, const T& val) - : Deque<T, inlineCapacity, HeapAllocator>(size, val) { + : Deque<T, 0, HeapAllocator>(size, val) { CheckType(); } HeapDeque& operator=(const HeapDeque& other) { HeapDeque<T> copy(other); - Deque<T, inlineCapacity, HeapAllocator>::Swap(copy); + Deque<T, 0, HeapAllocator>::Swap(copy); return *this; } - template <wtf_size_t otherCapacity> - HeapDeque(const HeapDeque<T, otherCapacity>& other) - : Deque<T, inlineCapacity, HeapAllocator>(other) {} + HeapDeque(const HeapDeque<T>& other) : Deque<T, 0, HeapAllocator>(other) {} }; +template <typename T> +struct GCInfoTrait<HeapDeque<T>> + : public GCInfoTrait<Deque<T, 0, HeapAllocator>> {}; + } // namespace blink namespace WTF { @@ -745,8 +775,8 @@ struct VectorTraits<blink::HeapVector<T, 0>> }; template <typename T> -struct VectorTraits<blink::HeapDeque<T, 0>> - : VectorTraitsBase<blink::HeapDeque<T, 0>> { +struct VectorTraits<blink::HeapDeque<T>> + : VectorTraitsBase<blink::HeapDeque<T>> { STATIC_ONLY(VectorTraits); static const bool kNeedsDestruction = false; static const bool kCanInitializeWithMemset = true; @@ -766,18 +796,6 @@ struct VectorTraits<blink::HeapVector<T, inlineCapacity>> static const bool kCanMoveWithMemcpy = VectorTraits<T>::kCanMoveWithMemcpy; }; -template <typename T, wtf_size_t inlineCapacity> -struct VectorTraits<blink::HeapDeque<T, inlineCapacity>> - : VectorTraitsBase<blink::HeapDeque<T, inlineCapacity>> { - STATIC_ONLY(VectorTraits); - static const bool kNeedsDestruction = VectorTraits<T>::kNeedsDestruction; - static const bool kCanInitializeWithMemset = - VectorTraits<T>::kCanInitializeWithMemset; - static const bool kCanClearUnusedSlotsWithMemset = - VectorTraits<T>::kCanClearUnusedSlotsWithMemset; - static const bool kCanMoveWithMemcpy = VectorTraits<T>::kCanMoveWithMemcpy; -}; - template <typename T> struct HashTraits<blink::Member<T>> : SimpleClassHashTraits<blink::Member<T>> { STATIC_ONLY(HashTraits); @@ -808,9 +826,6 @@ struct HashTraits<blink::Member<T>> : SimpleClassHashTraits<blink::Member<T>> { static void ConstructDeletedValue(blink::Member<T>& slot, bool) { slot = WTF::kHashTableDeletedValue; } - static bool IsDeletedValue(const blink::Member<T>& value) { - return value.IsHashTableDeletedValue(); - } }; template <typename T> @@ -841,6 +856,10 @@ struct HashTraits<blink::WeakMember<T>> } static PeekOutType Peek(const blink::WeakMember<T>& value) { return value; } + + static void ConstructDeletedValue(blink::WeakMember<T>& slot, bool) { + slot = WTF::kHashTableDeletedValue; + } }; template <typename T> diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_compact.cc b/chromium/third_party/blink/renderer/platform/heap/heap_compact.cc index 1d9a577d2e3..24194d0db0b 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_compact.cc +++ b/chromium/third_party/blink/renderer/platform/heap/heap_compact.cc @@ -38,7 +38,7 @@ class HeapCompact::MovableObjectFixups final { void AddCompactingPage(BasePage* page); // Adds a slot for compaction. Filters slots in dead objects. - void AddOrFilter(MovableReference* slot); + void AddOrFilter(const MovableReference*); // Relocates a backing store |from| -> |to|. void Relocate(Address from, Address to); @@ -89,7 +89,7 @@ class HeapCompact::MovableObjectFixups final { #if DCHECK_IS_ON() // The following two collections are used to allow refer back from a slot to // an already moved object. - HashSet<void*> moved_objects_; + HashSet<const void*> moved_objects_; HashMap<MovableReference*, MovableReference> interior_slot_to_object_; #endif // DCHECK_IS_ON() }; @@ -99,8 +99,9 @@ void HeapCompact::MovableObjectFixups::AddCompactingPage(BasePage* page) { relocatable_pages_.insert(page); } -void HeapCompact::MovableObjectFixups::AddOrFilter(MovableReference* slot) { - MovableReference value = *slot; +void HeapCompact::MovableObjectFixups::AddOrFilter( + const MovableReference* const_slot) { + const void* value = *const_slot; CHECK(value); // All slots and values are part of Oilpan's heap. @@ -111,13 +112,13 @@ void HeapCompact::MovableObjectFixups::AddOrFilter(MovableReference* slot) { // Slots handling. BasePage* const slot_page = - heap_->LookupPageForAddress(reinterpret_cast<Address>(slot)); + heap_->LookupPageForAddress(reinterpret_cast<ConstAddress>(const_slot)); CHECK(slot_page); HeapObjectHeader* const header = slot_page->IsLargeObjectPage() ? static_cast<LargeObjectPage*>(slot_page)->ObjectHeader() : static_cast<NormalPage*>(slot_page)->FindHeaderFromAddress( - reinterpret_cast<Address>(slot)); + reinterpret_cast<ConstAddress>(const_slot)); CHECK(header); // Filter the slot since the object that contains the slot is dead. if (!header->IsMarked()) @@ -125,7 +126,7 @@ void HeapCompact::MovableObjectFixups::AddOrFilter(MovableReference* slot) { // Value handling. BasePage* const value_page = - heap_->LookupPageForAddress(reinterpret_cast<Address>(value)); + heap_->LookupPageForAddress(reinterpret_cast<ConstAddress>(value)); CHECK(value_page); // The following cases are not compacted and do not require recording: @@ -142,7 +143,7 @@ void HeapCompact::MovableObjectFixups::AddOrFilter(MovableReference* slot) { // dynamic header lookup is required. HeapObjectHeader* const value_header = static_cast<NormalPage*>(value_page) - ->FindHeaderFromAddress(reinterpret_cast<Address>(value)); + ->FindHeaderFromAddress(reinterpret_cast<ConstAddress>(value)); CHECK(value_header); CHECK(value_header->IsMarked()); @@ -151,11 +152,12 @@ void HeapCompact::MovableObjectFixups::AddOrFilter(MovableReference* slot) { // times. auto fixup_it = fixups_.find(value); if (UNLIKELY(fixup_it != fixups_.end())) { - CHECK_EQ(slot, fixup_it->value); + CHECK_EQ(const_slot, fixup_it->value); return; } // Add regular fixup. + MovableReference* slot = const_cast<MovableReference*>(const_slot); fixups_.insert(value, slot); // Check whether the slot itself resides on a page that is compacted. @@ -301,7 +303,7 @@ void HeapCompact::MovableObjectFixups::VerifyUpdatedSlot( if (!*slot) return; BasePage* slot_page = - heap_->LookupPageForAddress(reinterpret_cast<Address>(*slot)); + heap_->LookupPageForAddress(reinterpret_cast<ConstAddress>(*slot)); // ref_page is null if *slot is pointing to an off-heap region. This may // happy if *slot is pointing to an inline buffer of HeapVector with // inline capacity. @@ -373,9 +375,7 @@ void HeapCompact::Initialize(ThreadState* state) { force_for_next_gc_ = false; } -bool HeapCompact::ShouldRegisterMovingAddress(Address address) { - CHECK(heap_->LookupPageForAddress(reinterpret_cast<Address>(address))); - +bool HeapCompact::ShouldRegisterMovingAddress() { return do_compact_; } @@ -442,8 +442,9 @@ void HeapCompact::FilterNonLiveSlots() { last_fixup_count_for_testing_ = 0; MovableReferenceWorklist::View traced_slots( heap_->GetMovableReferenceWorklist(), WorklistTaskId::MutatorThread); - MovableReference* slot; + const MovableReference* slot; while (traced_slots.Pop(&slot)) { + CHECK(heap_->LookupPageForAddress(reinterpret_cast<ConstAddress>(slot))); if (*slot) { Fixups().AddOrFilter(slot); last_fixup_count_for_testing_++; diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_compact.h b/chromium/third_party/blink/renderer/platform/heap/heap_compact.h index be7b142e17b..93624129098 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_compact.h +++ b/chromium/third_party/blink/renderer/platform/heap/heap_compact.h @@ -64,7 +64,7 @@ class PLATFORM_EXPORT HeapCompact final { } // See |Heap::ShouldRegisterMovingAddress()| documentation. - bool ShouldRegisterMovingAddress(Address address); + bool ShouldRegisterMovingAddress(); // Slots that are not contained within live objects are filtered. This can // happen when the write barrier for in-payload objects triggers but the outer diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_compact_test.cc b/chromium/third_party/blink/renderer/platform/heap/heap_compact_test.cc index e2eef4b7e05..f87e00b143f 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_compact_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/heap_compact_test.cc @@ -224,26 +224,6 @@ TEST_F(HeapCompactTest, CompactDeques) { EXPECT_EQ(static_cast<int>(7 - i), deque->at(i)->Value()); } -TEST_F(HeapCompactTest, CompactDequeVectors) { - Persistent<HeapDeque<IntVector>> deque = - MakeGarbageCollected<HeapDeque<IntVector>>(); - for (int i = 0; i < 8; ++i) { - IntWrapper* value = IntWrapper::Create(i, VectorsAreCompacted); - IntVector vector = IntVector(8, value); - deque->push_front(vector); - } - EXPECT_EQ(8u, deque->size()); - - for (wtf_size_t i = 0; i < deque->size(); ++i) - EXPECT_EQ(static_cast<int>(7 - i), deque->at(i).at(i)->Value()); - - PerformHeapCompaction(); - EXPECT_TRUE(IntWrapper::did_verify_at_least_once); - - for (wtf_size_t i = 0; i < deque->size(); ++i) - EXPECT_EQ(static_cast<int>(7 - i), deque->at(i).at(i)->Value()); -} - TEST_F(HeapCompactTest, CompactLinkedHashSet) { using OrderedHashSet = HeapLinkedHashSet<Member<IntWrapper>>; Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>(); @@ -357,6 +337,135 @@ TEST_F(HeapCompactTest, CompactLinkedHashSetNested) { } } +TEST_F(HeapCompactTest, CompactNewLinkedHashSet) { + using OrderedHashSet = HeapNewLinkedHashSet<Member<IntWrapper>>; + Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>(); + for (int i = 0; i < 13; ++i) { + IntWrapper* value = IntWrapper::Create(i, HashTablesAreCompacted); + set->insert(value); + } + EXPECT_EQ(13u, set->size()); + + int expected = 0; + for (IntWrapper* v : *set) { + EXPECT_EQ(expected, v->Value()); + expected++; + } + + for (int i = 1; i < 13; i += 2) { + auto it = set->begin(); + for (int j = 0; j < (i + 1) / 2; ++j) { + ++it; + } + set->erase(it); + } + EXPECT_EQ(7u, set->size()); + + expected = 0; + for (IntWrapper* v : *set) { + EXPECT_EQ(expected, v->Value()); + expected += 2; + } + + PerformHeapCompaction(); + EXPECT_TRUE(IntWrapper::did_verify_at_least_once); + + expected = 0; + for (IntWrapper* v : *set) { + EXPECT_EQ(expected, v->Value()); + expected += 2; + } + EXPECT_EQ(7u, set->size()); +} + +TEST_F(HeapCompactTest, CompactNewLinkedHashSetVector) { + using OrderedHashSet = HeapNewLinkedHashSet<Member<IntVector>>; + Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>(); + for (int i = 0; i < 13; ++i) { + IntWrapper* value = IntWrapper::Create(i); + IntVector* vector = MakeGarbageCollected<IntVector>(19, value); + set->insert(vector); + } + EXPECT_EQ(13u, set->size()); + + int expected = 0; + for (IntVector* v : *set) { + EXPECT_EQ(expected, (*v)[0]->Value()); + expected++; + } + + PerformHeapCompaction(); + EXPECT_TRUE(IntWrapper::did_verify_at_least_once); + + expected = 0; + for (IntVector* v : *set) { + EXPECT_EQ(expected, (*v)[0]->Value()); + expected++; + } +} + +TEST_F(HeapCompactTest, CompactNewLinkedHashSetMap) { + using Inner = HeapHashSet<Member<IntWrapper>>; + using OrderedHashSet = HeapNewLinkedHashSet<Member<Inner>>; + + Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>(); + for (int i = 0; i < 13; ++i) { + IntWrapper* value = IntWrapper::Create(i); + Inner* inner = MakeGarbageCollected<Inner>(); + inner->insert(value); + set->insert(inner); + } + EXPECT_EQ(13u, set->size()); + + int expected = 0; + for (const Inner* v : *set) { + EXPECT_EQ(1u, v->size()); + EXPECT_EQ(expected, (*v->begin())->Value()); + expected++; + } + + PerformHeapCompaction(); + EXPECT_TRUE(IntWrapper::did_verify_at_least_once); + + expected = 0; + for (const Inner* v : *set) { + EXPECT_EQ(1u, v->size()); + EXPECT_EQ(expected, (*v->begin())->Value()); + expected++; + } +} + +TEST_F(HeapCompactTest, CompactNewLinkedHashSetNested) { + using Inner = HeapNewLinkedHashSet<Member<IntWrapper>>; + using OrderedHashSet = HeapNewLinkedHashSet<Member<Inner>>; + + Persistent<OrderedHashSet> set = MakeGarbageCollected<OrderedHashSet>(); + for (int i = 0; i < 13; ++i) { + IntWrapper* value = IntWrapper::Create(i); + Inner* inner = MakeGarbageCollected<Inner>(); + inner->insert(value); + set->insert(inner); + } + EXPECT_EQ(13u, set->size()); + + int expected = 0; + for (const Inner* v : *set) { + EXPECT_EQ(1u, v->size()); + EXPECT_EQ(expected, (*v->begin())->Value()); + expected++; + } + + PerformHeapCompaction(); + EXPECT_TRUE(IntWrapper::did_verify_at_least_once); + + expected = 0; + for (const Inner* v : *set) { + EXPECT_EQ(1u, v->size()); + EXPECT_EQ(expected, (*v->begin())->Value()); + expected++; + } +} + TEST_F(HeapCompactTest, CompactInlinedBackingStore) { // Regression test: https://crbug.com/875044 // diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_page.cc b/chromium/third_party/blink/renderer/platform/heap/heap_page.cc index 953d4214834..ae8472c80d1 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_page.cc +++ b/chromium/third_party/blink/renderer/platform/heap/heap_page.cc @@ -89,21 +89,19 @@ namespace blink { void HeapObjectHeader::Finalize(Address object, size_t object_size) { HeapAllocHooks::FreeHookIfEnabled(object); - const GCInfo* gc_info = GCInfoTable::Get().GCInfoFromIndex(GcInfoIndex()); - if (gc_info->finalize) - gc_info->finalize(object); + const GCInfo& gc_info = GCInfo::From(GcInfoIndex()); + if (gc_info.finalize) + gc_info.finalize(object); ASAN_RETIRE_CONTAINER_ANNOTATION(object, object_size); } bool HeapObjectHeader::HasNonTrivialFinalizer() const { - const GCInfo* gc_info = GCInfoTable::Get().GCInfoFromIndex(GcInfoIndex()); - return gc_info->finalize; + return GCInfo::From(GcInfoIndex()).finalize; } const char* HeapObjectHeader::Name() const { - const GCInfo* gc_info = GCInfoTable::Get().GCInfoFromIndex(GcInfoIndex()); - return gc_info->name(Payload()).value; + return GCInfo::From(GcInfoIndex()).name(Payload()).value; } BaseArena::BaseArena(ThreadState* state, int index) @@ -132,7 +130,7 @@ void BaseArena::CollectStatistics(std::string name, ResetAllocationPoint(); if (!NameClient::HideInternalName()) { - size_t num_types = GCInfoTable::Get().GcInfoIndex() + 1; + const size_t num_types = GCInfoTable::Get().NumberOfGCInfos(); arena_stats.object_stats.num_types = num_types; arena_stats.object_stats.type_name.resize(num_types); arena_stats.object_stats.type_count.resize(num_types); @@ -156,7 +154,7 @@ void NormalPageArena::CollectFreeListStatistics( } #if DCHECK_IS_ON() -BasePage* BaseArena::FindPageFromAddress(Address address) { +BasePage* BaseArena::FindPageFromAddress(ConstAddress address) const { for (BasePage* page : swept_pages_) { if (page->Contains(address)) return page; @@ -215,6 +213,15 @@ void BaseArena::MakeConsistentForMutator() { VerifyObjectStartBitmap(); } +void BaseArena::Unmark() { + DCHECK(GetThreadState()->InAtomicMarkingPause()); + DCHECK(SweepingAndFinalizationCompleted()); + + for (BasePage* page : swept_pages_) { + page->Unmark(); + } +} + size_t BaseArena::ObjectPayloadSizeForTesting() { #if DCHECK_IS_ON() DCHECK(IsConsistentForGC()); @@ -228,7 +235,7 @@ size_t BaseArena::ObjectPayloadSizeForTesting() { return object_payload_size; } -void BaseArena::PrepareForSweep() { +void BaseArena::PrepareForSweep(BlinkGC::CollectionType collection_type) { DCHECK(GetThreadState()->InAtomicMarkingPause()); DCHECK(SweepingAndFinalizationCompleted()); @@ -237,10 +244,23 @@ void BaseArena::PrepareForSweep() { // Verification depends on the allocation point being cleared. VerifyObjectStartBitmap(); + if (collection_type == BlinkGC::CollectionType::kMinor) { + auto** first_young = + std::partition(swept_pages_.begin(), swept_pages_.end(), + [](BasePage* page) { return !page->IsYoung(); }); + for (auto** it = first_young; it != swept_pages_.end(); ++it) { + BasePage* page = *it; + page->MarkAsUnswept(); + page->SetAsYoung(false); + unswept_pages_.Push(page); + } + swept_pages_.erase(first_young, swept_pages_.end()); + return; + } + for (BasePage* page : swept_pages_) { page->MarkAsUnswept(); } - // Move all pages to a list of unswept pages. unswept_pages_.MoveFrom(std::move(swept_pages_)); DCHECK(swept_pages_.IsEmpty()); @@ -268,7 +288,7 @@ Address BaseArena::LazySweep(size_t allocation_size, size_t gc_info_index) { if (GetThreadState()->SweepForbidden()) return nullptr; - ThreadHeapStatsCollector::Scope stats_scope( + ThreadHeapStatsCollector::EnabledScope stats_scope( GetThreadState()->Heap().stats_collector(), ThreadHeapStatsCollector::kLazySweepOnAllocation); ThreadState::SweepForbiddenScope sweep_forbidden(GetThreadState()); @@ -311,11 +331,9 @@ bool BaseArena::LazySweepWithDeadline(base::TimeTicks deadline) { DCHECK(ScriptForbiddenScope::IsScriptForbidden()); size_t page_count = 1; - // TODO(bikineev): We should probably process pages in the reverse order. This - // will leave more work for concurrent sweeper and reduce memory footprint - // faster. - while (BasePage* page = unswept_pages_.PopLocked()) { - SweepUnsweptPage(page); + // First, process empty pages to faster reduce memory footprint. + while (BasePage* page = swept_unfinalized_empty_pages_.PopLocked()) { + page->FinalizeSweep(SweepResult::kPageEmpty); if (page_count % kDeadlineCheckInterval == 0) { if (deadline <= base::TimeTicks::Now()) { // Deadline has come. @@ -324,6 +342,7 @@ bool BaseArena::LazySweepWithDeadline(base::TimeTicks deadline) { } page_count++; } + // Second, execute finalizers to leave more work for concurrent sweeper. while (BasePage* page = swept_unfinalized_pages_.PopLocked()) { swept_pages_.PushLocked(page); page->FinalizeSweep(SweepResult::kPageNotEmpty); @@ -335,8 +354,9 @@ bool BaseArena::LazySweepWithDeadline(base::TimeTicks deadline) { } page_count++; } - while (BasePage* page = swept_unfinalized_empty_pages_.PopLocked()) { - page->FinalizeSweep(SweepResult::kPageEmpty); + // Help concurrent sweeper. + while (BasePage* page = unswept_pages_.PopLocked()) { + SweepUnsweptPage(page); if (page_count % kDeadlineCheckInterval == 0) { if (deadline <= base::TimeTicks::Now()) { // Deadline has come. @@ -362,18 +382,12 @@ void BaseArena::InvokeFinalizersOnSweptPages() { } } -bool BaseArena::ConcurrentSweepWithDeadline(base::TimeTicks deadline) { - static constexpr size_t kDeadlineCheckInterval = 10; - size_t page_count = 1; - while (BasePage* page = unswept_pages_.PopLocked()) { - SweepUnsweptPageOnConcurrentThread(page); - if (page_count % kDeadlineCheckInterval == 0 && - deadline <= base::TimeTicks::Now()) { - return SweepingCompleted(); - } - ++page_count; - } - return true; +bool BaseArena::ConcurrentSweepOnePage() { + BasePage* page = unswept_pages_.PopLocked(); + if (!page) + return true; + SweepUnsweptPageOnConcurrentThread(page); + return false; } void BaseArena::CompleteSweep() { @@ -384,14 +398,14 @@ void BaseArena::CompleteSweep() { // Some phases, e.g. verification, require iterability of a page. MakeIterable(); - // First, sweep and finalize pages. + // First, finalize pages that have been processed by concurrent sweepers. + InvokeFinalizersOnSweptPages(); + + // Then, sweep and finalize pages. while (BasePage* page = unswept_pages_.PopLocked()) { SweepUnsweptPage(page); } - // Then, finalize pages that have been processed by concurrent sweepers. - InvokeFinalizersOnSweptPages(); - // Verify object start bitmap after all freelists have been merged. VerifyObjectStartBitmap(); } @@ -410,9 +424,17 @@ NormalPageArena::NormalPageArena(ThreadState* state, int index) : BaseArena(state, index), current_allocation_point_(nullptr), remaining_allocation_size_(0), - last_remaining_allocation_size_(0), - promptly_freed_size_(0) { - ClearFreeLists(); + promptly_freed_size_(0) {} + +void NormalPageArena::AddToFreeList(Address address, size_t size) { +#if DCHECK_IS_ON() + DCHECK(FindPageFromAddress(address)); + DCHECK(FindPageFromAddress(address + size - 1)); +#endif + free_list_.Add(address, size); + static_cast<NormalPage*>(PageFromObject(address)) + ->object_start_bit_map() + ->SetBit(address); } void NormalPageArena::MakeConsistentForGC() { @@ -621,7 +643,7 @@ bool NormalPageArena::IsConsistentForGC() { return true; } -bool NormalPageArena::PagesToBeSweptContains(Address address) { +bool NormalPageArena::PagesToBeSweptContains(ConstAddress address) const { for (BasePage* page : unswept_pages_) { if (page->Contains(address)) return true; @@ -716,7 +738,7 @@ void NormalPageArena::PromptlyFreeObject(HeapObjectHeader* header) { if (IsObjectAllocatedAtAllocationPoint(header)) { current_allocation_point_ -= size; DCHECK_EQ(address, current_allocation_point_); - SetRemainingAllocationSize(remaining_allocation_size_ + size); + remaining_allocation_size_ += size; SET_MEMORY_INACCESSIBLE(address, size); // Memory that is part of the allocation point is not allowed to be part // of the object start bit map. @@ -763,7 +785,7 @@ bool NormalPageArena::ExpandObject(HeapObjectHeader* header, size_t new_size) { expand_size <= remaining_allocation_size_) { current_allocation_point_ += expand_size; DCHECK_GE(remaining_allocation_size_, expand_size); - SetRemainingAllocationSize(remaining_allocation_size_ - expand_size); + remaining_allocation_size_ -= expand_size; // Unpoison the memory used for the object (payload). SET_MEMORY_ACCESSIBLE(header->PayloadEnd(), expand_size); header->SetSize(allocation_size); @@ -782,7 +804,7 @@ bool NormalPageArena::ShrinkObject(HeapObjectHeader* header, size_t new_size) { size_t shrink_size = header->size() - allocation_size; if (IsObjectAllocatedAtAllocationPoint(header)) { current_allocation_point_ -= shrink_size; - SetRemainingAllocationSize(remaining_allocation_size_ + shrink_size); + remaining_allocation_size_ += shrink_size; SET_MEMORY_INACCESSIBLE(current_allocation_point_, shrink_size); header->SetSize(allocation_size); return true; @@ -790,9 +812,9 @@ bool NormalPageArena::ShrinkObject(HeapObjectHeader* header, size_t new_size) { DCHECK_GE(shrink_size, sizeof(HeapObjectHeader)); DCHECK_GT(header->GcInfoIndex(), 0u); Address shrink_address = header->PayloadEnd() - shrink_size; - HeapObjectHeader* freed_header = - new (NotNull, shrink_address) HeapObjectHeader( - shrink_size, header->GcInfoIndex(), HeapObjectHeader::kNormalPage); + HeapObjectHeader* freed_header = new (NotNull, shrink_address) + HeapObjectHeader(shrink_size, header->GcInfoIndex()); + // Since only size has been changed, we don't need to update object starts. PromptlyFreeObjectInFreeList(freed_header, shrink_size); #if DCHECK_IS_ON() DCHECK_EQ(PageFromObject(reinterpret_cast<Address>(header)), @@ -843,24 +865,6 @@ Address NormalPageArena::LazySweepPages(size_t allocation_size, return result; } -void NormalPageArena::SetRemainingAllocationSize( - size_t new_remaining_allocation_size) { - remaining_allocation_size_ = new_remaining_allocation_size; - - // Sync recorded allocated-object size using the recorded checkpoint in - // |remaining_allocation_size_|: - // - If checkpoint is larger, the allocated size has increased. - // - The allocated size has decreased, otherwise. - if (last_remaining_allocation_size_ > remaining_allocation_size_) { - GetThreadState()->Heap().stats_collector()->IncreaseAllocatedObjectSize( - last_remaining_allocation_size_ - remaining_allocation_size_); - } else if (last_remaining_allocation_size_ != remaining_allocation_size_) { - GetThreadState()->Heap().stats_collector()->DecreaseAllocatedObjectSize( - remaining_allocation_size_ - last_remaining_allocation_size_); - } - last_remaining_allocation_size_ = remaining_allocation_size_; -} - void NormalPageArena::SetAllocationPoint(Address point, size_t size) { #if DCHECK_IS_ON() if (point) { @@ -878,7 +882,11 @@ void NormalPageArena::SetAllocationPoint(Address point, size_t size) { } // Set up a new linear allocation area. current_allocation_point_ = point; - last_remaining_allocation_size_ = remaining_allocation_size_ = size; + remaining_allocation_size_ = size; + // Update last allocated region in ThreadHeap. This must also be done if the + // allocation point is set to 0 (before doing GC), so that the last allocated + // region is automatically reset after GC. + GetThreadState()->Heap().SetLastAllocatedRegion(point, size); if (point) { // Only, update allocated size and object start bitmap if the area is // actually set up with a non-null address. @@ -889,6 +897,8 @@ void NormalPageArena::SetAllocationPoint(Address point, size_t size) { // clearing the allocation point. NormalPage* page = reinterpret_cast<NormalPage*>(PageFromObject(point)); page->object_start_bit_map()->ClearBit(point); + // Mark page as containing young objects. + page->SetAsYoung(true); } } @@ -985,8 +995,8 @@ Address LargeObjectArena::DoAllocateLargeObjectPage(size_t allocation_size, DCHECK_GT(gc_info_index, 0u); LargeObjectPage* large_object = new (large_object_address) LargeObjectPage(page_memory, this, allocation_size); - HeapObjectHeader* header = new (NotNull, header_address) HeapObjectHeader( - kLargeObjectSizeInHeader, gc_info_index, HeapObjectHeader::kLargePage); + HeapObjectHeader* header = new (NotNull, header_address) + HeapObjectHeader(kLargeObjectSizeInHeader, gc_info_index); Address result = header_address + sizeof(*header); DCHECK(!(reinterpret_cast<uintptr_t>(result) & kAllocationMask)); @@ -997,6 +1007,10 @@ Address LargeObjectArena::DoAllocateLargeObjectPage(size_t allocation_size, swept_pages_.PushLocked(large_object); + // Update last allocated region in ThreadHeap. + GetThreadState()->Heap().SetLastAllocatedRegion(large_object->Payload(), + large_object->PayloadSize()); + // Add all segments of kBlinkPageSize to the bloom filter so that the large // object can be kept by derived pointers on stack. An alternative might be to // prohibit derived pointers to large objects, but that is dangerous since the @@ -1009,6 +1023,8 @@ Address LargeObjectArena::DoAllocateLargeObjectPage(size_t allocation_size, large_object->size()); GetThreadState()->Heap().stats_collector()->IncreaseAllocatedObjectSize( large_object->PayloadSize()); + // Add page to the list of young pages. + large_object->SetAsYoung(true); return result; } @@ -1053,7 +1069,9 @@ Address LargeObjectArena::LazySweepPages(size_t allocation_size, return result; } -FreeList::FreeList() : biggest_free_list_index_(0) {} +FreeList::FreeList() : biggest_free_list_index_(0) { + Clear(); +} void FreeList::Add(Address address, size_t size) { DCHECK_LT(size, BlinkPagePayloadSize()); @@ -1062,15 +1080,15 @@ void FreeList::Add(Address address, size_t size) { DCHECK(!((reinterpret_cast<uintptr_t>(address) + sizeof(HeapObjectHeader)) & kAllocationMask)); DCHECK(!(size & kAllocationMask)); + DCHECK(!PageFromObject(address)->IsLargeObjectPage()); ASAN_UNPOISON_MEMORY_REGION(address, size); FreeListEntry* entry; if (size < sizeof(*entry)) { // Create a dummy header with only a size and freelist bit set. DCHECK_GE(size, sizeof(HeapObjectHeader)); // Free list encode the size to mark the lost memory as freelist memory. - new (NotNull, address) HeapObjectHeader(size, kGcInfoIndexForFreeListHeader, - HeapObjectHeader::kNormalPage); - + new (NotNull, address) + HeapObjectHeader(size, kGcInfoIndexForFreeListHeader); ASAN_POISON_MEMORY_REGION(address, size); // This memory gets lost. Sweeping can reclaim it. return; @@ -1381,11 +1399,18 @@ void NormalPage::ToBeFinalizedObject::Finalize() { } void NormalPage::FinalizeSweep(SweepResult action) { + // Call finalizers. for (ToBeFinalizedObject& object : to_be_finalized_objects_) { object.Finalize(); } to_be_finalized_objects_.clear(); - +#if BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) + // Copy object start bit map. + DCHECK(cached_object_start_bit_map_); + object_start_bit_map_ = *cached_object_start_bit_map_; + cached_object_start_bit_map_.reset(); +#endif + // Merge freelists or unmap the page. if (action == SweepResult::kPageNotEmpty) { MergeFreeLists(); MarkAsSwept(); @@ -1407,6 +1432,11 @@ void NormalPage::AddToFreeList(Address start, unfinalized_freelist_.push_back(std::move(entry)); } else { cached_freelist_.Add(start, size); +#if BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) + cached_object_start_bit_map_->SetBit(start); +#else + object_start_bit_map_.SetBit(start); +#endif #if !DCHECK_IS_ON() && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER) if (Arena()->GetThreadState()->IsMemoryReducingGC()) { DiscardPages(start + sizeof(FreeListEntry), start + size); @@ -1433,7 +1463,14 @@ void NormalPage::MergeFreeLists() { } bool NormalPage::Sweep(FinalizeType finalize_type) { + ObjectStartBitmap* bitmap; +#if BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) + cached_object_start_bit_map_ = std::make_unique<ObjectStartBitmap>(Payload()); + bitmap = cached_object_start_bit_map_.get(); +#else object_start_bit_map()->Clear(); + bitmap = object_start_bit_map(); +#endif cached_freelist_.Clear(); unfinalized_freelist_.clear(); Address start_of_gap = Payload(); @@ -1478,8 +1515,10 @@ bool NormalPage::Sweep(FinalizeType finalize_type) { found_finalizer); found_finalizer = false; } - object_start_bit_map()->SetBit(header_address); + bitmap->SetBit(header_address); +#if !BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) header->Unmark<HeapObjectHeader::AccessMode::kAtomic>(); +#endif header_address += size; start_of_gap = header_address; } @@ -1543,7 +1582,9 @@ void NormalPage::SweepAndCompact(CompactionContext& context) { header_address += size; continue; } +#if !BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) header->Unmark(); +#endif // Allocate and copy over the live object. Address compact_frontier = current_page->Payload() + allocation_point; if (compact_frontier + size > current_page->PayloadEnd()) { @@ -1570,8 +1611,9 @@ void NormalPage::SweepAndCompact(CompactionContext& context) { // store object, let go of the container annotations. // Do that by unpoisoning the payload entirely. ASAN_UNPOISON_MEMORY_REGION(header, sizeof(HeapObjectHeader)); - if (is_vector_arena) + if (is_vector_arena) { ASAN_UNPOISON_MEMORY_REGION(payload, payload_size); + } #endif // Use a non-overlapping copy, if possible. if (current_page == this) @@ -1635,6 +1677,30 @@ void NormalPage::MakeConsistentForMutator() { VerifyObjectStartBitmapIsConsistentWithPayload(); } +// This is assumed to be called from the atomic pause, so no concurrency should +// be involved here. +void NormalPage::Unmark() { + const Address current_allocation_point = + ArenaForNormalPage()->CurrentAllocationPoint(); + const size_t allocation_area_size = + ArenaForNormalPage()->RemainingAllocationSize(); + for (Address header_address = Payload(); header_address < PayloadEnd();) { + // Since unmarking can happen inside IncrementalMarkingStart, the current + // allocation point can be set and we need to skip over it. + if (header_address == current_allocation_point && allocation_area_size) { + header_address += allocation_area_size; + continue; + } + HeapObjectHeader* header = + reinterpret_cast<HeapObjectHeader*>(header_address); + if (header->IsMarked()) { + header->Unmark(); + } + header_address += header->size(); + } + ClearCardTable(); +} + #if defined(ADDRESS_SANITIZER) void NormalPage::PoisonUnmarkedObjects() { for (Address header_address = Payload(); header_address < PayloadEnd();) { @@ -1647,8 +1713,9 @@ void NormalPage::PoisonUnmarkedObjects() { header_address += header->size(); continue; } - if (!header->IsMarked()) + if (!header->IsMarked()) { ASAN_POISON_MEMORY_REGION(header->Payload(), header->PayloadSize()); + } header_address += header->size(); } } @@ -1693,7 +1760,7 @@ void LargeObjectPage::VerifyMarking() { } Address ObjectStartBitmap::FindHeader( - Address address_maybe_pointing_to_the_middle_of_object) { + ConstAddress address_maybe_pointing_to_the_middle_of_object) const { size_t object_offset = address_maybe_pointing_to_the_middle_of_object - offset_; size_t object_start_number = object_offset / kAllocationGranularity; @@ -1716,7 +1783,7 @@ Address ObjectStartBitmap::FindHeader( } HeapObjectHeader* NormalPage::ConservativelyFindHeaderFromAddress( - Address address) { + ConstAddress address) const { if (!ContainedInObjectPayload(address)) return nullptr; if (ArenaForNormalPage()->IsInCurrentAllocationPointRegion(address)) @@ -1759,7 +1826,7 @@ void NormalPage::CollectStatistics( } #if DCHECK_IS_ON() -bool NormalPage::Contains(Address addr) { +bool NormalPage::Contains(ConstAddress addr) const { Address blink_page_start = RoundToBlinkPageStart(GetAddress()); // Page is at aligned address plus guard page size. DCHECK_EQ(blink_page_start, GetAddress() - kBlinkGuardPageSize); @@ -1791,15 +1858,22 @@ bool LargeObjectPage::Sweep(FinalizeType) { if (!ObjectHeader()->IsMarked()) { return true; } +#if !BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) ObjectHeader()->Unmark(); +#endif return false; } -void LargeObjectPage::MakeConsistentForMutator() { +void LargeObjectPage::Unmark() { HeapObjectHeader* header = ObjectHeader(); if (header->IsMarked()) { header->Unmark(); } + SetRemembered(false); +} + +void LargeObjectPage::MakeConsistentForMutator() { + Unmark(); } void LargeObjectPage::FinalizeSweep(SweepResult action) { @@ -1814,8 +1888,9 @@ void LargeObjectPage::FinalizeSweep(SweepResult action) { #if defined(ADDRESS_SANITIZER) void LargeObjectPage::PoisonUnmarkedObjects() { HeapObjectHeader* header = ObjectHeader(); - if (!header->IsMarked()) + if (!header->IsMarked()) { ASAN_POISON_MEMORY_REGION(header->Payload(), header->PayloadSize()); + } } #endif @@ -1840,7 +1915,7 @@ void LargeObjectPage::CollectStatistics( } #if DCHECK_IS_ON() -bool LargeObjectPage::Contains(Address object) { +bool LargeObjectPage::Contains(ConstAddress object) const { return RoundToBlinkPageStart(GetAddress()) <= object && object < RoundToBlinkPageEnd(GetAddress() + size()); } diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_page.h b/chromium/third_party/blink/renderer/platform/heap/heap_page.h index 4a937e53c1d..f9f0076112c 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_page.h +++ b/chromium/third_party/blink/renderer/platform/heap/heap_page.h @@ -32,6 +32,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_PAGE_H_ #include <stdint.h> +#include <array> #include <atomic> #include "base/bits.h" @@ -39,8 +40,10 @@ #include "build/build_config.h" #include "third_party/blink/renderer/platform/heap/blink_gc.h" #include "third_party/blink/renderer/platform/heap/gc_info.h" +#include "third_party/blink/renderer/platform/heap/heap_buildflags.h" #include "third_party/blink/renderer/platform/heap/thread_state.h" #include "third_party/blink/renderer/platform/heap/thread_state_statistics.h" +#include "third_party/blink/renderer/platform/heap/unsanitized_atomic.h" #include "third_party/blink/renderer/platform/heap/visitor.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -179,28 +182,6 @@ static_assert( namespace internal { -// This is needed due to asan complaining deep from std::atomic<>::load/store -// stacktraces. -class AsanUnpoisonScope { - public: - AsanUnpoisonScope(const void* addr, size_t size) - : addr_(addr), size_(size), was_poisoned_(false) { - if (!ASAN_REGION_IS_POISONED(const_cast<void*>(addr_), size_)) - return; - ASAN_UNPOISON_MEMORY_REGION(addr_, size_); - was_poisoned_ = true; - } - ~AsanUnpoisonScope() { - if (was_poisoned_) - ASAN_POISON_MEMORY_REGION(addr_, size_); - } - - private: - const void* addr_; - size_t size_; - bool was_poisoned_; -}; - NO_SANITIZE_ADDRESS constexpr uint16_t EncodeSize(size_t size) { // Essentially, gets optimized to >> 1. return static_cast<uint16_t>((size << kHeaderSizeShift) / @@ -219,7 +200,6 @@ class PLATFORM_EXPORT HeapObjectHeader { DISALLOW_NEW(); public: - enum HeaderLocation : uint8_t { kNormalPage, kLargePage }; enum class AccessMode : uint8_t { kNonAtomic, kAtomic }; static HeapObjectHeader* FromPayload(const void*); @@ -230,7 +210,7 @@ class PLATFORM_EXPORT HeapObjectHeader { static void CheckFromPayload(const void*); // If |gc_info_index| is 0, this header is interpreted as a free list header. - HeapObjectHeader(size_t, size_t, HeaderLocation); + HeapObjectHeader(size_t, size_t); template <AccessMode mode = AccessMode::kNonAtomic> NO_SANITIZE_ADDRESS bool IsFree() const { @@ -239,7 +219,8 @@ class PLATFORM_EXPORT HeapObjectHeader { template <AccessMode mode = AccessMode::kNonAtomic> NO_SANITIZE_ADDRESS uint32_t GcInfoIndex() const { - const uint16_t encoded = LoadEncoded<mode, EncodedHalf::kHigh>(); + const uint16_t encoded = + LoadEncoded<mode, EncodedHalf::kHigh, std::memory_order_acquire>(); return (encoded & kHeaderGCInfoIndexMask) >> kHeaderGCInfoIndexShift; } @@ -247,18 +228,20 @@ class PLATFORM_EXPORT HeapObjectHeader { size_t size() const; void SetSize(size_t size); + template <AccessMode = AccessMode::kNonAtomic> bool IsLargeObject() const; template <AccessMode = AccessMode::kNonAtomic> bool IsMarked() const; template <AccessMode = AccessMode::kNonAtomic> - void Mark(); - template <AccessMode = AccessMode::kNonAtomic> void Unmark(); template <AccessMode = AccessMode::kNonAtomic> bool TryMark(); template <AccessMode = AccessMode::kNonAtomic> + bool IsOld() const; + + template <AccessMode = AccessMode::kNonAtomic> bool IsInConstruction() const; template <AccessMode = AccessMode::kNonAtomic> void MarkFullyConstructed(); @@ -281,9 +264,13 @@ class PLATFORM_EXPORT HeapObjectHeader { private: enum class EncodedHalf : uint8_t { kLow, kHigh }; - template <AccessMode, EncodedHalf> + template <AccessMode, + EncodedHalf part, + std::memory_order = std::memory_order_seq_cst> uint16_t LoadEncoded() const; - template <AccessMode mode, EncodedHalf> + template <AccessMode mode, + EncodedHalf part, + std::memory_order = std::memory_order_seq_cst> void StoreEncoded(uint16_t bits, uint16_t mask); #if defined(ARCH_CPU_64_BITS) @@ -297,11 +284,7 @@ class FreeListEntry final : public HeapObjectHeader { public: NO_SANITIZE_ADDRESS explicit FreeListEntry(size_t size) - : HeapObjectHeader(size, - kGcInfoIndexForFreeListHeader, - HeapObjectHeader::kNormalPage), - next_(nullptr) { - } + : HeapObjectHeader(size, kGcInfoIndexForFreeListHeader), next_(nullptr) {} Address GetAddress() { return reinterpret_cast<Address>(this); } @@ -386,7 +369,7 @@ class FreeList { }; // Blink heap pages are set up with a guard page before and after the payload. -inline size_t BlinkPagePayloadSize() { +constexpr size_t BlinkPagePayloadSize() { return kBlinkPageSize - 2 * kBlinkGuardPageSize; } @@ -410,8 +393,8 @@ inline Address BlinkPageAddress(Address address) { kBlinkPageBaseMask); } -inline bool VTableInitialized(void* object_pointer) { - return !!(*reinterpret_cast<Address*>(object_pointer)); +inline bool VTableInitialized(const void* object_pointer) { + return !!(*reinterpret_cast<const ConstAddress*>(object_pointer)); } #if DCHECK_IS_ON() @@ -465,6 +448,7 @@ class BasePage { // Does not create free list entries for empty pages. virtual bool Sweep(FinalizeType) = 0; virtual void MakeConsistentForMutator() = 0; + virtual void Unmark() = 0; // Calls finalizers after sweeping is done. virtual void FinalizeSweep(SweepResult) = 0; @@ -477,11 +461,13 @@ class BasePage { ThreadState::Statistics::ArenaStatistics* arena_stats) = 0; #if DCHECK_IS_ON() - virtual bool Contains(Address) = 0; + virtual bool Contains(ConstAddress) const = 0; #endif - virtual size_t size() = 0; + virtual size_t size() const = 0; - Address GetAddress() { return reinterpret_cast<Address>(this); } + Address GetAddress() const { + return reinterpret_cast<Address>(const_cast<BasePage*>(this)); + } PageMemory* Storage() const { return storage_; } BaseArena* Arena() const { return arena_; } ThreadState* thread_state() const { return thread_state_; } @@ -505,6 +491,11 @@ class BasePage { return page_type_ == PageType::kLargeObjectPage; } + // Young pages are pages that contain at least a single young object. + bool IsYoung() const { return is_young_; } + + void SetAsYoung(bool young) { is_young_ = young; } + virtual void VerifyMarking() = 0; private: @@ -515,6 +506,7 @@ class BasePage { // Track the sweeping state of a page. Set to false at the start of a sweep, // true upon completion of sweeping that page. bool swept_ = true; + bool is_young_ = false; PageType page_type_; @@ -544,8 +536,11 @@ class PageStack : Vector<BasePage*> { } using Base::begin; - using Base::clear; using Base::end; + + using Base::clear; + using Base::erase; + using Base::IsEmpty; using Base::size; }; @@ -581,7 +576,7 @@ class PageStackThreadSafe : public PageStack { // - kBlinkPageSize // - kAllocationGranularity class PLATFORM_EXPORT ObjectStartBitmap { - DISALLOW_NEW(); + USING_FAST_MALLOC(ObjectStartBitmap); public: // Granularity of addresses added to the bitmap. @@ -597,7 +592,8 @@ class PLATFORM_EXPORT ObjectStartBitmap { // Finds an object header based on a // address_maybe_pointing_to_the_middle_of_object. Will search for an object // start in decreasing address order. - Address FindHeader(Address address_maybe_pointing_to_the_middle_of_object); + Address FindHeader( + ConstAddress address_maybe_pointing_to_the_middle_of_object) const; inline void SetBit(Address); inline void ClearBit(Address); @@ -625,7 +621,7 @@ class PLATFORM_EXPORT ObjectStartBitmap { inline void ObjectStartIndexAndBit(Address, size_t*, size_t*) const; - const Address offset_; + Address offset_; // The bitmap contains a bit for every kGranularity aligned address on a // a NormalPage, i.e., for a page of size kBlinkPageSize. uint8_t object_start_bit_map_[kReservedForBitmap]; @@ -636,12 +632,12 @@ class PLATFORM_EXPORT NormalPage final : public BasePage { NormalPage(PageMemory*, BaseArena*); ~NormalPage() override; - Address Payload() { return GetAddress() + PageHeaderSize(); } - size_t PayloadSize() { + Address Payload() const { return GetAddress() + PageHeaderSize(); } + static constexpr size_t PayloadSize() { return (BlinkPagePayloadSize() - PageHeaderSize()) & ~kAllocationMask; } - Address PayloadEnd() { return Payload() + PayloadSize(); } - bool ContainedInObjectPayload(Address address) { + Address PayloadEnd() const { return Payload() + PayloadSize(); } + bool ContainedInObjectPayload(ConstAddress address) const { return Payload() <= address && address < PayloadEnd(); } @@ -649,6 +645,7 @@ class PLATFORM_EXPORT NormalPage final : public BasePage { void RemoveFromHeap() override; bool Sweep(FinalizeType) override; void MakeConsistentForMutator() override; + void Unmark() override; void FinalizeSweep(SweepResult) override; #if defined(ADDRESS_SANITIZER) void PoisonUnmarkedObjects() override; @@ -661,17 +658,17 @@ class PLATFORM_EXPORT NormalPage final : public BasePage { // Returns true for the whole |kBlinkPageSize| page that the page is on, even // for the header, and the unmapped guard page at the start. That ensures the // result can be used to populate the negative page cache. - bool Contains(Address) override; + bool Contains(ConstAddress) const override; #endif - size_t size() override { return kBlinkPageSize; } - static size_t PageHeaderSize() { + size_t size() const override { return kBlinkPageSize; } + static constexpr size_t PageHeaderSize() { // Compute the amount of padding we have to add to a header to make the size // of the header plus the padding a multiple of 8 bytes. - size_t padding_size = + constexpr size_t kPaddingSize = (sizeof(NormalPage) + kAllocationGranularity - (sizeof(HeapObjectHeader) % kAllocationGranularity)) % kAllocationGranularity; - return sizeof(NormalPage) + padding_size; + return sizeof(NormalPage) + kPaddingSize; } inline NormalPageArena* ArenaForNormalPage() const; @@ -698,6 +695,9 @@ class PLATFORM_EXPORT NormalPage final : public BasePage { // Object start bitmap of this page. ObjectStartBitmap* object_start_bit_map() { return &object_start_bit_map_; } + const ObjectStartBitmap* object_start_bit_map() const { + return &object_start_bit_map_; + } // Verifies that the object start bitmap only contains a bit iff the object // is also reachable through iteration on the page. @@ -706,18 +706,87 @@ class PLATFORM_EXPORT NormalPage final : public BasePage { // Uses the object_start_bit_map_ to find an object for a given address. The // returned header is either nullptr, indicating that no object could be // found, or it is pointing to valid object or free list entry. - HeapObjectHeader* ConservativelyFindHeaderFromAddress(Address); + HeapObjectHeader* ConservativelyFindHeaderFromAddress(ConstAddress) const; // Uses the object_start_bit_map_ to find an object for a given address. It is // assumed that the address points into a valid heap object. Use the // conservative version if that assumption does not hold. template < HeapObjectHeader::AccessMode = HeapObjectHeader::AccessMode::kNonAtomic> - HeapObjectHeader* FindHeaderFromAddress(Address); + HeapObjectHeader* FindHeaderFromAddress(ConstAddress) const; void VerifyMarking() override; + // Marks a card corresponding to address. + void MarkCard(Address address); + + // Iterates over all objects in marked cards. + template <typename Function> + void IterateCardTable(Function function) const; + + // Clears all bits in the card table. + void ClearCardTable() { card_table_.Clear(); } + private: + // Data structure that divides a page in a number of cards each of 512 bytes + // size. Marked cards are stored in bytes, not bits, to make write barrier + // faster and reduce chances of false sharing. This gives only ~0.1% of memory + // overhead. Also, since there are guard pages before and after a Blink page, + // some of card bits are wasted and unneeded. + class CardTable final { + public: + struct value_type { + uint8_t bit; + size_t index; + }; + + struct iterator { + iterator& operator++() { + ++index; + return *this; + } + value_type operator*() const { return {table->table_[index], index}; } + bool operator!=(iterator other) const { + return table != other.table || index != other.index; + } + + size_t index = 0; + const CardTable* table = nullptr; + }; + + using const_iterator = iterator; + + static constexpr size_t kBitsPerCard = 9; + static constexpr size_t kCardSize = 1 << kBitsPerCard; + + const_iterator begin() const { return {FirstPayloadCard(), this}; } + const_iterator end() const { return {LastPayloadCard(), this}; } + + void Mark(size_t card) { + DCHECK_LE(FirstPayloadCard(), card); + DCHECK_GT(LastPayloadCard(), card); + table_[card] = 1; + } + + bool IsMarked(size_t card) const { + DCHECK_LE(FirstPayloadCard(), card); + DCHECK_GT(LastPayloadCard(), card); + return table_[card]; + } + + void Clear() { std::fill(table_.begin(), table_.end(), 0); } + + private: + static constexpr size_t FirstPayloadCard() { + return (kBlinkGuardPageSize + NormalPage::PageHeaderSize()) / kCardSize; + } + static constexpr size_t LastPayloadCard() { + return (kBlinkGuardPageSize + BlinkPagePayloadSize()) / kCardSize; + } + + std::array<uint8_t, kBlinkPageSize / kCardSize> table_{}; + }; + struct ToBeFinalizedObject { HeapObjectHeader* header; void Finalize(); @@ -727,16 +796,25 @@ class PLATFORM_EXPORT NormalPage final : public BasePage { size_t size; }; + template <typename Function> + void IterateOnCard(Function function, size_t card_number) const; + void MergeFreeLists(); void AddToFreeList(Address start, size_t size, FinalizeType finalize_type, bool found_finalizer); + CardTable card_table_; ObjectStartBitmap object_start_bit_map_; +#if BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) + std::unique_ptr<ObjectStartBitmap> cached_object_start_bit_map_; +#endif Vector<ToBeFinalizedObject> to_be_finalized_objects_; FreeList cached_freelist_; Vector<FutureFreelistEntry> unfinalized_freelist_; + + friend class CardTableTest; }; // Large allocations are allocated as separate objects and linked in a list. @@ -771,7 +849,7 @@ class PLATFORM_EXPORT LargeObjectPage final : public BasePage { // ObjectSize(): PayloadSize() + sizeof(HeapObjectHeader) // size(): ObjectSize() + PageHeaderSize() - HeapObjectHeader* ObjectHeader() { + HeapObjectHeader* ObjectHeader() const { Address header_address = GetAddress() + PageHeaderSize(); return reinterpret_cast<HeapObjectHeader*>(header_address); } @@ -781,18 +859,18 @@ class PLATFORM_EXPORT LargeObjectPage final : public BasePage { size_t ObjectSize() const { return object_size_; } // Returns the size of the page including the header. - size_t size() override { return PageHeaderSize() + object_size_; } + size_t size() const override { return PageHeaderSize() + object_size_; } // Returns the payload start of the underlying object. - Address Payload() { return ObjectHeader()->Payload(); } + Address Payload() const { return ObjectHeader()->Payload(); } // Returns the payload size of the underlying object. - size_t PayloadSize() { return object_size_ - sizeof(HeapObjectHeader); } + size_t PayloadSize() const { return object_size_ - sizeof(HeapObjectHeader); } // Points to the payload end of the underlying object. - Address PayloadEnd() { return Payload() + PayloadSize(); } + Address PayloadEnd() const { return Payload() + PayloadSize(); } - bool ContainedInObjectPayload(Address address) { + bool ContainedInObjectPayload(ConstAddress address) const { return Payload() <= address && address < PayloadEnd(); } @@ -800,6 +878,7 @@ class PLATFORM_EXPORT LargeObjectPage final : public BasePage { void RemoveFromHeap() override; bool Sweep(FinalizeType) override; void MakeConsistentForMutator() override; + void Unmark() override; void FinalizeSweep(SweepResult) override; void CollectStatistics( @@ -815,7 +894,7 @@ class PLATFORM_EXPORT LargeObjectPage final : public BasePage { // Returns true for any address that is on one of the pages that this large // object uses. That ensures that we can use a negative result to populate the // negative page cache. - bool Contains(Address) override; + bool Contains(ConstAddress) const override; #endif #ifdef ANNOTATE_CONTIGUOUS_CONTAINER @@ -823,12 +902,19 @@ class PLATFORM_EXPORT LargeObjectPage final : public BasePage { bool IsVectorBackingPage() const { return is_vector_backing_page_; } #endif + // Remembers the page as containing inter-generational pointers. + void SetRemembered(bool remembered) { + is_remembered_ = remembered; + } + bool IsRemembered() const { return is_remembered_; } + private: // The size of the underlying object including HeapObjectHeader. size_t object_size_; #ifdef ANNOTATE_CONTIGUOUS_CONTAINER bool is_vector_backing_page_; #endif + bool is_remembered_ = false; }; // Each thread has a number of thread arenas (e.g., Generic arenas, typed arenas @@ -852,17 +938,18 @@ class PLATFORM_EXPORT BaseArena { ThreadState::Statistics::FreeListStatistics*) {} #if DCHECK_IS_ON() - BasePage* FindPageFromAddress(Address); + BasePage* FindPageFromAddress(ConstAddress) const; #endif virtual void ClearFreeLists() {} virtual void MakeIterable() {} virtual void MakeConsistentForGC(); void MakeConsistentForMutator(); + void Unmark(); #if DCHECK_IS_ON() virtual bool IsConsistentForGC() = 0; #endif size_t ObjectPayloadSizeForTesting(); - void PrepareForSweep(); + void PrepareForSweep(BlinkGC::CollectionType); #if defined(ADDRESS_SANITIZER) void PoisonUnmarkedObjects(); #endif @@ -872,7 +959,8 @@ class PLATFORM_EXPORT BaseArena { // Returns true if we have swept all pages within the deadline. Returns false // otherwise. bool LazySweepWithDeadline(base::TimeTicks deadline); - bool ConcurrentSweepWithDeadline(base::TimeTicks deadline); + // Returns true if the arena has been fully swept. + bool ConcurrentSweepOnePage(); void CompleteSweep(); void InvokeFinalizersOnSweptPages(); @@ -919,15 +1007,7 @@ class PLATFORM_EXPORT BaseArena { class PLATFORM_EXPORT NormalPageArena final : public BaseArena { public: NormalPageArena(ThreadState*, int index); - void AddToFreeList(Address address, size_t size) { -#if DCHECK_IS_ON() - DCHECK(FindPageFromAddress(address)); - // TODO(palmer): Do we need to handle about integer overflow here (and in - // similar expressions elsewhere)? - DCHECK(FindPageFromAddress(address + size - 1)); -#endif - free_list_.Add(address, size); - } + void AddToFreeList(Address address, size_t size); void AddToFreeList(FreeList* other) { free_list_.MoveFrom(other); } void ClearFreeLists() override; void CollectFreeListStatistics( @@ -936,7 +1016,7 @@ class PLATFORM_EXPORT NormalPageArena final : public BaseArena { #if DCHECK_IS_ON() bool IsConsistentForGC() override; - bool PagesToBeSweptContains(Address); + bool PagesToBeSweptContains(ConstAddress) const; #endif Address AllocateObject(size_t allocation_size, size_t gc_info_index); @@ -964,7 +1044,7 @@ class PLATFORM_EXPORT NormalPageArena final : public BaseArena { Address CurrentAllocationPoint() const { return current_allocation_point_; } - bool IsInCurrentAllocationPointRegion(Address address) const { + bool IsInCurrentAllocationPointRegion(ConstAddress address) const { return HasCurrentAllocationArea() && (CurrentAllocationPoint() <= address) && (address < (CurrentAllocationPoint() + RemainingAllocationSize())); @@ -974,6 +1054,9 @@ class PLATFORM_EXPORT NormalPageArena final : public BaseArena { void MakeConsistentForGC() override; + template <typename Function> + void IterateAndClearCardTables(Function function); + private: void AllocatePage(); @@ -992,14 +1075,9 @@ class PLATFORM_EXPORT NormalPageArena final : public BaseArena { } void SetAllocationPoint(Address, size_t); - // Only use when adjusting the area from allocation and free and not when - // returning it to free list. - void SetRemainingAllocationSize(size_t); - FreeList free_list_; Address current_allocation_point_; size_t remaining_allocation_size_; - size_t last_remaining_allocation_size_; // The size of promptly freed objects in the heap. This counter is set to // zero before sweeping when clearing the free list and after coalescing. @@ -1017,6 +1095,9 @@ class LargeObjectArena final : public BaseArena { bool IsConsistentForGC() override { return true; } #endif + template <typename Function> + void IterateAndClearRememberedPages(Function function); + private: Address DoAllocateLargeObjectPage(size_t, size_t gc_info_index); Address LazySweepPages(size_t, size_t gc_info_index) override; @@ -1053,7 +1134,7 @@ inline HeapObjectHeader* HeapObjectHeader::FromInnerAddress( return page->IsLargeObjectPage() ? static_cast<LargeObjectPage*>(page)->ObjectHeader() : static_cast<NormalPage*>(page)->FindHeaderFromAddress<mode>( - reinterpret_cast<Address>(const_cast<void*>(address))); + reinterpret_cast<ConstAddress>(address)); } inline void HeapObjectHeader::CheckFromPayload(const void* payload) { @@ -1062,19 +1143,10 @@ inline void HeapObjectHeader::CheckFromPayload(const void* payload) { template <HeapObjectHeader::AccessMode mode> NO_SANITIZE_ADDRESS inline size_t HeapObjectHeader::size() const { - uint16_t encoded_low_value; - if (mode == AccessMode::kNonAtomic) { - encoded_low_value = encoded_low_; - } else { - // mode == AccessMode::kAtomic - // Relaxed load as size is immutable after construction while either - // marking or sweeping is running - internal::AsanUnpoisonScope unpoison_scope( - static_cast<const void*>(&encoded_low_), sizeof(encoded_low_)); - encoded_low_value = - reinterpret_cast<const std::atomic<uint16_t>&>(encoded_low_) - .load(std::memory_order_relaxed); - } + // Size is immutable after construction while either marking or sweeping + // is running so relaxed load (if mode == kAtomic) is enough. + uint16_t encoded_low_value = + LoadEncoded<mode, EncodedHalf::kLow, std::memory_order_relaxed>(); const size_t result = internal::DecodeSize(encoded_low_value); // Large objects should not refer to header->size() but use // LargeObjectPage::PayloadSize(). @@ -1090,21 +1162,24 @@ NO_SANITIZE_ADDRESS inline void HeapObjectHeader::SetSize(size_t size) { (encoded_low_ & ~kHeaderSizeMask)); } +template <HeapObjectHeader::AccessMode mode> NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::IsLargeObject() const { - return internal::DecodeSize(encoded_low_) == kLargeObjectSizeInHeader; + uint16_t encoded_low_value = + LoadEncoded<mode, EncodedHalf::kLow, std::memory_order_relaxed>(); + return internal::DecodeSize(encoded_low_value) == kLargeObjectSizeInHeader; } template <HeapObjectHeader::AccessMode mode> NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::IsInConstruction() const { - return (LoadEncoded<mode, EncodedHalf::kHigh>() & + return (LoadEncoded<mode, EncodedHalf::kHigh, std::memory_order_acquire>() & kHeaderIsInConstructionMask) == 0; } template <HeapObjectHeader::AccessMode mode> NO_SANITIZE_ADDRESS inline void HeapObjectHeader::MarkFullyConstructed() { DCHECK(IsInConstruction()); - StoreEncoded<mode, EncodedHalf::kHigh>(kHeaderIsInConstructionMask, - kHeaderIsInConstructionMask); + StoreEncoded<mode, EncodedHalf::kHigh, std::memory_order_release>( + kHeaderIsInConstructionMask, kHeaderIsInConstructionMask); } inline Address HeapObjectHeader::Payload() const { @@ -1130,20 +1205,22 @@ NO_SANITIZE_ADDRESS inline size_t HeapObjectHeader::PayloadSize() const { template <HeapObjectHeader::AccessMode mode> NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::IsMarked() const { - const uint16_t encoded = LoadEncoded<mode, EncodedHalf::kLow>(); + const uint16_t encoded = + LoadEncoded<mode, EncodedHalf::kLow, std::memory_order_relaxed>(); return encoded & kHeaderMarkBitMask; } template <HeapObjectHeader::AccessMode mode> -NO_SANITIZE_ADDRESS inline void HeapObjectHeader::Mark() { - DCHECK(!IsMarked<mode>()); - StoreEncoded<mode, EncodedHalf::kLow>(kHeaderMarkBitMask, kHeaderMarkBitMask); +NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::IsOld() const { + // Oilpan uses the sticky-mark-bits technique to encode old objects. + return IsMarked<mode>(); } template <HeapObjectHeader::AccessMode mode> NO_SANITIZE_ADDRESS inline void HeapObjectHeader::Unmark() { DCHECK(IsMarked<mode>()); - StoreEncoded<mode, EncodedHalf::kLow>(0u, kHeaderMarkBitMask); + StoreEncoded<mode, EncodedHalf::kLow, std::memory_order_relaxed>( + 0u, kHeaderMarkBitMask); } // The function relies on size bits being unmodified when the function is @@ -1156,16 +1233,12 @@ NO_SANITIZE_ADDRESS inline bool HeapObjectHeader::TryMark() { encoded_low_ |= kHeaderMarkBitMask; return true; } - internal::AsanUnpoisonScope unpoison_scope( - static_cast<const void*>(&encoded_low_), sizeof(encoded_low_)); - auto* atomic_encoded = - reinterpret_cast<std::atomic<uint16_t>*>(&encoded_low_); + auto* atomic_encoded = internal::AsUnsanitizedAtomic(&encoded_low_); uint16_t old_value = atomic_encoded->load(std::memory_order_relaxed); if (old_value & kHeaderMarkBitMask) return false; const uint16_t new_value = old_value | kHeaderMarkBitMask; return atomic_encoded->compare_exchange_strong(old_value, new_value, - std::memory_order_acq_rel, std::memory_order_relaxed); } @@ -1176,8 +1249,12 @@ inline Address NormalPageArena::AllocateObject(size_t allocation_size, current_allocation_point_ += allocation_size; remaining_allocation_size_ -= allocation_size; DCHECK_GT(gc_info_index, 0u); - new (NotNull, header_address) HeapObjectHeader( - allocation_size, gc_info_index, HeapObjectHeader::kNormalPage); + new (NotNull, header_address) + HeapObjectHeader(allocation_size, gc_info_index); + DCHECK(!PageFromObject(header_address)->IsLargeObjectPage()); + static_cast<NormalPage*>(PageFromObject(header_address)) + ->object_start_bit_map() + ->SetBit(header_address); Address result = header_address + sizeof(HeapObjectHeader); DCHECK(!(reinterpret_cast<uintptr_t>(result) & kAllocationMask)); @@ -1194,6 +1271,29 @@ inline NormalPageArena* NormalPage::ArenaForNormalPage() const { return static_cast<NormalPageArena*>(Arena()); } +// Iterates over all card tables and clears them. +template <typename Function> +inline void NormalPageArena::IterateAndClearCardTables(Function function) { + for (BasePage* page : swept_pages_) { + auto* normal_page = static_cast<NormalPage*>(page); + normal_page->IterateCardTable(function); + normal_page->ClearCardTable(); + } +} + +// Iterates over all pages that may contain inter-generational pointers. +template <typename Function> +inline void LargeObjectArena::IterateAndClearRememberedPages( + Function function) { + for (BasePage* page : swept_pages_) { + auto* large_page = static_cast<LargeObjectPage*>(page); + if (large_page->IsRemembered()) { + function(large_page->ObjectHeader()); + large_page->SetRemembered(false); + } + } +} + inline void ObjectStartBitmap::SetBit(Address header_address) { size_t cell_index, object_bit; ObjectStartIndexAndBit(header_address, &cell_index, &object_bit); @@ -1248,8 +1348,7 @@ inline void ObjectStartBitmap::Iterate(Callback callback) const { NO_SANITIZE_ADDRESS inline HeapObjectHeader::HeapObjectHeader( size_t size, - size_t gc_info_index, - HeaderLocation header_location) { + size_t gc_info_index) { // sizeof(HeapObjectHeader) must be equal to or smaller than // |kAllocationGranularity|, because |HeapObjectHeader| is used as a header // for a freed entry. Given that the smallest entry size is @@ -1264,52 +1363,44 @@ NO_SANITIZE_ADDRESS inline HeapObjectHeader::HeapObjectHeader( encoded_high_ = static_cast<uint16_t>(gc_info_index << kHeaderGCInfoIndexShift); encoded_low_ = internal::EncodeSize(size); - if (header_location == kNormalPage) { - DCHECK(!PageFromObject(this)->IsLargeObjectPage()); - static_cast<NormalPage*>(PageFromObject(this)) - ->object_start_bit_map() - ->SetBit(reinterpret_cast<Address>(this)); - } else { - DCHECK(PageFromObject(this)->IsLargeObjectPage()); - } DCHECK(IsInConstruction()); } -template <HeapObjectHeader::AccessMode mode, HeapObjectHeader::EncodedHalf part> +template <HeapObjectHeader::AccessMode mode, + HeapObjectHeader::EncodedHalf part, + std::memory_order memory_order> NO_SANITIZE_ADDRESS inline uint16_t HeapObjectHeader::LoadEncoded() const { const uint16_t& half = - (part == EncodedHalf::kLow ? encoded_low_ : encoded_high_); - internal::AsanUnpoisonScope unpoison_scope(static_cast<const void*>(&half), - sizeof(half)); + part == EncodedHalf::kLow ? encoded_low_ : encoded_high_; if (mode == AccessMode::kNonAtomic) return half; - return reinterpret_cast<const std::atomic<uint16_t>&>(half).load( - std::memory_order_acquire); + return internal::AsUnsanitizedAtomic(&half)->load(memory_order); } // Sets bits selected by the mask to the given value. Please note that atomicity // of the whole operation is not guaranteed. -template <HeapObjectHeader::AccessMode mode, HeapObjectHeader::EncodedHalf part> +template <HeapObjectHeader::AccessMode mode, + HeapObjectHeader::EncodedHalf part, + std::memory_order memory_order> NO_SANITIZE_ADDRESS inline void HeapObjectHeader::StoreEncoded(uint16_t bits, uint16_t mask) { DCHECK_EQ(static_cast<uint16_t>(0u), bits & ~mask); - uint16_t* half = (part == EncodedHalf::kLow ? &encoded_low_ : &encoded_high_); - internal::AsanUnpoisonScope unpoison_scope(static_cast<void*>(half), - sizeof(&half)); + uint16_t& half = part == EncodedHalf::kLow ? encoded_low_ : encoded_high_; if (mode == AccessMode::kNonAtomic) { - *half = (*half & ~mask) | bits; + half = (half & ~mask) | bits; return; } // We don't perform CAS loop here assuming that the data is constant and no // one except for us can change this half concurrently. - auto* atomic_encoded = reinterpret_cast<std::atomic<uint16_t>*>(half); + auto* atomic_encoded = internal::AsUnsanitizedAtomic(&half); uint16_t value = atomic_encoded->load(std::memory_order_relaxed); value = (value & ~mask) | bits; - atomic_encoded->store(value, std::memory_order_release); + atomic_encoded->store(value, memory_order); } template <HeapObjectHeader::AccessMode mode> -HeapObjectHeader* NormalPage::FindHeaderFromAddress(Address address) { +HeapObjectHeader* NormalPage::FindHeaderFromAddress( + ConstAddress address) const { DCHECK(ContainedInObjectPayload(address)); DCHECK(!ArenaForNormalPage()->IsInCurrentAllocationPointRegion(address)); HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>( @@ -1319,6 +1410,54 @@ HeapObjectHeader* NormalPage::FindHeaderFromAddress(Address address) { return header; } +template <typename Function> +void NormalPage::IterateCardTable(Function function) const { + // TODO(bikineev): Consider introducing a "dirty" per-page bit to avoid + // the loop (this may in turn pessimize barrier implementation). + for (auto card : card_table_) { + if (UNLIKELY(card.bit)) { + IterateOnCard(std::move(function), card.index); + } + } +} + +// Iterates over all objects in the specified marked card. Please note that +// since objects are not aligned by the card boundary, it starts from the +// object which may reside on a previous card. +template <typename Function> +void NormalPage::IterateOnCard(Function function, size_t card_number) const { +#if DCHECK_IS_ON() + DCHECK(card_table_.IsMarked(card_number)); + DCHECK(ArenaForNormalPage()->IsConsistentForGC()); +#endif + + const Address card_begin = RoundToBlinkPageStart(GetAddress()) + + (card_number << CardTable::kBitsPerCard); + const Address card_end = card_begin + CardTable::kCardSize; + // Generational barrier marks cards corresponding to slots (not source + // objects), therefore the potential source object may reside on a + // previous card. + HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>( + card_number == card_table_.begin().index + ? Payload() + : object_start_bit_map_.FindHeader(card_begin)); + for (; header < reinterpret_cast<HeapObjectHeader*>(card_end); + reinterpret_cast<Address&>(header) += header->size()) { + if (!header->IsFree()) { + function(header); + } + } +} + +inline void NormalPage::MarkCard(Address address) { +#if DCHECK_IS_ON() + DCHECK(Contains(address)); +#endif + const size_t byte = reinterpret_cast<size_t>(address) & kBlinkPageOffsetMask; + const size_t card = byte / CardTable::kCardSize; + card_table_.Mark(card); +} + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_PAGE_H_ diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector.cc b/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector.cc index 6b8dc5c4cfd..41b2a28062d 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector.cc +++ b/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector.cc @@ -14,11 +14,13 @@ namespace blink { void ThreadHeapStatsCollector::IncreaseCompactionFreedSize(size_t bytes) { DCHECK(is_started_); current_.compaction_freed_bytes += bytes; + current_.compaction_recorded_events = true; } void ThreadHeapStatsCollector::IncreaseCompactionFreedPages(size_t pages) { DCHECK(is_started_); current_.compaction_freed_pages += pages; + current_.compaction_recorded_events = true; } void ThreadHeapStatsCollector::IncreaseAllocatedObjectSize(size_t bytes) { @@ -100,11 +102,14 @@ void ThreadHeapStatsCollector::IncreaseCollectedWrapperCount(size_t count) { collected_wrapper_count_ += count; } -void ThreadHeapStatsCollector::NotifyMarkingStarted(BlinkGC::GCReason reason) { +void ThreadHeapStatsCollector::NotifyMarkingStarted( + BlinkGC::CollectionType collection_type, + BlinkGC::GCReason reason) { DCHECK(!is_started_); DCHECK(current_.marking_time().is_zero()); is_started_ = true; current_.reason = reason; + current_.collection_type = collection_type; } void ThreadHeapStatsCollector::NotifyMarkingCompleted(size_t marked_bytes) { @@ -170,6 +175,10 @@ base::TimeDelta ThreadHeapStatsCollector::estimated_marking_time() const { return base::TimeDelta::FromSecondsD(estimated_marking_time_in_seconds()); } +base::TimeDelta ThreadHeapStatsCollector::Event::roots_marking_time() const { + return scope_data[kVisitRoots]; +} + base::TimeDelta ThreadHeapStatsCollector::Event::incremental_marking_time() const { return scope_data[kIncrementalMarkingStartMarking] + @@ -195,8 +204,8 @@ base::TimeDelta ThreadHeapStatsCollector::Event::foreground_marking_time() base::TimeDelta ThreadHeapStatsCollector::Event::background_marking_time() const { - return base::TimeDelta::FromMicroseconds( - base::subtle::NoBarrier_Load(&concurrent_scope_data[kConcurrentMark])); + return base::TimeDelta::FromMicroseconds(base::subtle::NoBarrier_Load( + &concurrent_scope_data[kConcurrentMarkingStep])); } base::TimeDelta ThreadHeapStatsCollector::Event::marking_time() const { @@ -229,7 +238,7 @@ base::TimeDelta ThreadHeapStatsCollector::Event::foreground_sweeping_time() base::TimeDelta ThreadHeapStatsCollector::Event::background_sweeping_time() const { return base::TimeDelta::FromMicroseconds( - concurrent_scope_data[kConcurrentSweep]); + concurrent_scope_data[kConcurrentSweepingStep]); } base::TimeDelta ThreadHeapStatsCollector::Event::sweeping_time() const { diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector.h b/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector.h index e5e7207f379..c7fa3f9b571 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector.h +++ b/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector.h @@ -54,6 +54,7 @@ class PLATFORM_EXPORT ThreadHeapStatsObserver { V(InvokePreFinalizers) \ V(LazySweepInIdle) \ V(LazySweepOnAllocation) \ + V(MarkBailOutObjects) \ V(MarkInvokeEphemeronCallbacks) \ V(MarkProcessWorklist) \ V(MarkNotFullyConstructedObjects) \ @@ -63,17 +64,20 @@ class PLATFORM_EXPORT ThreadHeapStatsObserver { V(VisitDOMWrappers) \ V(VisitPersistentRoots) \ V(VisitPersistents) \ - V(VisitStackRoots) + V(VisitRoots) \ + V(VisitStackRoots) \ + V(VisitRememberedSets) #define FOR_ALL_CONCURRENT_SCOPES(V) \ - V(ConcurrentMark) \ - V(ConcurrentSweep) + V(ConcurrentMarkingStep) \ + V(ConcurrentSweepingStep) // Manages counters and statistics across garbage collection cycles. // // Usage: // ThreadHeapStatsCollector stats_collector; -// stats_collector.NotifyMarkingStarted(<BlinkGC::GCReason>); +// stats_collector.NotifyMarkingStarted(<BlinkGC::CollectionType>, +// <BlinkGC::GCReason>); // // Use tracer. // stats_collector.NotifySweepingCompleted(); // // Previous event is available using stats_collector.previous(). @@ -96,12 +100,13 @@ class PLATFORM_EXPORT ThreadHeapStatsCollector { kNumConcurrentScopeIds }; - constexpr static const char* ToString(Id id) { + constexpr static const char* ToString(Id id, BlinkGC::CollectionType type) { switch (id) { -#define CASE(name) \ - case k##name: \ - return "BlinkGC." #name; - +#define CASE(name) \ + case k##name: \ + return type == BlinkGC::CollectionType::kMajor ? "BlinkGC." #name \ + : "BlinkGC." #name \ + ".Minor"; FOR_ALL_SCOPES(CASE) #undef CASE default: @@ -110,12 +115,14 @@ class PLATFORM_EXPORT ThreadHeapStatsCollector { return nullptr; } - constexpr static const char* ToString(ConcurrentId id) { + constexpr static const char* ToString(ConcurrentId id, + BlinkGC::CollectionType type) { switch (id) { -#define CASE(name) \ - case k##name: \ - return "BlinkGC." #name; - +#define CASE(name) \ + case k##name: \ + return type == BlinkGC::CollectionType::kMajor ? "BlinkGC." #name \ + : "BlinkGC." #name \ + ".Minor"; FOR_ALL_CONCURRENT_SCOPES(CASE) #undef CASE default: @@ -140,60 +147,31 @@ class PLATFORM_EXPORT ThreadHeapStatsCollector { public: template <typename... Args> - inline InternalScope(ThreadHeapStatsCollector* tracer, - IdType id, - Args... args) + InternalScope(ThreadHeapStatsCollector* tracer, IdType id, Args... args) : tracer_(tracer), start_time_(base::TimeTicks::Now()), id_(id) { - StartTrace(id, args...); + StartTrace(args...); } - inline ~InternalScope() { - StopTrace(id_); + ~InternalScope() { + StopTrace(); IncreaseScopeTime(id_); } private: - constexpr static const char* TraceCategory() { - switch (trace_category) { - case kEnabled: - return "blink_gc"; - case kDisabled: - return TRACE_DISABLED_BY_DEFAULT("blink_gc"); - case kDevTools: - return "blink_gc,devtools.timeline"; - } - } - - void StartTrace(IdType id) { - TRACE_EVENT_BEGIN0(TraceCategory(), ToString(id)); - } + inline constexpr static const char* TraceCategory(); + inline void StartTrace(); template <typename Value1> - void StartTrace(IdType id, const char* k1, Value1 v1) { - TRACE_EVENT_BEGIN1(TraceCategory(), ToString(id), k1, v1); - } - + inline void StartTrace(const char* k1, Value1 v1); template <typename Value1, typename Value2> - void StartTrace(IdType id, - const char* k1, - Value1 v1, - const char* k2, - Value2 v2) { - TRACE_EVENT_BEGIN2(TraceCategory(), ToString(id), k1, v1, k2, v2); - } + inline void StartTrace(const char* k1, + Value1 v1, + const char* k2, + Value2 v2); + inline void StopTrace(); - void StopTrace(IdType id) { - TRACE_EVENT_END0(TraceCategory(), ToString(id)); - } - - void IncreaseScopeTime(Id) { - tracer_->IncreaseScopeTime(id_, base::TimeTicks::Now() - start_time_); - } - - void IncreaseScopeTime(ConcurrentId) { - tracer_->IncreaseConcurrentScopeTime( - id_, base::TimeTicks::Now() - start_time_); - } + inline void IncreaseScopeTime(Id); + inline void IncreaseScopeTime(ConcurrentId); ThreadHeapStatsCollector* const tracer_; const base::TimeTicks start_time_; @@ -250,6 +228,9 @@ class PLATFORM_EXPORT ThreadHeapStatsCollector { // Time spent in the final atomic pause in sweeping and compacting the heap. base::TimeDelta atomic_sweep_and_compact_time() const; + // Time spent marking the roots. + base::TimeDelta roots_marking_time() const; + // Time spent incrementally marking the heap. base::TimeDelta incremental_marking_time() const; @@ -278,9 +259,11 @@ class PLATFORM_EXPORT ThreadHeapStatsCollector { size_t marked_bytes = 0; size_t compaction_freed_bytes = 0; size_t compaction_freed_pages = 0; + bool compaction_recorded_events = false; base::TimeDelta scope_data[kNumScopeIds]; base::subtle::Atomic32 concurrent_scope_data[kNumConcurrentScopeIds]{0}; BlinkGC::GCReason reason = static_cast<BlinkGC::GCReason>(0); + BlinkGC::CollectionType collection_type = BlinkGC::CollectionType::kMajor; size_t object_size_in_bytes_before_sweeping = 0; size_t allocated_space_in_bytes_before_sweeping = 0; size_t partition_alloc_bytes_before_sweeping = 0; @@ -290,7 +273,7 @@ class PLATFORM_EXPORT ThreadHeapStatsCollector { }; // Indicates a new garbage collection cycle. - void NotifyMarkingStarted(BlinkGC::GCReason); + void NotifyMarkingStarted(BlinkGC::CollectionType, BlinkGC::GCReason); // Indicates that marking of the current garbage collection cycle is // completed. @@ -405,6 +388,71 @@ class PLATFORM_EXPORT ThreadHeapStatsCollector { FRIEND_TEST_ALL_PREFIXES(ThreadHeapStatsCollectorTest, StopResetsCurrent); }; +template <ThreadHeapStatsCollector::TraceCategory trace_category, + ThreadHeapStatsCollector::ScopeContext scope_category> +constexpr const char* +ThreadHeapStatsCollector::InternalScope<trace_category, + scope_category>::TraceCategory() { + switch (trace_category) { + case kEnabled: + return "blink_gc"; + case kDisabled: + return TRACE_DISABLED_BY_DEFAULT("blink_gc"); + case kDevTools: + return "blink_gc,devtools.timeline"; + } +} + +template <ThreadHeapStatsCollector::TraceCategory trace_category, + ThreadHeapStatsCollector::ScopeContext scope_category> +void ThreadHeapStatsCollector::InternalScope<trace_category, + scope_category>::StartTrace() { + TRACE_EVENT_BEGIN0(TraceCategory(), + ToString(id_, tracer_->current_.collection_type)); +} + +template <ThreadHeapStatsCollector::TraceCategory trace_category, + ThreadHeapStatsCollector::ScopeContext scope_category> +template <typename Value1> +void ThreadHeapStatsCollector::InternalScope<trace_category, scope_category>:: + StartTrace(const char* k1, Value1 v1) { + TRACE_EVENT_BEGIN1(TraceCategory(), + ToString(id_, tracer_->current_.collection_type), k1, v1); +} + +template <ThreadHeapStatsCollector::TraceCategory trace_category, + ThreadHeapStatsCollector::ScopeContext scope_category> +template <typename Value1, typename Value2> +void ThreadHeapStatsCollector::InternalScope<trace_category, scope_category>:: + StartTrace(const char* k1, Value1 v1, const char* k2, Value2 v2) { + TRACE_EVENT_BEGIN2(TraceCategory(), + ToString(id_, tracer_->current_.collection_type), k1, v1, + k2, v2); +} + +template <ThreadHeapStatsCollector::TraceCategory trace_category, + ThreadHeapStatsCollector::ScopeContext scope_category> +void ThreadHeapStatsCollector::InternalScope<trace_category, + scope_category>::StopTrace() { + TRACE_EVENT_END0(TraceCategory(), + ToString(id_, tracer_->current_.collection_type)); +} + +template <ThreadHeapStatsCollector::TraceCategory trace_category, + ThreadHeapStatsCollector::ScopeContext scope_category> +void ThreadHeapStatsCollector::InternalScope<trace_category, scope_category>:: + IncreaseScopeTime(Id) { + tracer_->IncreaseScopeTime(id_, base::TimeTicks::Now() - start_time_); +} + +template <ThreadHeapStatsCollector::TraceCategory trace_category, + ThreadHeapStatsCollector::ScopeContext scope_category> +void ThreadHeapStatsCollector::InternalScope<trace_category, scope_category>:: + IncreaseScopeTime(ConcurrentId) { + tracer_->IncreaseConcurrentScopeTime(id_, + base::TimeTicks::Now() - start_time_); +} + #undef FOR_ALL_SCOPES #undef FOR_ALL_CONCURRENT_SCOPES diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc b/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc index 314f68f9520..493a5d1658b 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc @@ -21,7 +21,8 @@ constexpr size_t kNoMarkedBytes = 0; TEST(ThreadHeapStatsCollectorTest, InitialEmpty) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); for (int i = 0; i < ThreadHeapStatsCollector::kNumScopeIds; i++) { EXPECT_EQ(base::TimeDelta(), stats_collector.current().scope_data[i]); } @@ -31,7 +32,8 @@ TEST(ThreadHeapStatsCollectorTest, InitialEmpty) { TEST(ThreadHeapStatsCollectorTest, IncreaseScopeTime) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kIncrementalMarkingStep, base::TimeDelta::FromMilliseconds(1)); @@ -44,7 +46,8 @@ TEST(ThreadHeapStatsCollectorTest, IncreaseScopeTime) { TEST(ThreadHeapStatsCollectorTest, StopMovesCurrentToPrevious) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kIncrementalMarkingStep, base::TimeDelta::FromMilliseconds(1)); @@ -57,7 +60,8 @@ TEST(ThreadHeapStatsCollectorTest, StopMovesCurrentToPrevious) { TEST(ThreadHeapStatsCollectorTest, StopResetsCurrent) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kIncrementalMarkingStep, base::TimeDelta::FromMilliseconds(1)); @@ -71,7 +75,8 @@ TEST(ThreadHeapStatsCollectorTest, StopResetsCurrent) { TEST(ThreadHeapStatsCollectorTest, StartStop) { ThreadHeapStatsCollector stats_collector; EXPECT_FALSE(stats_collector.is_started()); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); EXPECT_TRUE(stats_collector.is_started()); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.NotifySweepingCompleted(); @@ -81,12 +86,14 @@ TEST(ThreadHeapStatsCollectorTest, StartStop) { TEST(ThreadHeapStatsCollectorTest, ScopeToString) { EXPECT_STREQ("BlinkGC.IncrementalMarkingStartMarking", ThreadHeapStatsCollector::ToString( - ThreadHeapStatsCollector::kIncrementalMarkingStartMarking)); + ThreadHeapStatsCollector::kIncrementalMarkingStartMarking, + BlinkGC::CollectionType::kMajor)); } TEST(ThreadHeapStatsCollectorTest, UpdateReason) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.UpdateReason(BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifySweepingCompleted(); @@ -96,7 +103,8 @@ TEST(ThreadHeapStatsCollectorTest, UpdateReason) { TEST(ThreadHeapStatsCollectorTest, InitialEstimatedObjectSize) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); EXPECT_EQ(0u, stats_collector.object_size_in_bytes()); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.NotifySweepingCompleted(); @@ -104,7 +112,8 @@ TEST(ThreadHeapStatsCollectorTest, InitialEstimatedObjectSize) { TEST(ThreadHeapStatsCollectorTest, EstimatedObjectSizeNoMarkedBytes) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseAllocatedObjectSizeForTesting(512); EXPECT_EQ(512u, stats_collector.object_size_in_bytes()); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); @@ -113,10 +122,12 @@ TEST(ThreadHeapStatsCollectorTest, EstimatedObjectSizeNoMarkedBytes) { TEST(ThreadHeapStatsCollectorTest, EstimatedObjectSizeWithMarkedBytes) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); stats_collector.NotifySweepingCompleted(); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.IncreaseAllocatedObjectSizeForTesting(512); EXPECT_EQ(640u, stats_collector.object_size_in_bytes()); @@ -126,10 +137,12 @@ TEST(ThreadHeapStatsCollectorTest, EstimatedObjectSizeWithMarkedBytes) { TEST(ThreadHeapStatsCollectorTest, EstimatedObjectSizeDoNotCountCurrentlyMarkedBytes) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); stats_collector.NotifySweepingCompleted(); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); // Currently marked bytes should not account to the estimated object size. stats_collector.IncreaseAllocatedObjectSizeForTesting(512); @@ -141,7 +154,8 @@ TEST(ThreadHeapStatsCollectorTest, PreInitializedEstimatedMarkingTime) { // Checks that a marking time estimate can be retrieved before the first // garbage collection triggers. ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); EXPECT_LT(0u, stats_collector.estimated_marking_time_in_seconds()); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.NotifySweepingCompleted(); @@ -149,13 +163,15 @@ TEST(ThreadHeapStatsCollectorTest, PreInitializedEstimatedMarkingTime) { TEST(ThreadHeapStatsCollectorTest, EstimatedMarkingTime1) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure, base::TimeDelta::FromSeconds(1)); stats_collector.NotifyMarkingCompleted(1024); stats_collector.NotifySweepingCompleted(); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); EXPECT_DOUBLE_EQ(1.0, stats_collector.estimated_marking_time_in_seconds()); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.NotifySweepingCompleted(); @@ -163,13 +179,15 @@ TEST(ThreadHeapStatsCollectorTest, EstimatedMarkingTime1) { TEST(ThreadHeapStatsCollectorTest, EstimatedMarkingTime2) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure, base::TimeDelta::FromSeconds(1)); stats_collector.NotifyMarkingCompleted(1024); stats_collector.NotifySweepingCompleted(); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseAllocatedObjectSizeForTesting(512); EXPECT_DOUBLE_EQ(1.5, stats_collector.estimated_marking_time_in_seconds()); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); @@ -178,7 +196,8 @@ TEST(ThreadHeapStatsCollectorTest, EstimatedMarkingTime2) { TEST(ThreadHeapStatsCollectorTest, SubMilliSecondMarkingTime) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kIncrementalMarkingStartMarking, base::TimeDelta::FromMillisecondsD(.5)); @@ -191,7 +210,8 @@ TEST(ThreadHeapStatsCollectorTest, SubMilliSecondMarkingTime) { TEST(ThreadHeapStatsCollectorTest, AllocatedSpaceInBytesInitialZero) { ThreadHeapStatsCollector stats_collector; EXPECT_EQ(0u, stats_collector.allocated_space_bytes()); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); EXPECT_EQ(0u, stats_collector.allocated_space_bytes()); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); EXPECT_EQ(0u, stats_collector.allocated_space_bytes()); @@ -218,7 +238,8 @@ TEST(ThreadHeapStatsCollectorTest, AllocatedSpaceInBytesDecrease) { TEST(ThreadHeapStatsCollectorTest, EventPrevGCMarkedObjectSize) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(1024); stats_collector.NotifySweepingCompleted(); EXPECT_EQ(1024u, stats_collector.previous().marked_bytes); @@ -227,7 +248,8 @@ TEST(ThreadHeapStatsCollectorTest, EventPrevGCMarkedObjectSize) { TEST(ThreadHeapStatsCollectorTest, EventMarkingTimeFromIncrementalStandAloneGC) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kIncrementalMarkingStartMarking, base::TimeDelta::FromMilliseconds(7)); @@ -245,7 +267,8 @@ TEST(ThreadHeapStatsCollectorTest, TEST(ThreadHeapStatsCollectorTest, EventMarkingTimeFromIncrementalUnifiedGC) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kIncrementalMarkingStartMarking, base::TimeDelta::FromMilliseconds(7)); @@ -272,7 +295,8 @@ TEST(ThreadHeapStatsCollectorTest, EventMarkingTimeFromIncrementalUnifiedGC) { TEST(ThreadHeapStatsCollectorTest, EventMarkingTime) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kIncrementalMarkingStep, base::TimeDelta::FromMilliseconds(2)); @@ -287,7 +311,8 @@ TEST(ThreadHeapStatsCollectorTest, EventMarkingTime) { TEST(ThreadHeapStatsCollectorTest, EventAtomicMarkingTime) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kAtomicPauseMarkPrologue, base::TimeDelta::FromMilliseconds(5)); @@ -305,7 +330,8 @@ TEST(ThreadHeapStatsCollectorTest, EventAtomicMarkingTime) { TEST(ThreadHeapStatsCollectorTest, EventAtomicPause) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure, base::TimeDelta::FromMilliseconds(17)); @@ -320,7 +346,8 @@ TEST(ThreadHeapStatsCollectorTest, EventAtomicPause) { TEST(ThreadHeapStatsCollectorTest, EventMarkingTimePerByteInS) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( ThreadHeapStatsCollector::kAtomicPauseMarkTransitiveClosure, base::TimeDelta::FromSeconds(1)); @@ -332,7 +359,8 @@ TEST(ThreadHeapStatsCollectorTest, EventMarkingTimePerByteInS) { TEST(ThreadHeapStatsCollectorTest, EventSweepingTime) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kLazySweepInIdle, base::TimeDelta::FromMilliseconds(1)); @@ -352,7 +380,8 @@ TEST(ThreadHeapStatsCollectorTest, EventSweepingTime) { TEST(ThreadHeapStatsCollectorTest, EventCompactionFreedBytes) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.IncreaseCompactionFreedSize(512); stats_collector.NotifySweepingCompleted(); @@ -361,7 +390,8 @@ TEST(ThreadHeapStatsCollectorTest, EventCompactionFreedBytes) { TEST(ThreadHeapStatsCollectorTest, EventCompactionFreedPages) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.IncreaseCompactionFreedPages(3); stats_collector.NotifySweepingCompleted(); @@ -370,7 +400,8 @@ TEST(ThreadHeapStatsCollectorTest, EventCompactionFreedPages) { TEST(ThreadHeapStatsCollectorTest, EventInitialEstimatedLiveObjectRate) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); stats_collector.NotifySweepingCompleted(); EXPECT_DOUBLE_EQ(0.0, stats_collector.previous().live_object_rate); @@ -379,10 +410,12 @@ TEST(ThreadHeapStatsCollectorTest, EventInitialEstimatedLiveObjectRate) { TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateSameMarkedBytes) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); stats_collector.NotifySweepingCompleted(); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); stats_collector.NotifySweepingCompleted(); EXPECT_DOUBLE_EQ(1.0, stats_collector.previous().live_object_rate); @@ -391,10 +424,12 @@ TEST(ThreadHeapStatsCollectorTest, TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateHalfMarkedBytes) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(256); stats_collector.NotifySweepingCompleted(); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); stats_collector.NotifySweepingCompleted(); EXPECT_DOUBLE_EQ(0.5, stats_collector.previous().live_object_rate); @@ -402,10 +437,12 @@ TEST(ThreadHeapStatsCollectorTest, TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateNoMarkedBytes) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(256); stats_collector.NotifySweepingCompleted(); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifySweepingCompleted(); EXPECT_DOUBLE_EQ(0.0, stats_collector.previous().live_object_rate); } @@ -413,11 +450,13 @@ TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateNoMarkedBytes) { TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateWithAllocatedBytes1) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); stats_collector.NotifySweepingCompleted(); stats_collector.IncreaseAllocatedObjectSize(128); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); stats_collector.NotifySweepingCompleted(); EXPECT_DOUBLE_EQ(.5, stats_collector.previous().live_object_rate); @@ -426,11 +465,13 @@ TEST(ThreadHeapStatsCollectorTest, TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateWithAllocatedBytes2) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.NotifySweepingCompleted(); stats_collector.IncreaseAllocatedObjectSize(128); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); stats_collector.NotifySweepingCompleted(); EXPECT_DOUBLE_EQ(1.0, stats_collector.previous().live_object_rate); @@ -439,7 +480,8 @@ TEST(ThreadHeapStatsCollectorTest, TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateWithAllocatedBytes3) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.NotifySweepingCompleted(); EXPECT_DOUBLE_EQ(0, stats_collector.previous().live_object_rate); @@ -448,10 +490,12 @@ TEST(ThreadHeapStatsCollectorTest, TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateWithAllocatedBytes4) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(128); stats_collector.NotifySweepingCompleted(); - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.NotifySweepingCompleted(); EXPECT_DOUBLE_EQ(0, stats_collector.previous().live_object_rate); @@ -459,7 +503,8 @@ TEST(ThreadHeapStatsCollectorTest, TEST(ThreadHeapStatsCollectorTest, EventAllocatedSpaceBeforeSweeping1) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseAllocatedSpace(1024); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.IncreaseAllocatedSpace(2048); @@ -471,7 +516,8 @@ TEST(ThreadHeapStatsCollectorTest, EventAllocatedSpaceBeforeSweeping1) { TEST(ThreadHeapStatsCollectorTest, EventAllocatedSpaceBeforeSweeping2) { ThreadHeapStatsCollector stats_collector; - stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseAllocatedSpace(1024); stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.DecreaseAllocatedSpace(1024); @@ -497,7 +543,8 @@ class MockThreadHeapStatsObserver : public ThreadHeapStatsObserver { }; void FakeGC(ThreadHeapStatsCollector* stats_collector, size_t marked_bytes) { - stats_collector->NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector->NotifyMarkingStarted(BlinkGC::CollectionType::kMajor, + BlinkGC::GCReason::kForcedGCForTesting); stats_collector->NotifyMarkingCompleted(marked_bytes); stats_collector->NotifySweepingCompleted(); } diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_test.cc b/chromium/third_party/blink/renderer/platform/heap/heap_test.cc index b9bf108f948..bc9a3c031e7 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/heap_test.cc @@ -45,9 +45,9 @@ #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/bindings/buildflags.h" #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h" #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/heap/heap.h" -#include "third_party/blink/renderer/platform/heap/heap_linked_stack.h" #include "third_party/blink/renderer/platform/heap/heap_stats_collector.h" #include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" #include "third_party/blink/renderer/platform/heap/marking_visitor.h" @@ -76,7 +76,7 @@ class IntWrapper : public GarbageCollected<IntWrapper> { } static std::atomic_int destructor_calls_; - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} int Value() const { return x_; } @@ -262,14 +262,17 @@ class TestGCScope : public TestGCCollectGarbageScope { explicit TestGCScope(BlinkGC::StackState state) : TestGCCollectGarbageScope(state) { ThreadState::Current()->Heap().stats_collector()->NotifyMarkingStarted( + BlinkGC::CollectionType::kMajor, BlinkGC::GCReason::kForcedGCForTesting); ThreadState::Current()->AtomicPauseMarkPrologue( - state, BlinkGC::kAtomicMarking, BlinkGC::GCReason::kPreciseGC); + BlinkGC::CollectionType::kMajor, state, BlinkGC::kAtomicMarking, + BlinkGC::GCReason::kPreciseGC); } ~TestGCScope() { ThreadState::Current()->AtomicPauseMarkEpilogue(BlinkGC::kAtomicMarking); - ThreadState::Current()->AtomicPauseSweepAndCompact(BlinkGC::kAtomicMarking, - BlinkGC::kEagerSweeping); + ThreadState::Current()->AtomicPauseSweepAndCompact( + BlinkGC::CollectionType::kMajor, BlinkGC::kAtomicMarking, + BlinkGC::kEagerSweeping); ThreadState::Current()->AtomicPauseEpilogue(); } }; @@ -277,7 +280,7 @@ class TestGCScope : public TestGCCollectGarbageScope { class SimpleObject : public GarbageCollected<SimpleObject> { public: SimpleObject() = default; - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} char GetPayload(int i) { return payload[i]; } // This virtual method is unused but it is here to make sure // that this object has a vtable. This object is used @@ -297,7 +300,7 @@ class HeapTestSuperClass : public GarbageCollected<HeapTestSuperClass> { virtual ~HeapTestSuperClass() { ++destructor_calls_; } static int destructor_calls_; - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} }; int HeapTestSuperClass::destructor_calls_ = 0; @@ -335,7 +338,7 @@ class HeapAllocatedArray : public GarbageCollected<HeapAllocatedArray> { } int8_t at(size_t i) { return array_[i]; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: static const int kArraySize = 1000; @@ -533,7 +536,7 @@ class ThreadPersistentHeapTester : public ThreadedTesterBase { public: Local() = default; - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} }; class PersistentChain; @@ -561,7 +564,7 @@ class ThreadPersistentHeapTester : public ThreadedTesterBase { ref_counted_chain_ = base::AdoptRef(RefCountedChain::Create(count)); } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: scoped_refptr<RefCountedChain> ref_counted_chain_; @@ -590,7 +593,7 @@ class TraceCounter final : public GarbageCollected<TraceCounter> { public: TraceCounter() : trace_count_(0) {} - void Trace(blink::Visitor* visitor) { trace_count_++; } + void Trace(Visitor* visitor) { trace_count_++; } int TraceCount() const { return trace_count_; } private: @@ -601,7 +604,7 @@ TEST_F(HeapTest, IsHeapObjectAliveForConstPointer) { // See http://crbug.com/661363. auto* object = MakeGarbageCollected<SimpleObject>(); HeapObjectHeader* header = HeapObjectHeader::FromPayload(object); - header->Mark(); + EXPECT_TRUE(header->TryMark()); EXPECT_TRUE(ThreadHeap::IsHeapObjectAlive(object)); const SimpleObject* const_object = const_cast<const SimpleObject*>(object); EXPECT_TRUE(ThreadHeap::IsHeapObjectAlive(const_object)); @@ -611,9 +614,7 @@ class ClassWithMember : public GarbageCollected<ClassWithMember> { public: ClassWithMember() : trace_counter_(MakeGarbageCollected<TraceCounter>()) {} - void Trace(blink::Visitor* visitor) { - visitor->Trace(trace_counter_); - } + void Trace(Visitor* visitor) { visitor->Trace(trace_counter_); } int TraceCount() const { return trace_counter_->TraceCount(); } private: @@ -628,7 +629,7 @@ class SimpleFinalizedObject final static int destructor_calls_; - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} }; int SimpleFinalizedObject::destructor_calls_ = 0; @@ -654,7 +655,7 @@ class IntNode : public GarbageCollected<IntNode> { static IntNode* Create(int i) { return new IntNode(i); } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} int Value() { return value_; } @@ -674,7 +675,7 @@ class Bar : public GarbageCollected<Bar> { } bool HasBeenFinalized() const { return !magic_; } - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} static unsigned live_; protected: @@ -688,7 +689,7 @@ class Baz : public GarbageCollected<Baz> { public: explicit Baz(Bar* bar) : bar_(bar) {} - void Trace(blink::Visitor* visitor) { visitor->Trace(bar_); } + void Trace(Visitor* visitor) { visitor->Trace(bar_); } void Clear() { bar_.Release(); } @@ -705,16 +706,16 @@ class Foo : public Bar { Foo(Foo* foo) : Bar(), bar_(foo), points_to_foo_(true) {} - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { if (points_to_foo_) - visitor->Trace(static_cast<Foo*>(bar_)); + visitor->Trace(static_cast<const Foo*>(bar_)); else visitor->Trace(bar_); } private: - Bar* bar_; - bool points_to_foo_; + const Bar* bar_; + const bool points_to_foo_; }; class Bars : public Bar { @@ -726,7 +727,7 @@ class Bars : public Bar { } } - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { for (unsigned i = 0; i < width_; i++) visitor->Trace(bars_[i]); } @@ -746,7 +747,7 @@ class ConstructorAllocation : public GarbageCollected<ConstructorAllocation> { int_wrapper_ = MakeGarbageCollected<IntWrapper>(42); } - void Trace(blink::Visitor* visitor) { visitor->Trace(int_wrapper_); } + void Trace(Visitor* visitor) { visitor->Trace(int_wrapper_); } private: Member<IntWrapper> int_wrapper_; @@ -760,7 +761,7 @@ class LargeHeapObject final : public GarbageCollected<LargeHeapObject> { char Get(size_t i) { return data_[i]; } void Set(size_t i, char c) { data_[i] = c; } size_t length() { return kLength; } - void Trace(blink::Visitor* visitor) { visitor->Trace(int_wrapper_); } + void Trace(Visitor* visitor) { visitor->Trace(int_wrapper_); } static int destructor_calls_; private: @@ -805,7 +806,7 @@ class RefCountedAndGarbageCollected final keep_alive_.Clear(); } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} static int destructor_calls_; @@ -840,7 +841,7 @@ class RefCountedAndGarbageCollected2 final keep_alive_.Clear(); } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} static int destructor_calls_; @@ -856,7 +857,7 @@ class Weak : public Bar { Weak(Bar* strong_bar, Bar* weak_bar) : Bar(), strong_bar_(strong_bar), weak_bar_(weak_bar) {} - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { visitor->Trace(strong_bar_); visitor->template RegisterWeakCallbackMethod<Weak, &Weak::ZapWeakMembers>( this); @@ -880,7 +881,7 @@ class WithWeakMember : public Bar { WithWeakMember(Bar* strong_bar, Bar* weak_bar) : Bar(), strong_bar_(strong_bar), weak_bar_(weak_bar) {} - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { visitor->Trace(strong_bar_); visitor->Trace(weak_bar_); } @@ -899,7 +900,7 @@ class Observable final : public GarbageCollected<Observable> { public: explicit Observable(Bar* bar) : bar_(bar), was_destructed_(false) {} ~Observable() { was_destructed_ = true; } - void Trace(blink::Visitor* visitor) { visitor->Trace(bar_); } + void Trace(Visitor* visitor) { visitor->Trace(bar_); } // willFinalize is called by FinalizationObserver. willFinalize can touch // other on-heap objects. @@ -924,7 +925,7 @@ class ObservableWithPreFinalizer final public: ObservableWithPreFinalizer() : was_destructed_(false) {} ~ObservableWithPreFinalizer() { was_destructed_ = true; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} void Dispose() { EXPECT_FALSE(was_destructed_); dispose_was_called_ = true; @@ -947,7 +948,7 @@ class PreFinalizerBase : public GarbageCollected<PreFinalizerBase> { public: PreFinalizerBase() : was_destructed_(false) {} virtual ~PreFinalizerBase() { was_destructed_ = true; } - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} void Dispose() { EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_base); EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_sub_class); @@ -965,7 +966,7 @@ class PreFinalizerMixin : public GarbageCollectedMixin { public: ~PreFinalizerMixin() { was_destructed_ = true; } - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} void Dispose() { EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_base); EXPECT_TRUE(g_dispose_was_called_for_pre_finalizer_sub_class); @@ -986,7 +987,7 @@ class PreFinalizerSubClass : public PreFinalizerBase, public PreFinalizerMixin { public: PreFinalizerSubClass() : was_destructed_(false) {} ~PreFinalizerSubClass() override { was_destructed_ = true; } - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} void Dispose() { EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_base); EXPECT_FALSE(g_dispose_was_called_for_pre_finalizer_sub_class); @@ -1006,7 +1007,7 @@ class FinalizationObserver : public GarbageCollected<FinalizationObserver<T>> { bool DidCallWillFinalize() const { return did_call_will_finalize_; } - void Trace(blink::Visitor* visitor) { + void Trace(Visitor* visitor) { visitor->template RegisterWeakCallbackMethod< FinalizationObserver<T>, &FinalizationObserver<T>::ZapWeakMembers>( this); @@ -1087,7 +1088,7 @@ class PointsBack final : public GarbageCollected<PointsBack> { SuperClass* BackPointer() const { return back_pointer_; } - void Trace(blink::Visitor* visitor) { visitor->Trace(back_pointer_); } + void Trace(Visitor* visitor) { visitor->Trace(back_pointer_); } static int alive_count_; @@ -1113,7 +1114,7 @@ class SuperClass : public GarbageCollected<SuperClass> { EXPECT_EQ(super_class_count, SuperClass::alive_count_); } - virtual void Trace(blink::Visitor* visitor) { visitor->Trace(points_back_); } + virtual void Trace(Visitor* visitor) { visitor->Trace(points_back_); } PointsBack* GetPointsBack() const { return points_back_.Get(); } @@ -1129,7 +1130,7 @@ class SubData final : public GarbageCollected<SubData> { SubData() { ++alive_count_; } ~SubData() { --alive_count_; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} static int alive_count_; }; @@ -1144,7 +1145,7 @@ class SubClass : public SuperClass { } ~SubClass() override { --alive_count_; } - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { visitor->Trace(data_); SuperClass::Trace(visitor); } @@ -1159,7 +1160,7 @@ int SubClass::alive_count_ = 0; class Mixin : public GarbageCollectedMixin { public: - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} virtual char GetPayload(int i) { return padding_[i]; } @@ -1179,7 +1180,7 @@ class UseMixin : public SimpleObject, public Mixin { } static int trace_count_; - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { SimpleObject::Trace(visitor); Mixin::Trace(visitor); ++trace_count_; @@ -1194,7 +1195,7 @@ class VectorObject { public: VectorObject() { value_ = MakeGarbageCollected<SimpleFinalizedObject>(); } - void Trace(blink::Visitor* visitor) { visitor->Trace(value_); } + void Trace(Visitor* visitor) { visitor->Trace(value_); } private: Member<SimpleFinalizedObject> value_; @@ -1221,7 +1222,7 @@ class TerminatedArrayItem { TerminatedArrayItem(IntWrapper* payload) : payload_(payload), is_last_(false) {} - void Trace(blink::Visitor* visitor) { visitor->Trace(payload_); } + void Trace(Visitor* visitor) { visitor->Trace(payload_); } bool IsLastInArray() const { return is_last_; } void SetLastInArray(bool value) { is_last_ = value; } @@ -1247,7 +1248,7 @@ class OneKiloByteObject final : public GarbageCollected<OneKiloByteObject> { public: ~OneKiloByteObject() { destructor_calls_++; } char* Data() { return data_; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} static int destructor_calls_; private: @@ -1268,7 +1269,7 @@ class DynamicallySizedObject : public GarbageCollected<DynamicallySizedObject> { uint8_t Get(int i) { return *(reinterpret_cast<uint8_t*>(this) + i); } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: DynamicallySizedObject() = default; @@ -1288,7 +1289,7 @@ class FinalizationAllocator final MakeGarbageCollected<LargeHeapObject>(); } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: Persistent<IntWrapper>* wrapper_; @@ -1333,7 +1334,7 @@ class PreFinalizerBackingShrinkForbidden final EXPECT_EQ(0ul, map_.Capacity()); } - void Trace(blink::Visitor* visitor) { + void Trace(Visitor* visitor) { visitor->Trace(vector_); visitor->Trace(map_); } @@ -1369,7 +1370,7 @@ class PreFinalizerVectorBackingExpandForbidden final } } - void Trace(blink::Visitor* visitor) { visitor->Trace(vector_); } + void Trace(Visitor* visitor) { visitor->Trace(vector_); } private: HeapVector<Member<IntWrapper>> vector_; @@ -1398,7 +1399,7 @@ class PreFinalizerHashTableBackingExpandForbidden final } } - void Trace(blink::Visitor* visitor) { visitor->Trace(map_); } + void Trace(Visitor* visitor) { visitor->Trace(map_); } private: HeapHashMap<int, Member<IntWrapper>> map_; @@ -1422,7 +1423,7 @@ class PreFinalizerAllocationForbidden #endif // DCHECK_IS_ON() } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} }; TEST(HeapDeathTest, PreFinalizerAllocationForbidden) { @@ -1827,8 +1828,8 @@ TEST_F(HeapTest, LazySweepingPages) { for (int i = 0; i < 1000; i++) MakeGarbageCollected<SimpleFinalizedObject>(); ThreadState::Current()->CollectGarbage( - BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, - BlinkGC::kConcurrentAndLazySweeping, + BlinkGC::CollectionType::kMajor, BlinkGC::kNoHeapPointersOnStack, + BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping, BlinkGC::GCReason::kForcedGCForTesting); EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_); for (int i = 0; i < 10000; i++) @@ -1861,8 +1862,8 @@ TEST_F(HeapTest, LazySweepingLargeObjectPages) { for (int i = 0; i < 10; i++) MakeGarbageCollected<LargeHeapObject>(); ThreadState::Current()->CollectGarbage( - BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, - BlinkGC::kConcurrentAndLazySweeping, + BlinkGC::CollectionType::kMajor, BlinkGC::kNoHeapPointersOnStack, + BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping, BlinkGC::GCReason::kForcedGCForTesting); EXPECT_EQ(0, LargeHeapObject::destructor_calls_); for (int i = 0; i < 10; i++) { @@ -1873,8 +1874,8 @@ TEST_F(HeapTest, LazySweepingLargeObjectPages) { MakeGarbageCollected<LargeHeapObject>(); EXPECT_EQ(10, LargeHeapObject::destructor_calls_); ThreadState::Current()->CollectGarbage( - BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, - BlinkGC::kConcurrentAndLazySweeping, + BlinkGC::CollectionType::kMajor, BlinkGC::kNoHeapPointersOnStack, + BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping, BlinkGC::GCReason::kForcedGCForTesting); EXPECT_EQ(10, LargeHeapObject::destructor_calls_); PreciselyCollectGarbage(); @@ -2242,10 +2243,8 @@ class Container final : public GarbageCollected<Container> { HeapVector<Member<IntWrapper>, 2> vector; HeapVector<PairWrappedUnwrapped, 2> vector_wu; HeapVector<PairUnwrappedWrapped, 2> vector_uw; - HeapDeque<Member<IntWrapper>, 0> deque; - HeapDeque<PairWrappedUnwrapped, 0> deque_wu; - HeapDeque<PairUnwrappedWrapped, 0> deque_uw; - void Trace(blink::Visitor* visitor) { + HeapDeque<Member<IntWrapper>> deque; + void Trace(Visitor* visitor) { visitor->Trace(map); visitor->Trace(set); visitor->Trace(set2); @@ -2254,14 +2253,12 @@ class Container final : public GarbageCollected<Container> { visitor->Trace(vector_wu); visitor->Trace(vector_uw); visitor->Trace(deque); - visitor->Trace(deque_wu); - visitor->Trace(deque_uw); } }; struct NeedsTracingTrait { explicit NeedsTracingTrait(IntWrapper* wrapper) : wrapper_(wrapper) {} - void Trace(blink::Visitor* visitor) { visitor->Trace(wrapper_); } + void Trace(Visitor* visitor) { visitor->Trace(wrapper_); } Member<IntWrapper> wrapper_; }; @@ -2406,9 +2403,9 @@ TEST_F(HeapTest, HeapVectorOnStackLargeObjectPageSized) { ConservativelyCollectGarbage(); } -template <typename T, wtf_size_t inlineCapacity, typename U> -bool DequeContains(HeapDeque<T, inlineCapacity>& deque, U u) { - typedef typename HeapDeque<T, inlineCapacity>::iterator iterator; +template <typename T, typename U> +bool DequeContains(HeapDeque<T>& deque, U u) { + typedef typename HeapDeque<T>::iterator iterator; for (iterator it = deque.begin(); it != deque.end(); ++it) { if (*it == u) return true; @@ -2427,12 +2424,10 @@ TEST_F(HeapTest, HeapCollectionTypes) { typedef HeapHashCountedSet<Member<IntWrapper>> MemberCountedSet; typedef HeapVector<Member<IntWrapper>, 2> MemberVector; - typedef HeapDeque<Member<IntWrapper>, 0> MemberDeque; + typedef HeapDeque<Member<IntWrapper>> MemberDeque; typedef HeapVector<PairWrappedUnwrapped, 2> VectorWU; typedef HeapVector<PairUnwrappedWrapped, 2> VectorUW; - typedef HeapDeque<PairWrappedUnwrapped, 0> DequeWU; - typedef HeapDeque<PairUnwrappedWrapped, 0> DequeUW; Persistent<MemberMember> member_member = MakeGarbageCollected<MemberMember>(); Persistent<MemberMember> member_member2 = @@ -2454,10 +2449,6 @@ TEST_F(HeapTest, HeapCollectionTypes) { Persistent<VectorUW> vector_uw2 = MakeGarbageCollected<VectorUW>(); Persistent<MemberDeque> deque = MakeGarbageCollected<MemberDeque>(); Persistent<MemberDeque> deque2 = MakeGarbageCollected<MemberDeque>(); - Persistent<DequeWU> deque_wu = MakeGarbageCollected<DequeWU>(); - Persistent<DequeWU> deque_wu2 = MakeGarbageCollected<DequeWU>(); - Persistent<DequeUW> deque_uw = MakeGarbageCollected<DequeUW>(); - Persistent<DequeUW> deque_uw2 = MakeGarbageCollected<DequeUW>(); Persistent<Container> container = MakeGarbageCollected<Container>(); ClearOutOldGarbage(); @@ -2475,18 +2466,14 @@ TEST_F(HeapTest, HeapCollectionTypes) { auto* three_c(MakeGarbageCollected<IntWrapper>(3)); auto* three_d(MakeGarbageCollected<IntWrapper>(3)); auto* three_e(MakeGarbageCollected<IntWrapper>(3)); - auto* three_f(MakeGarbageCollected<IntWrapper>(3)); auto* three(MakeGarbageCollected<IntWrapper>(3)); auto* four_b(MakeGarbageCollected<IntWrapper>(4)); auto* four_c(MakeGarbageCollected<IntWrapper>(4)); auto* four_d(MakeGarbageCollected<IntWrapper>(4)); auto* four_e(MakeGarbageCollected<IntWrapper>(4)); - auto* four_f(MakeGarbageCollected<IntWrapper>(4)); auto* four(MakeGarbageCollected<IntWrapper>(4)); auto* five_c(MakeGarbageCollected<IntWrapper>(5)); auto* five_d(MakeGarbageCollected<IntWrapper>(5)); - auto* five_e(MakeGarbageCollected<IntWrapper>(5)); - auto* five_f(MakeGarbageCollected<IntWrapper>(5)); // Member Collections. member_member2->insert(one, two); @@ -2515,21 +2502,13 @@ TEST_F(HeapTest, HeapCollectionTypes) { deque2->push_back(three_e); deque2->push_back(four_e); vector_wu->push_back(PairWrappedUnwrapped(&*one_c, 42)); - deque_wu->push_back(PairWrappedUnwrapped(&*one_e, 42)); vector_wu2->push_back(PairWrappedUnwrapped(&*three_c, 43)); vector_wu2->push_back(PairWrappedUnwrapped(&*four_c, 44)); vector_wu2->push_back(PairWrappedUnwrapped(&*five_c, 45)); - deque_wu2->push_back(PairWrappedUnwrapped(&*three_e, 43)); - deque_wu2->push_back(PairWrappedUnwrapped(&*four_e, 44)); - deque_wu2->push_back(PairWrappedUnwrapped(&*five_e, 45)); vector_uw->push_back(PairUnwrappedWrapped(1, &*one_d)); vector_uw2->push_back(PairUnwrappedWrapped(103, &*three_d)); vector_uw2->push_back(PairUnwrappedWrapped(104, &*four_d)); vector_uw2->push_back(PairUnwrappedWrapped(105, &*five_d)); - deque_uw->push_back(PairUnwrappedWrapped(1, &*one_f)); - deque_uw2->push_back(PairUnwrappedWrapped(103, &*three_f)); - deque_uw2->push_back(PairUnwrappedWrapped(104, &*four_f)); - deque_uw2->push_back(PairUnwrappedWrapped(105, &*five_f)); EXPECT_TRUE(DequeContains(*deque, one_b)); @@ -2554,10 +2533,6 @@ TEST_F(HeapTest, HeapCollectionTypes) { EXPECT_EQ(3u, vector_uw2->size()); EXPECT_EQ(1u, deque->size()); EXPECT_EQ(2u, deque2->size()); - EXPECT_EQ(1u, deque_wu->size()); - EXPECT_EQ(3u, deque_wu2->size()); - EXPECT_EQ(1u, deque_uw->size()); - EXPECT_EQ(3u, deque_uw2->size()); MemberVector& cvec = container->vector; cvec.swap(*vector.Get()); @@ -2579,16 +2554,6 @@ TEST_F(HeapTest, HeapCollectionTypes) { deque2->Swap(c_deque); deque->Swap(c_deque); - DequeWU& c_deque_wu = container->deque_wu; - c_deque_wu.Swap(*deque_wu.Get()); - deque_wu2->Swap(c_deque_wu); - deque_wu->Swap(c_deque_wu); - - DequeUW& c_deque_uw = container->deque_uw; - c_deque_uw.Swap(*deque_uw.Get()); - deque_uw2->Swap(c_deque_uw); - deque_uw->Swap(c_deque_uw); - // Swap set and set2 in a roundabout way. MemberSet& cset1 = container->set; MemberSet& cset2 = container->set2; @@ -2645,22 +2610,6 @@ TEST_F(HeapTest, HeapCollectionTypes) { EXPECT_TRUE(vector_uw->Contains(PairUnwrappedWrapped(105, &*five_d))); EXPECT_TRUE(vector_uw2->Contains(PairUnwrappedWrapped(1, &*one_d))); EXPECT_FALSE(vector_uw2->Contains(PairUnwrappedWrapped(103, &*three_d))); - EXPECT_TRUE( - DequeContains(*deque_wu, PairWrappedUnwrapped(&*three_e, 43))); - EXPECT_TRUE(DequeContains(*deque_wu, PairWrappedUnwrapped(&*four_e, 44))); - EXPECT_TRUE(DequeContains(*deque_wu, PairWrappedUnwrapped(&*five_e, 45))); - EXPECT_TRUE(DequeContains(*deque_wu2, PairWrappedUnwrapped(&*one_e, 42))); - EXPECT_FALSE( - DequeContains(*deque_wu2, PairWrappedUnwrapped(&*three_e, 43))); - EXPECT_TRUE( - DequeContains(*deque_uw, PairUnwrappedWrapped(103, &*three_f))); - EXPECT_TRUE( - DequeContains(*deque_uw, PairUnwrappedWrapped(104, &*four_f))); - EXPECT_TRUE( - DequeContains(*deque_uw, PairUnwrappedWrapped(105, &*five_f))); - EXPECT_TRUE(DequeContains(*deque_uw2, PairUnwrappedWrapped(1, &*one_f))); - EXPECT_FALSE( - DequeContains(*deque_uw2, PairUnwrappedWrapped(103, &*three_f))); } PreciselyCollectGarbage(); @@ -2678,7 +2627,6 @@ TEST_F(HeapTest, HeapCollectionTypes) { EXPECT_EQ(1u, vector2->size()); EXPECT_EQ(2u, deque->size()); EXPECT_EQ(1u, deque2->size()); - EXPECT_EQ(3u, deque_uw->size()); EXPECT_EQ(1u, deque2->size()); EXPECT_TRUE(member_member->at(one) == two); @@ -2713,10 +2661,6 @@ TEST_F(HeapTest, HeapCollectionTypes) { EXPECT_EQ(1u, vector_uw2->size()); EXPECT_EQ(2u, deque->size()); EXPECT_EQ(1u, deque2->size()); - EXPECT_EQ(3u, deque_wu->size()); - EXPECT_EQ(1u, deque_wu2->size()); - EXPECT_EQ(3u, deque_uw->size()); - EXPECT_EQ(1u, deque_uw2->size()); } TEST_F(HeapTest, PersistentVector) { @@ -2962,7 +2906,7 @@ class NonTrivialObject final { deque_.push_back(MakeGarbageCollected<IntWrapper>(num)); vector_.push_back(MakeGarbageCollected<IntWrapper>(num)); } - void Trace(blink::Visitor* visitor) { + void Trace(Visitor* visitor) { visitor->Trace(deque_); visitor->Trace(vector_); } @@ -3184,6 +3128,9 @@ TEST_F(HeapTest, HeapWeakLinkedHashSet) { OrderedSetHelper<HeapLinkedHashSet<WeakMember<IntWrapper>>>(false); ClearOutOldGarbage(); OrderedSetHelper<HeapListHashSet<Member<IntWrapper>>>(true); + ClearOutOldGarbage(); + // TODO(keinakashima): add a test case for WeakMember once it's supported + OrderedSetHelper<HeapNewLinkedHashSet<Member<IntWrapper>>>(true); } class ThingWithDestructor { @@ -3311,6 +3258,7 @@ typedef HeapLinkedHashSet<PairWeakStrong> WeakStrongLinkedSet; typedef HeapLinkedHashSet<PairWeakUnwrapped> WeakUnwrappedLinkedSet; typedef HeapLinkedHashSet<PairStrongWeak> StrongWeakLinkedSet; typedef HeapLinkedHashSet<PairUnwrappedWeak> UnwrappedWeakLinkedSet; +// TODO(bartekn): add HeapNewLinkedHashSet cases once WeakMember is supported typedef HeapHashCountedSet<PairWeakStrong> WeakStrongCountedSet; typedef HeapHashCountedSet<PairWeakUnwrapped> WeakUnwrappedCountedSet; typedef HeapHashCountedSet<PairStrongWeak> StrongWeakCountedSet; @@ -3383,70 +3331,6 @@ void CheckPairSets(Persistent<WSSet>& weak_strong, EXPECT_TRUE(unwrapped_weak->Contains(PairUnwrappedWeak(2, &*two))); } -template <typename WSSet, typename SWSet, typename WUSet, typename UWSet> -void WeakPairsHelper() { - IntWrapper::destructor_calls_ = 0; - - Persistent<HeapVector<Member<IntWrapper>>> keep_numbers_alive = - MakeGarbageCollected<HeapVector<Member<IntWrapper>>>(); - - Persistent<WSSet> weak_strong = MakeGarbageCollected<WSSet>(); - Persistent<SWSet> strong_weak = MakeGarbageCollected<SWSet>(); - Persistent<WUSet> weak_unwrapped = MakeGarbageCollected<WUSet>(); - Persistent<UWSet> unwrapped_weak = MakeGarbageCollected<UWSet>(); - - Persistent<IntWrapper> two = MakeGarbageCollected<IntWrapper>(2); - - weak_strong->insert( - PairWeakStrong(MakeGarbageCollected<IntWrapper>(1), &*two)); - weak_strong->insert(PairWeakStrong(&*two, &*two)); - strong_weak->insert( - PairStrongWeak(&*two, MakeGarbageCollected<IntWrapper>(1))); - strong_weak->insert(PairStrongWeak(&*two, &*two)); - weak_unwrapped->insert( - PairWeakUnwrapped(MakeGarbageCollected<IntWrapper>(1), 2)); - weak_unwrapped->insert(PairWeakUnwrapped(&*two, 2)); - unwrapped_weak->insert( - PairUnwrappedWeak(2, MakeGarbageCollected<IntWrapper>(1))); - unwrapped_weak->insert(PairUnwrappedWeak(2, &*two)); - - CheckPairSets<WSSet, SWSet, WUSet, UWSet>( - weak_strong, strong_weak, weak_unwrapped, unwrapped_weak, true, two); - - TestSupportingGC::PreciselyCollectGarbage(); - CheckPairSets<WSSet, SWSet, WUSet, UWSet>( - weak_strong, strong_weak, weak_unwrapped, unwrapped_weak, false, two); -} - -TEST_F(HeapTest, HeapWeakPairs) { - { - typedef HeapHashSet<PairWeakStrong> WeakStrongSet; - typedef HeapHashSet<PairWeakUnwrapped> WeakUnwrappedSet; - typedef HeapHashSet<PairStrongWeak> StrongWeakSet; - typedef HeapHashSet<PairUnwrappedWeak> UnwrappedWeakSet; - WeakPairsHelper<WeakStrongSet, StrongWeakSet, WeakUnwrappedSet, - UnwrappedWeakSet>(); - } - - { - typedef HeapListHashSet<PairWeakStrong> WeakStrongSet; - typedef HeapListHashSet<PairWeakUnwrapped> WeakUnwrappedSet; - typedef HeapListHashSet<PairStrongWeak> StrongWeakSet; - typedef HeapListHashSet<PairUnwrappedWeak> UnwrappedWeakSet; - WeakPairsHelper<WeakStrongSet, StrongWeakSet, WeakUnwrappedSet, - UnwrappedWeakSet>(); - } - - { - typedef HeapLinkedHashSet<PairWeakStrong> WeakStrongSet; - typedef HeapLinkedHashSet<PairWeakUnwrapped> WeakUnwrappedSet; - typedef HeapLinkedHashSet<PairStrongWeak> StrongWeakSet; - typedef HeapLinkedHashSet<PairUnwrappedWeak> UnwrappedWeakSet; - WeakPairsHelper<WeakStrongSet, StrongWeakSet, WeakUnwrappedSet, - UnwrappedWeakSet>(); - } -} - TEST_F(HeapTest, HeapWeakCollectionTypes) { IntWrapper::destructor_calls_ = 0; @@ -3455,6 +3339,7 @@ TEST_F(HeapTest, HeapWeakCollectionTypes) { typedef HeapHashMap<WeakMember<IntWrapper>, WeakMember<IntWrapper>> WeakWeak; typedef HeapHashSet<WeakMember<IntWrapper>> WeakSet; typedef HeapLinkedHashSet<WeakMember<IntWrapper>> WeakOrderedSet; + // TODO(bartekn): add HeapNewLinkedHashSet case once WeakMember is supported ClearOutOldGarbage(); @@ -4026,28 +3911,19 @@ TEST_F(HeapTest, CollectionNesting3) { ClearOutOldGarbage(); IntWrapper::destructor_calls_ = 0; typedef HeapVector<Member<IntWrapper>> IntVector; - typedef HeapDeque<Member<IntWrapper>> IntDeque; HeapVector<IntVector>* vector = MakeGarbageCollected<HeapVector<IntVector>>(); - HeapDeque<IntDeque>* deque = MakeGarbageCollected<HeapDeque<IntDeque>>(); vector->push_back(IntVector()); - deque->push_back(IntDeque()); HeapVector<IntVector>::iterator it = vector->begin(); - HeapDeque<IntDeque>::iterator it2 = deque->begin(); EXPECT_EQ(0u, it->size()); - EXPECT_EQ(0u, it2->size()); it->push_back(MakeGarbageCollected<IntWrapper>(42)); - it2->push_back(MakeGarbageCollected<IntWrapper>(42)); EXPECT_EQ(1u, it->size()); - EXPECT_EQ(1u, it2->size()); Persistent<HeapVector<IntVector>> keep_alive(vector); - Persistent<HeapDeque<IntDeque>> keep_alive2(deque); PreciselyCollectGarbage(); EXPECT_EQ(1u, it->size()); - EXPECT_EQ(1u, it2->size()); EXPECT_EQ(0, IntWrapper::destructor_calls_); } @@ -4080,42 +3956,13 @@ TEST_F(HeapTest, EmbeddedInVector) { EXPECT_EQ(6, SimpleFinalizedObject::destructor_calls_); } -TEST_F(HeapTest, EmbeddedInDeque) { - ClearOutOldGarbage(); - SimpleFinalizedObject::destructor_calls_ = 0; - { - Persistent<HeapDeque<VectorObject, 2>> inline_deque = - MakeGarbageCollected<HeapDeque<VectorObject, 2>>(); - Persistent<HeapDeque<VectorObject>> outline_deque = - MakeGarbageCollected<HeapDeque<VectorObject>>(); - VectorObject i1, i2; - inline_deque->push_back(i1); - inline_deque->push_back(i2); - - VectorObject o1, o2; - outline_deque->push_back(o1); - outline_deque->push_back(o2); - - Persistent<HeapDeque<VectorObjectInheritedTrace>> deque_inherited_trace = - MakeGarbageCollected<HeapDeque<VectorObjectInheritedTrace>>(); - VectorObjectInheritedTrace it1, it2; - deque_inherited_trace->push_back(it1); - deque_inherited_trace->push_back(it2); - - PreciselyCollectGarbage(); - EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_); - } - PreciselyCollectGarbage(); - EXPECT_EQ(6, SimpleFinalizedObject::destructor_calls_); -} - class InlinedVectorObject { DISALLOW_NEW(); public: InlinedVectorObject() = default; ~InlinedVectorObject() { destructor_calls_++; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} static int destructor_calls_; }; @@ -4129,7 +3976,7 @@ class InlinedVectorObjectWithVtable { InlinedVectorObjectWithVtable() = default; virtual ~InlinedVectorObjectWithVtable() { destructor_calls_++; } virtual void VirtualMethod() {} - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} static int destructor_calls_; }; @@ -4155,7 +4002,7 @@ class InlinedVectorObjectWrapper final vector3_.push_back(i2); } - void Trace(blink::Visitor* visitor) { + void Trace(Visitor* visitor) { visitor->Trace(vector1_); visitor->Trace(vector2_); visitor->Trace(vector3_); @@ -4180,7 +4027,7 @@ class InlinedVectorObjectWithVtableWrapper final vector3_.push_back(i2); } - void Trace(blink::Visitor* visitor) { + void Trace(Visitor* visitor) { visitor->Trace(vector1_); visitor->Trace(vector2_); visitor->Trace(vector3_); @@ -4298,34 +4145,6 @@ void RawPtrInHashHelper() { } } -TEST_F(HeapTest, HeapLinkedStack) { - ClearOutOldGarbage(); - IntWrapper::destructor_calls_ = 0; - - HeapLinkedStack<TerminatedArrayItem>* stack = - MakeGarbageCollected<HeapLinkedStack<TerminatedArrayItem>>(); - - const wtf_size_t kStackSize = 10; - - for (wtf_size_t i = 0; i < kStackSize; i++) - stack->Push(TerminatedArrayItem(MakeGarbageCollected<IntWrapper>(i))); - - ConservativelyCollectGarbage(); - EXPECT_EQ(0, IntWrapper::destructor_calls_); - EXPECT_EQ(kStackSize, stack->size()); - while (!stack->IsEmpty()) { - EXPECT_EQ(stack->size() - 1, - static_cast<size_t>(stack->Peek().Payload()->Value())); - stack->Pop(); - } - - Persistent<HeapLinkedStack<TerminatedArrayItem>> p_stack = stack; - - PreciselyCollectGarbage(); - EXPECT_EQ(kStackSize, static_cast<size_t>(IntWrapper::destructor_calls_)); - EXPECT_EQ(0u, p_stack->size()); -} - TEST_F(HeapTest, AllocationDuringFinalization) { ClearOutOldGarbage(); IntWrapper::destructor_calls_ = 0; @@ -4430,7 +4249,7 @@ TEST_F(HeapTest, DestructorsCalled) { class MixinA : public GarbageCollectedMixin { public: MixinA() : obj_(MakeGarbageCollected<IntWrapper>(100)) {} - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { trace_count_++; visitor->Trace(obj_); } @@ -4445,7 +4264,7 @@ int MixinA::trace_count_ = 0; class MixinB : public GarbageCollectedMixin { public: MixinB() : obj_(MakeGarbageCollected<IntWrapper>(101)) {} - void Trace(blink::Visitor* visitor) override { visitor->Trace(obj_); } + void Trace(Visitor* visitor) override { visitor->Trace(obj_); } Member<IntWrapper> obj_; }; @@ -4456,7 +4275,7 @@ class MultipleMixins : public GarbageCollected<MultipleMixins>, public: MultipleMixins() : obj_(MakeGarbageCollected<IntWrapper>(102)) {} - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { visitor->Trace(obj_); MixinA::Trace(visitor); MixinB::Trace(visitor); @@ -4468,7 +4287,7 @@ class DerivedMultipleMixins : public MultipleMixins { public: DerivedMultipleMixins() : obj_(MakeGarbageCollected<IntWrapper>(103)) {} - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { trace_called_++; visitor->Trace(obj_); MultipleMixins::Trace(visitor); @@ -4686,7 +4505,7 @@ TEST_F(HeapTest, EphemeronsInEphemerons) { class EphemeronWrapper : public GarbageCollected<EphemeronWrapper> { public: - void Trace(blink::Visitor* visitor) { visitor->Trace(map_); } + void Trace(Visitor* visitor) { visitor->Trace(map_); } typedef HeapHashMap<WeakMember<IntWrapper>, Member<EphemeronWrapper>> Map; Map& GetMap() { return map_; } @@ -4778,7 +4597,7 @@ class Link1 : public GarbageCollected<Link1> { public: Link1(IntWrapper* link) : link_(link) {} - void Trace(blink::Visitor* visitor) { visitor->Trace(link_); } + void Trace(Visitor* visitor) { visitor->Trace(link_); } IntWrapper* Link() { return link_; } @@ -4820,9 +4639,7 @@ class TraceIfNeededTester final TraceIfNeededTester() = default; explicit TraceIfNeededTester(const T& obj) : obj_(obj) {} - void Trace(blink::Visitor* visitor) { - TraceIfNeeded<T>::Trace(visitor, obj_); - } + void Trace(Visitor* visitor) { TraceIfNeeded<T>::Trace(visitor, obj_); } T& Obj() { return obj_; } ~TraceIfNeededTester() = default; @@ -4835,7 +4652,7 @@ class PartObject { public: PartObject() : obj_(MakeGarbageCollected<SimpleObject>()) {} - void Trace(blink::Visitor* visitor) { visitor->Trace(obj_); } + void Trace(Visitor* visitor) { visitor->Trace(obj_); } private: Member<SimpleObject> obj_; @@ -4864,7 +4681,7 @@ class AllocatesOnAssignment { inline bool IsDeleted() const { return value_.IsHashTableDeletedValue(); } - void Trace(blink::Visitor* visitor) { visitor->Trace(value_); } + void Trace(Visitor* visitor) { visitor->Trace(value_); } int Value() { return value_->Value(); } @@ -4940,14 +4757,14 @@ TEST_F(HeapTest, GCInHashMapOperations) { class PartObjectWithVirtualMethod { public: - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} }; class ObjectWithVirtualPartObject : public GarbageCollected<ObjectWithVirtualPartObject> { public: ObjectWithVirtualPartObject() : dummy_(AllocateAndReturnBool()) {} - void Trace(blink::Visitor* visitor) { visitor->Trace(part_); } + void Trace(Visitor* visitor) { visitor->Trace(part_); } private: bool dummy_; @@ -4965,7 +4782,7 @@ class AllocInSuperConstructorArgumentSuper public: AllocInSuperConstructorArgumentSuper(bool value) : value_(value) {} virtual ~AllocInSuperConstructorArgumentSuper() = default; - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} bool Value() { return value_; } private: @@ -4995,7 +4812,7 @@ class NonNodeAllocatingNodeInDestructor final node_ = new Persistent<IntNode>(IntNode::Create(10)); } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} static Persistent<IntNode>* node_; }; @@ -5014,7 +4831,7 @@ class DeepEagerly final : public GarbageCollected<DeepEagerly> { public: DeepEagerly(DeepEagerly* next) : next_(next) {} - void Trace(blink::Visitor* visitor) { + void Trace(Visitor* visitor) { int calls = ++s_trace_calls_; if (s_trace_lazy_ <= 2) visitor->Trace(next_); @@ -5118,7 +4935,7 @@ class PartObjectWithRef { public: PartObjectWithRef(int i) : value_(SimpleRefValue::Create(i)) {} - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} int Value() const { return value_->Value(); } @@ -5132,68 +4949,6 @@ WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::PartObjectWithRef) namespace blink { -TEST_F(HeapTest, DequePartObjectsExpand) { - // Test expansion of HeapDeque<PartObject> - - using PartDeque = HeapDeque<PartObjectWithRef>; - - Persistent<PartDeque> deque = MakeGarbageCollected<PartDeque>(); - // Auxillary Deque used to prevent 'inline' buffer expansion. - Persistent<PartDeque> deque_unused = MakeGarbageCollected<PartDeque>(); - - // Append a sequence, bringing about repeated expansions of the - // deque's buffer. - int i = 0; - for (; i < 60; ++i) { - deque->push_back(PartObjectWithRef(i)); - deque_unused->push_back(PartObjectWithRef(i)); - } - - EXPECT_EQ(60u, deque->size()); - i = 0; - for (const PartObjectWithRef& part : *deque) { - EXPECT_EQ(i, part.Value()); - i++; - } - - // Remove most of the queued objects and have the buffer's start index - // 'point' somewhere into the buffer, just behind the end index. - for (i = 0; i < 50; ++i) - deque->TakeFirst(); - - EXPECT_EQ(10u, deque->size()); - i = 0; - for (const PartObjectWithRef& part : *deque) { - EXPECT_EQ(50 + i, part.Value()); - i++; - } - - // Append even more, eventually causing an expansion of the underlying - // buffer once the end index wraps around and reaches the start index. - for (i = 0; i < 70; ++i) - deque->push_back(PartObjectWithRef(60 + i)); - - // Verify that the final buffer expansion copied the start and end segments - // of the old buffer to both ends of the expanded buffer, along with - // re-adjusting both start&end indices in terms of that expanded buffer. - EXPECT_EQ(80u, deque->size()); - i = 0; - for (const PartObjectWithRef& part : *deque) { - EXPECT_EQ(i + 50, part.Value()); - i++; - } - - for (i = 0; i < 70; ++i) - deque->push_back(PartObjectWithRef(130 + i)); - - EXPECT_EQ(150u, deque->size()); - i = 0; - for (const PartObjectWithRef& part : *deque) { - EXPECT_EQ(i + 50, part.Value()); - i++; - } -} - TEST_F(HeapTest, HeapVectorPartObjects) { HeapVector<PartObjectWithRef> vector1; HeapVector<PartObjectWithRef> vector2; @@ -5234,7 +4989,7 @@ class TestMixinAllocationA : public GarbageCollected<TestMixinAllocationA>, public: TestMixinAllocationA() = default; - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} }; class TestMixinAllocationB : public TestMixinAllocationA { @@ -5245,7 +5000,7 @@ class TestMixinAllocationB : public TestMixinAllocationA { // Construct object during a mixin construction. : a_(MakeGarbageCollected<TestMixinAllocationA>()) {} - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { visitor->Trace(a_); TestMixinAllocationA::Trace(visitor); } @@ -5260,7 +5015,7 @@ class TestMixinAllocationC final : public TestMixinAllocationB { public: TestMixinAllocationC() { DCHECK(!ThreadState::Current()->IsGCForbidden()); } - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { TestMixinAllocationB::Trace(visitor); } }; @@ -5368,7 +5123,7 @@ class ThreadedClearOnShutdownTester::HeapObject final GetHeapObjectSet().insert(MakeGarbageCollected<HeapObject>(false)); } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: bool test_destructor_; @@ -5415,7 +5170,7 @@ class WithWeakConstObject final : public GarbageCollected<WithWeakConstObject> { public: WithWeakConstObject(const IntWrapper* int_wrapper) : wrapper_(int_wrapper) {} - void Trace(blink::Visitor* visitor) { visitor->Trace(wrapper_); } + void Trace(Visitor* visitor) { visitor->Trace(wrapper_); } const IntWrapper* Value() const { return wrapper_; } @@ -5473,6 +5228,9 @@ TEST_F(HeapTest, IsGarbageCollected) { static_assert( WTF::IsGarbageCollectedType<HeapLinkedHashSet<Member<IntWrapper>>>::value, "HeapLinkedHashSet"); + static_assert(WTF::IsGarbageCollectedType< + HeapNewLinkedHashSet<Member<IntWrapper>>>::value, + "HeapNewLinkedHashSet"); static_assert( WTF::IsGarbageCollectedType<HeapListHashSet<Member<IntWrapper>>>::value, "HeapListHashSet"); @@ -5549,6 +5307,8 @@ TEST_F(HeapTest, GarbageCollectedMixinIsAliveDuringConstruction) { using P = HeapVector<Member<HeapLinkedHashSet<Member<IntWrapper>>>>; MakeGarbageCollected<P>(); + using Q = HeapVector<Member<HeapNewLinkedHashSet<Member<IntWrapper>>>>; + MakeGarbageCollected<Q>(); } TEST_F(HeapTest, PersistentAssignsDeletedValue) { @@ -5602,7 +5362,8 @@ TEST_F(HeapTest, AccessDeletedBackingStore) { } BaseArena* map_arena = PageFromObject(map)->Arena(); // Sweep normal arena, but don't call finalizers. - map_arena->ConcurrentSweepWithDeadline(base::TimeTicks::Max()); + while (!map_arena->ConcurrentSweepOnePage()) { + } // Now complete sweeping with PerformIdleLazySweep and call finalizers. while (thread_state->IsSweepingInProgress()) { thread_state->PerformIdleLazySweep(base::TimeTicks::Max()); @@ -5629,4 +5390,25 @@ TEST_F(HeapTest, CallMostDerivedFinalizer) { EXPECT_EQ(1, GCDerived::destructor_called); } +#if defined(ADDRESS_SANITIZER) +TEST(HeapDeathTest, DieOnPoisonedObjectHeaderAccess) { + auto* ptr = MakeGarbageCollected<IntWrapper>(1); + HeapObjectHeader* header = HeapObjectHeader::FromPayload(ptr); + auto* low = reinterpret_cast<uint16_t*>(header); + auto access = [low] { + volatile uint16_t half = WTF::AsAtomicPtr(low)->load(); + WTF::AsAtomicPtr(low)->store(half); + }; + EXPECT_DEATH(access(), ""); +} + +TEST_F(HeapTest, SuccessfulUnsanitizedAccessToObjectHeader) { + auto* ptr = MakeGarbageCollected<IntWrapper>(1); + HeapObjectHeader* header = HeapObjectHeader::FromPayload(ptr); + auto* low = reinterpret_cast<uint16_t*>(header); + volatile uint16_t half = internal::AsUnsanitizedAtomic(low)->load(); + internal::AsUnsanitizedAtomic(low)->store(half); +} +#endif // ADDRESS_SANITIZER + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.cc b/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.cc index 3292be44245..2efb5e40779 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.cc +++ b/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.cc @@ -11,11 +11,14 @@ namespace blink { +std::atomic_int IntegerObject::destructor_calls{0}; + // static void TestSupportingGC::PreciselyCollectGarbage( BlinkGC::SweepingType sweeping_type) { ThreadState::Current()->CollectGarbage( - BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, sweeping_type, + BlinkGC::CollectionType::kMajor, BlinkGC::kNoHeapPointersOnStack, + BlinkGC::kAtomicMarking, sweeping_type, BlinkGC::GCReason::kForcedGCForTesting); } @@ -23,10 +26,16 @@ void TestSupportingGC::PreciselyCollectGarbage( void TestSupportingGC::ConservativelyCollectGarbage( BlinkGC::SweepingType sweeping_type) { ThreadState::Current()->CollectGarbage( - BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking, sweeping_type, + BlinkGC::CollectionType::kMajor, BlinkGC::kHeapPointersOnStack, + BlinkGC::kAtomicMarking, sweeping_type, BlinkGC::GCReason::kForcedGCForTesting); } +TestSupportingGC::~TestSupportingGC() { + // Complete sweeping before |task_environment_| is destroyed. + CompleteSweepingIfNeeded(); +} + void TestSupportingGC::ClearOutOldGarbage() { PreciselyCollectGarbage(); ThreadHeap& heap = ThreadState::Current()->Heap(); diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.h b/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.h index a9d37305606..a2a53e2e96e 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.h +++ b/chromium/third_party/blink/renderer/platform/heap/heap_test_utilities.h @@ -29,6 +29,8 @@ class TestSupportingGC : public testing::Test { static void ConservativelyCollectGarbage( BlinkGC::SweepingType sweeping_type = BlinkGC::kEagerSweeping); + ~TestSupportingGC() override; + // Performs multiple rounds of garbage collections until no more memory can be // freed. This is useful to avoid other garbage collections having to deal // with stale memory. @@ -171,7 +173,15 @@ class IncrementalMarkingTestDriver { class IntegerObject : public GarbageCollected<IntegerObject> { public: - void Trace(blink::Visitor* visitor) {} + static std::atomic_int destructor_calls; + + explicit IntegerObject(int x) : x_(x) {} + + virtual ~IntegerObject() { + destructor_calls.fetch_add(1, std::memory_order_relaxed); + } + + virtual void Trace(Visitor* visitor) {} int Value() const { return x_; } @@ -181,8 +191,6 @@ class IntegerObject : public GarbageCollected<IntegerObject> { unsigned GetHash() { return IntHash<int>::GetHash(x_); } - explicit IntegerObject(int x) : x_(x) {} - private: int x_; }; diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_thread_test.cc b/chromium/third_party/blink/renderer/platform/heap/heap_thread_test.cc index 80ef9b05470..c1fbe2d720b 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_thread_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/heap_thread_test.cc @@ -17,7 +17,13 @@ namespace blink { class HeapThreadTest : public TestSupportingGC {}; -class HeapThreadDeathTest : public TestSupportingGC {}; + +class HeapThreadDeathTest : public TestSupportingGC { + public: + HeapThreadDeathTest() { + testing::FLAGS_gtest_death_test_style = "threadsafe"; + } +}; namespace heap_thread_test { @@ -69,7 +75,7 @@ static void ParkWorkerThread() { class Object : public GarbageCollected<Object> { public: Object() {} - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} }; class AlternatingThreadTester { @@ -136,14 +142,10 @@ class MemberSameThreadCheckTester : public AlternatingThreadTester { }; #if DCHECK_IS_ON() -// TODO(keishi) This test is flaky on mac-rel bot. -// crbug.com/709069 -#if !defined(OS_MACOSX) TEST_F(HeapThreadDeathTest, MemberSameThreadCheck) { EXPECT_DEATH(MemberSameThreadCheckTester().Test(), ""); } #endif -#endif class PersistentSameThreadCheckTester : public AlternatingThreadTester { private: @@ -159,20 +161,16 @@ class PersistentSameThreadCheckTester : public AlternatingThreadTester { }; #if DCHECK_IS_ON() -// TODO(keishi) This test is flaky on mac-rel bot. -// crbug.com/709069 -#if !defined(OS_MACOSX) TEST_F(HeapThreadDeathTest, PersistentSameThreadCheck) { EXPECT_DEATH(PersistentSameThreadCheckTester().Test(), ""); } #endif -#endif class MarkingSameThreadCheckTester : public AlternatingThreadTester { private: class MainThreadObject final : public GarbageCollected<MainThreadObject> { public: - void Trace(blink::Visitor* visitor) { visitor->Trace(set_); } + void Trace(Visitor* visitor) { visitor->Trace(set_); } void AddToSet(Object* object) { set_.insert(42, object); } private: @@ -199,8 +197,6 @@ class MarkingSameThreadCheckTester : public AlternatingThreadTester { }; #if DCHECK_IS_ON() -// TODO(keishi) This test is flaky on mac-rel bot. https://crbug.com/709069, and -// times out on other bots. https://crbug.com/993148. TEST_F(HeapThreadDeathTest, DISABLED_MarkingSameThreadCheck) { // This will crash during marking, at the DCHECK in Visitor::markHeader() or // earlier. @@ -215,7 +211,7 @@ class DestructorLockingObject virtual ~DestructorLockingObject() { ++destructor_calls_; } static int destructor_calls_; - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} }; int DestructorLockingObject::destructor_calls_ = 0; @@ -254,8 +250,9 @@ class CrossThreadWeakPersistentTester : public AlternatingThreadTester { // Step 4: Run a GC. ThreadState::Current()->CollectGarbage( - BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, - BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGCForTesting); + BlinkGC::CollectionType::kMajor, BlinkGC::kNoHeapPointersOnStack, + BlinkGC::kAtomicMarking, BlinkGC::kEagerSweeping, + BlinkGC::GCReason::kForcedGCForTesting); SwitchToMainThread(); } diff --git a/chromium/third_party/blink/renderer/platform/heap/heap_traits_test.cc b/chromium/third_party/blink/renderer/platform/heap/heap_traits_test.cc index 539c26c3757..2e50a064992 100644 --- a/chromium/third_party/blink/renderer/platform/heap/heap_traits_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/heap_traits_test.cc @@ -22,12 +22,12 @@ struct Empty {}; // Similar to an IDL union or dictionary, which have Trace() methods but are // not garbage-collected types themselves. struct StructWithTraceMethod { - void Trace(blink::Visitor*) {} + void Trace(Visitor*) {} }; struct GarbageCollectedStruct : public GarbageCollected<GarbageCollectedStruct> { - void Trace(blink::Visitor*) {} + void Trace(Visitor*) {} }; // AddMemberIfNeeded<T> diff --git a/chromium/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/chromium/third_party/blink/renderer/platform/heap/incremental_marking_test.cc index c3725ad1524..5132cc58f40 100644 --- a/chromium/third_party/blink/renderer/platform/heap/incremental_marking_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/incremental_marking_test.cc @@ -39,12 +39,11 @@ class BackingVisitor : public Visitor { void ProcessBackingStore(HeapObjectHeader* header) { EXPECT_TRUE(header->IsMarked()); header->Unmark(); - GCInfoTable::Get() - .GCInfoFromIndex(header->GcInfoIndex()) - ->trace(this, header->Payload()); + + GCInfo::From(header->GcInfoIndex()).trace(this, header->Payload()); } - void Visit(void* obj, TraceDescriptor desc) final { + void Visit(const void* obj, TraceDescriptor desc) final { EXPECT_TRUE(obj); auto** pos = std::find(objects_->begin(), objects_->end(), obj); if (objects_->end() != pos) @@ -53,12 +52,12 @@ class BackingVisitor : public Visitor { HeapObjectHeader* const header = HeapObjectHeader::FromPayload(desc.base_object_payload); if (!header->IsMarked()) - header->Mark(); + EXPECT_TRUE(header->TryMark()); } bool VisitEphemeronKeyValuePair( - void* key, - void* value, + const void* key, + const void* value, EphemeronTracingCallback key_trace_callback, EphemeronTracingCallback value_trace_callback) final { const bool key_is_dead = key_trace_callback(this, key); @@ -70,22 +69,23 @@ class BackingVisitor : public Visitor { } // Unused overrides. - void VisitWeak(void* object, - void* object_weak_ref, + void VisitWeak(const void* object, + const void* object_weak_ref, TraceDescriptor desc, WeakCallback callback) final {} - void VisitBackingStoreStrongly(void* object, - void** object_slot, + void VisitBackingStoreStrongly(const void* object, + const void* const* object_slot, TraceDescriptor desc) final {} - void VisitBackingStoreWeakly(void*, - void**, + void VisitBackingStoreWeakly(const void*, + const void* const*, TraceDescriptor, TraceDescriptor, WeakCallback, - void*) final {} - void VisitBackingStoreOnly(void*, void**) final {} - void RegisterBackingStoreCallback(void* slot, MovingObjectCallback) final {} - void RegisterWeakCallback(WeakCallback, void*) final {} + const void*) final {} + void VisitBackingStoreOnly(const void*, const void* const*) final {} + void RegisterBackingStoreCallback(const void* slot, + MovingObjectCallback) final {} + void RegisterWeakCallback(WeakCallback, const void*) final {} void Visit(const TraceWrapperV8Reference<v8::Value>&) final {} private: @@ -103,7 +103,7 @@ class IncrementalMarkingScopeBase { thread_state_->IsSweepingInProgress()) { TestSupportingGC::PreciselyCollectGarbage(); } - heap_.SetupWorklists(); + heap_.SetupWorklists(false); } ~IncrementalMarkingScopeBase() { @@ -280,6 +280,20 @@ class Object : public LinkedObject { void Trace(Visitor* visitor) { LinkedObject::Trace(visitor); } }; +class RawPtrObjectWithManualWriteBarrier + : public GarbageCollected<RawPtrObjectWithManualWriteBarrier> { + public: + void Trace(Visitor* v) { v->Trace(object_); } + + void Set(Object* object) { + object_ = object; + MarkingVisitor::WriteBarrier(&object_); + } + + private: + Object* object_ = nullptr; +}; + // ============================================================================= // Basic infrastructure support. =============================================== // ============================================================================= @@ -294,20 +308,22 @@ TEST_F(IncrementalMarkingTest, EnableDisableBarrier) { } TEST_F(IncrementalMarkingTest, ManualWriteBarrierTriggersWhenMarkingIsOn) { - auto* object = MakeGarbageCollected<Object>(); + auto* object1 = MakeGarbageCollected<Object>(); + auto* object2 = MakeGarbageCollected<RawPtrObjectWithManualWriteBarrier>(); { - ExpectWriteBarrierFires scope(ThreadState::Current(), {object}); - EXPECT_FALSE(object->IsMarked()); - MarkingVisitor::WriteBarrier(object); - EXPECT_TRUE(object->IsMarked()); + ExpectWriteBarrierFires scope(ThreadState::Current(), {object1}); + EXPECT_FALSE(object1->IsMarked()); + object2->Set(object1); + EXPECT_TRUE(object1->IsMarked()); } } TEST_F(IncrementalMarkingTest, ManualWriteBarrierBailoutWhenMarkingIsOff) { - auto* object = MakeGarbageCollected<Object>(); - EXPECT_FALSE(object->IsMarked()); - MarkingVisitor::WriteBarrier(object); - EXPECT_FALSE(object->IsMarked()); + auto* object1 = MakeGarbageCollected<Object>(); + auto* object2 = MakeGarbageCollected<RawPtrObjectWithManualWriteBarrier>(); + EXPECT_FALSE(object1->IsMarked()); + object2->Set(object1); + EXPECT_FALSE(object1->IsMarked()); } // ============================================================================= @@ -328,7 +344,7 @@ TEST_F(IncrementalMarkingTest, MemberSetUnmarkedObject) { TEST_F(IncrementalMarkingTest, MemberSetMarkedObjectNoBarrier) { auto* parent = MakeGarbageCollected<Object>(); auto* child = MakeGarbageCollected<Object>(); - HeapObjectHeader::FromPayload(child)->Mark(); + EXPECT_TRUE(HeapObjectHeader::FromPayload(child)->TryMark()); { ExpectNoWriteBarrierFires scope(ThreadState::Current(), {child}); parent->set_next(child); @@ -349,10 +365,10 @@ TEST_F(IncrementalMarkingTest, MemberInitializingStoreNoBarrier) { } TEST_F(IncrementalMarkingTest, MemberReferenceAssignMember) { - auto* obj = MakeGarbageCollected<Object>(); - Member<Object> m1; - Member<Object>& m2 = m1; - Member<Object> m3(obj); + auto* obj = MakeGarbageCollected<LinkedObject>(); + auto* ref_obj = MakeGarbageCollected<LinkedObject>(); + Member<LinkedObject>& m2 = ref_obj->next_ref(); + Member<LinkedObject> m3(obj); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj}); m2 = m3; @@ -360,7 +376,8 @@ TEST_F(IncrementalMarkingTest, MemberReferenceAssignMember) { } TEST_F(IncrementalMarkingTest, MemberSetDeletedValueNoBarrier) { - Member<Object> m; + auto* obj = MakeGarbageCollected<LinkedObject>(); + Member<LinkedObject>& m = obj->next_ref(); { ExpectNoWriteBarrierFires scope(ThreadState::Current(), {}); m = WTF::kHashTableDeletedValue; @@ -368,26 +385,32 @@ TEST_F(IncrementalMarkingTest, MemberSetDeletedValueNoBarrier) { } TEST_F(IncrementalMarkingTest, MemberCopyDeletedValueNoBarrier) { - Member<Object> m1(WTF::kHashTableDeletedValue); + auto* obj1 = MakeGarbageCollected<LinkedObject>(); + Member<LinkedObject>& m1 = obj1->next_ref(); + m1 = WTF::kHashTableDeletedValue; { ExpectNoWriteBarrierFires scope(ThreadState::Current(), {}); - Member<Object> m2(m1); + auto* obj2 = MakeGarbageCollected<LinkedObject>(); + obj2->next_ref() = m1; } } TEST_F(IncrementalMarkingTest, MemberHashTraitConstructDeletedValueNoBarrier) { - Member<Object> m1; + auto* obj = MakeGarbageCollected<LinkedObject>(); + Member<LinkedObject>& m = obj->next_ref(); { ExpectNoWriteBarrierFires scope(ThreadState::Current(), {}); - HashTraits<Member<Object>>::ConstructDeletedValue(m1, false); + HashTraits<Member<LinkedObject>>::ConstructDeletedValue(m, false); } } TEST_F(IncrementalMarkingTest, MemberHashTraitIsDeletedValueNoBarrier) { - Member<Object> m1(MakeGarbageCollected<Object>()); + auto* obj = + MakeGarbageCollected<LinkedObject>(MakeGarbageCollected<LinkedObject>()); + Member<LinkedObject>& m = obj->next_ref(); { ExpectNoWriteBarrierFires scope(ThreadState::Current(), {}); - EXPECT_FALSE(HashTraits<Member<Object>>::IsDeletedValue(m1)); + EXPECT_FALSE(HashTraits<Member<LinkedObject>>::IsDeletedValue(m)); } } @@ -402,7 +425,7 @@ class Mixin : public GarbageCollectedMixin { Mixin() : next_(nullptr) {} virtual ~Mixin() {} - void Trace(blink::Visitor* visitor) override { visitor->Trace(next_); } + void Trace(Visitor* visitor) override { visitor->Trace(next_); } virtual void Bar() {} @@ -424,7 +447,7 @@ class Child : public GarbageCollected<Child>, Child() : ClassWithVirtual(), Mixin() {} ~Child() override {} - void Trace(blink::Visitor* visitor) override { Mixin::Trace(visitor); } + void Trace(Visitor* visitor) override { Mixin::Trace(visitor); } void Foo() override {} void Bar() override {} @@ -436,7 +459,7 @@ class ParentWithMixinPointer : public GarbageCollected<ParentWithMixinPointer> { void set_mixin(Mixin* mixin) { mixin_ = mixin; } - virtual void Trace(blink::Visitor* visitor) { visitor->Trace(mixin_); } + virtual void Trace(Visitor* visitor) { visitor->Trace(mixin_); } protected: Member<Mixin> mixin_; @@ -460,7 +483,7 @@ TEST_F(IncrementalMarkingTest, NoWriteBarrierOnMarkedMixinApplication) { ParentWithMixinPointer* parent = MakeGarbageCollected<ParentWithMixinPointer>(); auto* child = MakeGarbageCollected<Child>(); - HeapObjectHeader::FromPayload(child)->Mark(); + EXPECT_TRUE(HeapObjectHeader::FromPayload(child)->TryMark()); Mixin* mixin = static_cast<Mixin*>(child); EXPECT_NE(static_cast<void*>(child), static_cast<void*>(mixin)); { @@ -484,7 +507,7 @@ class NonGarbageCollectedContainer { NonGarbageCollectedContainer(Object* obj, int y) : obj_(obj), y_(y) {} virtual ~NonGarbageCollectedContainer() {} - virtual void Trace(blink::Visitor* visitor) { visitor->Trace(obj_); } + virtual void Trace(Visitor* visitor) { visitor->Trace(obj_); } private: Member<Object> obj_; @@ -499,7 +522,7 @@ class NonGarbageCollectedContainerRoot { : next_(obj1, y), obj_(obj2) {} virtual ~NonGarbageCollectedContainerRoot() {} - virtual void Trace(blink::Visitor* visitor) { + virtual void Trace(Visitor* visitor) { visitor->Trace(next_); visitor->Trace(obj_); } @@ -513,158 +536,168 @@ class NonGarbageCollectedContainerRoot { TEST_F(IncrementalMarkingTest, HeapVectorPushBackMember) { auto* obj = MakeGarbageCollected<Object>(); - HeapVector<Member<Object>> vec; + auto* vec = MakeGarbageCollected<HeapVector<Member<Object>>>(); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj}); - vec.push_back(obj); + vec->push_back(obj); } } TEST_F(IncrementalMarkingTest, HeapVectorPushBackNonGCedContainer) { auto* obj = MakeGarbageCollected<Object>(); - HeapVector<NonGarbageCollectedContainer> vec; + auto* vec = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>(); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj}); - vec.push_back(NonGarbageCollectedContainer(obj, 1)); + vec->push_back(NonGarbageCollectedContainer(obj, 1)); } } TEST_F(IncrementalMarkingTest, HeapVectorPushBackStdPair) { auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapVector<std::pair<Member<Object>, Member<Object>>> vec; + auto* vec = MakeGarbageCollected< + HeapVector<std::pair<Member<Object>, Member<Object>>>>(); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - vec.push_back(std::make_pair(Member<Object>(obj1), Member<Object>(obj2))); + vec->push_back(std::make_pair(Member<Object>(obj1), Member<Object>(obj2))); } } TEST_F(IncrementalMarkingTest, HeapVectorEmplaceBackMember) { auto* obj = MakeGarbageCollected<Object>(); - HeapVector<Member<Object>> vec; + auto* vec = MakeGarbageCollected<HeapVector<Member<Object>>>(); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj}); - vec.emplace_back(obj); + vec->emplace_back(obj); } } TEST_F(IncrementalMarkingTest, HeapVectorEmplaceBackNonGCedContainer) { auto* obj = MakeGarbageCollected<Object>(); - HeapVector<NonGarbageCollectedContainer> vec; + auto* vec = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>(); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj}); - vec.emplace_back(obj, 1); + vec->emplace_back(obj, 1); } } TEST_F(IncrementalMarkingTest, HeapVectorEmplaceBackStdPair) { auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapVector<std::pair<Member<Object>, Member<Object>>> vec; + auto* vec = MakeGarbageCollected< + HeapVector<std::pair<Member<Object>, Member<Object>>>>(); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - vec.emplace_back(obj1, obj2); + vec->emplace_back(obj1, obj2); } } TEST_F(IncrementalMarkingTest, HeapVectorCopyMember) { auto* object = MakeGarbageCollected<Object>(); - HeapVector<Member<Object>> vec1; - vec1.push_back(object); + auto* vec1 = MakeGarbageCollected<HeapVector<Member<Object>>>(); + vec1->push_back(object); { ExpectWriteBarrierFires scope(ThreadState::Current(), {object}); - HeapVector<Member<Object>> vec2(vec1); + MakeGarbageCollected<HeapVector<Member<Object>>>(*vec1); } } TEST_F(IncrementalMarkingTest, HeapVectorCopyNonGCedContainer) { auto* obj = MakeGarbageCollected<Object>(); - HeapVector<NonGarbageCollectedContainer> vec1; - vec1.emplace_back(obj, 1); + auto* vec1 = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>(); + vec1->emplace_back(obj, 1); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj}); - HeapVector<NonGarbageCollectedContainer> vec2(vec1); + MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>(*vec1); } } TEST_F(IncrementalMarkingTest, HeapVectorCopyStdPair) { + using ValueType = std::pair<Member<Object>, Member<Object>>; auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapVector<std::pair<Member<Object>, Member<Object>>> vec1; - vec1.emplace_back(obj1, obj2); + auto* vec1 = MakeGarbageCollected<HeapVector<ValueType>>(); + vec1->emplace_back(obj1, obj2); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - HeapVector<std::pair<Member<Object>, Member<Object>>> vec2(vec1); + MakeGarbageCollected<HeapVector<ValueType>>(*vec1); } } TEST_F(IncrementalMarkingTest, HeapVectorMoveMember) { auto* obj = MakeGarbageCollected<Object>(); - HeapVector<Member<Object>> vec1; - vec1.push_back(obj); + auto* vec1 = MakeGarbageCollected<HeapVector<Member<Object>>>(); + vec1->push_back(obj); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj}); - HeapVector<Member<Object>> vec2(std::move(vec1)); + MakeGarbageCollected<HeapVector<Member<Object>>>(std::move(*vec1)); } } TEST_F(IncrementalMarkingTest, HeapVectorMoveNonGCedContainer) { auto* obj = MakeGarbageCollected<Object>(); - HeapVector<NonGarbageCollectedContainer> vec1; - vec1.emplace_back(obj, 1); + auto* vec1 = MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>(); + vec1->emplace_back(obj, 1); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj}); - HeapVector<NonGarbageCollectedContainer> vec2(std::move(vec1)); + MakeGarbageCollected<HeapVector<NonGarbageCollectedContainer>>( + std::move(*vec1)); } } TEST_F(IncrementalMarkingTest, HeapVectorMoveStdPair) { + using ValueType = std::pair<Member<Object>, Member<Object>>; + using VectorType = HeapVector<ValueType>; auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapVector<std::pair<Member<Object>, Member<Object>>> vec1; - vec1.emplace_back(obj1, obj2); + auto* vec1 = MakeGarbageCollected<VectorType>(); + vec1->emplace_back(obj1, obj2); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - HeapVector<std::pair<Member<Object>, Member<Object>>> vec2(std::move(vec1)); + MakeGarbageCollected<VectorType>(std::move(*vec1)); } } TEST_F(IncrementalMarkingTest, HeapVectorSwapMember) { + using VectorType = HeapVector<Member<Object>>; auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapVector<Member<Object>> vec1; - vec1.push_back(obj1); - HeapVector<Member<Object>> vec2; - vec2.push_back(obj2); + auto* vec1 = MakeGarbageCollected<VectorType>(); + vec1->push_back(obj1); + auto* vec2 = MakeGarbageCollected<VectorType>(); + vec2->push_back(obj2); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - std::swap(vec1, vec2); + std::swap(*vec1, *vec2); } } TEST_F(IncrementalMarkingTest, HeapVectorSwapNonGCedContainer) { + using VectorType = HeapVector<NonGarbageCollectedContainer>; auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapVector<NonGarbageCollectedContainer> vec1; - vec1.emplace_back(obj1, 1); - HeapVector<NonGarbageCollectedContainer> vec2; - vec2.emplace_back(obj2, 2); + auto* vec1 = MakeGarbageCollected<VectorType>(); + vec1->emplace_back(obj1, 1); + auto* vec2 = MakeGarbageCollected<VectorType>(); + vec2->emplace_back(obj2, 2); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - std::swap(vec1, vec2); + std::swap(*vec1, *vec2); } } TEST_F(IncrementalMarkingTest, HeapVectorSwapStdPair) { + using ValueType = std::pair<Member<Object>, Member<Object>>; + using VectorType = HeapVector<ValueType>; auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapVector<std::pair<Member<Object>, Member<Object>>> vec1; - vec1.emplace_back(obj1, nullptr); - HeapVector<std::pair<Member<Object>, Member<Object>>> vec2; - vec2.emplace_back(nullptr, obj2); + auto* vec1 = MakeGarbageCollected<VectorType>(); + vec1->emplace_back(obj1, nullptr); + auto* vec2 = MakeGarbageCollected<VectorType>(); + vec2->emplace_back(nullptr, obj2); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - std::swap(vec1, vec2); + std::swap(*vec1, *vec2); } } @@ -826,23 +859,23 @@ void CopyNoBarrier() { template <typename Container> void Move() { auto* obj = MakeGarbageCollected<Object>(); - Container container1; - Container container2; - container1.insert(obj); + auto* container1 = MakeGarbageCollected<Container>(); + auto* container2 = MakeGarbageCollected<Container>(); + container1->insert(obj); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj}); - container2 = std::move(container1); + *container2 = std::move(*container1); } } template <typename Container> void MoveNoBarrier() { auto* obj = MakeGarbageCollected<Object>(); - Container container1; - container1.insert(obj); + auto* container1 = MakeGarbageCollected<Container>(); + container1->insert(obj); { ExpectNoWriteBarrierFires scope(ThreadState::Current(), {obj}); - Container container2(std::move(container1)); + auto* container2 = MakeGarbageCollected<Container>(std::move(*container1)); } } @@ -850,13 +883,13 @@ template <typename Container> void Swap() { auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - Container container1; - container1.insert(obj1); - Container container2; - container2.insert(obj2); + auto* container1 = MakeGarbageCollected<Container>(); + container1->insert(obj1); + auto* container2 = MakeGarbageCollected<Container>(); + container2->insert(obj2); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - std::swap(container1, container2); + std::swap(*container1, *container2); } } @@ -864,13 +897,13 @@ template <typename Container> void SwapNoBarrier() { auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - Container container1; - container1.insert(obj1); - Container container2; - container2.insert(obj2); + auto* container1 = MakeGarbageCollected<Container>(); + container1->insert(obj1); + auto* container2 = MakeGarbageCollected<Container>(); + container2->insert(obj2); { ExpectNoWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - std::swap(container1, container2); + std::swap(*container1, *container2); } } @@ -928,6 +961,9 @@ TEST_F(IncrementalMarkingTest, HeapLinkedHashSetSwap) { Swap<HeapLinkedHashSet<WeakMember<Object>>>(); } +// TODO(keinakashima): add tests for NewLinkedHashSet after supporting +// WeakMember + // ============================================================================= // HeapHashCountedSet support. ================================================= // ============================================================================= @@ -945,26 +981,30 @@ TEST_F(IncrementalMarkingTest, HeapHashCountedSetSwap) { { auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapHashCountedSet<Member<Object>> container1; - container1.insert(obj1); - HeapHashCountedSet<Member<Object>> container2; - container2.insert(obj2); + auto* container1 = + MakeGarbageCollected<HeapHashCountedSet<Member<Object>>>(); + container1->insert(obj1); + auto* container2 = + MakeGarbageCollected<HeapHashCountedSet<Member<Object>>>(); + container2->insert(obj2); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - container1.swap(container2); + container1->swap(*container2); } } { auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapHashCountedSet<WeakMember<Object>> container1; - container1.insert(obj1); - HeapHashCountedSet<WeakMember<Object>> container2; - container2.insert(obj2); + auto* container1 = + MakeGarbageCollected<HeapHashCountedSet<WeakMember<Object>>>(); + container1->insert(obj1); + auto* container2 = + MakeGarbageCollected<HeapHashCountedSet<WeakMember<Object>>>(); + container2->insert(obj2); { // Weak references are strongified for the current cycle. ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - container1.swap(container2); + container1->swap(*container2); } } } @@ -1132,47 +1172,55 @@ TEST_F(IncrementalMarkingTest, HeapHashMapCopyWeakMemberMember) { TEST_F(IncrementalMarkingTest, HeapHashMapMoveMember) { auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapHashMap<Member<Object>, Member<Object>> map1; - map1.insert(obj1, obj2); + auto* map1 = + MakeGarbageCollected<HeapHashMap<Member<Object>, Member<Object>>>(); + map1->insert(obj1, obj2); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - HeapHashMap<Member<Object>, Member<Object>> map2(std::move(map1)); + MakeGarbageCollected<HeapHashMap<Member<Object>, Member<Object>>>( + std::move(*map1)); } } TEST_F(IncrementalMarkingTest, HeapHashMapMoveWeakMember) { auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1; - map1.insert(obj1, obj2); + auto* map1 = MakeGarbageCollected< + HeapHashMap<WeakMember<Object>, WeakMember<Object>>>(); + map1->insert(obj1, obj2); { // Weak references are strongified for the current cycle. ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2(std::move(map1)); + MakeGarbageCollected<HeapHashMap<WeakMember<Object>, WeakMember<Object>>>( + std::move(*map1)); } } TEST_F(IncrementalMarkingTest, HeapHashMapMoveMemberWeakMember) { auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapHashMap<Member<Object>, WeakMember<Object>> map1; - map1.insert(obj1, obj2); + auto* map1 = + MakeGarbageCollected<HeapHashMap<Member<Object>, WeakMember<Object>>>(); + map1->insert(obj1, obj2); { // Weak references are strongified for the current cycle. ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - HeapHashMap<Member<Object>, WeakMember<Object>> map2(std::move(map1)); + MakeGarbageCollected<HeapHashMap<Member<Object>, WeakMember<Object>>>( + std::move(*map1)); } } TEST_F(IncrementalMarkingTest, HeapHashMapMoveWeakMemberMember) { auto* obj1 = MakeGarbageCollected<Object>(); auto* obj2 = MakeGarbageCollected<Object>(); - HeapHashMap<WeakMember<Object>, Member<Object>> map1; - map1.insert(obj1, obj2); + auto* map1 = + MakeGarbageCollected<HeapHashMap<WeakMember<Object>, Member<Object>>>(); + map1->insert(obj1, obj2); { // Weak references are strongified for the current cycle. ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2}); - HeapHashMap<WeakMember<Object>, Member<Object>> map2(std::move(map1)); + MakeGarbageCollected<HeapHashMap<WeakMember<Object>, Member<Object>>>( + std::move(*map1)); } } @@ -1181,14 +1229,16 @@ TEST_F(IncrementalMarkingTest, HeapHashMapSwapMemberMember) { auto* obj2 = MakeGarbageCollected<Object>(); auto* obj3 = MakeGarbageCollected<Object>(); auto* obj4 = MakeGarbageCollected<Object>(); - HeapHashMap<Member<Object>, Member<Object>> map1; - map1.insert(obj1, obj2); - HeapHashMap<Member<Object>, Member<Object>> map2; - map2.insert(obj3, obj4); + auto* map1 = + MakeGarbageCollected<HeapHashMap<Member<Object>, Member<Object>>>(); + map1->insert(obj1, obj2); + auto* map2 = + MakeGarbageCollected<HeapHashMap<Member<Object>, Member<Object>>>(); + map2->insert(obj3, obj4); { ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2, obj3, obj4}); - std::swap(map1, map2); + std::swap(*map1, *map2); } } @@ -1197,15 +1247,17 @@ TEST_F(IncrementalMarkingTest, HeapHashMapSwapWeakMemberWeakMember) { auto* obj2 = MakeGarbageCollected<Object>(); auto* obj3 = MakeGarbageCollected<Object>(); auto* obj4 = MakeGarbageCollected<Object>(); - HeapHashMap<WeakMember<Object>, WeakMember<Object>> map1; - map1.insert(obj1, obj2); - HeapHashMap<WeakMember<Object>, WeakMember<Object>> map2; - map2.insert(obj3, obj4); + auto* map1 = MakeGarbageCollected< + HeapHashMap<WeakMember<Object>, WeakMember<Object>>>(); + map1->insert(obj1, obj2); + auto* map2 = MakeGarbageCollected< + HeapHashMap<WeakMember<Object>, WeakMember<Object>>>(); + map2->insert(obj3, obj4); { // Weak references are strongified for the current cycle. ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2, obj3, obj4}); - std::swap(map1, map2); + std::swap(*map1, *map2); } } @@ -1214,15 +1266,17 @@ TEST_F(IncrementalMarkingTest, HeapHashMapSwapMemberWeakMember) { auto* obj2 = MakeGarbageCollected<Object>(); auto* obj3 = MakeGarbageCollected<Object>(); auto* obj4 = MakeGarbageCollected<Object>(); - HeapHashMap<Member<Object>, WeakMember<Object>> map1; - map1.insert(obj1, obj2); - HeapHashMap<Member<Object>, WeakMember<Object>> map2; - map2.insert(obj3, obj4); + auto* map1 = + MakeGarbageCollected<HeapHashMap<Member<Object>, WeakMember<Object>>>(); + map1->insert(obj1, obj2); + auto* map2 = + MakeGarbageCollected<HeapHashMap<Member<Object>, WeakMember<Object>>>(); + map2->insert(obj3, obj4); { // Weak references are strongified for the current cycle. ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2, obj3, obj4}); - std::swap(map1, map2); + std::swap(*map1, *map2); } } @@ -1231,15 +1285,17 @@ TEST_F(IncrementalMarkingTest, HeapHashMapSwapWeakMemberMember) { auto* obj2 = MakeGarbageCollected<Object>(); auto* obj3 = MakeGarbageCollected<Object>(); auto* obj4 = MakeGarbageCollected<Object>(); - HeapHashMap<WeakMember<Object>, Member<Object>> map1; - map1.insert(obj1, obj2); - HeapHashMap<WeakMember<Object>, Member<Object>> map2; - map2.insert(obj3, obj4); + auto* map1 = + MakeGarbageCollected<HeapHashMap<WeakMember<Object>, Member<Object>>>(); + map1->insert(obj1, obj2); + auto* map2 = + MakeGarbageCollected<HeapHashMap<WeakMember<Object>, Member<Object>>>(); + map2->insert(obj3, obj4); { // Weak references are strongified for the current cycle. ExpectWriteBarrierFires scope(ThreadState::Current(), {obj1, obj2, obj3, obj4}); - std::swap(map1, map2); + std::swap(*map1, *map2); } } @@ -1415,57 +1471,21 @@ TEST_F(IncrementalMarkingTest, DropBackingStore) { driver.FinishGC(); } -TEST_F(IncrementalMarkingTest, WeakCallbackDoesNotReviveDeletedValue) { - // Regression test: https://crbug.com/870196 - - // std::pair avoids treating the hashset backing as weak backing. - using WeakStore = HeapHashCountedSet<std::pair<WeakMember<Object>, size_t>>; - - Persistent<WeakStore> persistent(MakeGarbageCollected<WeakStore>()); - // Create at least two entries to avoid completely emptying out the data - // structure. The values for .second are chosen to be non-null as they - // would otherwise count as empty and be skipped during iteration after the - // first part died. - persistent->insert({MakeGarbageCollected<Object>(), 1}); - persistent->insert({MakeGarbageCollected<Object>(), 2}); - IncrementalMarkingTestDriver driver(ThreadState::Current()); - driver.Start(); - // The backing is not treated as weak backing and thus eagerly processed, - // effectively registering the slots of WeakMembers. - driver.FinishSteps(); - // The following deletes the first found entry. The second entry is left - // untouched. - for (auto& entries : *persistent) { - persistent->erase(entries.key); - break; - } - driver.FinishGC(); - - size_t count = 0; - for (const auto& entry : *persistent) { - count++; - // Use the entry to keep compilers happy. - if (entry.key.second > 0) { - } - } - CHECK_EQ(1u, count); -} - TEST_F(IncrementalMarkingTest, NoBackingFreeDuringIncrementalMarking) { // Regression test: https://crbug.com/870306 // Only reproduces in ASAN configurations. - using WeakStore = HeapHashCountedSet<std::pair<WeakMember<Object>, size_t>>; + using WeakStore = HeapHashCountedSet<WeakMember<Object>>; Persistent<WeakStore> persistent(MakeGarbageCollected<WeakStore>()); - // Prefill the collection to grow backing store. A new backing store allocaton - // would trigger the write barrier, mitigating the bug where a backing store - // is promptly freed. + // Prefill the collection to grow backing store. A new backing store + // allocationwould trigger the write barrier, mitigating the bug where + // a backing store is promptly freed. for (size_t i = 0; i < 8; i++) { - persistent->insert({MakeGarbageCollected<Object>(), i}); + persistent->insert(MakeGarbageCollected<Object>()); } IncrementalMarkingTestDriver driver(ThreadState::Current()); driver.Start(); - persistent->insert({MakeGarbageCollected<Object>(), 8}); + persistent->insert(MakeGarbageCollected<Object>()); // Is not allowed to free the backing store as the previous insert may have // registered a slot. persistent->clear(); @@ -1536,8 +1556,9 @@ TEST_F(IncrementalMarkingTest, ConservativeGCWhileCompactionScheduled) { driver.Start(); driver.FinishSteps(); ThreadState::Current()->CollectGarbage( - BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking, - BlinkGC::kConcurrentAndLazySweeping, BlinkGC::GCReason::kConservativeGC); + BlinkGC::CollectionType::kMajor, BlinkGC::kHeapPointersOnStack, + BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping, + BlinkGC::GCReason::kConservativeGC); // Heap compaction should be canceled if incremental marking finishes with a // conservative GC. @@ -1792,11 +1813,12 @@ class Destructed final : public GarbageCollected<Destructed> { size_t Destructed::n_destructed = 0; -class Wrapper final : public GarbageCollected<Wrapper> { +class LinkedHashSetWrapper final + : public GarbageCollected<LinkedHashSetWrapper> { public: using HashType = HeapLinkedHashSet<Member<Destructed>>; - Wrapper() { + LinkedHashSetWrapper() { for (size_t i = 0; i < 10; ++i) { hash_set_.insert(MakeGarbageCollected<Destructed>()); } @@ -1812,7 +1834,7 @@ class Wrapper final : public GarbageCollected<Wrapper> { HashType hash_set_; }; -TEST_F(IncrementalMarkingTest, MovingCallback) { +TEST_F(IncrementalMarkingTest, LinkedHashSetMovingCallback) { ClearOutOldGarbage(); Destructed::n_destructed = 0; @@ -1820,14 +1842,15 @@ TEST_F(IncrementalMarkingTest, MovingCallback) { HeapHashSet<Member<Destructed>> to_be_destroyed; to_be_destroyed.ReserveCapacityForSize(100); } - Persistent<Wrapper> wrapper = MakeGarbageCollected<Wrapper>(); + Persistent<LinkedHashSetWrapper> wrapper = + MakeGarbageCollected<LinkedHashSetWrapper>(); IncrementalMarkingTestDriver driver(ThreadState::Current()); ThreadState::Current()->EnableCompactionForNextGCForTesting(); driver.Start(); driver.FinishSteps(); - // Destroy the link between original HeapLinkedHashSet object and its backign + // Destroy the link between original HeapLinkedHashSet object and its backing // store. wrapper->Swap(); DCHECK(wrapper->hash_set_.IsEmpty()); @@ -1837,5 +1860,52 @@ TEST_F(IncrementalMarkingTest, MovingCallback) { EXPECT_EQ(10u, Destructed::n_destructed); } +class NewLinkedHashSetWrapper final + : public GarbageCollected<NewLinkedHashSetWrapper> { + public: + using HashType = HeapNewLinkedHashSet<Member<Destructed>>; + + NewLinkedHashSetWrapper() { + for (size_t i = 0; i < 10; ++i) { + hash_set_.insert(MakeGarbageCollected<Destructed>()); + } + } + + void Trace(Visitor* v) { v->Trace(hash_set_); } + + void Swap() { + HashType hash_set; + hash_set_.Swap(hash_set); + } + + HashType hash_set_; +}; + +TEST_F(IncrementalMarkingTest, NewLinkedHashSetMovingCallback) { + ClearOutOldGarbage(); + + Destructed::n_destructed = 0; + { + HeapHashSet<Member<Destructed>> to_be_destroyed; + to_be_destroyed.ReserveCapacityForSize(100); + } + Persistent<NewLinkedHashSetWrapper> wrapper = + MakeGarbageCollected<NewLinkedHashSetWrapper>(); + + IncrementalMarkingTestDriver driver(ThreadState::Current()); + ThreadState::Current()->EnableCompactionForNextGCForTesting(); + driver.Start(); + driver.FinishSteps(); + + // Destroy the link between original NewHeapLinkedHashSet object and its + // backing store. + wrapper->Swap(); + DCHECK(wrapper->hash_set_.IsEmpty()); + + PreciselyCollectGarbage(); + + EXPECT_EQ(10u, Destructed::n_destructed); +} + } // namespace incremental_marking_test } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/marking_verifier.cc b/chromium/third_party/blink/renderer/platform/heap/marking_verifier.cc index 5e0e7fed6db..eb900719237 100644 --- a/chromium/third_party/blink/renderer/platform/heap/marking_verifier.cc +++ b/chromium/third_party/blink/renderer/platform/heap/marking_verifier.cc @@ -14,22 +14,21 @@ void MarkingVerifier::VerifyObject(HeapObjectHeader* header) { if (header->IsFree() || !header->IsMarked()) return; - const GCInfo* info = - GCInfoTable::Get().GCInfoFromIndex(header->GcInfoIndex()); + const GCInfo& info = GCInfo::From(header->GcInfoIndex()); const bool can_verify = - !info->has_v_table || blink::VTableInitialized(header->Payload()); + !info.has_v_table || blink::VTableInitialized(header->Payload()); if (can_verify) { parent_ = header; - info->trace(this, header->Payload()); + info.trace(this, header->Payload()); } } -void MarkingVerifier::Visit(void* object, TraceDescriptor desc) { +void MarkingVerifier::Visit(const void* object, TraceDescriptor desc) { VerifyChild(object, desc.base_object_payload); } -void MarkingVerifier::VisitWeak(void* object, - void* object_weak_ref, +void MarkingVerifier::VisitWeak(const void* object, + const void* object_weak_ref, TraceDescriptor desc, WeakCallback callback) { // Weak objects should have been cleared at this point. As a consequence, all @@ -38,8 +37,8 @@ void MarkingVerifier::VisitWeak(void* object, VerifyChild(object, desc.base_object_payload); } -void MarkingVerifier::VisitBackingStoreStrongly(void* object, - void**, +void MarkingVerifier::VisitBackingStoreStrongly(const void* object, + const void* const*, TraceDescriptor desc) { if (!object) return; @@ -50,12 +49,12 @@ void MarkingVerifier::VisitBackingStoreStrongly(void* object, VerifyChild(object, desc.base_object_payload); } -void MarkingVerifier::VisitBackingStoreWeakly(void* object, - void**, +void MarkingVerifier::VisitBackingStoreWeakly(const void* object, + const void* const*, TraceDescriptor strong_desc, TraceDescriptor weak_desc, WeakCallback, - void*) { + const void*) { if (!object) return; @@ -67,7 +66,8 @@ void MarkingVerifier::VisitBackingStoreWeakly(void* object, VerifyChild(object, weak_desc.base_object_payload); } -void MarkingVerifier::VerifyChild(void* object, void* base_object_payload) { +void MarkingVerifier::VerifyChild(const void* object, + const void* base_object_payload) { CHECK(object); // Verification may check objects that are currently under construction and // would require vtable access to figure out their headers. A nullptr in @@ -82,15 +82,10 @@ void MarkingVerifier::VerifyChild(void* object, void* base_object_payload) { // ones. CHECK(child_header); if (!child_header->IsMarked()) { - // Pre-finalizers may allocate. In that case the newly allocated objects - // reside on a page that is not scheduled for sweeping. - if (PageFromObject(child_header->Payload())->HasBeenSwept()) - return; - + CHECK(!PageFromObject(child_header->Payload())->HasBeenSwept()); LOG(FATAL) << "MarkingVerifier: Encountered unmarked object. " << std::endl << std::endl - << "Hint (use v8_enable_raw_heap_snapshots for better naming): " - << std::endl + << "Hint: " << std::endl << parent_->Name() << std::endl << "\\-> " << child_header->Name() << std::endl; } diff --git a/chromium/third_party/blink/renderer/platform/heap/marking_verifier.h b/chromium/third_party/blink/renderer/platform/heap/marking_verifier.h index 14e53676d96..495e8dd4c1b 100644 --- a/chromium/third_party/blink/renderer/platform/heap/marking_verifier.h +++ b/chromium/third_party/blink/renderer/platform/heap/marking_verifier.h @@ -17,29 +17,31 @@ class MarkingVerifier final : public Visitor { void VerifyObject(HeapObjectHeader* header); - void Visit(void* object, TraceDescriptor desc) final; - void VisitWeak(void* object, - void* object_weak_ref, + void Visit(const void* object, TraceDescriptor desc) final; + void VisitWeak(const void* object, + const void* object_weak_ref, TraceDescriptor desc, WeakCallback callback) final; - void VisitBackingStoreStrongly(void*, void**, TraceDescriptor) final; + void VisitBackingStoreStrongly(const void*, + const void* const*, + TraceDescriptor) final; - void VisitBackingStoreWeakly(void*, - void**, + void VisitBackingStoreWeakly(const void*, + const void* const*, TraceDescriptor, TraceDescriptor, WeakCallback, - void*) final; + const void*) final; // Unused overrides. - void VisitBackingStoreOnly(void*, void**) final {} - void RegisterBackingStoreCallback(void*, MovingObjectCallback) final {} - void RegisterWeakCallback(WeakCallback, void*) final {} + void VisitBackingStoreOnly(const void*, const void* const*) final {} + void RegisterBackingStoreCallback(const void*, MovingObjectCallback) final {} + void RegisterWeakCallback(WeakCallback, const void*) final {} void Visit(const TraceWrapperV8Reference<v8::Value>&) final {} private: - void VerifyChild(void* object, void* base_object_payload); + void VerifyChild(const void* object, const void* base_object_payload); HeapObjectHeader* parent_ = nullptr; }; diff --git a/chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc b/chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc index 8ed5a880494..d0c5db8b742 100644 --- a/chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc +++ b/chromium/third_party/blink/renderer/platform/heap/marking_visitor.cc @@ -10,19 +10,12 @@ namespace blink { -namespace { - -ALWAYS_INLINE bool IsHashTableDeleteValue(const void* value) { - return value == reinterpret_cast<void*>(-1); -} - -} // namespace - MarkingVisitorCommon::MarkingVisitorCommon(ThreadState* state, MarkingMode marking_mode, int task_id) : Visitor(state), marking_worklist_(Heap().GetMarkingWorklist(), task_id), + write_barrier_worklist_(Heap().GetWriteBarrierWorklist(), task_id), not_fully_constructed_worklist_(Heap().GetNotFullyConstructedWorklist(), task_id), weak_callback_worklist_(Heap().GetWeakCallbackWorklist(), task_id), @@ -35,38 +28,38 @@ MarkingVisitorCommon::MarkingVisitorCommon(ThreadState* state, task_id_(task_id) {} void MarkingVisitorCommon::FlushCompactionWorklists() { + if (marking_mode_ != kGlobalMarkingWithCompaction) + return; movable_reference_worklist_.FlushToGlobal(); backing_store_callback_worklist_.FlushToGlobal(); } void MarkingVisitorCommon::RegisterWeakCallback(WeakCallback callback, - void* object) { + const void* object) { weak_callback_worklist_.Push({callback, object}); } -void MarkingVisitorCommon::RegisterBackingStoreReference(void** slot) { +void MarkingVisitorCommon::RegisterBackingStoreReference( + const void* const* slot) { if (marking_mode_ != kGlobalMarkingWithCompaction) return; - MovableReference* movable_reference = - reinterpret_cast<MovableReference*>(slot); - if (Heap().ShouldRegisterMovingAddress( - reinterpret_cast<Address>(movable_reference))) { - movable_reference_worklist_.Push(movable_reference); + if (Heap().ShouldRegisterMovingAddress()) { + movable_reference_worklist_.Push(slot); } } void MarkingVisitorCommon::RegisterBackingStoreCallback( - void* backing, + const void* backing, MovingObjectCallback callback) { if (marking_mode_ != kGlobalMarkingWithCompaction) return; - if (Heap().ShouldRegisterMovingAddress(reinterpret_cast<Address>(backing))) { + if (Heap().ShouldRegisterMovingAddress()) { backing_store_callback_worklist_.Push({backing, callback}); } } -void MarkingVisitorCommon::VisitWeak(void* object, - void* object_weak_ref, +void MarkingVisitorCommon::VisitWeak(const void* object, + const void* object_weak_ref, TraceDescriptor desc, WeakCallback callback) { // Filter out already marked values. The write barrier for WeakMember @@ -79,9 +72,10 @@ void MarkingVisitorCommon::VisitWeak(void* object, RegisterWeakCallback(callback, object_weak_ref); } -void MarkingVisitorCommon::VisitBackingStoreStrongly(void* object, - void** object_slot, - TraceDescriptor desc) { +void MarkingVisitorCommon::VisitBackingStoreStrongly( + const void* object, + const void* const* object_slot, + TraceDescriptor desc) { RegisterBackingStoreReference(object_slot); if (!object) return; @@ -90,24 +84,30 @@ void MarkingVisitorCommon::VisitBackingStoreStrongly(void* object, // All work is registered through RegisterWeakCallback. void MarkingVisitorCommon::VisitBackingStoreWeakly( - void* object, - void** object_slot, + const void* object, + const void* const* object_slot, TraceDescriptor strong_desc, TraceDescriptor weak_desc, WeakCallback weak_callback, - void* weak_callback_parameter) { + const void* weak_callback_parameter) { RegisterBackingStoreReference(object_slot); + + // In case there's no object present, weakness processing is omitted. The GC + // relies on the fact that in such cases touching the weak data structure will + // strongify its references. if (!object) return; - RegisterWeakCallback(weak_callback, weak_callback_parameter); + // Register final weak processing of the backing store. + RegisterWeakCallback(weak_callback, weak_callback_parameter); + // Register ephemeron callbacks if necessary. if (weak_desc.callback) weak_table_worklist_.Push(weak_desc); } bool MarkingVisitorCommon::VisitEphemeronKeyValuePair( - void* key, - void* value, + const void* key, + const void* value, EphemeronTracingCallback key_trace_callback, EphemeronTracingCallback value_trace_callback) { const bool key_is_dead = key_trace_callback(this, key); @@ -118,8 +118,9 @@ bool MarkingVisitorCommon::VisitEphemeronKeyValuePair( return false; } -void MarkingVisitorCommon::VisitBackingStoreOnly(void* object, - void** object_slot) { +void MarkingVisitorCommon::VisitBackingStoreOnly( + const void* object, + const void* const* object_slot) { RegisterBackingStoreReference(object_slot); if (!object) return; @@ -129,19 +130,9 @@ void MarkingVisitorCommon::VisitBackingStoreOnly(void* object, } // static -bool MarkingVisitor::WriteBarrierSlow(void* value) { - if (!value || IsHashTableDeleteValue(value)) - return false; - - // It is guaranteed that managed references point to either GarbageCollected - // or GarbageCollectedMixin. Mixins are restricted to regular objects sizes. - // It is thus possible to get to the page header by aligning properly. - BasePage* base_page = PageFromObject(value); - - ThreadState* const thread_state = base_page->thread_state(); - if (!thread_state->IsIncrementalMarking()) - return false; - +bool MarkingVisitor::MarkValue(void* value, + BasePage* base_page, + ThreadState* thread_state) { HeapObjectHeader* header; if (LIKELY(!base_page->IsLargeObjectPage())) { header = reinterpret_cast<HeapObjectHeader*>( @@ -169,7 +160,47 @@ bool MarkingVisitor::WriteBarrierSlow(void* value) { return true; } -void MarkingVisitor::TraceMarkedBackingStoreSlow(void* value) { +// static +bool MarkingVisitor::WriteBarrierSlow(void* value) { + if (!value || IsHashTableDeleteValue(value)) + return false; + + // It is guaranteed that managed references point to either GarbageCollected + // or GarbageCollectedMixin. Mixins are restricted to regular objects sizes. + // It is thus possible to get to the page header by aligning properly. + BasePage* base_page = PageFromObject(value); + + ThreadState* const thread_state = base_page->thread_state(); + if (!thread_state->IsIncrementalMarking()) + return false; + + return MarkValue(value, base_page, thread_state); +} + +void MarkingVisitor::GenerationalBarrierSlow(Address slot, + ThreadState* thread_state) { + BasePage* slot_page = thread_state->Heap().LookupPageForAddress(slot); + DCHECK(slot_page); + + if (UNLIKELY(slot_page->IsLargeObjectPage())) { + auto* large_page = static_cast<LargeObjectPage*>(slot_page); + if (UNLIKELY(large_page->ObjectHeader()->IsOld())) { + large_page->SetRemembered(true); + } + return; + } + + auto* normal_page = static_cast<NormalPage*>(slot_page); + const HeapObjectHeader* source_header = reinterpret_cast<HeapObjectHeader*>( + normal_page->object_start_bit_map()->FindHeader(slot)); + DCHECK_LT(0u, source_header->GcInfoIndex()); + DCHECK_GT(source_header->PayloadEnd(), slot); + if (UNLIKELY(source_header->IsOld())) { + normal_page->MarkCard(slot); + } +} + +void MarkingVisitor::TraceMarkedBackingStoreSlow(const void* value) { if (!value) return; @@ -183,33 +214,29 @@ void MarkingVisitor::TraceMarkedBackingStoreSlow(void* value) { DCHECK(thread_state->CurrentVisitor()); // No weak handling for write barriers. Modifying weakly reachable objects // strongifies them for the current cycle. - GCInfoTable::Get() - .GCInfoFromIndex(header->GcInfoIndex()) - ->trace(thread_state->CurrentVisitor(), value); + + GCInfo::From(header->GcInfoIndex()) + .trace(thread_state->CurrentVisitor(), value); } MarkingVisitor::MarkingVisitor(ThreadState* state, MarkingMode marking_mode) - : MarkingVisitorBase(state, marking_mode, WorklistTaskId::MutatorThread), - write_barrier_worklist_(Heap().GetWriteBarrierWorklist(), - WorklistTaskId::MutatorThread) { + : MarkingVisitorBase(state, marking_mode, WorklistTaskId::MutatorThread) { DCHECK(state->InAtomicMarkingPause()); DCHECK(state->CheckThread()); } -void MarkingVisitor::DynamicallyMarkAddress(Address address) { +void MarkingVisitor::DynamicallyMarkAddress(ConstAddress address) { HeapObjectHeader* const header = HeapObjectHeader::FromInnerAddress(address); DCHECK(header); DCHECK(!IsInConstruction(header)); - const GCInfo* gc_info = - GCInfoTable::Get().GCInfoFromIndex(header->GcInfoIndex()); if (MarkHeaderNoTracing(header)) { - marking_worklist_.Push( - {reinterpret_cast<void*>(header->Payload()), gc_info->trace}); + marking_worklist_.Push({reinterpret_cast<void*>(header->Payload()), + GCInfo::From(header->GcInfoIndex()).trace}); } } void MarkingVisitor::ConservativelyMarkAddress(BasePage* page, - Address address) { + ConstAddress address) { #if DCHECK_IS_ON() DCHECK(page->Contains(address)); #endif @@ -223,10 +250,9 @@ void MarkingVisitor::ConservativelyMarkAddress(BasePage* page, // Simple case for fully constructed objects. This just adds the object to the // regular marking worklist. - const GCInfo* gc_info = - GCInfoTable::Get().GCInfoFromIndex(header->GcInfoIndex()); if (!IsInConstruction(header)) { - MarkHeader(header, {header->Payload(), gc_info->trace}); + MarkHeader(header, + {header->Payload(), GCInfo::From(header->GcInfoIndex()).trace}); return; } @@ -260,14 +286,18 @@ void MarkingVisitor::ConservativelyMarkAddress(BasePage* page, AccountMarkedBytes(header); } -void MarkingVisitor::FlushMarkingWorklist() { +void MarkingVisitor::FlushMarkingWorklists() { marking_worklist_.FlushToGlobal(); + write_barrier_worklist_.FlushToGlobal(); } ConcurrentMarkingVisitor::ConcurrentMarkingVisitor(ThreadState* state, MarkingMode marking_mode, int task_id) - : MarkingVisitorBase(state, marking_mode, task_id) { + : MarkingVisitorBase(state, marking_mode, task_id), + not_safe_to_concurrently_trace_worklist_( + Heap().GetNotSafeToConcurrentlyTraceWorklist(), + task_id) { DCHECK(!state->CheckThread()); DCHECK_NE(WorklistTaskId::MutatorThread, task_id); } @@ -275,12 +305,16 @@ ConcurrentMarkingVisitor::ConcurrentMarkingVisitor(ThreadState* state, void ConcurrentMarkingVisitor::FlushWorklists() { // Flush marking worklists for further marking on the mutator thread. marking_worklist_.FlushToGlobal(); + write_barrier_worklist_.FlushToGlobal(); not_fully_constructed_worklist_.FlushToGlobal(); weak_callback_worklist_.FlushToGlobal(); weak_table_worklist_.FlushToGlobal(); + not_safe_to_concurrently_trace_worklist_.FlushToGlobal(); // Flush compaction worklists. - movable_reference_worklist_.FlushToGlobal(); - backing_store_callback_worklist_.FlushToGlobal(); + if (marking_mode_ == kGlobalMarkingWithCompaction) { + movable_reference_worklist_.FlushToGlobal(); + backing_store_callback_worklist_.FlushToGlobal(); + } } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/marking_visitor.h b/chromium/third_party/blink/renderer/platform/heap/marking_visitor.h index 0bb441d6b6a..24ff86413cb 100644 --- a/chromium/third_party/blink/renderer/platform/heap/marking_visitor.h +++ b/chromium/third_party/blink/renderer/platform/heap/marking_visitor.h @@ -12,6 +12,14 @@ namespace blink { +namespace { + +ALWAYS_INLINE bool IsHashTableDeleteValue(const void* value) { + return value == reinterpret_cast<void*>(-1); +} + +} // namespace + class BasePage; // Base visitor used to mark Oilpan objects on any thread. @@ -24,23 +32,25 @@ class PLATFORM_EXPORT MarkingVisitorCommon : public Visitor { kGlobalMarkingWithCompaction, }; - void VisitWeak(void*, void*, TraceDescriptor, WeakCallback) final; - void VisitBackingStoreStrongly(void*, void**, TraceDescriptor) final; - void VisitBackingStoreWeakly(void*, - void**, + void VisitWeak(const void*, const void*, TraceDescriptor, WeakCallback) final; + void VisitBackingStoreStrongly(const void*, + const void* const*, + TraceDescriptor) final; + void VisitBackingStoreWeakly(const void*, + const void* const*, TraceDescriptor, TraceDescriptor, WeakCallback, - void*) final; - bool VisitEphemeronKeyValuePair(void*, - void*, + const void*) final; + bool VisitEphemeronKeyValuePair(const void*, + const void*, EphemeronTracingCallback, EphemeronTracingCallback) final; // Used to only mark the backing store when it has been registered for weak // processing. In this case, the contents are processed separately using // the corresponding traits but the backing store requires marking. - void VisitBackingStoreOnly(void*, void**) final; + void VisitBackingStoreOnly(const void*, const void* const*) final; // This callback mechanism is needed to account for backing store objects // containing intra-object pointers, all of which must be relocated/rebased @@ -48,8 +58,8 @@ class PLATFORM_EXPORT MarkingVisitorCommon : public Visitor { // // For Blink, |HeapLinkedHashSet<>| is currently the only abstraction which // relies on this feature. - void RegisterBackingStoreCallback(void*, MovingObjectCallback) final; - void RegisterWeakCallback(WeakCallback, void*) final; + void RegisterBackingStoreCallback(const void*, MovingObjectCallback) final; + void RegisterWeakCallback(WeakCallback, const void*) final; // Flush private segments remaining in visitor's worklists to global pools. void FlushCompactionWorklists(); @@ -71,9 +81,10 @@ class PLATFORM_EXPORT MarkingVisitorCommon : public Visitor { // marked upon calling. bool MarkHeaderNoTracing(HeapObjectHeader*); - void RegisterBackingStoreReference(void** slot); + void RegisterBackingStoreReference(const void* const* slot); MarkingWorklist::View marking_worklist_; + WriteBarrierWorklist::View write_barrier_worklist_; NotFullyConstructedWorklist::View not_fully_constructed_worklist_; WeakCallbackWorklist::View weak_callback_worklist_; MovableReferenceWorklist::View movable_reference_worklist_; @@ -88,7 +99,8 @@ ALWAYS_INLINE void MarkingVisitorCommon::AccountMarkedBytes( HeapObjectHeader* header) { marked_bytes_ += header->IsLargeObject() - ? reinterpret_cast<LargeObjectPage*>(PageFromObject(header))->size() + ? reinterpret_cast<LargeObjectPage*>(PageFromObject(header)) + ->ObjectSize() : header->size(); } @@ -110,7 +122,7 @@ ALWAYS_INLINE bool MarkingVisitorCommon::MarkHeaderNoTracing( template <class Specialized> class PLATFORM_EXPORT MarkingVisitorBase : public MarkingVisitorCommon { public: - void Visit(void* object, TraceDescriptor desc) final; + void Visit(const void* object, TraceDescriptor desc) final; // Unused cross-component visit methods. void Visit(const TraceWrapperV8Reference<v8::Value>&) override {} @@ -125,7 +137,7 @@ class PLATFORM_EXPORT MarkingVisitorBase : public MarkingVisitorCommon { }; template <class Specialized> -inline void MarkingVisitorBase<Specialized>::Visit(void* object, +inline void MarkingVisitorBase<Specialized>::Visit(const void* object, TraceDescriptor desc) { DCHECK(object); if (desc.base_object_payload == BlinkGC::kNotFullyConstructedObject) { @@ -161,17 +173,20 @@ class PLATFORM_EXPORT MarkingVisitor // Returns whether an object is in construction. static bool IsInConstruction(HeapObjectHeader* header); - // Write barrier that adds |value| to the set of marked objects. The barrier - // bails out if marking is off or the object is not yet marked. Returns true - // if the object was marked on this call. - static bool WriteBarrier(void* value); + // Write barrier that adds a value the |slot| refers to to the set of marked + // objects. The barrier bails out if marking is off or the object is not yet + // marked. Returns true if the value has been marked on this call. + template <typename T> + static bool WriteBarrier(T** slot); + + static void GenerationalBarrier(Address slot, ThreadState* state); // Eagerly traces an already marked backing store ensuring that all its // children are discovered by the marker. The barrier bails out if marking // is off and on individual objects reachable if they are already marked. The // barrier uses the callback function through GcInfo, so it will not inline // any templated type-specific code. - static void TraceMarkedBackingStore(void* value); + static void TraceMarkedBackingStore(const void* value); MarkingVisitor(ThreadState*, MarkingMode); ~MarkingVisitor() override = default; @@ -179,21 +194,21 @@ class PLATFORM_EXPORT MarkingVisitor // Conservatively marks an object if pointed to by Address. The object may // be in construction as the scan is conservative without relying on a // Trace method. - void ConservativelyMarkAddress(BasePage*, Address); + void ConservativelyMarkAddress(BasePage*, ConstAddress); // Marks an object dynamically using any address within its body and adds a // tracing callback for processing of the object. The object is not allowed // to be in construction. - void DynamicallyMarkAddress(Address); + void DynamicallyMarkAddress(ConstAddress); - void FlushMarkingWorklist(); + void FlushMarkingWorklists(); private: - // Exact version of the marking write barriers. + // Exact version of the marking and generational write barriers. static bool WriteBarrierSlow(void*); - static void TraceMarkedBackingStoreSlow(void*); - - WriteBarrierWorklist::View write_barrier_worklist_; + static void GenerationalBarrierSlow(Address, ThreadState*); + static bool MarkValue(void*, BasePage*, ThreadState*); + static void TraceMarkedBackingStoreSlow(const void*); }; // static @@ -204,17 +219,45 @@ ALWAYS_INLINE bool MarkingVisitor::IsInConstruction(HeapObjectHeader* header) { } // static -ALWAYS_INLINE bool MarkingVisitor::WriteBarrier(void* value) { +template <typename T> +ALWAYS_INLINE bool MarkingVisitor::WriteBarrier(T** slot) { +#if BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) + void* value = *slot; + if (!value || IsHashTableDeleteValue(value)) + return false; + + // Dijkstra barrier if concurrent marking is in progress. + BasePage* value_page = PageFromObject(value); + ThreadState* thread_state = value_page->thread_state(); + + if (UNLIKELY(thread_state->IsIncrementalMarking())) + return MarkValue(value, value_page, thread_state); + + GenerationalBarrier(reinterpret_cast<Address>(slot), thread_state); + return false; +#else if (!ThreadState::IsAnyIncrementalMarking()) return false; // Avoid any further checks and dispatch to a call at this point. Aggressive // inlining otherwise pollutes the regular execution paths. - return WriteBarrierSlow(value); + return WriteBarrierSlow(*slot); +#endif } // static -ALWAYS_INLINE void MarkingVisitor::TraceMarkedBackingStore(void* value) { +ALWAYS_INLINE void MarkingVisitor::GenerationalBarrier(Address slot, + ThreadState* state) { + // First, check if the source object is in the last allocated region of heap. + if (LIKELY(state->Heap().IsInLastAllocatedRegion(slot))) + return; + if (UNLIKELY(state->IsOnStack(slot))) + return; + GenerationalBarrierSlow(slot, state); +} + +// static +ALWAYS_INLINE void MarkingVisitor::TraceMarkedBackingStore(const void* value) { if (!ThreadState::IsAnyIncrementalMarking()) return; @@ -234,8 +277,30 @@ class PLATFORM_EXPORT ConcurrentMarkingVisitor ~ConcurrentMarkingVisitor() override = default; virtual void FlushWorklists(); + + // Concurrent variant of MarkingVisitorCommon::AccountMarkedBytes. + void AccountMarkedBytesSafe(HeapObjectHeader*); + + bool IsConcurrent() const override { return true; } + + bool ConcurrentTracingBailOut(TraceDescriptor desc) override { + not_safe_to_concurrently_trace_worklist_.Push(desc); + return true; + } + + private: + NotSafeToConcurrentlyTraceWorklist::View + not_safe_to_concurrently_trace_worklist_; }; +ALWAYS_INLINE void ConcurrentMarkingVisitor::AccountMarkedBytesSafe( + HeapObjectHeader* header) { + marked_bytes_ += + header->IsLargeObject<HeapObjectHeader::AccessMode::kAtomic>() + ? static_cast<LargeObjectPage*>(PageFromObject(header))->ObjectSize() + : header->size<HeapObjectHeader::AccessMode::kAtomic>(); +} + // static ALWAYS_INLINE bool ConcurrentMarkingVisitor::IsInConstruction( HeapObjectHeader* header) { diff --git a/chromium/third_party/blink/renderer/platform/heap/member.h b/chromium/third_party/blink/renderer/platform/heap/member.h index 6c6f324271f..1610b12d7f2 100644 --- a/chromium/third_party/blink/renderer/platform/heap/member.h +++ b/chromium/third_party/blink/renderer/platform/heap/member.h @@ -113,21 +113,21 @@ class MemberBase { MemberBase(const MemberBase& other) : raw_(other) { SaveCreationThreadState(); CheckPointer(); - WriteBarrier(); + // No write barrier for initializing stores. } template <typename U> MemberBase(const Persistent<U>& other) : raw_(other) { SaveCreationThreadState(); CheckPointer(); - WriteBarrier(); + // No write barrier for initializing stores. } template <typename U> MemberBase(const MemberBase<U>& other) : raw_(other) { SaveCreationThreadState(); CheckPointer(); - WriteBarrier(); + // No write barrier for initializing stores. } template <typename U> @@ -195,16 +195,37 @@ class MemberBase { return result; } + static bool IsMemberHashTableDeletedValue(const T* t) { + return t == reinterpret_cast<T*>(kHashTableDeletedRawValue); + } + bool IsHashTableDeletedValue() const { - return GetRaw() == reinterpret_cast<T*>(kHashTableDeletedRawValue); + return IsMemberHashTableDeletedValue(GetRaw()); } protected: static constexpr intptr_t kHashTableDeletedRawValue = -1; + enum class AtomicCtorTag { Atomic }; + + // MemberBase ctors that use atomic write to set raw_. + + MemberBase(AtomicCtorTag, T* raw) { + SetRaw(raw); + SaveCreationThreadState(); + CheckPointer(); + // No write barrier for initializing stores. + } + + MemberBase(AtomicCtorTag, T& raw) { + SetRaw(&raw); + SaveCreationThreadState(); + CheckPointer(); + // No write barrier for initializing stores. + } + void WriteBarrier() const { - MarkingVisitor::WriteBarrier( - const_cast<typename std::remove_const<T>::type*>(GetRaw())); + MarkingVisitor::WriteBarrier(const_cast<std::remove_const_t<T>**>(&raw_)); } void CheckPointer() { @@ -224,10 +245,7 @@ class MemberBase { } ALWAYS_INLINE void SetRaw(T* raw) { - // TOOD(omerkatz): replace this cast with std::atomic_ref (C++20) once it - // becomes available - reinterpret_cast<std::atomic<T*>*>(&raw_)->store(raw, - std::memory_order_relaxed); + WTF::AsAtomicPtr(&raw_)->store(raw, std::memory_order_relaxed); } ALWAYS_INLINE T* GetRaw() const { return raw_; } @@ -235,17 +253,10 @@ class MemberBase { // Thread safe version of Get() for marking visitors. // This is used to prevent data races between concurrent marking visitors // and writes on the main thread. - T* GetSafe() const { + const T* GetSafe() const { // TOOD(omerkatz): replace this cast with std::atomic_ref (C++20) once it // becomes available - return reinterpret_cast<std::atomic<T*>*>( - const_cast<typename std::remove_const<T>::type**>(&raw_)) - ->load(std::memory_order_relaxed); - } - - // Thread safe version of IsHashTableDeletedValue for use while tracing. - bool IsHashTableDeletedValueSafe() const { - return GetSafe() == reinterpret_cast<T*>(kHashTableDeletedRawValue); + return WTF::AsAtomicPtr(&raw_)->load(std::memory_order_relaxed); } T* raw_; @@ -321,7 +332,11 @@ class Member : public MemberBase<T, TracenessMemberConfiguration::kTraced> { return *this; } - protected: + private: + using typename Parent::AtomicCtorTag; + Member(AtomicCtorTag atomic, T* raw) : Parent(atomic, raw) {} + Member(AtomicCtorTag atomic, T& raw) : Parent(atomic, raw) {} + template <typename P, typename Traits, typename Allocator> friend class WTF::ConstructTraits; }; @@ -404,6 +419,12 @@ class UntracedMember final UntracedMember(WTF::HashTableDeletedValueType x) : Parent(x) {} + UntracedMember& operator=(const UntracedMember& other) { + this->SetRaw(other); + this->CheckPointer(); + return *this; + } + template <typename U> UntracedMember& operator=(const Persistent<U>& other) { this->SetRaw(other); @@ -500,7 +521,12 @@ class ConstructTraits<blink::Member<T>, Traits, Allocator> { template <typename... Args> static blink::Member<T>* ConstructAndNotifyElement(void* location, Args&&... args) { - blink::Member<T>* object = Construct(location, std::forward<Args>(args)...); + // ConstructAndNotifyElement updates an existing Member which might + // also be comncurrently traced while we update it. The regular ctors + // for Member don't use an atomic write which can lead to data races. + blink::Member<T>* object = + Construct(location, blink::Member<T>::AtomicCtorTag::Atomic, + std::forward<Args>(args)...); NotifyNewElement(object); return object; } diff --git a/chromium/third_party/blink/renderer/platform/heap/minor_gc_test.cc b/chromium/third_party/blink/renderer/platform/heap/minor_gc_test.cc new file mode 100644 index 00000000000..c30e976d294 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/heap/minor_gc_test.cc @@ -0,0 +1,295 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/heap/heap_page.h" +#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/heap/thread_state.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" + +namespace blink { + +namespace { + +class SimpleGCedBase : public GarbageCollected<SimpleGCedBase> { + public: + static size_t destructed_objects; + + virtual ~SimpleGCedBase() { ++destructed_objects; } + + void Trace(Visitor* v) { v->Trace(next); } + + Member<SimpleGCedBase> next; +}; + +size_t SimpleGCedBase::destructed_objects; + +template <size_t Size> +class SimpleGCed final : public SimpleGCedBase { + char array[Size]; +}; + +using Small = SimpleGCed<64>; +using Large = SimpleGCed<1024 * 1024>; + +template <typename Type> +struct OtherType; +template <> +struct OtherType<Small> { + using Type = Large; +}; +template <> +struct OtherType<Large> { + using Type = Small; +}; + +class MinorGCTest : public TestSupportingGC { + public: + MinorGCTest() { + ClearOutOldGarbage(); + SimpleGCedBase::destructed_objects = 0; + } + + static size_t DestructedObjects() { + return SimpleGCedBase::destructed_objects; + } + + static void CollectMinor() { Collect(BlinkGC::CollectionType::kMinor); } + static void CollectMajor() { Collect(BlinkGC::CollectionType::kMajor); } + + private: + static void Collect(BlinkGC::CollectionType type) { + ThreadState::Current()->CollectGarbage( + type, BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, + BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGCForTesting); + } +}; + +template <typename SmallOrLarge> +class MinorGCTestForType : public MinorGCTest { + public: + using Type = SmallOrLarge; +}; + +} // namespace + +using ObjectTypes = ::testing::Types<Small, Large>; +TYPED_TEST_SUITE(MinorGCTestForType, ObjectTypes); + +TYPED_TEST(MinorGCTestForType, MinorCollection) { + using Type = typename TestFixture::Type; + + MakeGarbageCollected<Type>(); + EXPECT_EQ(0u, TestFixture::DestructedObjects()); + MinorGCTest::CollectMinor(); + EXPECT_EQ(1u, TestFixture::DestructedObjects()); + + Type* prev = nullptr; + for (size_t i = 0; i < 64; ++i) { + auto* ptr = MakeGarbageCollected<Type>(); + ptr->next = prev; + prev = ptr; + } + + MinorGCTest::CollectMinor(); + EXPECT_EQ(65u, TestFixture::DestructedObjects()); +} + +TYPED_TEST(MinorGCTestForType, StickyBits) { + using Type = typename TestFixture::Type; + + Persistent<Type> p1 = MakeGarbageCollected<Type>(); + TestFixture::CollectMinor(); + EXPECT_TRUE(HeapObjectHeader::FromPayload(p1.Get())->IsOld()); + TestFixture::CollectMajor(); + EXPECT_TRUE(HeapObjectHeader::FromPayload(p1.Get())->IsOld()); + EXPECT_EQ(0u, TestFixture::DestructedObjects()); +} + +TYPED_TEST(MinorGCTestForType, OldObjectIsNotVisited) { + using Type = typename TestFixture::Type; + + Persistent<Type> p = MakeGarbageCollected<Type>(); + TestFixture::CollectMinor(); + EXPECT_EQ(0u, TestFixture::DestructedObjects()); + EXPECT_TRUE(HeapObjectHeader::FromPayload(p.Get())->IsOld()); + + // Check that the old deleted object won't be visited during minor GC. + Type* raw = p.Release(); + TestFixture::CollectMinor(); + EXPECT_EQ(0u, TestFixture::DestructedObjects()); + EXPECT_TRUE(HeapObjectHeader::FromPayload(raw)->IsOld()); + EXPECT_FALSE(HeapObjectHeader::FromPayload(raw)->IsFree()); + + // Check that the old deleted object will be revisited in major GC. + TestFixture::CollectMajor(); + EXPECT_EQ(1u, TestFixture::DestructedObjects()); +} + +template <typename Type1, typename Type2> +void InterGenerationalPointerTest() { + Persistent<Type1> old = MakeGarbageCollected<Type1>(); + MinorGCTest::CollectMinor(); + EXPECT_TRUE(HeapObjectHeader::FromPayload(old.Get())->IsOld()); + + // Allocate young objects. + Type2* young = nullptr; + for (size_t i = 0; i < 64; ++i) { + auto* ptr = MakeGarbageCollected<Type2>(); + ptr->next = young; + young = ptr; + EXPECT_FALSE(HeapObjectHeader::FromPayload(young)->IsOld()); + } + + // Issue generational barrier. + old->next = young; + + // Check that the remembered set is visited. + MinorGCTest::CollectMinor(); + EXPECT_EQ(0u, MinorGCTest::DestructedObjects()); + for (size_t i = 0; i < 64; ++i) { + EXPECT_TRUE(HeapObjectHeader::FromPayload(young)->IsOld()); + EXPECT_FALSE(HeapObjectHeader::FromPayload(young)->IsFree()); + young = static_cast<Type2*>(young->next.Get()); + } + + old.Release(); + MinorGCTest::CollectMajor(); + EXPECT_EQ(65u, MinorGCTest::DestructedObjects()); +} + +TYPED_TEST(MinorGCTestForType, InterGenerationalPointerForSamePageTypes) { + using Type = typename TestFixture::Type; + InterGenerationalPointerTest<Type, Type>(); +} + +TYPED_TEST(MinorGCTestForType, InterGenerationalPointerForDifferentPageTypes) { + using Type = typename TestFixture::Type; + InterGenerationalPointerTest<Type, typename OtherType<Type>::Type>(); +} + +TYPED_TEST(MinorGCTestForType, InterGenerationalPointerInCollection) { + using Type = typename TestFixture::Type; + + static constexpr size_t kCollectionSize = 128; + Persistent<HeapVector<Member<Type>>> old = + MakeGarbageCollected<HeapVector<Member<Type>>>(); + old->resize(kCollectionSize); + void* raw_backing = old->data(); + EXPECT_FALSE(HeapObjectHeader::FromPayload(raw_backing)->IsOld()); + MinorGCTest::CollectMinor(); + EXPECT_TRUE(HeapObjectHeader::FromPayload(raw_backing)->IsOld()); + + // Issue barrier for every second member. + size_t i = 0; + for (auto& member : *old) { + if (i % 2) { + member = MakeGarbageCollected<Type>(); + } else { + MakeGarbageCollected<Type>(); + } + ++i; + } + + // Check that the remembered set is visited. + MinorGCTest::CollectMinor(); + EXPECT_EQ(kCollectionSize / 2, MinorGCTest::DestructedObjects()); + for (const auto& member : *old) { + if (member) { + EXPECT_TRUE(HeapObjectHeader::FromPayload(member.Get())->IsOld()); + EXPECT_FALSE(HeapObjectHeader::FromPayload(member.Get())->IsFree()); + } + } + + old.Release(); + MinorGCTest::CollectMajor(); + EXPECT_EQ(kCollectionSize, MinorGCTest::DestructedObjects()); +} + +TYPED_TEST(MinorGCTestForType, InterGenerationalPointerInPlaceBarrier) { + using Type = typename TestFixture::Type; + using ValueType = std::pair<WTF::String, Member<Type>>; + using CollectionType = HeapVector<ValueType>; + + static constexpr size_t kCollectionSize = 1; + + Persistent<CollectionType> old = MakeGarbageCollected<CollectionType>(); + old->ReserveInitialCapacity(kCollectionSize); + + void* raw_backing = old->data(); + EXPECT_FALSE(HeapObjectHeader::FromPayload(raw_backing)->IsOld()); + MinorGCTest::CollectMinor(); + EXPECT_TRUE(HeapObjectHeader::FromPayload(raw_backing)->IsOld()); + + // Issue barrier (in HeapAllocator::NotifyNewElement). + old->push_back(std::make_pair("test", MakeGarbageCollected<Type>())); + + // Check that the remembered set is visited. + MinorGCTest::CollectMinor(); + + // No objects destructed. + EXPECT_EQ(0u, MinorGCTest::DestructedObjects()); + EXPECT_EQ(1u, old->size()); + + { + Type* member = (*old)[0].second; + EXPECT_TRUE(HeapObjectHeader::FromPayload(member)->IsOld()); + EXPECT_FALSE(HeapObjectHeader::FromPayload(member)->IsFree()); + } + + old.Release(); + MinorGCTest::CollectMajor(); + EXPECT_EQ(1u, MinorGCTest::DestructedObjects()); +} + +TYPED_TEST(MinorGCTestForType, + InterGenerationalPointerNotifyingBunchOfElements) { + using Type = typename TestFixture::Type; + using ValueType = std::pair<int, Member<Type>>; + using CollectionType = HeapVector<ValueType>; + static_assert(WTF::VectorTraits<ValueType>::kCanCopyWithMemcpy, + "Only when copying with memcpy the " + "Allocator::NotifyNewElements is called"); + + Persistent<CollectionType> old = MakeGarbageCollected<CollectionType>(); + old->ReserveInitialCapacity(1); + + void* raw_backing = old->data(); + EXPECT_FALSE(HeapObjectHeader::FromPayload(raw_backing)->IsOld()); + + // Mark old backing. + MinorGCTest::CollectMinor(); + EXPECT_TRUE(HeapObjectHeader::FromPayload(raw_backing)->IsOld()); + + Persistent<CollectionType> young = MakeGarbageCollected<CollectionType>(); + + // Add a single element to the young container. + young->push_back(std::make_pair(1, MakeGarbageCollected<Type>())); + + // Copy young container and issue barrier in HeapAllocator::NotifyNewElements. + *old = *young; + + // Release young container. + young.Release(); + + // Check that the remembered set is visited. + MinorGCTest::CollectMinor(); + + // Nothing must be destructed since the old vector backing was revisited. + EXPECT_EQ(0u, MinorGCTest::DestructedObjects()); + EXPECT_EQ(1u, old->size()); + + { + Type* member = (*old)[0].second; + EXPECT_TRUE(HeapObjectHeader::FromPayload(member)->IsOld()); + EXPECT_FALSE(HeapObjectHeader::FromPayload(member)->IsFree()); + } + + old.Release(); + MinorGCTest::CollectMajor(); + EXPECT_EQ(1u, MinorGCTest::DestructedObjects()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/page_memory.cc b/chromium/third_party/blink/renderer/platform/heap/page_memory.cc index c94a5866657..6552456eebc 100644 --- a/chromium/third_party/blink/renderer/platform/heap/page_memory.cc +++ b/chromium/third_party/blink/renderer/platform/heap/page_memory.cc @@ -56,7 +56,8 @@ void PageMemoryRegion::PageDeleted(Address page) { // we should probably have a way to distinguish physical memory OOM from // virtual address space OOM. static NOINLINE void BlinkGCOutOfMemory() { - OOM_CRASH(); + // TODO(lizeb): Add the real allocation size here as well. + OOM_CRASH(0); } PageMemoryRegion* PageMemoryRegion::Allocate(size_t size, @@ -72,7 +73,7 @@ PageMemoryRegion* PageMemoryRegion::Allocate(size_t size, return new PageMemoryRegion(base, size, num_pages, region_tree); } -PageMemoryRegion* RegionTree::Lookup(Address address) { +PageMemoryRegion* RegionTree::Lookup(ConstAddress address) { auto it = set_.upper_bound(address); // This check also covers set_.size() > 0, since for empty vectors it is // guaranteed that begin() == end(). diff --git a/chromium/third_party/blink/renderer/platform/heap/page_memory.h b/chromium/third_party/blink/renderer/platform/heap/page_memory.h index 40884d69b93..249a1eb668c 100644 --- a/chromium/third_party/blink/renderer/platform/heap/page_memory.h +++ b/chromium/third_party/blink/renderer/platform/heap/page_memory.h @@ -24,7 +24,7 @@ class MemoryRegion { DCHECK_GT(size, 0u); } - bool Contains(Address addr) const { + bool Contains(ConstAddress addr) const { return base_ <= addr && addr < (base_ + size_); } @@ -73,7 +73,7 @@ class PageMemoryRegion : public MemoryRegion { region_tree); } - BasePage* PageFromAddress(Address address) { + BasePage* PageFromAddress(ConstAddress address) { DCHECK(Contains(address)); if (!in_use_[Index(address)]) return nullptr; @@ -85,11 +85,11 @@ class PageMemoryRegion : public MemoryRegion { private: PageMemoryRegion(Address base, size_t, unsigned num_pages, RegionTree*); - unsigned Index(Address address) const { + unsigned Index(ConstAddress address) const { DCHECK(Contains(address)); if (is_large_page_) return 0; - size_t offset = BlinkPageAddress(address) - Base(); + size_t offset = BlinkPageAddress(const_cast<Address>(address)) - Base(); DCHECK_EQ(offset % kBlinkPageSize, 0u); return static_cast<unsigned>(offset / kBlinkPageSize); } @@ -112,12 +112,12 @@ class RegionTree { public: void Add(PageMemoryRegion*); void Remove(PageMemoryRegion*); - PageMemoryRegion* Lookup(Address); + PageMemoryRegion* Lookup(ConstAddress); private: // Using flat_map allows to improve locality to minimize cache misses and // balance binary lookup. - base::flat_map<Address, PageMemoryRegion*> set_; + base::flat_map<ConstAddress, PageMemoryRegion*> set_; }; // Representation of the memory used for a Blink heap page. diff --git a/chromium/third_party/blink/renderer/platform/heap/persistent.h b/chromium/third_party/blink/renderer/platform/heap/persistent.h index 0b098c10186..fdc613835af 100644 --- a/chromium/third_party/blink/renderer/platform/heap/persistent.h +++ b/chromium/third_party/blink/renderer/platform/heap/persistent.h @@ -39,10 +39,16 @@ class PersistentLocation final { base::Location location_; }; -#if BUILDFLAG(RAW_HEAP_SNAPSHOTS) +#if !BUILDFLAG(FROM_HERE_USES_LOCATION_BUILTINS) && \ + BUILDFLAG(RAW_HEAP_SNAPSHOTS) +#if !BUILDFLAG(ENABLE_LOCATION_SOURCE) +#define PERSISTENT_FROM_HERE \ + PersistentLocation(::base::Location::CreateFromHere(__FILE__)) +#else #define PERSISTENT_FROM_HERE \ PersistentLocation( \ ::base::Location::CreateFromHere(__func__, __FILE__, __LINE__)) +#endif #else #define PERSISTENT_FROM_HERE PersistentLocation() #endif // BUILDFLAG(RAW_HEAP_SNAPSHOTS) @@ -298,7 +304,7 @@ class PersistentBase { UninitializeUnsafe(); } - void TracePersistent(Visitor* visitor) { + void TracePersistent(Visitor* visitor) const { static_assert(sizeof(T), "T must be fully defined"); static_assert(IsGarbageCollectedType<T>::value, "T needs to be a garbage collected object"); @@ -385,11 +391,12 @@ class PersistentBase { } static void HandleWeakPersistent(const WeakCallbackInfo&, - void* persistent_pointer) { + const void* persistent_pointer) { using Base = PersistentBase<typename std::remove_const<T>::type, weaknessConfiguration, crossThreadnessConfiguration>; - Base* persistent = reinterpret_cast<Base*>(persistent_pointer); + Base* persistent = + reinterpret_cast<Base*>(const_cast<void*>(persistent_pointer)); T* object = persistent->Get(); if (object && !ThreadHeap::IsHeapObjectAlive(object)) ClearWeakPersistent(persistent); diff --git a/chromium/third_party/blink/renderer/platform/heap/persistent_node.cc b/chromium/third_party/blink/renderer/platform/heap/persistent_node.cc index a0a6bb8c996..1449205eeb5 100644 --- a/chromium/third_party/blink/renderer/platform/heap/persistent_node.cc +++ b/chromium/third_party/blink/renderer/platform/heap/persistent_node.cc @@ -15,7 +15,7 @@ namespace { class DummyGCBase final : public GarbageCollected<DummyGCBase> { public: - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} }; } diff --git a/chromium/third_party/blink/renderer/platform/heap/persistent_node.h b/chromium/third_party/blink/renderer/platform/heap/persistent_node.h index bc76a0174b1..b591db1e793 100644 --- a/chromium/third_party/blink/renderer/platform/heap/persistent_node.h +++ b/chromium/third_party/blink/renderer/platform/heap/persistent_node.h @@ -74,7 +74,7 @@ class PersistentNode final { // Instead we call the constructor with a TraceCallback which knows the // type of the most specific child and calls trace directly. See // TraceMethodDelegate in Visitor.h for how this is done. - void TracePersistentNode(Visitor* visitor) { + void TracePersistentNode(Visitor* visitor) const { DCHECK(!IsUnused()); DCHECK(trace_); trace_(visitor, self_); diff --git a/chromium/third_party/blink/renderer/platform/heap/persistent_test.cc b/chromium/third_party/blink/renderer/platform/heap/persistent_test.cc index bc30d02baba..474509cd185 100644 --- a/chromium/third_party/blink/renderer/platform/heap/persistent_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/persistent_test.cc @@ -21,7 +21,7 @@ class Receiver : public GarbageCollected<Receiver> { public: void Increment(int* counter) { ++*counter; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} }; TEST_F(PersistentTest, BindCancellation) { diff --git a/chromium/third_party/blink/renderer/platform/heap/thread_state.cc b/chromium/third_party/blink/renderer/platform/heap/thread_state.cc index 27c2a8b8d19..477223077e2 100644 --- a/chromium/third_party/blink/renderer/platform/heap/thread_state.cc +++ b/chromium/third_party/blink/renderer/platform/heap/thread_state.cc @@ -109,7 +109,7 @@ constexpr base::TimeDelta kConcurrentMarkingStepDuration = // // TODO(omerkatz): kNumberOfMarkingTasks should be set heuristically // instead of a constant. -constexpr uint8_t kNumberOfConcurrentMarkingTasks = 1u; +constexpr uint8_t kNumberOfConcurrentMarkingTasks = 3u; constexpr size_t kMaxTerminationGCLoops = 20; @@ -127,8 +127,6 @@ class WorkerPoolTaskRunner : public base::TaskRunner { worker_pool::PostTask(location, WTF::CrossThreadBindOnce(std::move(task))); return true; } - - bool RunsTasksInCurrentSequence() const override { return false; } }; } // namespace @@ -147,6 +145,11 @@ class ThreadState::IncrementalMarkingScheduler { ScheduleTask(); } + void Restart() { + DCHECK(!task_.IsActive()); + ScheduleTask(); + } + // Cancels incremental marking task in case there is any pending. void Cancel() { task_.Cancel(); } @@ -169,16 +172,15 @@ class ThreadState::IncrementalMarkingScheduler { void Dispatch() { switch (thread_state_->GetGCState()) { - case ThreadState::kIncrementalGCScheduled: - thread_state_->IncrementalMarkingStart(reason_); - ScheduleTask(); - break; case ThreadState::kIncrementalMarkingStepScheduled: thread_state_->IncrementalMarkingStep( BlinkGC::kNoHeapPointersOnStack, next_incremental_marking_step_duration_); UpdateIncrementalMarkingStepDuration(); - ScheduleTask(); + if (thread_state_->GetGCState() != + ThreadState::kIncrementalMarkingStepPaused) { + ScheduleTask(); + } break; case ThreadState::kIncrementalMarkingFinalizeScheduled: thread_state_->IncrementalMarkingFinalize(); @@ -221,8 +223,6 @@ ThreadState::ThreadState() incremental_marking_scheduler_( std::make_unique<IncrementalMarkingScheduler>(this)), marker_scheduler_(std::make_unique<CancelableTaskScheduler>( - base::MakeRefCounted<WorkerPoolTaskRunner>())), - sweeper_scheduler_(std::make_unique<CancelableTaskScheduler>( base::MakeRefCounted<WorkerPoolTaskRunner>())) { DCHECK(CheckThread()); DCHECK(!**thread_specific_); @@ -265,6 +265,7 @@ void ThreadState::AttachToIsolate( v8_build_embedder_graph_ = v8_build_embedder_graph; unified_heap_controller_.reset(new UnifiedHeapController(this)); isolate_->SetEmbedderHeapTracer(unified_heap_controller_.get()); + unified_heap_controller_.get()->SetStackStart(WTF::GetStackStart()); if (v8::HeapProfiler* profiler = isolate->GetHeapProfiler()) { profiler->AddBuildEmbedderGraphCallback(v8_build_embedder_graph, nullptr); } @@ -288,7 +289,8 @@ void ThreadState::RunTerminationGC() { DCHECK(!IsMainThread()); DCHECK(CheckThread()); - FinishIncrementalMarkingIfRunning(BlinkGC::kNoHeapPointersOnStack, + FinishIncrementalMarkingIfRunning(BlinkGC::CollectionType::kMajor, + BlinkGC::kNoHeapPointersOnStack, BlinkGC::kIncrementalAndConcurrentMarking, BlinkGC::kConcurrentAndLazySweeping, BlinkGC::GCReason::kThreadTerminationGC); @@ -309,7 +311,8 @@ void ThreadState::RunTerminationGC() { int current_count = GetPersistentRegion()->NodesInUse(); DCHECK_GE(current_count, 0); while (current_count != old_count) { - CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, + CollectGarbage(BlinkGC::CollectionType::kMajor, + BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, BlinkGC::kEagerSweeping, BlinkGC::GCReason::kThreadTerminationGC); // Release the thread-local static persistents that were @@ -328,7 +331,8 @@ void ThreadState::RunTerminationGC() { i < kMaxTerminationGCLoops && GetPersistentRegion()->NodesInUse(); i++) { GetPersistentRegion()->PrepareForThreadStateTermination(this); - CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, + CollectGarbage(BlinkGC::CollectionType::kMajor, + BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, BlinkGC::kEagerSweeping, BlinkGC::GCReason::kThreadTerminationGC); } @@ -375,7 +379,9 @@ void ThreadState::VisitAsanFakeStackForPointer(MarkingVisitor* visitor, NO_SANITIZE_ADDRESS NO_SANITIZE_HWADDRESS NO_SANITIZE_THREAD -void ThreadState::VisitStack(MarkingVisitor* visitor, Address* end_of_stack) { +void ThreadState::VisitStackImpl(MarkingVisitor* visitor, + Address* start_of_stack, + Address* end_of_stack) { DCHECK_EQ(current_gc_data_.stack_state, BlinkGC::kHeapPointersOnStack); // Ensure that current is aligned by address size otherwise the loop below @@ -383,7 +389,7 @@ void ThreadState::VisitStack(MarkingVisitor* visitor, Address* end_of_stack) { Address* current = reinterpret_cast<Address*>( reinterpret_cast<intptr_t>(end_of_stack) & ~(sizeof(Address) - 1)); - for (; current < start_of_stack_; ++current) { + for (; current < start_of_stack; ++current) { Address ptr = *current; #if defined(MEMORY_SANITIZER) // |ptr| may be uninitialized by design. Mark it as initialized to keep @@ -394,10 +400,22 @@ void ThreadState::VisitStack(MarkingVisitor* visitor, Address* end_of_stack) { __msan_unpoison(&ptr, sizeof(ptr)); #endif heap_->CheckAndMarkPointer(visitor, ptr); - VisitAsanFakeStackForPointer(visitor, ptr, start_of_stack_, end_of_stack); + VisitAsanFakeStackForPointer(visitor, ptr, start_of_stack, end_of_stack); } } +void ThreadState::VisitStack(MarkingVisitor* visitor, Address* end_of_stack) { + VisitStackImpl(visitor, start_of_stack_, end_of_stack); +} + +void ThreadState::VisitUnsafeStack(MarkingVisitor* visitor) { +#if HAS_FEATURE(safe_stack) + VisitStackImpl(visitor, + static_cast<Address*>(__builtin___get_unsafe_stack_top()), + static_cast<Address*>(__builtin___get_unsafe_stack_ptr())); +#endif // HAS_FEATURE(safe_stack) +} + void ThreadState::VisitDOMWrappers(Visitor* visitor) { if (v8_trace_roots_) { ThreadHeapStatsCollector::Scope stats_scope( @@ -424,6 +442,12 @@ void ThreadState::VisitPersistents(Visitor* visitor) { } } +void ThreadState::VisitRememberedSets(MarkingVisitor* visitor) { + ThreadHeapStatsCollector::EnabledScope stats_scope( + Heap().stats_collector(), ThreadHeapStatsCollector::kVisitRememberedSets); + Heap().VisitRememberedSets(visitor); +} + void ThreadState::VisitWeakPersistents(Visitor* visitor) { ProcessHeap::GetCrossThreadWeakPersistentRegion().TraceNodes(visitor); weak_persistent_region_->TraceNodes(visitor); @@ -503,8 +527,7 @@ void ThreadState::PerformIdleLazySweep(base::TimeTicks deadline) { ThreadHeapStatsCollector::EnabledScope stats_scope( Heap().stats_collector(), ThreadHeapStatsCollector::kLazySweepInIdle, "idleDeltaInSeconds", (deadline - base::TimeTicks::Now()).InSecondsF()); - sweep_completed = - Heap().AdvanceSweep(ThreadHeap::SweepingType::kMutator, deadline); + sweep_completed = Heap().AdvanceLazySweep(deadline); // We couldn't finish the sweeping within the deadline. // We request another idle task for the remaining sweeping. if (sweep_completed) { @@ -519,28 +542,15 @@ void ThreadState::PerformIdleLazySweep(base::TimeTicks deadline) { } } -void ThreadState::PerformConcurrentSweep() { +void ThreadState::PerformConcurrentSweep(base::JobDelegate* job) { VLOG(2) << "[state:" << this << "] [threadid:" << CurrentThread() << "] " << "ConcurrentSweep"; - // As opposed to PerformIdleLazySweep, this function doesn't receive deadline - // from the scheduler, but defines it itself. - static constexpr base::TimeDelta kConcurrentSweepStepDuration = - base::TimeDelta::FromMilliseconds(2); - // Concurrent sweeper doesn't call finalizers - this guarantees that sweeping - // is not called recursively. ThreadHeapStatsCollector::EnabledConcurrentScope stats_scope( - Heap().stats_collector(), ThreadHeapStatsCollector::kConcurrentSweep); - const bool finished = Heap().AdvanceSweep( - ThreadHeap::SweepingType::kConcurrent, - base::TimeTicks::Now() + kConcurrentSweepStepDuration); - if (!finished) { - // Reschedule itself. It is safe even if the task timeouts and reposts - // itself while the mutator thread is waiting on CancelAndWait(). The - // mutator thread will simply wake up and cancel the newly post task itself. - sweeper_scheduler_->ScheduleTask( - WTF::CrossThreadBindOnce(&ThreadState::PerformConcurrentSweep, - WTF::CrossThreadUnretained(this))); - } + Heap().stats_collector(), + ThreadHeapStatsCollector::kConcurrentSweepingStep); + + if (Heap().AdvanceConcurrentSweep(job)) + has_unswept_pages_.store(false, std::memory_order_relaxed); } void ThreadState::StartIncrementalMarking(BlinkGC::GCReason reason) { @@ -570,13 +580,21 @@ void ThreadState::ScheduleConcurrentAndLazySweep() { return; } - static constexpr size_t kNumberOfSweepingTasks = 1u; - - for (size_t i = 0; i < kNumberOfSweepingTasks; ++i) { - sweeper_scheduler_->ScheduleTask( - WTF::CrossThreadBindOnce(&ThreadState::PerformConcurrentSweep, - WTF::CrossThreadUnretained(this))); - } + has_unswept_pages_ = true; + sweeper_handle_ = base::PostJob( + FROM_HERE, + {base::TaskPriority::USER_VISIBLE, + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, + ConvertToBaseRepeatingCallback( + WTF::CrossThreadBindRepeating(&ThreadState::PerformConcurrentSweep, + WTF::CrossThreadUnretained(this))), + ConvertToBaseRepeatingCallback(WTF::CrossThreadBindRepeating( + [](ThreadState* state) -> size_t { + return state->has_unswept_pages_.load(std::memory_order_relaxed) + ? 1 + : 0; + }, + WTF::CrossThreadUnretained(this)))); } void ThreadState::SchedulePreciseGC() { @@ -628,7 +646,8 @@ void ThreadState::SetGCState(GCState gc_state) { DCHECK(CheckThread()); VERIFY_STATE_TRANSITION(gc_state_ == kNoGCScheduled || gc_state_ == kIncrementalMarkingStepScheduled || - gc_state_ == kIncrementalGCScheduled); + gc_state_ == kIncrementalGCScheduled || + gc_state_ == kIncrementalMarkingStepPaused); break; case kIncrementalMarkingFinalizeScheduled: DCHECK(CheckThread()); @@ -700,7 +719,8 @@ void ThreadState::RunScheduledGC(BlinkGC::StackState stack_state) { CollectAllGarbageForTesting(); break; case kPreciseGCScheduled: - CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, + CollectGarbage(BlinkGC::CollectionType::kMajor, + BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping, BlinkGC::GCReason::kPreciseGC); break; @@ -709,9 +729,11 @@ void ThreadState::RunScheduledGC(BlinkGC::StackState stack_state) { } } -void ThreadState::AtomicPauseMarkPrologue(BlinkGC::StackState stack_state, - BlinkGC::MarkingType marking_type, - BlinkGC::GCReason reason) { +void ThreadState::AtomicPauseMarkPrologue( + BlinkGC::CollectionType collection_type, + BlinkGC::StackState stack_state, + BlinkGC::MarkingType marking_type, + BlinkGC::GCReason reason) { ThreadHeapStatsCollector::EnabledScope mark_prologue_scope( Heap().stats_collector(), ThreadHeapStatsCollector::kAtomicPauseMarkPrologue, "epoch", gc_age_, @@ -719,10 +741,10 @@ void ThreadState::AtomicPauseMarkPrologue(BlinkGC::StackState stack_state, EnterAtomicPause(); EnterNoAllocationScope(); EnterGCForbiddenScope(); - // Compaction needs to be canceled when incremental marking ends with a - // conservative GC. - if (stack_state == BlinkGC::kHeapPointersOnStack) - Heap().Compaction()->Cancel(); + + if (HeapPointersOnStackForced()) { + stack_state = BlinkGC::kHeapPointersOnStack; + } if (IsMarkingInProgress()) { // Incremental marking is already in progress. Only update the state @@ -736,18 +758,26 @@ void ThreadState::AtomicPauseMarkPrologue(BlinkGC::StackState stack_state, available_concurrent_marking_task_ids_.clear(); } #if DCHECK_IS_ON() - MarkingWorklist* worklist = Heap().GetMarkingWorklist(); + MarkingWorklist* marking_worklist = Heap().GetMarkingWorklist(); + WriteBarrierWorklist* write_barrier_worklist = + Heap().GetWriteBarrierWorklist(); for (int concurrent_task = WorklistTaskId::ConcurrentThreadBase; - concurrent_task < worklist->num_tasks(); ++concurrent_task) { - DCHECK(worklist->IsLocalEmpty(concurrent_task)); + concurrent_task < MarkingWorklist::kNumTasks; ++concurrent_task) { + DCHECK(marking_worklist->IsLocalEmpty(concurrent_task)); + DCHECK(write_barrier_worklist->IsLocalEmpty(concurrent_task)); } #endif // DCHECK_IS_ON() + // Compaction needs to be canceled when incremental marking ends with a + // conservative GC. + if (stack_state == BlinkGC::kHeapPointersOnStack) + Heap().Compaction()->Cancel(); DisableIncrementalMarkingBarrier(); current_gc_data_.reason = reason; current_gc_data_.stack_state = stack_state; Heap().stats_collector()->UpdateReason(reason); } else { - MarkPhasePrologue(stack_state, marking_type, reason); + DCHECK(!Heap().Compaction()->IsCompacting()); + MarkPhasePrologue(collection_type, stack_state, marking_type, reason); } if (stack_state == BlinkGC::kNoHeapPointersOnStack) { @@ -776,7 +806,7 @@ void ThreadState::CompleteSweep() { if (!IsSweepingInProgress()) return; - // completeSweep() can be called recursively if finalizers can allocate + // CompleteSweep() can be called recursively if finalizers can allocate // memory and the allocation triggers completeSweep(). This check prevents // the sweeping from being executed recursively. if (SweepForbidden()) @@ -794,6 +824,10 @@ void ThreadState::CompleteSweep() { ThreadHeapStatsCollector::EnabledScope stats_scope( Heap().stats_collector(), ThreadHeapStatsCollector::kCompleteSweep, "forced", IsForcedGC(current_gc_data_.reason)); + // Boost priority of sweeping job to complete ASAP and avoid taking time on + // the main thread. + if (sweeper_handle_) + sweeper_handle_.UpdatePriority(base::TaskPriority::USER_BLOCKING); Heap().CompleteSweep(); SynchronizeAndFinishConcurrentSweeping(); @@ -809,7 +843,8 @@ void ThreadState::SynchronizeAndFinishConcurrentSweeping() { DCHECK(SweepForbidden()); // Wait for concurrent sweepers. - sweeper_scheduler_->CancelAndWait(); + if (sweeper_handle_) + sweeper_handle_.Cancel(); // Concurrent sweepers may perform some work at the last stage (e.g. // sweeping the last page and preparing finalizers). @@ -890,6 +925,8 @@ void UpdateHistograms(const ThreadHeapStatsCollector::Event& event) { UMA_HISTOGRAM_TIMES("BlinkGC.TimeForAtomicPhaseMarking", event.atomic_marking_time()); UMA_HISTOGRAM_TIMES("BlinkGC.TimeForGCCycle", event.gc_cycle_time()); + UMA_HISTOGRAM_TIMES("BlinkGC.TimeForMarkingRoots", + event.roots_marking_time()); UMA_HISTOGRAM_TIMES("BlinkGC.TimeForIncrementalMarking", event.incremental_marking_time()); UMA_HISTOGRAM_TIMES("BlinkGC.TimeForMarking.Foreground", @@ -916,15 +953,14 @@ void UpdateHistograms(const ThreadHeapStatsCollector::Event& event) { "BlinkGC.TimeForGlobalWeakProcessing", event.scope_data[ThreadHeapStatsCollector::kMarkWeakProcessing]); - base::TimeDelta marking_duration = event.marking_time(); - constexpr size_t kMinObjectSizeForReportingThroughput = 1024 * 1024; + base::TimeDelta marking_duration = event.foreground_marking_time(); + constexpr size_t kMinMarkedBytesForReportingThroughput = 1024 * 1024; if (base::TimeTicks::IsHighResolution() && - (event.object_size_in_bytes_before_sweeping > - kMinObjectSizeForReportingThroughput) && + (event.marked_bytes > kMinMarkedBytesForReportingThroughput) && !marking_duration.is_zero()) { DCHECK_GT(marking_duration.InMillisecondsF(), 0.0); const int main_thread_marking_throughput_mb_per_s = static_cast<int>( - static_cast<double>(event.object_size_in_bytes_before_sweeping) / + static_cast<double>(event.marked_bytes) / marking_duration.InMillisecondsF() * 1000 / 1024 / 1024); UMA_HISTOGRAM_COUNTS_100000("BlinkGC.MainThreadMarkingThroughput", main_thread_marking_throughput_mb_per_s); @@ -933,8 +969,10 @@ void UpdateHistograms(const ThreadHeapStatsCollector::Event& event) { DEFINE_STATIC_LOCAL( CustomCountHistogram, object_size_freed_by_heap_compaction, ("BlinkGC.ObjectSizeFreedByHeapCompaction", 1, 4 * 1024 * 1024, 50)); - object_size_freed_by_heap_compaction.Count( - CappedSizeInKB(event.compaction_freed_bytes)); + if (event.compaction_recorded_events) { + object_size_freed_by_heap_compaction.Count( + CappedSizeInKB(event.compaction_freed_bytes)); + } DEFINE_STATIC_LOCAL(CustomCountHistogram, object_size_before_gc_histogram, ("BlinkGC.ObjectSizeBeforeGC", 1, 4 * 1024 * 1024, 50)); @@ -1024,7 +1062,10 @@ void ThreadState::PushRegistersAndVisitStack() { DCHECK(CheckThread()); DCHECK(IsGCForbidden()); DCHECK_EQ(current_gc_data_.stack_state, BlinkGC::kHeapPointersOnStack); + // Visit registers, native stack, and asan fake stack. PushAllRegisters(this, ThreadState::VisitStackAfterPushingRegisters); + // For builds that use safe stack, also visit the unsafe stack. + VisitUnsafeStack(static_cast<MarkingVisitor*>(CurrentVisitor())); } void ThreadState::AddObserver(BlinkGCObserver* observer) { @@ -1077,21 +1118,6 @@ void ThreadState::FreePersistentNode(PersistentRegion* persistent_region, DCHECK(!static_persistents_.Contains(persistent_node)); } -void ThreadState::RegisterPreFinalizer(void* object, - PreFinalizerCallback callback) { -#if DCHECK_IS_ON() - DCHECK(CheckThread()); -#endif - DCHECK(!SweepForbidden()); - - HeapObjectHeader* header = HeapObjectHeader::FromInnerAddress(object); - DCHECK(ordered_pre_finalizers_.end() == - std::find(ordered_pre_finalizers_.begin(), - ordered_pre_finalizers_.end(), - PreFinalizer{header, object, callback})); - ordered_pre_finalizers_.push_back(PreFinalizer{header, object, callback}); -} - void ThreadState::InvokePreFinalizers() { DCHECK(CheckThread()); DCHECK(!SweepForbidden()); @@ -1099,22 +1125,23 @@ void ThreadState::InvokePreFinalizers() { ThreadHeapStatsCollector::Scope stats_scope( Heap().stats_collector(), ThreadHeapStatsCollector::kInvokePreFinalizers); SweepForbiddenScope sweep_forbidden(this); - // Pre finalizers are forbidden from allocating objects. + // Pre finalizers are forbidden from allocating objects NoAllocationScope no_allocation_scope(this); // Call the prefinalizers in the opposite order to their registration. + // + // Deque does not support modification during iteration, so + // copy items first. + // + // The prefinalizer callback wrapper returns |true| when its associated + // object is unreachable garbage and the prefinalizer callback has run. + // The registered prefinalizer entry must then be removed and deleted. Deque<PreFinalizer> remaining_ordered_pre_finalizers; for (auto rit = ordered_pre_finalizers_.rbegin(); rit != ordered_pre_finalizers_.rend(); ++rit) { const PreFinalizer& pre_finalizer = *rit; - // Check if pre-finalizer should be executed. - if (pre_finalizer.header->IsMarked()) { - // Re-queue for checking in next garbage collection. + if (!(pre_finalizer.second)(pre_finalizer.first)) remaining_ordered_pre_finalizers.push_front(pre_finalizer); - } else { - // Execute pre-finalizer. - pre_finalizer.callback(pre_finalizer.object); - } } ordered_pre_finalizers_ = std::move(remaining_ordered_pre_finalizers); @@ -1151,7 +1178,8 @@ void ThreadState::IncrementalMarkingStart(BlinkGC::GCReason reason) { DCHECK(!IsMarkingInProgress()); // Sweeping is performed in driver functions. DCHECK(!IsSweepingInProgress()); - Heap().stats_collector()->NotifyMarkingStarted(reason); + Heap().stats_collector()->NotifyMarkingStarted( + BlinkGC::CollectionType::kMajor, reason); { ThreadHeapStatsCollector::EnabledScope stats_scope( Heap().stats_collector(), @@ -1159,7 +1187,8 @@ void ThreadState::IncrementalMarkingStart(BlinkGC::GCReason reason) { BlinkGC::ToString(reason)); AtomicPauseScope atomic_pause_scope(this); ScriptForbiddenScope script_forbidden_scope; - MarkPhasePrologue(BlinkGC::kNoHeapPointersOnStack, + MarkPhasePrologue(BlinkGC::CollectionType::kMajor, + BlinkGC::kNoHeapPointersOnStack, BlinkGC::kIncrementalAndConcurrentMarking, reason); { MutexLocker persistent_lock(ProcessHeap::CrossThreadPersistentMutex()); @@ -1172,14 +1201,19 @@ void ThreadState::IncrementalMarkingStart(BlinkGC::GCReason reason) { // No active concurrent markers yet, so it is safe to write to // concurrently_marked_bytes_ without a lock. concurrently_marked_bytes_ = 0; - current_gc_data_.visitor->FlushMarkingWorklist(); + current_gc_data_.visitor->FlushMarkingWorklists(); // Check that the marking worklist has enough private segments for all // concurrent marking tasks. const uint8_t max_concurrent_task_id = WorklistTaskId::ConcurrentThreadBase + kNumberOfConcurrentMarkingTasks; - DCHECK_LE(max_concurrent_task_id, - Heap().GetMarkingWorklist()->num_tasks()); + static_assert( + MarkingWorklist::kNumTasks == WriteBarrierWorklist::kNumTasks, + "Marking worklist and write-barrier worklist should be the " + "same size"); + static_assert(max_concurrent_task_id <= MarkingWorklist::kNumTasks, + "Number of concurrent marking tasks should not exceed " + "number of tasks in worlkist"); // Initialize concurrent marking task ids. for (uint8_t i = WorklistTaskId::ConcurrentThreadBase; i < max_concurrent_task_id; ++i) { @@ -1218,7 +1252,7 @@ void ThreadState::IncrementalMarkingStep(BlinkGC::StackState stack_state, if (base::FeatureList::IsEnabled( blink::features::kBlinkHeapConcurrentMarking)) { - complete = complete && ConcurrentMarkingStep(); + complete = ConcurrentMarkingStep() && complete; } if (complete) { @@ -1239,8 +1273,8 @@ void ThreadState::IncrementalMarkingStep(BlinkGC::StackState stack_state, } bool ThreadState::ConcurrentMarkingStep() { - current_gc_data_.visitor->FlushMarkingWorklist(); - if (!Heap().GetMarkingWorklist()->IsGlobalPoolEmpty()) { + current_gc_data_.visitor->FlushMarkingWorklists(); + if (Heap().HasWorkForConcurrentMarking()) { ScheduleConcurrentMarking(); return false; } @@ -1263,11 +1297,13 @@ void ThreadState::IncrementalMarkingFinalize() { // UMA accounting and allow follow up GCs if necessary. DCHECK_EQ(BlinkGC::kIncrementalAndConcurrentMarking, current_gc_data_.marking_type); - CollectGarbage(BlinkGC::kNoHeapPointersOnStack, current_gc_data_.marking_type, + CollectGarbage(current_gc_data_.collection_type, + BlinkGC::kNoHeapPointersOnStack, current_gc_data_.marking_type, BlinkGC::kConcurrentAndLazySweeping, current_gc_data_.reason); } bool ThreadState::FinishIncrementalMarkingIfRunning( + BlinkGC::CollectionType collection_type, BlinkGC::StackState stack_state, BlinkGC::MarkingType marking_type, BlinkGC::SweepingType sweeping_type, @@ -1279,14 +1315,23 @@ bool ThreadState::FinishIncrementalMarkingIfRunning( if (IsUnifiedGCMarkingInProgress()) { unified_heap_controller()->FinalizeTracing(); } else { - RunAtomicPause(stack_state, marking_type, sweeping_type, reason); + RunAtomicPause(collection_type, stack_state, marking_type, sweeping_type, + reason); } return true; } return false; } -void ThreadState::CollectGarbage(BlinkGC::StackState stack_state, +void ThreadState::RestartIncrementalMarkingIfPaused() { + if (GetGCState() != ThreadState::kIncrementalMarkingStepPaused) + return; + SetGCState(ThreadState::kIncrementalMarkingStepScheduled); + incremental_marking_scheduler_->Restart(); +} + +void ThreadState::CollectGarbage(BlinkGC::CollectionType collection_type, + BlinkGC::StackState stack_state, BlinkGC::MarkingType marking_type, BlinkGC::SweepingType sweeping_type, BlinkGC::GCReason reason) { @@ -1302,7 +1347,7 @@ void ThreadState::CollectGarbage(BlinkGC::StackState stack_state, GetIsolate(), RuntimeCallStats::CounterId::kCollectGarbage); const bool was_incremental_marking = FinishIncrementalMarkingIfRunning( - stack_state, marking_type, sweeping_type, reason); + collection_type, stack_state, marking_type, sweeping_type, reason); // We don't want floating garbage for the specific garbage collection types // mentioned below. In this case we will follow up with a regular full @@ -1315,8 +1360,9 @@ void ThreadState::CollectGarbage(BlinkGC::StackState stack_state, if (should_do_full_gc) { CompleteSweep(); SetGCState(kNoGCScheduled); - Heap().stats_collector()->NotifyMarkingStarted(reason); - RunAtomicPause(stack_state, marking_type, sweeping_type, reason); + Heap().stats_collector()->NotifyMarkingStarted(collection_type, reason); + RunAtomicPause(collection_type, stack_state, marking_type, sweeping_type, + reason); } const base::TimeDelta total_collect_garbage_time = @@ -1423,6 +1469,7 @@ class ClearReferencesInDeadObjectsVisitor final } // namespace void ThreadState::AtomicPauseSweepAndCompact( + BlinkGC::CollectionType collection_type, BlinkGC::MarkingType marking_type, BlinkGC::SweepingType sweeping_type) { ThreadHeapStatsCollector::EnabledScope stats( @@ -1434,7 +1481,7 @@ void ThreadState::AtomicPauseSweepAndCompact( DCHECK(InAtomicMarkingPause()); DCHECK(CheckThread()); - Heap().PrepareForSweep(); + Heap().PrepareForSweep(collection_type); // We have to set the GCPhase to Sweeping before calling pre-finalizers // to disallow a GC during the pre-finalizers. @@ -1449,14 +1496,11 @@ void ThreadState::AtomicPauseSweepAndCompact( unified_heap_controller()->IterateTracedGlobalHandles(&visitor); } - // Allocation is allowed during the pre-finalizers and destructors. - // However, they must not mutate an object graph in a way in which - // a dead object gets resurrected. InvokePreFinalizers(); - // Slots filtering requires liveness information which is only present before - // sweeping any arena. - { + if (collection_type == BlinkGC::CollectionType::kMajor) { + // Slots filtering requires liveness information which is only present + // before sweeping any arena. ThreadHeapStatsCollector::Scope stats_scope( Heap().stats_collector(), ThreadHeapStatsCollector::kAtomicPauseCompaction); @@ -1467,14 +1511,14 @@ void ThreadState::AtomicPauseSweepAndCompact( // Last point where all mark bits are present. VerifyMarking(marking_type); - // Any sweep compaction must happen after pre-finalizers, as it will - // finalize dead objects in compactable arenas (e.g., backing stores - // for container objects.) - // - // As per-contract for prefinalizers, those finalizable objects must - // still be accessible when the prefinalizer runs, hence we cannot - // schedule compaction until those have run. - { + if (collection_type == BlinkGC::CollectionType::kMajor) { + // Any sweep compaction must happen after pre-finalizers, as it will + // finalize dead objects in compactable arenas (e.g., backing stores + // for container objects.) + // + // As per-contract for prefinalizers, those finalizable objects must + // still be accessible when the prefinalizer runs, hence we cannot + // schedule compaction until those have run. SweepForbiddenScope scope(this); NoAllocationScope no_allocation_scope(this); Heap().Compact(); @@ -1573,7 +1617,8 @@ void ThreadState::PoisonUnmarkedObjects() { } #endif // ADDRESS_SANITIZER -void ThreadState::RunAtomicPause(BlinkGC::StackState stack_state, +void ThreadState::RunAtomicPause(BlinkGC::CollectionType collection_type, + BlinkGC::StackState stack_state, BlinkGC::MarkingType marking_type, BlinkGC::SweepingType sweeping_type, BlinkGC::GCReason reason) { @@ -1582,11 +1627,11 @@ void ThreadState::RunAtomicPause(BlinkGC::StackState stack_state, TRACE_EVENT1("blink_gc,devtools.timeline", "BlinkGC.AtomicPhase", "forced", IsForcedGC(reason)); - AtomicPauseMarkPrologue(stack_state, marking_type, reason); + AtomicPauseMarkPrologue(collection_type, stack_state, marking_type, reason); AtomicPauseMarkRoots(stack_state, marking_type, reason); AtomicPauseMarkTransitiveClosure(); AtomicPauseMarkEpilogue(marking_type); - AtomicPauseSweepAndCompact(marking_type, sweeping_type); + AtomicPauseSweepAndCompact(collection_type, marking_type, sweeping_type); AtomicPauseEpilogue(); } @@ -1599,19 +1644,30 @@ MarkingVisitor::MarkingMode GetMarkingMode(bool should_compact) { } // namespace -void ThreadState::MarkPhasePrologue(BlinkGC::StackState stack_state, +void ThreadState::MarkPhasePrologue(BlinkGC::CollectionType collection_type, + BlinkGC::StackState stack_state, BlinkGC::MarkingType marking_type, BlinkGC::GCReason reason) { SetGCPhase(GCPhase::kMarking); - Heap().SetupWorklists(); const bool compaction_enabled = Heap().Compaction()->ShouldCompact(stack_state, marking_type, reason); + + Heap().SetupWorklists(compaction_enabled); + if (compaction_enabled) { Heap().Compaction()->Initialize(this); } +#if BUILDFLAG(BLINK_HEAP_YOUNG_GENERATION) + if (collection_type == BlinkGC::CollectionType::kMajor) { + // Unmark heap before doing major collection cycle. + Heap().Unmark(); + } +#endif + current_gc_data_.reason = reason; + current_gc_data_.collection_type = collection_type; current_gc_data_.visitor = IsUnifiedGCMarkingInProgress() ? std::make_unique<UnifiedHeapMarkingVisitor>( @@ -1623,6 +1679,9 @@ void ThreadState::MarkPhasePrologue(BlinkGC::StackState stack_state, } void ThreadState::MarkPhaseVisitRoots() { + ThreadHeapStatsCollector::EnabledScope stats_scope( + Heap().stats_collector(), ThreadHeapStatsCollector::kVisitRoots); + Visitor* visitor = current_gc_data_.visitor.get(); VisitPersistents(visitor); @@ -1644,10 +1703,15 @@ void ThreadState::MarkPhaseVisitRoots() { } if (current_gc_data_.stack_state == BlinkGC::kHeapPointersOnStack) { - ThreadHeapStatsCollector::Scope stats_scope( + ThreadHeapStatsCollector::Scope stack_stats_scope( Heap().stats_collector(), ThreadHeapStatsCollector::kVisitStackRoots); PushRegistersAndVisitStack(); } + + // Visit remembered sets (card tables) for minor collections. + if (current_gc_data_.collection_type == BlinkGC::CollectionType::kMinor) { + VisitRememberedSets(static_cast<MarkingVisitor*>(visitor)); + } } bool ThreadState::MarkPhaseAdvanceMarking(base::TimeTicks deadline) { @@ -1710,8 +1774,8 @@ void ThreadState::CollectAllGarbageForTesting(BlinkGC::StackState stack_state) { // We need to run multiple GCs to collect a chain of persistent handles. size_t previous_live_objects = 0; for (int i = 0; i < 5; ++i) { - CollectGarbage(stack_state, BlinkGC::kAtomicMarking, - BlinkGC::kEagerSweeping, + CollectGarbage(BlinkGC::CollectionType::kMajor, stack_state, + BlinkGC::kAtomicMarking, BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGCForTesting); const size_t live_objects = Heap().stats_collector()->previous().marked_bytes; @@ -1743,7 +1807,8 @@ void ThreadState::PerformConcurrentMark() { VLOG(2) << "[state:" << this << "] [threadid:" << CurrentThread() << "] " << "ConcurrentMark"; ThreadHeapStatsCollector::EnabledConcurrentScope stats_scope( - Heap().stats_collector(), ThreadHeapStatsCollector::kConcurrentMark); + Heap().stats_collector(), + ThreadHeapStatsCollector::kConcurrentMarkingStep); uint8_t task_id; { diff --git a/chromium/third_party/blink/renderer/platform/heap/thread_state.h b/chromium/third_party/blink/renderer/platform/heap/thread_state.h index 42b55d95df5..f41cd050868 100644 --- a/chromium/third_party/blink/renderer/platform/heap/thread_state.h +++ b/chromium/third_party/blink/renderer/platform/heap/thread_state.h @@ -31,10 +31,12 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_THREAD_STATE_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_THREAD_STATE_H_ +#include <atomic> #include <memory> #include "base/macros.h" #include "base/synchronization/lock.h" +#include "base/task/post_job.h" #include "third_party/blink/renderer/platform/heap/atomic_entry_flag.h" #include "third_party/blink/renderer/platform/heap/blink_gc.h" #include "third_party/blink/renderer/platform/heap/threading_traits.h" @@ -62,7 +64,6 @@ class IncrementalMarkingScope; } // namespace incremental_marking_test class CancelableTaskScheduler; -class HeapObjectHeader; class MarkingVisitor; class PersistentNode; class PersistentRegion; @@ -94,13 +95,17 @@ class Visitor; // Member<Bar> bar_; // }; #define USING_PRE_FINALIZER(Class, preFinalizer) \ - private: \ - static void PreFinalizerDispatch(void* object) { \ - reinterpret_cast<Class*>(object)->Class::preFinalizer(); \ + public: \ + static bool InvokePreFinalizer(void* object) { \ + Class* self = reinterpret_cast<Class*>(object); \ + if (ThreadHeap::IsHeapObjectAlive(self)) \ + return false; \ + self->Class::preFinalizer(); \ + return true; \ } \ \ - friend class ThreadState::PreFinalizerRegistration<Class>; \ - ThreadState::PreFinalizerRegistration<Class> prefinalizer_dummy_{this}; \ + private: \ + ThreadState::PrefinalizerRegistration<Class> prefinalizer_dummy_{this}; \ using UsingPreFinalizerMacroNeedsTrailingSemiColon = char class PLATFORM_EXPORT BlinkGCObserver { @@ -130,16 +135,24 @@ class PLATFORM_EXPORT ThreadState final { // Register the pre-finalizer for the |self| object. The class T be using // USING_PRE_FINALIZER() macro. template <typename T> - class PreFinalizerRegistration final { + class PrefinalizerRegistration final { DISALLOW_NEW(); public: - PreFinalizerRegistration(T* self) { - static_assert(sizeof(&T::PreFinalizerDispatch) > 0, + PrefinalizerRegistration(T* self) { + static_assert(sizeof(&T::InvokePreFinalizer) > 0, "USING_PRE_FINALIZER(T) must be defined."); ThreadState* state = ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState(); - state->RegisterPreFinalizer(self, T::PreFinalizerDispatch); +#if DCHECK_IS_ON() + DCHECK(state->CheckThread()); +#endif + DCHECK(!state->SweepForbidden()); + DCHECK(std::find(state->ordered_pre_finalizers_.begin(), + state->ordered_pre_finalizers_.end(), + PreFinalizer(self, T::InvokePreFinalizer)) == + state->ordered_pre_finalizers_.end()); + state->ordered_pre_finalizers_.emplace_back(self, T::InvokePreFinalizer); } }; @@ -169,11 +182,11 @@ class PLATFORM_EXPORT ThreadState final { class AtomicPauseScope; class GCForbiddenScope; class LsanDisabledScope; - class MainThreadGCForbiddenScope; class NoAllocationScope; class StatisticsCollector; struct Statistics; class SweepForbiddenScope; + class HeapPointersOnStackScope; using V8TraceRootsCallback = void (*)(v8::Isolate*, Visitor*); using V8BuildEmbedderGraphCallback = void (*)(v8::Isolate*, @@ -240,7 +253,7 @@ class PLATFORM_EXPORT ThreadState final { } void PerformIdleLazySweep(base::TimeTicks deadline); - void PerformConcurrentSweep(); + void PerformConcurrentSweep(base::JobDelegate*); void SchedulePreciseGC(); void ScheduleForcedGCForTesting(); @@ -279,7 +292,8 @@ class PLATFORM_EXPORT ThreadState final { void EnableCompactionForNextGCForTesting(); - bool FinishIncrementalMarkingIfRunning(BlinkGC::StackState, + bool FinishIncrementalMarkingIfRunning(BlinkGC::CollectionType, + BlinkGC::StackState, BlinkGC::MarkingType, BlinkGC::SweepingType, BlinkGC::GCReason); @@ -287,6 +301,8 @@ class PLATFORM_EXPORT ThreadState final { void EnableIncrementalMarkingBarrier(); void DisableIncrementalMarkingBarrier(); + void RestartIncrementalMarkingIfPaused(); + void CompleteSweep(); // Returns whether it is currently allowed to allocate an object. Mainly used @@ -335,7 +351,8 @@ class PLATFORM_EXPORT ThreadState final { v8::Isolate* GetIsolate() const { return isolate_; } // Use CollectAllGarbageForTesting below for testing! - void CollectGarbage(BlinkGC::StackState, + void CollectGarbage(BlinkGC::CollectionType, + BlinkGC::StackState, BlinkGC::MarkingType, BlinkGC::SweepingType, BlinkGC::GCReason); @@ -354,6 +371,12 @@ class PLATFORM_EXPORT ThreadState final { return &FromObject(object)->Heap() == &Heap(); } + ALWAYS_INLINE bool IsOnStack(Address address) const { + return reinterpret_cast<Address>(start_of_stack_) >= address && + address >= (reinterpret_cast<Address>(reinterpret_cast<uintptr_t>( + WTF::GetCurrentStackPosition()))); + } + int GcAge() const { return gc_age_; } MarkingVisitor* CurrentVisitor() const { @@ -366,17 +389,6 @@ class PLATFORM_EXPORT ThreadState final { private: class IncrementalMarkingScheduler; - using PreFinalizerCallback = void (*)(void*); - struct PreFinalizer { - HeapObjectHeader* header; - void* object; - PreFinalizerCallback callback; - - bool operator==(const PreFinalizer& other) const { - return object == other.object && callback == other.callback; - } - }; - // Duration of one incremental marking step. Should be short enough that it // doesn't cause jank even though it is scheduled as a normal task. static constexpr base::TimeDelta kDefaultIncrementalMarkingStepDuration = @@ -428,7 +440,8 @@ class PLATFORM_EXPORT ThreadState final { // The following methods are used to compose RunAtomicPause. Public users // should use the CollectGarbage entrypoint. Internal users should use these // methods to compose a full garbage collection. - void AtomicPauseMarkPrologue(BlinkGC::StackState, + void AtomicPauseMarkPrologue(BlinkGC::CollectionType, + BlinkGC::StackState, BlinkGC::MarkingType, BlinkGC::GCReason); void AtomicPauseMarkRoots(BlinkGC::StackState, @@ -436,20 +449,23 @@ class PLATFORM_EXPORT ThreadState final { BlinkGC::GCReason); void AtomicPauseMarkTransitiveClosure(); void AtomicPauseMarkEpilogue(BlinkGC::MarkingType); - void AtomicPauseSweepAndCompact(BlinkGC::MarkingType marking_type, + void AtomicPauseSweepAndCompact(BlinkGC::CollectionType, + BlinkGC::MarkingType marking_type, BlinkGC::SweepingType sweeping_type); void AtomicPauseEpilogue(); // RunAtomicPause composes the final atomic pause that finishes a mark-compact // phase of a garbage collection. Depending on SweepingType it may also finish // sweeping or schedule lazy/concurrent sweeping. - void RunAtomicPause(BlinkGC::StackState, + void RunAtomicPause(BlinkGC::CollectionType, + BlinkGC::StackState, BlinkGC::MarkingType, BlinkGC::SweepingType, BlinkGC::GCReason); // The version is needed to be able to start incremental marking. - void MarkPhasePrologue(BlinkGC::StackState, + void MarkPhasePrologue(BlinkGC::CollectionType, + BlinkGC::StackState, BlinkGC::MarkingType, BlinkGC::GCReason); void MarkPhaseEpilogue(BlinkGC::MarkingType); @@ -463,7 +479,9 @@ class PLATFORM_EXPORT ThreadState final { // Visit local thread stack and trace all pointers conservatively. Never call // directly but always call through |PushRegistersAndVisitStack|. + void VisitStackImpl(MarkingVisitor*, Address*, Address*); void VisitStack(MarkingVisitor*, Address*); + void VisitUnsafeStack(MarkingVisitor*); // Visit the asan fake stack frame corresponding to a slot on the real machine // stack if there is one. Never call directly but always call through @@ -482,6 +500,9 @@ class PLATFORM_EXPORT ThreadState final { // Visit all DOM wrappers allocatd on this thread. void VisitDOMWrappers(Visitor*); + // Visit card tables (remembered sets) containing inter-generational pointers. + void VisitRememberedSets(MarkingVisitor*); + // Incremental marking implementation functions. void IncrementalMarkingStartForTesting(); void IncrementalMarkingStart(BlinkGC::GCReason); @@ -513,7 +534,6 @@ class PLATFORM_EXPORT ThreadState final { void SynchronizeAndFinishConcurrentSweeping(); - void RegisterPreFinalizer(void*, PreFinalizerCallback); void InvokePreFinalizers(); // Adds the given observer to the ThreadState's observer list. This doesn't @@ -531,6 +551,13 @@ class PLATFORM_EXPORT ThreadState final { reason == BlinkGC::GCReason::kForcedGCForTesting; } + // Returns whether stack scanning is forced. This is currently only used in + // platform tests where non nested tasks can be run with heap pointers on + // stack. + bool HeapPointersOnStackForced() const { + return heap_pointers_on_stack_forced_; + } + #if defined(ADDRESS_SANITIZER) // Poisons payload of unmarked objects. // @@ -551,6 +578,7 @@ class PLATFORM_EXPORT ThreadState final { bool in_atomic_pause_ = false; bool sweep_forbidden_ = false; + bool heap_pointers_on_stack_forced_ = false; bool incremental_marking_ = false; bool should_optimize_for_load_time_ = false; size_t no_allocation_count_ = 0; @@ -562,6 +590,9 @@ class PLATFORM_EXPORT ThreadState final { BlinkGC::GCReason reason_for_scheduled_gc_ = BlinkGC::GCReason::kForcedGCForTesting; + using PreFinalizerCallback = bool (*)(void*); + using PreFinalizer = std::pair<void*, PreFinalizerCallback>; + // Pre-finalizers are called in the reverse order in which they are // registered by the constructors (including constructors of Mixin objects) // for an object, by processing the ordered_pre_finalizers_ back-to-front. @@ -587,6 +618,7 @@ class PLATFORM_EXPORT ThreadState final { int gc_age_ = 0; struct GCData { + BlinkGC::CollectionType collection_type; BlinkGC::StackState stack_state; BlinkGC::MarkingType marking_type; BlinkGC::GCReason reason; @@ -602,14 +634,15 @@ class PLATFORM_EXPORT ThreadState final { base::Lock concurrent_marker_bootstrapping_lock_; size_t concurrently_marked_bytes_ = 0; - std::unique_ptr<CancelableTaskScheduler> sweeper_scheduler_; + base::JobHandle sweeper_handle_; + std::atomic_bool has_unswept_pages_{false}; friend class BlinkGCObserver; friend class incremental_marking_test::IncrementalMarkingScope; friend class IncrementalMarkingTestDriver; friend class HeapAllocator; template <typename T> - friend class PreFinalizerRegistration; + friend class PrefinalizerRegistration; friend class TestGCScope; friend class TestSupportingGC; friend class ThreadStateSchedulingTest; diff --git a/chromium/third_party/blink/renderer/platform/heap/thread_state_scopes.h b/chromium/third_party/blink/renderer/platform/heap/thread_state_scopes.h index 0dea83fe654..bde958dc4ad 100644 --- a/chromium/third_party/blink/renderer/platform/heap/thread_state_scopes.h +++ b/chromium/third_party/blink/renderer/platform/heap/thread_state_scopes.h @@ -47,19 +47,6 @@ class ThreadState::SweepForbiddenScope final { ThreadState* const state_; }; -class ThreadState::MainThreadGCForbiddenScope final { - STACK_ALLOCATED(); - - public: - MainThreadGCForbiddenScope() : thread_state_(ThreadState::MainThreadState()) { - thread_state_->EnterGCForbiddenScope(); - } - ~MainThreadGCForbiddenScope() { thread_state_->LeaveGCForbiddenScope(); } - - private: - ThreadState* const thread_state_; -}; - class ThreadState::GCForbiddenScope final { STACK_ALLOCATED(); @@ -90,6 +77,23 @@ class ThreadState::AtomicPauseScope final { GCForbiddenScope gc_forbidden_scope; }; +class ThreadState::HeapPointersOnStackScope final { + STACK_ALLOCATED(); + + public: + explicit HeapPointersOnStackScope(ThreadState* state) : state_(state) { + DCHECK(!state_->heap_pointers_on_stack_forced_); + state_->heap_pointers_on_stack_forced_ = true; + } + ~HeapPointersOnStackScope() { + DCHECK(state_->heap_pointers_on_stack_forced_); + state_->heap_pointers_on_stack_forced_ = false; + } + + private: + ThreadState* const state_; +}; + #if defined(LEAK_SANITIZER) class ThreadState::LsanDisabledScope final { STACK_ALLOCATED(); diff --git a/chromium/third_party/blink/renderer/platform/heap/threading_traits.h b/chromium/third_party/blink/renderer/platform/heap/threading_traits.h index 3c5579ff1f7..95fa0c7e20a 100644 --- a/chromium/third_party/blink/renderer/platform/heap/threading_traits.h +++ b/chromium/third_party/blink/renderer/platform/heap/threading_traits.h @@ -32,7 +32,7 @@ enum ThreadAffinity { // Remove them. class Node; class NodeList; -class NodeRareDataBase; +class NodeRareData; template < typename T, @@ -40,7 +40,7 @@ template < WTF::IsSubclass<typename std::remove_const<T>::type, Node>::value || WTF::IsSubclass<typename std::remove_const<T>::type, NodeList>::value || WTF::IsSubclass<typename std::remove_const<T>::type, - NodeRareDataBase>::value> + NodeRareData>::value> struct DefaultThreadingTrait; template <typename T> @@ -56,10 +56,6 @@ struct DefaultThreadingTrait<T, true> { }; class HeapAllocator; -template <typename Table> -class HeapHashTableBacking; -template <typename T, typename Traits> -class HeapVectorBacking; template <typename T> class Member; template <typename T> @@ -118,12 +114,6 @@ struct ThreadingTrait<Vector<T, inlineCapacity, HeapAllocator>> { static const ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity; }; -template <typename T, typename Traits> -struct ThreadingTrait<HeapVectorBacking<T, Traits>> { - STATIC_ONLY(ThreadingTrait); - static const ThreadAffinity kAffinity = ThreadingTrait<T>::Affinity; -}; - template <typename T, size_t inlineCapacity> struct ThreadingTrait<Deque<T, inlineCapacity, HeapAllocator>> { STATIC_ONLY(ThreadingTrait); @@ -136,25 +126,13 @@ struct ThreadingTrait<HashCountedSet<T, U, V, HeapAllocator>> { static const ThreadAffinity kAffinity = ThreadingTrait<T>::kAffinity; }; -template <typename Table> -struct ThreadingTrait<HeapHashTableBacking<Table>> { - STATIC_ONLY(ThreadingTrait); - using Key = typename Table::KeyType; - using Value = typename Table::ValueType; - static const ThreadAffinity kAffinity = - (ThreadingTrait<Key>::Affinity == kMainThreadOnly) && - (ThreadingTrait<Value>::Affinity == kMainThreadOnly) - ? kMainThreadOnly - : kAnyThread; -}; - template <typename T, typename U, typename V, typename W, typename X> class HeapHashMap; template <typename T, typename U, typename V> class HeapHashSet; template <typename T, wtf_size_t inlineCapacity> class HeapVector; -template <typename T, wtf_size_t inlineCapacity> +template <typename T> class HeapDeque; template <typename T, typename U, typename V> class HeapHashCountedSet; @@ -174,9 +152,9 @@ struct ThreadingTrait<HeapVector<T, inlineCapacity>> : public ThreadingTrait<Vector<T, inlineCapacity, HeapAllocator>> { STATIC_ONLY(ThreadingTrait); }; -template <typename T, size_t inlineCapacity> -struct ThreadingTrait<HeapDeque<T, inlineCapacity>> - : public ThreadingTrait<Deque<T, inlineCapacity, HeapAllocator>> { +template <typename T> +struct ThreadingTrait<HeapDeque<T>> + : public ThreadingTrait<Deque<T, 0, HeapAllocator>> { STATIC_ONLY(ThreadingTrait); }; template <typename T, typename U, typename V> diff --git a/chromium/third_party/blink/renderer/platform/heap/trace_traits.h b/chromium/third_party/blink/renderer/platform/heap/trace_traits.h index 583aa7f78d3..4333dbf1edb 100644 --- a/chromium/third_party/blink/renderer/platform/heap/trace_traits.h +++ b/chromium/third_party/blink/renderer/platform/heap/trace_traits.h @@ -21,6 +21,8 @@ namespace blink { +template <typename Table> +class HeapHashTableBacking; template <typename ValueArg, wtf_size_t inlineCapacity> class HeapListHashSetAllocator; template <typename T> @@ -35,11 +37,11 @@ template <typename T> struct AdjustPointerTrait<T, false> { STATIC_ONLY(AdjustPointerTrait); - static TraceDescriptor GetTraceDescriptor(void* self) { + static TraceDescriptor GetTraceDescriptor(const void* self) { return {self, TraceTrait<T>::Trace}; } - static HeapObjectHeader* GetHeapObjectHeader(void* self) { + static HeapObjectHeader* GetHeapObjectHeader(const void* self) { return HeapObjectHeader::FromPayload(self); } }; @@ -48,12 +50,12 @@ template <typename T> struct AdjustPointerTrait<T, true> { STATIC_ONLY(AdjustPointerTrait); - static TraceDescriptor GetTraceDescriptor(void* self) { - return static_cast<T*>(self)->GetTraceDescriptor(); + static TraceDescriptor GetTraceDescriptor(const void* self) { + return static_cast<const T*>(self)->GetTraceDescriptor(); } - static HeapObjectHeader* GetHeapObjectHeader(void* self) { - return static_cast<T*>(self)->GetHeapObjectHeader(); + static HeapObjectHeader* GetHeapObjectHeader(const void* self) { + return static_cast<const T*>(self)->GetHeapObjectHeader(); } }; @@ -63,13 +65,13 @@ struct TraceIfNeeded; template <typename T> struct TraceIfNeeded<T, false> { STATIC_ONLY(TraceIfNeeded); - static void Trace(blink::Visitor*, T&) {} + static void Trace(Visitor*, const T&) {} }; template <typename T> struct TraceIfNeeded<T, true> { STATIC_ONLY(TraceIfNeeded); - static void Trace(blink::Visitor* visitor, T& t) { visitor->Trace(t); } + static void Trace(Visitor* visitor, const T& t) { visitor->Trace(t); } }; template <WTF::WeakHandlingFlag weakness, @@ -87,9 +89,9 @@ struct TraceCollectionIfEnabled<weakness, WTF::kNoWeakHandling> { STATIC_ONLY(TraceCollectionIfEnabled); - static bool IsAlive(T&) { return true; } + static bool IsAlive(const T&) { return true; } - static bool Trace(blink::Visitor*, void*) { + static bool Trace(Visitor*, const void*) { static_assert(!WTF::IsTraceableInCollectionTrait<Traits>::value, "T should not be traced"); return false; @@ -104,9 +106,9 @@ struct TraceCollectionIfEnabled<WTF::kNoWeakHandling, WTF::kWeakHandling> { STATIC_ONLY(TraceCollectionIfEnabled); - static bool Trace(blink::Visitor* visitor, void* t) { + static bool Trace(Visitor* visitor, const void* t) { return WTF::TraceInCollectionTrait<WTF::kNoWeakHandling, T, Traits>::Trace( - visitor, *reinterpret_cast<T*>(t)); + visitor, *reinterpret_cast<const T*>(t)); } }; @@ -118,16 +120,16 @@ template <WTF::WeakHandlingFlag weakness, struct TraceCollectionIfEnabled { STATIC_ONLY(TraceCollectionIfEnabled); - static bool IsAlive(T& traceable) { + static bool IsAlive(const T& traceable) { return WTF::TraceInCollectionTrait<weakness, T, Traits>::IsAlive(traceable); } - static bool Trace(blink::Visitor* visitor, void* t) { + static bool Trace(Visitor* visitor, const void* t) { static_assert(WTF::IsTraceableInCollectionTrait<Traits>::value || weakness == WTF::kWeakHandling, "Traits should be traced"); return WTF::TraceInCollectionTrait<weakness, T, Traits>::Trace( - visitor, *reinterpret_cast<T*>(t)); + visitor, *reinterpret_cast<const T*>(t)); } }; @@ -148,120 +150,32 @@ struct TraceTrait { STATIC_ONLY(TraceTrait); public: - static TraceDescriptor GetTraceDescriptor(void* self) { - return AdjustPointerTrait<T>::GetTraceDescriptor(static_cast<T*>(self)); + static TraceDescriptor GetTraceDescriptor(const void* self) { + return AdjustPointerTrait<T>::GetTraceDescriptor( + static_cast<const T*>(self)); } - static TraceDescriptor GetWeakTraceDescriptor(void* self) { + static TraceDescriptor GetWeakTraceDescriptor(const void* self) { return {self, nullptr}; } - static HeapObjectHeader* GetHeapObjectHeader(void* self) { - return AdjustPointerTrait<T>::GetHeapObjectHeader(static_cast<T*>(self)); + static HeapObjectHeader* GetHeapObjectHeader(const void* self) { + return AdjustPointerTrait<T>::GetHeapObjectHeader( + static_cast<const T*>(self)); } - static void Trace(Visitor*, void* self); + static void Trace(Visitor*, const void* self); }; template <typename T> struct TraceTrait<const T> : public TraceTrait<T> {}; template <typename T> -void TraceTrait<T>::Trace(Visitor* visitor, void* self) { +void TraceTrait<T>::Trace(Visitor* visitor, const void* self) { static_assert(WTF::IsTraceable<T>::value, "T should not be traced"); - static_cast<T*>(self)->Trace(visitor); + static_cast<T*>(const_cast<void*>(self))->Trace(visitor); } -template <typename T, typename Traits> -struct TraceTrait<HeapVectorBacking<T, Traits>> { - STATIC_ONLY(TraceTrait); - using Backing = HeapVectorBacking<T, Traits>; - - public: - static TraceDescriptor GetTraceDescriptor(void* self) { - return {self, TraceTrait<Backing>::Trace}; - } - - static void Trace(blink::Visitor* visitor, void* self) { - static_assert(!WTF::IsWeak<T>::value, - "Weakness is not supported in HeapVector and HeapDeque"); - if (WTF::IsTraceableInCollectionTrait<Traits>::value) { - WTF::TraceInCollectionTrait<WTF::kNoWeakHandling, - HeapVectorBacking<T, Traits>, - void>::Trace(visitor, self); - } - } -}; - -// The trace trait for the heap hashtable backing is used when we find a -// direct pointer to the backing from the conservative stack scanner. This -// normally indicates that there is an ongoing iteration over the table, and so -// we disable weak processing of table entries. When the backing is found -// through the owning hash table we mark differently, in order to do weak -// processing. -template <typename Table> -struct TraceTrait<HeapHashTableBacking<Table>> { - STATIC_ONLY(TraceTrait); - using Backing = HeapHashTableBacking<Table>; - using ValueType = typename Table::ValueTraits::TraitType; - using Traits = typename Table::ValueTraits; - - public: - static TraceDescriptor GetTraceDescriptor(void* self) { - return {self, Trace<WTF::kNoWeakHandling>}; - } - - static TraceDescriptor GetWeakTraceDescriptor(void* self) { - return GetWeakTraceDescriptorImpl<ValueType>::GetWeakTraceDescriptor(self); - } - - template <WTF::WeakHandlingFlag WeakHandling = WTF::kNoWeakHandling> - static void Trace(Visitor* visitor, void* self) { - static_assert(WTF::IsTraceableInCollectionTrait<Traits>::value || - WTF::IsWeak<ValueType>::value, - "T should not be traced"); - WTF::TraceInCollectionTrait<WeakHandling, Backing, void>::Trace(visitor, - self); - } - - private: - template <typename ValueType> - struct GetWeakTraceDescriptorImpl { - static TraceDescriptor GetWeakTraceDescriptor(void* backing) { - return {backing, nullptr}; - } - }; - - template <typename K, typename V> - struct GetWeakTraceDescriptorImpl<WTF::KeyValuePair<K, V>> { - static TraceDescriptor GetWeakTraceDescriptor(void* backing) { - return GetWeakTraceDescriptorKVPImpl<K, V>::GetWeakTraceDescriptor( - backing); - } - - template <typename KeyType, - typename ValueType, - bool ephemeron_semantics = (WTF::IsWeak<KeyType>::value && - !WTF::IsWeak<ValueType>::value && - WTF::IsTraceable<ValueType>::value) || - (WTF::IsWeak<ValueType>::value && - !WTF::IsWeak<KeyType>::value && - WTF::IsTraceable<KeyType>::value)> - struct GetWeakTraceDescriptorKVPImpl { - static TraceDescriptor GetWeakTraceDescriptor(void* backing) { - return {backing, nullptr}; - } - }; - - template <typename KeyType, typename ValueType> - struct GetWeakTraceDescriptorKVPImpl<KeyType, ValueType, true> { - static TraceDescriptor GetWeakTraceDescriptor(void* backing) { - return {backing, Trace<WTF::kWeakHandling>}; - } - }; - }; -}; - // This trace trait for std::pair will null weak members if their referent is // collected. If you have a collection that contain weakness it does not remove // entries from the collection that contain nulled weak members. @@ -270,7 +184,7 @@ struct TraceTrait<std::pair<T, U>> { STATIC_ONLY(TraceTrait); public: - static void Trace(blink::Visitor* visitor, std::pair<T, U>* pair) { + static void Trace(Visitor* visitor, const std::pair<T, U>* pair) { TraceIfNeeded<T>::Trace(visitor, pair->first); TraceIfNeeded<U>::Trace(visitor, pair->second); } @@ -285,7 +199,7 @@ struct TraceTrait<base::Optional<T>> { STATIC_ONLY(TraceTrait); public: - static void Trace(blink::Visitor* visitor, base::Optional<T>* optional) { + static void Trace(Visitor* visitor, const base::Optional<T>* optional) { if (*optional != base::nullopt) { TraceIfNeeded<T>::Trace(visitor, optional->value()); } @@ -304,9 +218,10 @@ struct EphemeronKeyValuePair { using KeyTraits = _KeyTraits; using ValueTraits = _ValueTraits; - EphemeronKeyValuePair(KeyType* k, ValueType* v) : key(k), value(v) {} - KeyType* key; - ValueType* value; + EphemeronKeyValuePair(const KeyType* k, const ValueType* v) + : key(k), value(v) {} + const KeyType* key; + const ValueType* value; }; template <typename _KeyType, @@ -322,7 +237,7 @@ struct EphemeronKeyValuePair<_KeyType, _ValueTraits, _KeyTraits, false> { - EphemeronKeyValuePair(_KeyType* k, _ValueType* v) + EphemeronKeyValuePair(const _KeyType* k, const _ValueType* v) : EphemeronKeyValuePair<_ValueType, _KeyType, _ValueTraits, @@ -342,9 +257,9 @@ namespace WTF { // weak elements. template <typename T, typename Traits> struct TraceInCollectionTrait<kNoWeakHandling, T, Traits> { - static bool IsAlive(T& t) { return true; } + static bool IsAlive(const T& t) { return true; } - static bool Trace(blink::Visitor* visitor, T& t) { + static bool Trace(blink::Visitor* visitor, const T& t) { static_assert(IsTraceableInCollectionTrait<Traits>::value, "T should not be traced"); visitor->Trace(t); @@ -353,142 +268,45 @@ struct TraceInCollectionTrait<kNoWeakHandling, T, Traits> { }; template <typename T, typename Traits> -struct TraceInCollectionTrait<kNoWeakHandling, blink::WeakMember<T>, Traits> { - static bool Trace(blink::Visitor* visitor, blink::WeakMember<T>& t) { - // Extract raw pointer to avoid using the WeakMember<> overload in Visitor. - visitor->Trace(t.Get()); +struct TraceInCollectionTrait<kNoWeakHandling, blink::Member<T>, Traits> { + static bool IsAlive(const blink::Member<T>& t) { return true; } + static bool Trace(blink::Visitor* visitor, const blink::Member<T>& t) { + visitor->TraceMaybeDeleted(t); return false; } }; -// Catch-all for types that have HashTrait support for tracing with weakness. -// Empty to enforce specialization. -template <typename T, typename Traits> -struct TraceInCollectionTrait<kWeakHandling, T, Traits> {}; - template <typename T, typename Traits> -struct TraceInCollectionTrait<kWeakHandling, blink::WeakMember<T>, Traits> { - static bool IsAlive(blink::WeakMember<T>& value) { - return blink::ThreadHeap::IsHeapObjectAlive(value); - } - - static bool Trace(blink::Visitor* visitor, blink::WeakMember<T>& value) { - return !blink::ThreadHeap::IsHeapObjectAlive(value); +struct TraceInCollectionTrait<kWeakHandling, blink::Member<T>, Traits> { + static bool IsAlive(const blink::Member<T>& t) { return true; } + static bool Trace(blink::Visitor* visitor, const blink::Member<T>& t) { + visitor->TraceMaybeDeleted(t); + return false; } }; -// This trace method is used only for on-stack HeapVectors found in -// conservative scanning. On-heap HeapVectors are traced by Vector::trace. template <typename T, typename Traits> -struct TraceInCollectionTrait<kNoWeakHandling, - blink::HeapVectorBacking<T, Traits>, - void> { - static bool Trace(blink::Visitor* visitor, void* self) { - // HeapVectorBacking does not know the exact size of the vector - // and just knows the capacity of the vector. Due to the constraint, - // HeapVectorBacking can support only the following objects: - // - // - An object that has a vtable. In this case, HeapVectorBacking - // traces only slots that are not zeroed out. This is because if - // the object has a vtable, the zeroed slot means that it is - // an unused slot (Remember that the unused slots are guaranteed - // to be zeroed out by VectorUnusedSlotClearer). - // - // - An object that can be initialized with memset. In this case, - // HeapVectorBacking traces all slots including unused slots. - // This is fine because the fact that the object can be initialized - // with memset indicates that it is safe to treat the zerod slot - // as a valid object. - static_assert(!IsTraceableInCollectionTrait<Traits>::value || - Traits::kCanClearUnusedSlotsWithMemset || - std::is_polymorphic<T>::value, - "HeapVectorBacking doesn't support objects that cannot be " - "cleared as unused with memset."); - - // This trace method is instantiated for vectors where - // IsTraceableInCollectionTrait<Traits>::value is false, but the trace - // method should not be called. Thus we cannot static-assert - // IsTraceableInCollectionTrait<Traits>::value but should runtime-assert it. - DCHECK(IsTraceableInCollectionTrait<Traits>::value); - - T* array = reinterpret_cast<T*>(self); - blink::HeapObjectHeader* header = - blink::HeapObjectHeader::FromPayload(self); - // Use the payload size as recorded by the heap to determine how many - // elements to trace. - size_t length = header->PayloadSize() / sizeof(T); -#ifdef ANNOTATE_CONTIGUOUS_CONTAINER - // As commented above, HeapVectorBacking can trace unused slots - // (which are already zeroed out). - ANNOTATE_CHANGE_SIZE(array, length, 0, length); -#endif - if (std::is_polymorphic<T>::value) { - char* pointer = reinterpret_cast<char*>(array); - for (unsigned i = 0; i < length; ++i) { - char* element = pointer + i * sizeof(T); - if (blink::VTableInitialized(element)) - blink::TraceIfNeeded< - T, IsTraceableInCollectionTrait<Traits>::value>::Trace(visitor, - array[i]); - } - } else { - for (size_t i = 0; i < length; ++i) - blink::TraceIfNeeded< - T, IsTraceableInCollectionTrait<Traits>::value>::Trace(visitor, - array[i]); - } +struct TraceInCollectionTrait<kNoWeakHandling, blink::WeakMember<T>, Traits> { + static bool Trace(blink::Visitor* visitor, const blink::WeakMember<T>& t) { + visitor->TraceMaybeDeleted(t); return false; } }; -// This trace method is for tracing a HashTableBacking either through regular -// tracing (via the relevant TraceTraits) or when finding a HashTableBacking -// through conservative stack scanning (which will treat all references in the -// backing strongly). -template <WTF::WeakHandlingFlag WeakHandling, typename Table> -struct TraceHashTableBackingInCollectionTrait { - using Value = typename Table::ValueType; - using Traits = typename Table::ValueTraits; - - static bool Trace(blink::Visitor* visitor, void* self) { - static_assert(IsTraceableInCollectionTrait<Traits>::value || - WTF::IsWeak<Value>::value, - "Table should not be traced"); - Value* array = reinterpret_cast<Value*>(self); - blink::HeapObjectHeader* header = - blink::HeapObjectHeader::FromPayload(self); - // Use the payload size as recorded by the heap to determine how many - // elements to trace. - size_t length = header->PayloadSize() / sizeof(Value); - for (size_t i = 0; i < length; ++i) { - if (!HashTableHelper<Value, typename Table::ExtractorType, - typename Table::KeyTraitsType>:: - IsEmptyOrDeletedBucket(array[i])) { - blink::TraceCollectionIfEnabled<WeakHandling, Value, Traits>::Trace( - visitor, &array[i]); - } - } - return false; - } -}; +// Catch-all for types that have HashTrait support for tracing with weakness. +// Empty to enforce specialization. +template <typename T, typename Traits> +struct TraceInCollectionTrait<kWeakHandling, T, Traits> {}; -template <typename Table> -struct TraceInCollectionTrait<kNoWeakHandling, - blink::HeapHashTableBacking<Table>, - void> { - static bool Trace(blink::Visitor* visitor, void* self) { - return TraceHashTableBackingInCollectionTrait<kNoWeakHandling, - Table>::Trace(visitor, self); +template <typename T, typename Traits> +struct TraceInCollectionTrait<kWeakHandling, blink::WeakMember<T>, Traits> { + static bool IsAlive(const blink::WeakMember<T>& value) { + return blink::ThreadHeap::IsHeapObjectAlive(value); } -}; -template <typename Table> -struct TraceInCollectionTrait<kWeakHandling, - blink::HeapHashTableBacking<Table>, - void> { - static bool Trace(blink::Visitor* visitor, void* self) { - return TraceHashTableBackingInCollectionTrait<kWeakHandling, Table>::Trace( - visitor, self); + static bool Trace(blink::Visitor* visitor, + const blink::WeakMember<T>& value) { + return !blink::ThreadHeap::IsHeapObjectAlive(value); } }; @@ -522,109 +340,46 @@ struct TraceInCollectionTrait< blink::HeapListHashSetAllocator<T, inlineCapacity>>; using Table = HashTable<Node*, U, V, W, X, Y, blink::HeapAllocator>; - static bool Trace(blink::Visitor* visitor, void* self) { - Node** array = reinterpret_cast<Node**>(self); + static bool Trace(blink::Visitor* visitor, const void* self) { + const Node* const* array = reinterpret_cast<const Node* const*>(self); blink::HeapObjectHeader* header = blink::HeapObjectHeader::FromPayload(self); size_t length = header->PayloadSize() / sizeof(Node*); + const bool is_concurrent = visitor->IsConcurrent(); for (size_t i = 0; i < length; ++i) { - if (!HashTableHelper<Node*, typename Table::ExtractorType, - typename Table::KeyTraitsType>:: - IsEmptyOrDeletedBucket(array[i])) { - visitor->Trace(array[i]); + const Node* node; + if (is_concurrent) { + // If tracing concurrently, IsEmptyOrDeletedBucket can cause data + // races. Loading array[i] atomically prevents possible data races. + // array[i] is of type Node* so can directly loaded atomically. + node = AsAtomicPtr(&array[i])->load(std::memory_order_relaxed); + } else { + node = array[i]; + } + if (!HashTableHelper< + const Node*, typename Table::ExtractorType, + typename Table::KeyTraitsType>::IsEmptyOrDeletedBucket(node)) { + visitor->Trace(node); } } return false; } }; -// Key value pairs, as used in HashMap. To disambiguate template choice we have -// to have two versions, first the one with no special weak handling, then the -// one with weak handling. -template <typename Key, typename Value, typename Traits> -struct TraceInCollectionTrait<kNoWeakHandling, - KeyValuePair<Key, Value>, - Traits> { - using EphemeronHelper = - blink::EphemeronKeyValuePair<Key, - Value, - typename Traits::KeyTraits, - typename Traits::ValueTraits>; - - static bool Trace(blink::Visitor* visitor, KeyValuePair<Key, Value>& self) { - if (WTF::IsWeak<Key>::value != WTF::IsWeak<Value>::value) { - // Strongification of Weak/Strong and Strong/Weak. - EphemeronHelper helper(&self.key, &self.value); - visitor->VisitEphemeronKeyValuePair( - helper.key, helper.value, - blink::TraceCollectionIfEnabled< - kNoWeakHandling, typename EphemeronHelper::KeyType, - typename EphemeronHelper::KeyTraits>::Trace, - blink::TraceCollectionIfEnabled< - kNoWeakHandling, typename EphemeronHelper::ValueType, - typename EphemeronHelper::ValueTraits>::Trace); - } else { - // Strongification of Strong/Strong or Weak/Weak. Order does not matter - // here. - blink::TraceCollectionIfEnabled< - kNoWeakHandling, Key, typename Traits::KeyTraits>::Trace(visitor, - &self.key); - blink::TraceCollectionIfEnabled< - kNoWeakHandling, Value, - typename Traits::ValueTraits>::Trace(visitor, &self.value); - } - return false; - } -}; - -template <typename Key, typename Value, typename Traits> -struct TraceInCollectionTrait<kWeakHandling, KeyValuePair<Key, Value>, Traits> { - using EphemeronHelper = - blink::EphemeronKeyValuePair<Key, - Value, - typename Traits::KeyTraits, - typename Traits::ValueTraits>; - - static bool IsAlive(KeyValuePair<Key, Value>& self) { - // Needed for Weak/Weak, Strong/Weak (reverse ephemeron), and Weak/Strong - // (ephemeron). Order of invocation does not matter as tracing weak key or - // value does not have any side effects. - return blink::TraceCollectionIfEnabled< - WeakHandlingTrait<Key>::value, Key, - typename Traits::KeyTraits>::IsAlive(self.key) && - blink::TraceCollectionIfEnabled< - WeakHandlingTrait<Value>::value, Value, - typename Traits::ValueTraits>::IsAlive(self.value); - } - - static bool Trace(blink::Visitor* visitor, KeyValuePair<Key, Value>& self) { - EphemeronHelper helper(&self.key, &self.value); - return visitor->VisitEphemeronKeyValuePair( - helper.key, helper.value, - blink::TraceCollectionIfEnabled< - WeakHandlingTrait<typename EphemeronHelper::KeyType>::value, - typename EphemeronHelper::KeyType, - typename EphemeronHelper::KeyTraits>::Trace, - blink::TraceCollectionIfEnabled< - WeakHandlingTrait<typename EphemeronHelper::ValueType>::value, - typename EphemeronHelper::ValueType, - typename EphemeronHelper::ValueTraits>::Trace); - } -}; - // Nodes used by LinkedHashSet. Again we need two versions to disambiguate the // template. template <typename Value, typename Traits> struct TraceInCollectionTrait<kNoWeakHandling, LinkedHashSetNode<Value>, Traits> { - static bool IsAlive(LinkedHashSetNode<Value>& self) { + static bool IsAlive(const LinkedHashSetNode<Value>& self) { return TraceInCollectionTrait< kNoWeakHandling, Value, typename Traits::ValueTraits>::IsAlive(self.value_); } - static bool Trace(blink::Visitor* visitor, LinkedHashSetNode<Value>& self) { + static bool Trace(blink::Visitor* visitor, + const LinkedHashSetNode<Value>& self) { static_assert( IsTraceableInCollectionTrait<Traits>::value || IsWeak<Value>::value, "T should not be traced"); @@ -636,13 +391,14 @@ struct TraceInCollectionTrait<kNoWeakHandling, template <typename Value, typename Traits> struct TraceInCollectionTrait<kWeakHandling, LinkedHashSetNode<Value>, Traits> { - static bool IsAlive(LinkedHashSetNode<Value>& self) { + static bool IsAlive(const LinkedHashSetNode<Value>& self) { return TraceInCollectionTrait< kWeakHandling, Value, typename Traits::ValueTraits>::IsAlive(self.value_); } - static bool Trace(blink::Visitor* visitor, LinkedHashSetNode<Value>& self) { + static bool Trace(blink::Visitor* visitor, + const LinkedHashSetNode<Value>& self) { return TraceInCollectionTrait< kWeakHandling, Value, typename Traits::ValueTraits>::Trace(visitor, self.value_); @@ -661,7 +417,7 @@ struct TraceInCollectionTrait< ListHashSetNode<Value, blink::HeapListHashSetAllocator<Value, inlineCapacity>>; - static bool Trace(blink::Visitor* visitor, Node* node) { + static bool Trace(blink::Visitor* visitor, const Node* node) { static_assert(!IsWeak<Node>::value, "ListHashSet does not support weakness"); static_assert(IsTraceableInCollectionTrait<Traits>::value, diff --git a/chromium/third_party/blink/renderer/platform/heap/unified_heap_controller.cc b/chromium/third_party/blink/renderer/platform/heap/unified_heap_controller.cc index d618263c29e..a607b88200a 100644 --- a/chromium/third_party/blink/renderer/platform/heap/unified_heap_controller.cc +++ b/chromium/third_party/blink/renderer/platform/heap/unified_heap_controller.cc @@ -46,7 +46,8 @@ void UnifiedHeapController::TracePrologue( // Be conservative here as a new garbage collection gets started right away. thread_state_->FinishIncrementalMarkingIfRunning( - BlinkGC::kHeapPointersOnStack, BlinkGC::kIncrementalAndConcurrentMarking, + BlinkGC::CollectionType::kMajor, BlinkGC::kHeapPointersOnStack, + BlinkGC::kIncrementalAndConcurrentMarking, BlinkGC::kConcurrentAndLazySweeping, thread_state_->current_gc_data_.reason); @@ -65,7 +66,7 @@ void UnifiedHeapController::EnterFinalPause(EmbedderStackState stack_state) { ThreadHeapStatsCollector::BlinkGCInV8Scope nested_scope( thread_state_->Heap().stats_collector()); thread_state_->AtomicPauseMarkPrologue( - ToBlinkGCStackState(stack_state), + BlinkGC::CollectionType::kMajor, ToBlinkGCStackState(stack_state), BlinkGC::kIncrementalAndConcurrentMarking, thread_state_->current_gc_data_.reason); thread_state_->AtomicPauseMarkRoots(ToBlinkGCStackState(stack_state), @@ -82,6 +83,7 @@ void UnifiedHeapController::TraceEpilogue( thread_state_->AtomicPauseMarkEpilogue( BlinkGC::kIncrementalAndConcurrentMarking); thread_state_->AtomicPauseSweepAndCompact( + BlinkGC::CollectionType::kMajor, BlinkGC::kIncrementalAndConcurrentMarking, BlinkGC::kConcurrentAndLazySweeping); @@ -104,9 +106,9 @@ void UnifiedHeapController::RegisterV8References( const bool was_in_atomic_pause = thread_state()->in_atomic_pause(); if (!was_in_atomic_pause) ThreadState::Current()->EnterAtomicPause(); - for (auto& internal_fields : internal_fields_of_potential_wrappers) { - WrapperTypeInfo* wrapper_type_info = - reinterpret_cast<WrapperTypeInfo*>(internal_fields.first); + for (const auto& internal_fields : internal_fields_of_potential_wrappers) { + const WrapperTypeInfo* wrapper_type_info = + reinterpret_cast<const WrapperTypeInfo*>(internal_fields.first); if (wrapper_type_info->gin_embedder != gin::GinEmbedder::kEmbedderBlink) { continue; } @@ -123,7 +125,7 @@ bool UnifiedHeapController::AdvanceTracing(double deadline_in_ms) { ThreadHeapStatsCollector::BlinkGCInV8Scope nested_scope( thread_state_->Heap().stats_collector()); if (!thread_state_->in_atomic_pause()) { - ThreadHeapStatsCollector::Scope advance_tracing_scope( + ThreadHeapStatsCollector::EnabledScope advance_tracing_scope( thread_state_->Heap().stats_collector(), ThreadHeapStatsCollector::kUnifiedMarkingStep); // V8 calls into embedder tracing from its own marking to ensure @@ -133,6 +135,14 @@ bool UnifiedHeapController::AdvanceTracing(double deadline_in_ms) { base::TimeTicks deadline = base::TimeTicks() + base::TimeDelta::FromMillisecondsD(deadline_in_ms); is_tracing_done_ = thread_state_->MarkPhaseAdvanceMarking(deadline); + if (!is_tracing_done_) { + thread_state_->RestartIncrementalMarkingIfPaused(); + } + if (base::FeatureList::IsEnabled( + blink::features::kBlinkHeapConcurrentMarking)) { + is_tracing_done_ = + thread_state_->ConcurrentMarkingStep() && is_tracing_done_; + } return is_tracing_done_; } thread_state_->AtomicPauseMarkTransitiveClosure(); diff --git a/chromium/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.cc b/chromium/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.cc index ce94f8becfd..62f9340f754 100644 --- a/chromium/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.cc +++ b/chromium/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.cc @@ -28,8 +28,6 @@ UnifiedHeapMarkingVisitorBase::UnifiedHeapMarkingVisitorBase( void UnifiedHeapMarkingVisitorBase::VisitImpl( const TraceWrapperV8Reference<v8::Value>& v8_reference) { - if (v8_reference.Get().IsEmpty()) - return; DCHECK(isolate_); if (task_id_ != WorklistTaskId::MutatorThread) { // This is a temporary solution. Pushing directly from concurrent threads @@ -40,6 +38,8 @@ void UnifiedHeapMarkingVisitorBase::VisitImpl( v8_references_worklist_.Push(&v8_reference); return; } + if (v8_reference.Get().IsEmpty()) + return; controller_->RegisterEmbedderReference( v8_reference.template Cast<v8::Data>().Get()); } @@ -69,7 +69,7 @@ void UnifiedHeapMarkingVisitor::WriteBarrier( void UnifiedHeapMarkingVisitor::WriteBarrier( v8::Isolate* isolate, const WrapperTypeInfo* wrapper_type_info, - void* object) { + const void* object) { // |object| here is either ScriptWrappable or CustomWrappable. if (!ThreadState::IsAnyIncrementalMarking()) diff --git a/chromium/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h b/chromium/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h index b396d3c59c1..d2e663116cc 100644 --- a/chromium/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h +++ b/chromium/third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h @@ -49,7 +49,7 @@ class PLATFORM_EXPORT UnifiedHeapMarkingVisitor public: // Write barriers for annotating a write during incremental marking. static void WriteBarrier(const TraceWrapperV8Reference<v8::Value>&); - static void WriteBarrier(v8::Isolate*, const WrapperTypeInfo*, void*); + static void WriteBarrier(v8::Isolate*, const WrapperTypeInfo*, const void*); UnifiedHeapMarkingVisitor(ThreadState*, MarkingMode, v8::Isolate*); ~UnifiedHeapMarkingVisitor() override = default; diff --git a/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.cc b/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.cc new file mode 100644 index 00000000000..a33e2b21ee0 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.cc @@ -0,0 +1,63 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/heap/unsanitized_atomic.h" + +#include "cstdint" + +#include "base/compiler_specific.h" + +#if HAS_FEATURE(address_sanitizer) +#error "Must be built without asan." +#endif + +namespace blink { +namespace internal { + +template <typename T> +void UnsanitizedAtomic<T>::store(T value, std::memory_order order) { + Base::store(value, order); +} + +template <typename T> +T UnsanitizedAtomic<T>::load(std::memory_order order) const { + return Base::load(order); +} + +template <typename T> +bool UnsanitizedAtomic<T>::compare_exchange_strong(T& expected, + T desired, + std::memory_order order) { + return Base::compare_exchange_strong(expected, desired, order); +} + +template <typename T> +bool UnsanitizedAtomic<T>::compare_exchange_strong( + T& expected, + T desired, + std::memory_order succ_order, + std::memory_order fail_order) { + return Base::compare_exchange_strong(expected, desired, succ_order, + fail_order); +} + +template <typename T> +bool UnsanitizedAtomic<T>::compare_exchange_weak(T& expected, + T desired, + std::memory_order order) { + return Base::compare_exchange_weak(expected, desired, order); +} + +template <typename T> +bool UnsanitizedAtomic<T>::compare_exchange_weak(T& expected, + T desired, + std::memory_order succ_order, + std::memory_order fail_order) { + return Base::compare_exchange_weak(expected, desired, succ_order, fail_order); +} + +template class PLATFORM_EXPORT UnsanitizedAtomic<uint16_t>; + +} // namespace internal +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.h b/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.h new file mode 100644 index 00000000000..fc93c9b4dd8 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/heap/unsanitized_atomic.h @@ -0,0 +1,61 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_UNSANITIZED_ATOMIC_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_UNSANITIZED_ATOMIC_H_ + +#include <atomic> + +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +namespace blink { +namespace internal { + +// Simple wrapper for std::atomic<> that makes sure that accesses to underlying +// data are not sanitized. This is needed because the no_sanitize_address +// attribute doesn't propagate down to callees. Must be used with care. +// Currently is only used to access poisoned HeapObjectHeader. For derived or +// user types an explicit instantiation must be added to unsanitized_atomic.cc. +template <typename T> +class PLATFORM_EXPORT UnsanitizedAtomic final : private std::atomic<T> { + using Base = std::atomic<T>; + + public: + void store(T, std::memory_order = std::memory_order_seq_cst); + T load(std::memory_order = std::memory_order_seq_cst) const; + + bool compare_exchange_strong(T&, + T, + std::memory_order = std::memory_order_seq_cst); + bool compare_exchange_strong(T&, T, std::memory_order, std::memory_order); + + bool compare_exchange_weak(T&, + T, + std::memory_order = std::memory_order_seq_cst); + bool compare_exchange_weak(T&, T, std::memory_order, std::memory_order); +}; + +template <typename T> +auto* AsUnsanitizedAtomic(T* ptr) { +#if defined(ADDRESS_SANITIZER) + return reinterpret_cast<UnsanitizedAtomic<T>*>(ptr); +#else + return WTF::AsAtomicPtr(ptr); +#endif +} + +template <typename T> +const auto* AsUnsanitizedAtomic(const T* ptr) { +#if defined(ADDRESS_SANITIZER) + return reinterpret_cast<const UnsanitizedAtomic<T>*>(ptr); +#else + return WTF::AsAtomicPtr(ptr); +#endif +} + +} // namespace internal +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_UNSANITIZED_ATOMIC_H_ diff --git a/chromium/third_party/blink/renderer/platform/heap/visitor.h b/chromium/third_party/blink/renderer/platform/heap/visitor.h index 67e195fe821..d0d6e096441 100644 --- a/chromium/third_party/blink/renderer/platform/heap/visitor.h +++ b/chromium/third_party/blink/renderer/platform/heap/visitor.h @@ -69,19 +69,19 @@ class TraceWrapperV8Reference; // // This interface is safe to use on concurrent threads. All accesses (reads) // from member are done atomically. -template <typename T, void (T::*method)(Visitor*)> +template <typename T, void (T::*method)(Visitor*) const> struct TraceMethodDelegate { STATIC_ONLY(TraceMethodDelegate); - static void Trampoline(Visitor* visitor, void* self) { - (reinterpret_cast<T*>(self)->*method)(visitor); + static void Trampoline(Visitor* visitor, const void* self) { + (reinterpret_cast<T*>(const_cast<void*>(self))->*method)(visitor); } }; template <typename T, void (T::*method)(const WeakCallbackInfo&)> struct WeakCallbackMethodDelegate { STATIC_ONLY(WeakCallbackMethodDelegate); - static void Trampoline(const WeakCallbackInfo& info, void* self) { - (reinterpret_cast<T*>(self)->*method)(info); + static void Trampoline(const WeakCallbackInfo& info, const void* self) { + (reinterpret_cast<T*>(const_cast<void*>(self))->*method)(info); } }; @@ -105,24 +105,47 @@ class PLATFORM_EXPORT Visitor { "T needs to be a garbage collected object"); if (!t) return; - VisitRoot(const_cast<T*>(t), TraceDescriptorFor(t), location); + VisitRoot(t, TraceDescriptorFor(t), location); } template <typename T> void Trace(const Member<T>& t) { - DCHECK(!t.IsHashTableDeletedValueSafe()); - Trace(t.GetSafe()); + const T* value = t.GetSafe(); + + DCHECK(!Member<T>::IsMemberHashTableDeletedValue(value)); + + Trace(value); } - // Fallback methods used only when we need to trace raw pointers of T. This is - // the case when a member is a union where we do not support members. template <typename T> - void Trace(const T* t) { - Trace(const_cast<T*>(t)); + ALWAYS_INLINE void TraceMaybeDeleted(const Member<T>& t) { + const T* value = t.GetSafe(); + + if (Member<T>::IsMemberHashTableDeletedValue(value)) + return; + + Trace<T>(value); } + // TraceMayBeDeleted strongifies WeakMembers. + template <typename T> + ALWAYS_INLINE void TraceMaybeDeleted(const WeakMember<T>& t) { + const T* value = t.GetSafe(); + + if (WeakMember<T>::IsMemberHashTableDeletedValue(value)) + return; + + Trace<T>(value); + } + + // Fallback methods used only when we need to trace raw pointers of T. This is + // the case when a member is a union where we do not support members. template <typename T> void Trace(T* t) { + Trace(const_cast<const T*>(t)); + } + template <typename T> + void Trace(const T* t) { static_assert(sizeof(T), "T must be fully defined"); static_assert(IsGarbageCollectedType<T>::value, "T needs to be a garbage collected object"); @@ -132,40 +155,42 @@ class PLATFORM_EXPORT Visitor { } template <typename T> - void TraceBackingStoreStrongly(T* backing_store, T** backing_store_slot) { + void TraceBackingStoreStrongly(const T* backing_store, + const T* const* backing_store_slot) { static_assert(sizeof(T), "T must be fully defined"); static_assert(IsGarbageCollectedType<T>::value, "T needs to be a garbage collected object"); - VisitBackingStoreStrongly(backing_store, - reinterpret_cast<void**>(backing_store_slot), - TraceDescriptorFor(backing_store)); + VisitBackingStoreStrongly( + backing_store, reinterpret_cast<const void* const*>(backing_store_slot), + TraceDescriptorFor(backing_store)); } template <typename HashTable, typename T> - void TraceBackingStoreWeakly(T* backing_store, - T** backing_store_slot, + void TraceBackingStoreWeakly(const T* backing_store, + const T* const* backing_store_slot, WeakCallback weak_callback, - void* weak_callback_parameter) { + const void* weak_callback_parameter) { static_assert(sizeof(T), "T must be fully defined"); static_assert(IsGarbageCollectedType<T>::value, "T needs to be a garbage collected object"); - VisitBackingStoreWeakly(backing_store, - reinterpret_cast<void**>(backing_store_slot), - TraceDescriptorFor(backing_store), - WeakTraceDescriptorFor(backing_store), - weak_callback, weak_callback_parameter); + VisitBackingStoreWeakly( + backing_store, reinterpret_cast<const void* const*>(backing_store_slot), + TraceDescriptorFor(backing_store), + WeakTraceDescriptorFor(backing_store), weak_callback, + weak_callback_parameter); } template <typename T> - void TraceBackingStoreOnly(T* backing_store, T** backing_store_slot) { + void TraceBackingStoreOnly(const T* backing_store, + const T* const* backing_store_slot) { static_assert(sizeof(T), "T must be fully defined"); static_assert(IsGarbageCollectedType<T>::value, "T needs to be a garbage collected object"); - VisitBackingStoreOnly(backing_store, - reinterpret_cast<void**>(backing_store_slot)); + VisitBackingStoreOnly(backing_store, reinterpret_cast<const void* const*>( + backing_store_slot)); } // WeakMember version of the templated trace method. It doesn't keep @@ -175,19 +200,17 @@ class PLATFORM_EXPORT Visitor { // picking the correct overload, so all these trace methods have to have // the same constness on their argument to allow the type to decide. template <typename T> - void Trace(const WeakMember<T>& const_weak_member) { + void Trace(const WeakMember<T>& weak_member) { static_assert(sizeof(T), "T must be fully defined"); static_assert(IsGarbageCollectedType<T>::value, "T needs to be a garbage collected object"); - WeakMember<T>& weak_member = const_cast<WeakMember<T>&>(const_weak_member); - std::remove_const_t<T>* value = - const_cast<std::remove_const_t<T>*>(weak_member.GetSafe()); + const T* value = weak_member.GetSafe(); if (!value) return; - DCHECK(!weak_member.IsHashTableDeletedValueSafe()); + DCHECK(!WeakMember<T>::IsMemberHashTableDeletedValue(value)); VisitWeak(value, &weak_member, TraceDescriptorFor(value), &HandleWeakCell<T>); } @@ -204,11 +227,11 @@ class PLATFORM_EXPORT Visitor { void Trace(const T& t) { static_assert(sizeof(T), "T must be fully defined"); if (std::is_polymorphic<T>::value) { - intptr_t vtable = *reinterpret_cast<const intptr_t*>(&t); + const intptr_t vtable = *reinterpret_cast<const intptr_t*>(&t); if (!vtable) return; } - TraceTrait<T>::Trace(this, &const_cast<T&>(t)); + TraceTrait<T>::Trace(this, &t); } // Registers an instance method using |RegisterWeakCallback|. See description @@ -216,7 +239,7 @@ class PLATFORM_EXPORT Visitor { template <typename T, void (T::*method)(const WeakCallbackInfo&)> void RegisterWeakCallbackMethod(const T* obj) { RegisterWeakCallback(&WeakCallbackMethodDelegate<T, method>::Trampoline, - const_cast<T*>(obj)); + obj); } // Cross-component tracing interface. @@ -228,32 +251,39 @@ class PLATFORM_EXPORT Visitor { // Dynamic visitor interface. - virtual void VisitRoot(void* t, TraceDescriptor desc, const base::Location&) { + virtual void VisitRoot(const void* t, + TraceDescriptor desc, + const base::Location&) { Visit(t, desc); } // Visits an object through a strong reference. - virtual void Visit(void*, TraceDescriptor) = 0; + virtual void Visit(const void*, TraceDescriptor) = 0; // Visits an object through a weak reference. - virtual void VisitWeak(void*, void*, TraceDescriptor, WeakCallback) = 0; + virtual void VisitWeak(const void*, + const void*, + TraceDescriptor, + WeakCallback) = 0; // Visitors for collection backing stores. - virtual void VisitBackingStoreStrongly(void*, void**, TraceDescriptor) = 0; - virtual void VisitBackingStoreWeakly(void*, - void**, + virtual void VisitBackingStoreStrongly(const void*, + const void* const*, + TraceDescriptor) = 0; + virtual void VisitBackingStoreWeakly(const void*, + const void* const*, TraceDescriptor, TraceDescriptor, WeakCallback, - void*) = 0; - virtual void VisitBackingStoreOnly(void*, void**) = 0; + const void*) = 0; + virtual void VisitBackingStoreOnly(const void*, const void* const*) = 0; // Visits ephemeron pairs which are a combination of weak and strong keys and // values. - using EphemeronTracingCallback = bool (*)(Visitor*, void*); + using EphemeronTracingCallback = bool (*)(Visitor*, const void*); virtual bool VisitEphemeronKeyValuePair( - void* key, - void* value, + const void* key, + const void* value, EphemeronTracingCallback key_trace_callback, EphemeronTracingCallback value_trace_callback) { return true; @@ -265,7 +295,7 @@ class PLATFORM_EXPORT Visitor { // Registers backing store pointers so that they can be moved and properly // updated. - virtual void RegisterBackingStoreCallback(void* backing, + virtual void RegisterBackingStoreCallback(const void* backing, MovingObjectCallback) = 0; // Adds a |callback| that is invoked with |parameter| after liveness has been @@ -280,22 +310,32 @@ class PLATFORM_EXPORT Visitor { // - Clearing out pointers is allowed. // - Removing elements from heap collections is allowed as these collections // are aware of custom weakness and won't resize their backings. - virtual void RegisterWeakCallback(WeakCallback callback, void* parameter) = 0; + virtual void RegisterWeakCallback(WeakCallback callback, + const void* parameter) = 0; + + virtual bool IsConcurrent() const { return false; } + + // TODO(crbug/986235): ConcurrentTracingBailOut is part of a temporary + // bailout mechanism to avoid tracing collections on concurrent threads. + // This method and any usage of it will be removed as soon as making all + // collections cuncurrent-safe is finished. + // The same also applies to NotSafeToConcurrentlyTraceWorklist in heap.h. + virtual bool ConcurrentTracingBailOut(TraceDescriptor desc) { return false; } protected: template <typename T> static inline TraceDescriptor TraceDescriptorFor(const T* traceable) { - return TraceTrait<T>::GetTraceDescriptor(const_cast<T*>(traceable)); + return TraceTrait<T>::GetTraceDescriptor(traceable); } template <typename T> static inline TraceDescriptor WeakTraceDescriptorFor(const T* traceable) { - return TraceTrait<T>::GetWeakTraceDescriptor(const_cast<T*>(traceable)); + return TraceTrait<T>::GetWeakTraceDescriptor(traceable); } private: template <typename T> - static void HandleWeakCell(const WeakCallbackInfo&, void*); + static void HandleWeakCell(const WeakCallbackInfo&, const void*); ThreadState* const state_; }; diff --git a/chromium/third_party/blink/renderer/platform/heap/weakness_marking_test.cc b/chromium/third_party/blink/renderer/platform/heap/weakness_marking_test.cc index 2a8869b664c..86408b94779 100644 --- a/chromium/third_party/blink/renderer/platform/heap/weakness_marking_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/weakness_marking_test.cc @@ -198,6 +198,50 @@ TEST_F(WeaknessMarkingTest, TracableEphemeronIsRegsitered) { EXPECT_NE(old_ephemeron_count, ephemeron_count); } +// TODO(keinakashima): add tests for NewLinkedHashSet after supporting +// WeakMember +TEST_F(WeaknessMarkingTest, SwapIntoAlreadyProcessedWeakSet) { + // Regression test: https://crbug.com/1038623 + // + // Test ensures that an empty weak set that has already been marked sets up + // weakness callbacks. This is important as another backing may be swapped in + // at some point after marking it initially. + using WeakLinkedSet = HeapLinkedHashSet<WeakMember<IntegerObject>>; + Persistent<WeakLinkedSet> holder1(MakeGarbageCollected<WeakLinkedSet>()); + Persistent<WeakLinkedSet> holder2(MakeGarbageCollected<WeakLinkedSet>()); + holder1->insert(MakeGarbageCollected<IntegerObject>(1)); + IncrementalMarkingTestDriver driver(ThreadState::Current()); + driver.Start(); + driver.FinishSteps(); + holder1->Swap(*holder2.Get()); + driver.FinishGC(); +} + +TEST_F(WeaknessMarkingTest, EmptyEphemeronCollection) { + // Tests that an empty ephemeron collection does not crash in the GC when + // processing a non-existent backing store. + using Map = HeapHashMap<Member<IntegerObject>, WeakMember<IntegerObject>>; + Persistent<Map> map = MakeGarbageCollected<Map>(); + TestSupportingGC::PreciselyCollectGarbage(); +} + +TEST_F(WeaknessMarkingTest, ClearWeakHashTableAfterMarking) { + // Regression test: https://crbug.com/1054363 + // + // Test ensures that no marked backing with weak pointers to dead object is + // left behind after marking. The test creates a backing that is floating + // garbage. The marking verifier ensures that all buckets are properly + // deleted. + using Set = HeapHashSet<WeakMember<IntegerObject>>; + Persistent<Set> holder(MakeGarbageCollected<Set>()); + holder->insert(MakeGarbageCollected<IntegerObject>(1)); + IncrementalMarkingTestDriver driver(ThreadState::Current()); + driver.Start(); + driver.FinishSteps(); + holder->clear(); + driver.FinishGC(); +} + } // namespace weakness_marking_test } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/worklist.h b/chromium/third_party/blink/renderer/platform/heap/worklist.h index 7664cacf6be..b3f341077ae 100644 --- a/chromium/third_party/blink/renderer/platform/heap/worklist.h +++ b/chromium/third_party/blink/renderer/platform/heap/worklist.h @@ -11,6 +11,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_WORKLIST_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_WORKLIST_H_ +#include <atomic> #include <cstddef> #include <utility> @@ -30,14 +31,16 @@ namespace blink { // // Work stealing is best effort, i.e., there is no way to inform other tasks // of the need of items. -template <typename _EntryType, int segment_size, int max_tasks = 2> +template <typename _EntryType, int segment_size, int num_tasks = 4> class Worklist { USING_FAST_MALLOC(Worklist); - using WorklistType = Worklist<_EntryType, segment_size, max_tasks>; + using WorklistType = Worklist<_EntryType, segment_size, num_tasks>; public: using EntryType = _EntryType; + static constexpr int kNumTasks = num_tasks; + class View { DISALLOW_NEW(); @@ -79,11 +82,8 @@ class Worklist { static constexpr size_t kSegmentCapacity = segment_size; - Worklist() : Worklist(max_tasks) {} - - explicit Worklist(int num_tasks) : num_tasks_(num_tasks) { - CHECK_LE(num_tasks, max_tasks); - for (int i = 0; i < num_tasks_; i++) { + Worklist() { + for (int i = 0; i < kNumTasks; i++) { private_push_segment(i) = NewSegment(); private_pop_segment(i) = NewSegment(); } @@ -91,7 +91,7 @@ class Worklist { ~Worklist() { CHECK(IsGlobalEmpty()); - for (int i = 0; i < num_tasks_; i++) { + for (int i = 0; i < kNumTasks; i++) { DCHECK(private_push_segment(i)); DCHECK(private_pop_segment(i)); delete private_push_segment(i); @@ -100,7 +100,7 @@ class Worklist { } bool Push(int task_id, EntryType entry) { - DCHECK_LT(task_id, num_tasks_); + DCHECK_LT(task_id, kNumTasks); DCHECK(private_push_segment(task_id)); if (!private_push_segment(task_id)->Push(entry)) { PublishPushSegmentToGlobal(task_id); @@ -112,7 +112,7 @@ class Worklist { } bool Pop(int task_id, EntryType* entry) { - DCHECK_LT(task_id, num_tasks_); + DCHECK_LT(task_id, kNumTasks); DCHECK(private_pop_segment(task_id)); if (!private_pop_segment(task_id)->Pop(entry)) { if (!private_push_segment(task_id)->IsEmpty()) { @@ -137,7 +137,7 @@ class Worklist { bool IsGlobalPoolEmpty() const { return global_pool_.IsEmpty(); } bool IsGlobalEmpty() const { - for (int i = 0; i < num_tasks_; i++) { + for (int i = 0; i < kNumTasks; i++) { if (!IsLocalEmpty(i)) return false; } @@ -153,6 +153,9 @@ class Worklist { private_push_segment(task_id)->Size(); } + // Thread-safe but may return an outdated result. + size_t GlobalPoolSize() const { return global_pool_.Size(); } + size_t LocalPushSegmentSize(int task_id) const { return private_push_segment(task_id)->Size(); } @@ -161,7 +164,7 @@ class Worklist { // // Assumes that no other tasks are running. void Clear() { - for (int i = 0; i < num_tasks_; i++) { + for (int i = 0; i < kNumTasks; i++) { private_pop_segment(i)->Clear(); private_push_segment(i)->Clear(); } @@ -178,7 +181,7 @@ class Worklist { // Assumes that no other tasks are running. template <typename Callback> void Update(Callback callback) { - for (int i = 0; i < num_tasks_; i++) { + for (int i = 0; i < kNumTasks; i++) { private_pop_segment(i)->Update(callback); private_push_segment(i)->Update(callback); } @@ -196,12 +199,9 @@ class Worklist { } void MergeGlobalPool(Worklist* other) { - auto pair = other->global_pool_.Extract(); - global_pool_.MergeList(pair.first, pair.second); + global_pool_.Merge(&other->global_pool_); } - int num_tasks() const { return num_tasks_; } - private: FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentCreate); FRIEND_TEST_ALL_PREFIXES(WorklistTest, SegmentPush); @@ -284,11 +284,14 @@ class Worklist { base::AutoLock guard(lock_); segment->set_next(top_); set_top(segment); + size_.fetch_add(1, std::memory_order_relaxed); } inline bool Pop(Segment** segment) { base::AutoLock guard(lock_); if (top_) { + DCHECK_LT(0U, size_); + size_.fetch_sub(1, std::memory_order_relaxed); *segment = top_; set_top(top_->next()); return true; @@ -301,8 +304,16 @@ class Worklist { reinterpret_cast<const base::subtle::AtomicWord*>(&top_)) == 0; } + inline size_t Size() const { + // It is safe to read |size_| without a lock since this variable is + // atomic, keeping in mind that threads may not immediately see the new + // value when it is updated. + return TS_UNCHECKED_READ(size_).load(std::memory_order_relaxed); + } + void Clear() { base::AutoLock guard(lock_); + size_.store(0, std::memory_order_relaxed); Segment* current = top_; while (current) { Segment* tmp = current; @@ -321,6 +332,8 @@ class Worklist { while (current) { current->Update(callback); if (current->IsEmpty()) { + DCHECK_LT(0U, size_); + size_.fetch_sub(1, std::memory_order_relaxed); if (!prev) { top_ = current->next(); } else { @@ -345,28 +358,28 @@ class Worklist { } } - std::pair<Segment*, Segment*> Extract() { + void Merge(GlobalPool* other) { Segment* top = nullptr; + size_t other_size = 0; { - base::AutoLock guard(lock_); - if (!top_) - return std::make_pair(nullptr, nullptr); - top = top_; - set_top(nullptr); + base::AutoLock guard(other->lock_); + if (!other->top_) + return; + top = other->top_; + other_size = other->size_.load(std::memory_order_relaxed); + other->size_.store(0, std::memory_order_relaxed); + other->set_top(nullptr); } + Segment* end = top; while (end->next()) end = end->next(); - return std::make_pair(top, end); - } - void MergeList(Segment* start, Segment* end) { - if (!start) - return; { base::AutoLock guard(lock_); + size_.fetch_add(other_size, std::memory_order_relaxed); end->set_next(top_); - set_top(start); + set_top(top); } } @@ -378,7 +391,8 @@ class Worklist { } mutable base::Lock lock_; - Segment* top_; + Segment* top_ GUARDED_BY(lock_); + std::atomic<size_t> size_ GUARDED_BY(lock_){0}; }; ALWAYS_INLINE Segment*& private_push_segment(int task_id) { @@ -430,9 +444,8 @@ class Worklist { return new Segment(); } - PrivateSegmentHolder private_segments_[max_tasks]; + PrivateSegmentHolder private_segments_[kNumTasks]; GlobalPool global_pool_; - int num_tasks_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap/worklist_test.cc b/chromium/third_party/blink/renderer/platform/heap/worklist_test.cc index f563a75c199..1030cbab9ac 100644 --- a/chromium/third_party/blink/renderer/platform/heap/worklist_test.cc +++ b/chromium/third_party/blink/renderer/platform/heap/worklist_test.cc @@ -155,13 +155,16 @@ TEST(WorklistTest, LocalPushStaysPrivate) { SomeObject dummy; SomeObject* retrieved = nullptr; EXPECT_TRUE(worklist.IsGlobalEmpty()); + EXPECT_EQ(0U, worklist.GlobalPoolSize()); EXPECT_TRUE(worklist_view1.Push(&dummy)); EXPECT_FALSE(worklist.IsGlobalEmpty()); + EXPECT_EQ(0U, worklist.GlobalPoolSize()); EXPECT_FALSE(worklist_view2.Pop(&retrieved)); EXPECT_EQ(nullptr, retrieved); EXPECT_TRUE(worklist_view1.Pop(&retrieved)); EXPECT_EQ(&dummy, retrieved); EXPECT_TRUE(worklist.IsGlobalEmpty()); + EXPECT_EQ(0U, worklist.GlobalPoolSize()); } TEST(WorklistTest, GlobalUpdateNull) { @@ -175,6 +178,7 @@ TEST(WorklistTest, GlobalUpdateNull) { EXPECT_TRUE(worklist_view.Push(object)); worklist.Update([](SomeObject* object, SomeObject** out) { return false; }); EXPECT_TRUE(worklist.IsGlobalEmpty()); + EXPECT_EQ(0U, worklist.GlobalPoolSize()); } TEST(WorklistTest, GlobalUpdate) { @@ -216,6 +220,7 @@ TEST(WorklistTest, FlushToGlobalPushSegment) { objectA = reinterpret_cast<SomeObject*>(&objectA); EXPECT_TRUE(worklist_view0.Push(objectA)); worklist.FlushToGlobal(0); + EXPECT_EQ(1U, worklist.GlobalPoolSize()); EXPECT_TRUE(worklist_view1.Pop(&object)); } @@ -230,6 +235,7 @@ TEST(WorklistTest, FlushToGlobalPopSegment) { EXPECT_TRUE(worklist_view0.Push(objectA)); EXPECT_TRUE(worklist_view0.Pop(&object)); worklist.FlushToGlobal(0); + EXPECT_EQ(1U, worklist.GlobalPoolSize()); EXPECT_TRUE(worklist_view1.Pop(&object)); } @@ -242,8 +248,10 @@ TEST(WorklistTest, Clear) { EXPECT_TRUE(worklist_view.Push(object)); } EXPECT_TRUE(worklist_view.Push(object)); + EXPECT_EQ(1U, worklist.GlobalPoolSize()); worklist.Clear(); EXPECT_TRUE(worklist.IsGlobalEmpty()); + EXPECT_EQ(0U, worklist.GlobalPoolSize()); } TEST(WorklistTest, SingleSegmentSteal) { @@ -259,6 +267,7 @@ TEST(WorklistTest, SingleSegmentSteal) { EXPECT_TRUE(worklist_view1.Push(nullptr)); EXPECT_TRUE(worklist_view1.Pop(&retrieved)); EXPECT_EQ(nullptr, retrieved); + EXPECT_EQ(1U, worklist.GlobalPoolSize()); // Stealing. for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) { EXPECT_TRUE(worklist_view2.Pop(&retrieved)); @@ -266,6 +275,7 @@ TEST(WorklistTest, SingleSegmentSteal) { EXPECT_FALSE(worklist_view1.Pop(&retrieved)); } EXPECT_TRUE(worklist.IsGlobalEmpty()); + EXPECT_EQ(0U, worklist.GlobalPoolSize()); } TEST(WorklistTest, MultipleSegmentsStolen) { @@ -287,11 +297,13 @@ TEST(WorklistTest, MultipleSegmentsStolen) { EXPECT_TRUE(worklist_view1.Push(&dummy3)); EXPECT_TRUE(worklist_view1.Pop(&retrieved)); EXPECT_EQ(&dummy3, retrieved); + EXPECT_EQ(2U, worklist.GlobalPoolSize()); // Stealing. EXPECT_TRUE(worklist_view2.Pop(&retrieved)); SomeObject* const expect_bag2 = retrieved; EXPECT_TRUE(worklist_view3.Pop(&retrieved)); SomeObject* const expect_bag3 = retrieved; + EXPECT_EQ(0U, worklist.GlobalPoolSize()); EXPECT_NE(expect_bag2, expect_bag3); EXPECT_TRUE(expect_bag2 == &dummy1 || expect_bag2 == &dummy2); EXPECT_TRUE(expect_bag3 == &dummy1 || expect_bag3 == &dummy2); @@ -320,10 +332,13 @@ TEST(WorklistTest, MergeGlobalPool) { EXPECT_TRUE(worklist_view1.Push(nullptr)); EXPECT_TRUE(worklist_view1.Pop(&retrieved)); EXPECT_EQ(nullptr, retrieved); + EXPECT_EQ(1U, worklist1.GlobalPoolSize()); // Merging global pool into a new Worklist. TestWorklist worklist2; TestWorklist::View worklist_view2(&worklist2, 0); + EXPECT_EQ(0U, worklist2.GlobalPoolSize()); worklist2.MergeGlobalPool(&worklist1); + EXPECT_EQ(1U, worklist2.GlobalPoolSize()); EXPECT_FALSE(worklist2.IsGlobalEmpty()); for (size_t i = 0; i < TestWorklist::kSegmentCapacity; i++) { EXPECT_TRUE(worklist_view2.Pop(&retrieved)); diff --git a/chromium/third_party/blink/renderer/platform/heap/write_barrier_perftest.cc b/chromium/third_party/blink/renderer/platform/heap/write_barrier_perftest.cc index 2cc9848ddbd..92bf0523d47 100644 --- a/chromium/third_party/blink/renderer/platform/heap/write_barrier_perftest.cc +++ b/chromium/third_party/blink/renderer/platform/heap/write_barrier_perftest.cc @@ -4,7 +4,7 @@ #include "base/callback.h" #include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_test.h" +#include "testing/perf/perf_result_reporter.h" #include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" #include "third_party/blink/renderer/platform/heap/persistent.h" @@ -14,6 +14,21 @@ class WriteBarrierPerfTest : public TestSupportingGC {}; namespace { +constexpr char kMetricPrefixWriteBarrier[] = "WriteBarrier."; +constexpr char kMetricWritesDuringGcRunsPerS[] = "writes_during_gc"; +constexpr char kMetricWritesOutsideGcRunsPerS[] = "writes_outside_gc"; +constexpr char kMetricRelativeSpeedDifferenceUnitless[] = + "relative_speed_difference"; + +perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) { + perf_test::PerfResultReporter reporter(kMetricPrefixWriteBarrier, story_name); + reporter.RegisterImportantMetric(kMetricWritesDuringGcRunsPerS, "runs/s"); + reporter.RegisterImportantMetric(kMetricWritesOutsideGcRunsPerS, "runs/s"); + reporter.RegisterImportantMetric(kMetricRelativeSpeedDifferenceUnitless, + "unitless"); + return reporter; +} + class PerfDummyObject : public GarbageCollected<PerfDummyObject> { public: PerfDummyObject() = default; @@ -61,19 +76,16 @@ TEST_F(WriteBarrierPerfTest, MemberWritePerformance) { PreciselyCollectGarbage(); // Reporting. - perf_test::PrintResult( - "WriteBarrierPerfTest", " writes during GC", "", - static_cast<double>(kNumElements) / during_gc_duration.InMillisecondsF(), - "writes/ms", true); - perf_test::PrintResult( - "WriteBarrierPerfTest", " writes outside GC", "", - static_cast<double>(kNumElements) / outside_gc_duration.InMillisecondsF(), - "writes/ms", true); - perf_test::PrintResult("WriteBarrierPerfTest", " relative speed difference", - "", - during_gc_duration.InMillisecondsF() / - outside_gc_duration.InMillisecondsF(), - "times", true); + auto reporter = SetUpReporter("member_write_performance"); + reporter.AddResult( + kMetricWritesDuringGcRunsPerS, + static_cast<double>(kNumElements) / during_gc_duration.InSecondsF()); + reporter.AddResult( + kMetricWritesOutsideGcRunsPerS, + static_cast<double>(kNumElements) / outside_gc_duration.InSecondsF()); + reporter.AddResult( + kMetricRelativeSpeedDifferenceUnitless, + during_gc_duration.InSecondsF() / outside_gc_duration.InSecondsF()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/heap_observer_list.h b/chromium/third_party/blink/renderer/platform/heap_observer_list.h new file mode 100644 index 00000000000..1c439a68ae8 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/heap_observer_list.h @@ -0,0 +1,91 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_OBSERVER_LIST_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_OBSERVER_LIST_H_ + +#include "base/auto_reset.h" +#include "third_party/blink/renderer/platform/heap/handle.h" + +namespace blink { + +// A list of observers. Ensures list is not mutated while iterating. Observers +// are not retained. +template <class ObserverType> +class PLATFORM_EXPORT HeapObserverList { + DISALLOW_NEW(); + + public: + // Add an observer to this list. An observer should not be added to the same + // list more than once. + void AddObserver(ObserverType* observer) { + CHECK(iteration_state_ & kAllowingAddition); + DCHECK(!HasObserver(observer)); + observers_.insert(observer); + } + + // Removes the given observer from this list. Does nothing if this observer is + // not in this list. + void RemoveObserver(ObserverType* observer) { + CHECK(iteration_state_ & kAllowingRemoval); + observers_.erase(observer); + } + + // Determine whether a particular observer is in the list. + bool HasObserver(ObserverType* observer) const { + DCHECK(!IsIteratingOverObservers()); + return observers_.Contains(observer); + } + + // Returns true if the list is being iterated over. + bool IsIteratingOverObservers() const { + return iteration_state_ != kNotIterating; + } + + // Removes all the observers from this list. + void Clear() { + CHECK(iteration_state_ & kAllowingRemoval); + observers_.clear(); + } + + // Safely iterate over the registered lifecycle observers. + // + // Adding or removing observers is not allowed during iteration. The callable + // will only be called synchronously inside ForEachObserver(). + // + // Sample usage: + // ForEachObserver([](ObserverType* observer) { + // observer->SomeMethod(); + // }); + template <typename ForEachCallable> + void ForEachObserver(const ForEachCallable& callable) const { + base::AutoReset<IterationState> scope(&iteration_state_, kAllowingNone); + for (ObserverType* observer : observers_) { + callable(observer); + } + } + + void Trace(Visitor* visitor) { visitor->Trace(observers_); } + + private: + using ObserverSet = HeapLinkedHashSet<WeakMember<ObserverType>>; + + // TODO(keishi): Clean up iteration state once transition from + // LifecycleObserver is complete. + enum IterationState { + kAllowingNone = 0, + kAllowingAddition = 1, + kAllowingRemoval = 1 << 1, + kNotIterating = kAllowingAddition | kAllowingRemoval, + }; + + // Iteration state is recorded while iterating the observer set, + // optionally barring add or remove mutations. + mutable IterationState iteration_state_ = kNotIterating; + ObserverSet observers_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_OBSERVER_LIST_H_ diff --git a/chromium/third_party/blink/renderer/platform/heap_observer_list_test.cc b/chromium/third_party/blink/renderer/platform/heap_observer_list_test.cc new file mode 100644 index 00000000000..1797cf0482f --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/heap_observer_list_test.cc @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2013 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "third_party/blink/renderer/platform/heap_observer_list.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/heap/thread_state.h" + +namespace blink { + +class TestingObserver; + +class TestingNotifier final : public GarbageCollected<TestingNotifier> { + public: + TestingNotifier() = default; + + HeapObserverList<TestingObserver>& ObserverList() { return observer_list_; } + + void Trace(Visitor* visitor) { visitor->Trace(observer_list_); } + + private: + HeapObserverList<TestingObserver> observer_list_; +}; + +class TestingObserver final : public GarbageCollected<TestingObserver> { + public: + TestingObserver() = default; + void OnNotification() { count_++; } + int Count() { return count_; } + void Trace(Visitor* visitor) {} + + private: + int count_ = 0; +}; + +void Notify(HeapObserverList<TestingObserver>& observer_list) { + observer_list.ForEachObserver( + [](TestingObserver* observer) { observer->OnNotification(); }); +} + +TEST(HeapObserverListTest, AddRemove) { + Persistent<TestingNotifier> notifier = + MakeGarbageCollected<TestingNotifier>(); + Persistent<TestingObserver> observer = + MakeGarbageCollected<TestingObserver>(); + + notifier->ObserverList().AddObserver(observer); + + EXPECT_EQ(observer->Count(), 0); + Notify(notifier->ObserverList()); + EXPECT_EQ(observer->Count(), 1); + + notifier->ObserverList().RemoveObserver(observer); + + Notify(notifier->ObserverList()); + EXPECT_EQ(observer->Count(), 1); +} + +TEST(HeapObserverListTest, HasObserver) { + Persistent<TestingNotifier> notifier = + MakeGarbageCollected<TestingNotifier>(); + Persistent<TestingObserver> observer = + MakeGarbageCollected<TestingObserver>(); + + EXPECT_FALSE(notifier->ObserverList().HasObserver(observer)); + + notifier->ObserverList().AddObserver(observer); + EXPECT_TRUE(notifier->ObserverList().HasObserver(observer.Get())); + + notifier->ObserverList().RemoveObserver(observer); + EXPECT_FALSE(notifier->ObserverList().HasObserver(observer.Get())); +} + +TEST(HeapObserverListTest, GarbageCollect) { + Persistent<TestingNotifier> notifier = + MakeGarbageCollected<TestingNotifier>(); + Persistent<TestingObserver> observer = + MakeGarbageCollected<TestingObserver>(); + notifier->ObserverList().AddObserver(observer); + + ThreadState::Current()->CollectAllGarbageForTesting(); + Notify(notifier->ObserverList()); + EXPECT_EQ(observer->Count(), 1); + + WeakPersistent<TestingObserver> weak_ref = observer.Get(); + observer = nullptr; + ThreadState::Current()->CollectAllGarbageForTesting(); + EXPECT_EQ(weak_ref.Get(), nullptr); +} + +TEST(HeapObserverListTest, IsIteratingOverObservers) { + Persistent<TestingNotifier> notifier = + MakeGarbageCollected<TestingNotifier>(); + Persistent<TestingObserver> observer = + MakeGarbageCollected<TestingObserver>(); + notifier->ObserverList().AddObserver(observer); + + EXPECT_FALSE(notifier->ObserverList().IsIteratingOverObservers()); + notifier->ObserverList().ForEachObserver([&](TestingObserver* observer) { + EXPECT_TRUE(notifier->ObserverList().IsIteratingOverObservers()); + }); +} + +TEST(HeapObserverListTest, ForEachObserverOrder) { + Persistent<TestingNotifier> notifier = + MakeGarbageCollected<TestingNotifier>(); + Persistent<TestingObserver> observer1 = + MakeGarbageCollected<TestingObserver>(); + Persistent<TestingObserver> observer2 = + MakeGarbageCollected<TestingObserver>(); + + HeapVector<Member<TestingObserver>> seen_observers; + + notifier->ObserverList().AddObserver(observer1); + notifier->ObserverList().AddObserver(observer2); + notifier->ObserverList().ForEachObserver( + [&](TestingObserver* observer) { seen_observers.push_back(observer); }); + + ASSERT_EQ(2u, seen_observers.size()); + EXPECT_EQ(observer1.Get(), seen_observers[0].Get()); + EXPECT_EQ(observer2.Get(), seen_observers[1].Get()); + + seen_observers.clear(); + + notifier->ObserverList().RemoveObserver(observer1); + notifier->ObserverList().AddObserver(observer1); + notifier->ObserverList().ForEachObserver( + [&](TestingObserver* observer) { seen_observers.push_back(observer); }); + + ASSERT_EQ(2u, seen_observers.size()); + EXPECT_EQ(observer2.Get(), seen_observers[0].Get()); + EXPECT_EQ(observer1.Get(), seen_observers[1].Get()); + + seen_observers.clear(); + + notifier->ObserverList().Clear(); + notifier->ObserverList().ForEachObserver( + [&](TestingObserver* observer) { seen_observers.push_back(observer); }); + ASSERT_EQ(0u, seen_observers.size()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/OWNERS b/chromium/third_party/blink/renderer/platform/image-decoders/OWNERS index 9cf2710a1d5..f6310a37e22 100644 --- a/chromium/third_party/blink/renderer/platform/image-decoders/OWNERS +++ b/chromium/third_party/blink/renderer/platform/image-decoders/OWNERS @@ -1,6 +1,6 @@ urvang@chromium.org pkasting@chromium.org noel@chromium.org -scroggo@chromium.org +scroggo@google.com # COMPONENT: Internals>Images>Codecs diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.h b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.h index f99b89349df..b5e12645ec5 100644 --- a/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.h +++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_decoder.h @@ -261,9 +261,9 @@ class PLATFORM_EXPORT ImageDecoder { // method, i.e., IsDecodedSizeAvailable() must return true. virtual cc::ImageHeaderMetadata MakeMetadataForDecodeAcceleration() const; - // This will only differ from size() for ICO (where each frame is a + // This will only differ from Size() for ICO (where each frame is a // different icon) or other formats where different frames are different - // sizes. This does NOT differ from size() for GIF or WebP, since + // sizes. This does NOT differ from Size() for GIF or WebP, since // decoding GIF or WebP composites any smaller frames against previous // frames to create full-size frames. virtual IntSize FrameSizeAtIndex(size_t) const { return Size(); } diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/image_frame_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/image_frame_test.cc index 37abdaf7376..ba51a4ba05b 100644 --- a/chromium/third_party/blink/renderer/platform/image-decoders/image_frame_test.cc +++ b/chromium/third_party/blink/renderer/platform/image-decoders/image_frame_test.cc @@ -26,9 +26,11 @@ class ImageFrameTest : public testing::Test { src_8888 = SkPackARGB32(src_8888_a, src_8888_r, src_8888_g, src_8888_b); dst_8888 = SkPackARGB32(0xA0, 0x60, 0x70, 0x80); +#if SK_PMCOLOR_BYTE_ORDER(B, G, R, A) + pixel_format_n32 = skcms_PixelFormat_BGRA_8888; +#else pixel_format_n32 = skcms_PixelFormat_RGBA_8888; - if (kN32_SkColorType == kRGBA_8888_SkColorType) - pixel_format_n32 = skcms_PixelFormat_BGRA_8888; +#endif skcms_Transform(&src_8888, pixel_format_n32, skcms_AlphaFormat_Unpremul, nullptr, &src_f16, skcms_PixelFormat_RGBA_hhhh, diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc index 7c01efc1273..1a7ed7d26d2 100644 --- a/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc +++ b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc @@ -892,6 +892,8 @@ bool JPEGImageDecoder::SetSize(unsigned width, unsigned height) { } void JPEGImageDecoder::OnSetData(SegmentReader* data) { + if (reader_) + reader_->SetData(data); // TODO(crbug.com/943519): Incremental YUV decoding is not currently // supported. if (IsAllDataReceived()) { @@ -903,8 +905,6 @@ void JPEGImageDecoder::OnSetData(SegmentReader* data) { allow_decode_to_yuv_ &= IsSizeAvailable() && reader_->Info()->out_color_space == JCS_YCbCr; } - if (reader_) - reader_->SetData(data); } void JPEGImageDecoder::SetDecodedSize(unsigned width, unsigned height) { diff --git a/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc index a82cb44e183..a25603243b9 100644 --- a/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc +++ b/chromium/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc @@ -49,10 +49,13 @@ static const size_t kLargeEnoughSize = 1000 * 1000; namespace { -std::unique_ptr<JPEGImageDecoder> CreateJPEGDecoder(size_t max_decoded_bytes) { +std::unique_ptr<JPEGImageDecoder> CreateJPEGDecoder( + size_t max_decoded_bytes, + ImageDecoder::OverrideAllowDecodeToYuv decodeToYUV = + ImageDecoder::OverrideAllowDecodeToYuv::kDeny) { return std::make_unique<JPEGImageDecoder>( ImageDecoder::kAlphaNotPremultiplied, ColorBehavior::TransformToSRGB(), - max_decoded_bytes, ImageDecoder::OverrideAllowDecodeToYuv::kDefault); + max_decoded_bytes, decodeToYUV); } std::unique_ptr<ImageDecoder> CreateJPEGDecoder() { @@ -87,8 +90,8 @@ void ReadYUV(size_t max_decoded_bytes, scoped_refptr<SharedBuffer> data = ReadFile(image_file_path); ASSERT_TRUE(data); - std::unique_ptr<JPEGImageDecoder> decoder = - CreateJPEGDecoder(max_decoded_bytes); + std::unique_ptr<JPEGImageDecoder> decoder = CreateJPEGDecoder( + max_decoded_bytes, ImageDecoder::OverrideAllowDecodeToYuv::kDefault); decoder->SetDecodeToYuvForTesting(true); decoder->SetData(data.get(), true); @@ -274,7 +277,8 @@ TEST(JPEGImageDecoderTest, yuv) { scoped_refptr<SharedBuffer> data = ReadFile(jpeg_file); ASSERT_TRUE(data); - std::unique_ptr<JPEGImageDecoder> decoder = CreateJPEGDecoder(230 * 230 * 4); + std::unique_ptr<JPEGImageDecoder> decoder = CreateJPEGDecoder( + 230 * 230 * 4, ImageDecoder::OverrideAllowDecodeToYuv::kDefault); decoder->SetDecodeToYuvForTesting(true); decoder->SetData(data.get(), true); @@ -552,4 +556,23 @@ INSTANTIATE_TEST_SUITE_P(JPEGImageDecoderTest, ColorSpaceUMATest, ::testing::ValuesIn(kColorSpaceUMATestParams)); +TEST(JPEGImageDecoderTest, PartialDataWithoutSize) { + const char* jpeg_file = "/images/resources/lenna.jpg"; + scoped_refptr<SharedBuffer> full_data = ReadFile(jpeg_file); + ASSERT_TRUE(full_data); + + constexpr size_t kDataLengthWithoutSize = 4; + ASSERT_LT(kDataLengthWithoutSize, full_data->size()); + scoped_refptr<SharedBuffer> partial_data = + SharedBuffer::Create(full_data->Data(), kDataLengthWithoutSize); + + std::unique_ptr<ImageDecoder> decoder = CreateJPEGDecoder(); + decoder->SetData(partial_data.get(), false); + EXPECT_FALSE(decoder->IsSizeAvailable()); + EXPECT_FALSE(decoder->Failed()); + decoder->SetData(full_data.get(), true); + EXPECT_TRUE(decoder->IsSizeAvailable()); + EXPECT_FALSE(decoder->Failed()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.cc b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.cc index 7ecb3436e48..97cf9c92a25 100644 --- a/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.cc +++ b/chromium/third_party/blink/renderer/platform/image-encoders/image_encoder_utils.cc @@ -33,7 +33,7 @@ enum RequestedImageMimeType { ImageEncodingMimeType ImageEncoderUtils::ToEncodingMimeType( const String& mime_type_name, const EncodeReason encode_reason) { - String lowercase_mime_type = mime_type_name.DeprecatedLower(); + String lowercase_mime_type = mime_type_name.LowerASCII(); RequestedImageMimeType requested_mime_type; if (mime_type_name.IsNull()) diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/instance_counters.h b/chromium/third_party/blink/renderer/platform/instrumentation/instance_counters.h index 7ccf114c838..6a3443121a5 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/instance_counters.h +++ b/chromium/third_party/blink/renderer/platform/instrumentation/instance_counters.h @@ -38,33 +38,24 @@ namespace blink { -#define INSTANCE_COUNTERS_LIST(V) \ - V(AudioHandler) \ - V(Document) \ - V(Frame) \ - V(JSEventListener) \ - V(LayoutObject) \ - V(MediaKeySession) \ - V(MediaKeys) \ - V(Node) \ - V(Resource) \ - V(ContextLifecycleStateObserver) \ - V(V8PerContextData) \ - V(WorkerGlobalScope) \ - V(UACSSResource) \ - V(RTCPeerConnection) \ - V(ResourceFetcher) \ - V(AdSubframe) \ - V(DetachedScriptState) \ - V(V8CallInDetachedWindowByNavigation) \ - V(V8CallInDetachedWindowByNavigationAfter10s) \ - V(V8CallInDetachedWindowByNavigationAfter1min) \ - V(V8CallInDetachedWindowByClosing) \ - V(V8CallInDetachedWindowByClosingAfter10s) \ - V(V8CallInDetachedWindowByClosingAfter1min) \ - V(V8CallInDetachedWindowByOtherReason) \ - V(V8CallInDetachedWindowByOtherReasonAfter10s) \ - V(V8CallInDetachedWindowByOtherReasonAfter1min) +#define INSTANCE_COUNTERS_LIST(V) \ + V(AudioHandler) \ + V(Document) \ + V(Frame) \ + V(JSEventListener) \ + V(LayoutObject) \ + V(MediaKeySession) \ + V(MediaKeys) \ + V(Node) \ + V(Resource) \ + V(ContextLifecycleStateObserver) \ + V(V8PerContextData) \ + V(WorkerGlobalScope) \ + V(UACSSResource) \ + V(RTCPeerConnection) \ + V(ResourceFetcher) \ + V(AdSubframe) \ + V(DetachedScriptState) // Atomic counters of the number of instances of objects that exist. // diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.cc b/chromium/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.cc index 074c7272786..8db183664dc 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.cc +++ b/chromium/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.cc @@ -7,6 +7,7 @@ #include "base/allocator/partition_allocator/memory_reclaimer.h" #include "base/feature_list.h" #include "base/system/sys_info.h" +#include "base/trace_event/common/trace_event_common.h" #include "build/build_config.h" #include "third_party/blink/public/common/device_memory/approximated_device_memory.h" #include "third_party/blink/public/common/features.h" @@ -100,7 +101,8 @@ void MemoryPressureListenerRegistry::UnregisterClient( void MemoryPressureListenerRegistry::OnMemoryPressure( WebMemoryPressureLevel level) { - TRACE_EVENT0("blink", "MemoryPressureListenerRegistry::onMemoryPressure"); + TRACE_EVENT1("blink", "MemoryPressureListenerRegistry::onMemoryPressure", + "level", level); CHECK(IsMainThread()); for (auto& client : clients_) client->OnMemoryPressure(level); @@ -131,7 +133,7 @@ void MemoryPressureListenerRegistry::ClearThreadSpecificMemory() { FontGlobalContext::ClearMemory(); } -void MemoryPressureListenerRegistry::Trace(blink::Visitor* visitor) { +void MemoryPressureListenerRegistry::Trace(Visitor* visitor) { visitor->Trace(clients_); } diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h b/chromium/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h index 9bab204ceeb..cc24601b37a 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h +++ b/chromium/third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h @@ -31,7 +31,9 @@ class PLATFORM_EXPORT MemoryPressureListenerRegistry final public: static MemoryPressureListenerRegistry& Instance(); - // Whether the device Blink runs on is a low-end device. + // See: SysUtils::IsLowEndDevice for the full details of what "low-end" means. + // This returns true for devices that can use more extreme tradeoffs for + // performance. Many low memory devices (<=1GB) are not considered low-end. // Can be overridden in web tests via internals. static bool IsLowEndDevice(); @@ -57,7 +59,7 @@ class PLATFORM_EXPORT MemoryPressureListenerRegistry final void OnPurgeMemory(); - void Trace(blink::Visitor*); + void Trace(Visitor*); private: friend class Internals; diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.cc b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.cc index 93e6ae85dba..9991d6abb44 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.cc +++ b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.cc @@ -63,4 +63,13 @@ void DocumentResourceCoordinator::OnNonPersistentNotificationCreated() { service_->OnNonPersistentNotificationCreated(); } +void DocumentResourceCoordinator::SetHadFormInteraction() { + // Only send this signal for the first interaction as it doesn't get cleared + // for the lifetime of the frame and it's inefficient to send this message + // for every keystroke. + if (!had_form_interaction_) + service_->SetHadFormInteraction(); + had_form_interaction_ = true; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.h b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.h index 5d0ee9e07fd..73797f732ca 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.h +++ b/chromium/third_party/blink/renderer/platform/instrumentation/resource_coordinator/document_resource_coordinator.h @@ -34,6 +34,7 @@ class PLATFORM_EXPORT DocumentResourceCoordinator final { // A one way switch that marks a frame as being an adframe. void SetIsAdFrame(); void OnNonPersistentNotificationCreated(); + void SetHadFormInteraction(); private: explicit DocumentResourceCoordinator(const BrowserInterfaceBrokerProxy&); @@ -41,6 +42,8 @@ class PLATFORM_EXPORT DocumentResourceCoordinator final { mojo::Remote<performance_manager::mojom::blink::DocumentCoordinationUnit> service_; + bool had_form_interaction_ = false; + DISALLOW_COPY_AND_ASSIGN(DocumentResourceCoordinator); }; diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.cc index f94bc634a74..37ca752b695 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.cc +++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.cc @@ -6,7 +6,7 @@ namespace blink { -void MemoryCacheDumpClient::Trace(blink::Visitor* visitor) {} +void MemoryCacheDumpClient::Trace(Visitor* visitor) {} MemoryCacheDumpProvider* MemoryCacheDumpProvider::Instance() { DEFINE_STATIC_LOCAL(MemoryCacheDumpProvider, instance, ()); diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h index c99c5dc3e6d..e996a368083 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h +++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h @@ -22,7 +22,7 @@ class PLATFORM_EXPORT MemoryCacheDumpClient : public GarbageCollectedMixin { virtual bool OnMemoryDump(WebMemoryDumpLevelOfDetail, WebProcessMemoryDump*) = 0; - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; }; // This class is wrapper around MemoryCache to take memory snapshots. It dumps diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.cc index 6d75c7dfc91..c36eafdad5d 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.cc +++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.cc @@ -12,113 +12,128 @@ namespace blink { -TracedValue::TracedValue() = default; +TracedValue::TracedValue() + : TracedValue(std::make_unique<base::trace_event::TracedValue>()) {} TracedValue::~TracedValue() = default; void TracedValue::SetInteger(const char* name, int value) { - traced_value_.SetInteger(name, value); + traced_value_->SetInteger(name, value); } void TracedValue::SetIntegerWithCopiedName(const char* name, int value) { - traced_value_.SetIntegerWithCopiedName(name, value); + traced_value_->SetIntegerWithCopiedName(name, value); } void TracedValue::SetDouble(const char* name, double value) { - traced_value_.SetDouble(name, value); + traced_value_->SetDouble(name, value); } void TracedValue::SetDoubleWithCopiedName(const char* name, double value) { - traced_value_.SetDoubleWithCopiedName(name, value); + traced_value_->SetDoubleWithCopiedName(name, value); } void TracedValue::SetBoolean(const char* name, bool value) { - traced_value_.SetBoolean(name, value); + traced_value_->SetBoolean(name, value); } void TracedValue::SetBooleanWithCopiedName(const char* name, bool value) { - traced_value_.SetBooleanWithCopiedName(name, value); + traced_value_->SetBooleanWithCopiedName(name, value); } void TracedValue::SetString(const char* name, const String& value) { StringUTF8Adaptor adaptor(value); - traced_value_.SetString(name, adaptor.AsStringPiece()); + traced_value_->SetString(name, adaptor.AsStringPiece()); } void TracedValue::SetValue(const char* name, TracedValue* value) { - traced_value_.SetValue(name, &value->traced_value_); + traced_value_->SetValue(name, value->traced_value_.get()); } void TracedValue::SetStringWithCopiedName(const char* name, const String& value) { StringUTF8Adaptor adaptor(value); - traced_value_.SetStringWithCopiedName(name, adaptor.AsStringPiece()); + traced_value_->SetStringWithCopiedName(name, adaptor.AsStringPiece()); } void TracedValue::BeginDictionary(const char* name) { - traced_value_.BeginDictionary(name); + traced_value_->BeginDictionary(name); } void TracedValue::BeginDictionaryWithCopiedName(const char* name) { - traced_value_.BeginDictionaryWithCopiedName(name); + traced_value_->BeginDictionaryWithCopiedName(name); } void TracedValue::BeginArray(const char* name) { - traced_value_.BeginArray(name); + traced_value_->BeginArray(name); } void TracedValue::BeginArrayWithCopiedName(const char* name) { - traced_value_.BeginArrayWithCopiedName(name); + traced_value_->BeginArrayWithCopiedName(name); } void TracedValue::EndDictionary() { - traced_value_.EndDictionary(); + traced_value_->EndDictionary(); } void TracedValue::PushInteger(int value) { - traced_value_.AppendInteger(value); + traced_value_->AppendInteger(value); } void TracedValue::PushDouble(double value) { - traced_value_.AppendDouble(value); + traced_value_->AppendDouble(value); } void TracedValue::PushBoolean(bool value) { - traced_value_.AppendBoolean(value); + traced_value_->AppendBoolean(value); } void TracedValue::PushString(const String& value) { StringUTF8Adaptor adaptor(value); - traced_value_.AppendString(adaptor.AsStringPiece()); + traced_value_->AppendString(adaptor.AsStringPiece()); } void TracedValue::BeginArray() { - traced_value_.BeginArray(); + traced_value_->BeginArray(); } void TracedValue::BeginDictionary() { - traced_value_.BeginDictionary(); + traced_value_->BeginDictionary(); } void TracedValue::EndArray() { - traced_value_.EndArray(); -} - -String TracedValue::ToString() const { - return String(traced_value_.ToString().c_str()); + traced_value_->EndArray(); } void TracedValue::AppendAsTraceFormat(std::string* out) const { - traced_value_.AppendAsTraceFormat(out); + traced_value_->AppendAsTraceFormat(out); } bool TracedValue::AppendToProto(ProtoAppender* appender) { - return traced_value_.AppendToProto(appender); + return traced_value_->AppendToProto(appender); } void TracedValue::EstimateTraceMemoryOverhead( base::trace_event::TraceEventMemoryOverhead* overhead) { - traced_value_.EstimateTraceMemoryOverhead(overhead); + traced_value_->EstimateTraceMemoryOverhead(overhead); +} + +TracedValueJSON::TracedValueJSON() + : TracedValue(std::make_unique<base::trace_event::TracedValueJSON>()) {} +TracedValueJSON::~TracedValueJSON() = default; + +String TracedValueJSON::ToJSON() const { + return String( + static_cast<base::trace_event::TracedValueJSON*>(traced_value_.get()) + ->ToJSON() + .c_str()); +} + +String TracedValueJSON::ToFormattedJSON() const { + return String( + static_cast<base::trace_event::TracedValueJSON*>(traced_value_.get()) + ->ToFormattedJSON() + .c_str()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h index d496bf52855..effbd5f6857 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h +++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h @@ -5,6 +5,9 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_TRACED_VALUE_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_TRACED_VALUE_H_ +#include <memory> +#include <string> + #include "base/macros.h" #include "base/trace_event/traced_value.h" #include "third_party/blink/renderer/platform/platform_export.h" @@ -13,7 +16,7 @@ namespace blink { // Thin wrapper around base::trace_event::TracedValue. -class PLATFORM_EXPORT TracedValue final +class PLATFORM_EXPORT TracedValue : public base::trace_event::ConvertableToTraceFormat { public: TracedValue(); @@ -44,21 +47,32 @@ class PLATFORM_EXPORT TracedValue final void BeginArray(); void BeginDictionary(); - String ToString() const; + protected: + explicit TracedValue( + std::unique_ptr<base::trace_event::TracedValue> traced_value) + : traced_value_(std::move(traced_value)) {} + std::unique_ptr<base::trace_event::TracedValue> traced_value_; private: // ConvertableToTraceFormat - void AppendAsTraceFormat(std::string*) const final; bool AppendToProto(ProtoAppender* appender) final; void EstimateTraceMemoryOverhead( base::trace_event::TraceEventMemoryOverhead*) final; - base::trace_event::TracedValue traced_value_; - DISALLOW_COPY_AND_ASSIGN(TracedValue); }; +// Thin wrapper around base::trace_event::TracedValueJSON. +class PLATFORM_EXPORT TracedValueJSON final : public TracedValue { + public: + TracedValueJSON(); + ~TracedValueJSON() final; + + String ToJSON() const; + String ToFormattedJSON() const; +}; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_INSTRUMENTATION_TRACING_TRACED_VALUE_H_ diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value_test.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value_test.cc index ee380f810b6..3544b8cf0e6 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value_test.cc +++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/traced_value_test.cc @@ -4,27 +4,27 @@ #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h" +#include <utility> + #include "base/json/json_reader.h" #include "base/values.h" #include "testing/gtest/include/gtest/gtest.h" -#include <memory> namespace blink { -std::unique_ptr<base::Value> ParseTracedValue( - std::unique_ptr<TracedValue> value) { - base::JSONReader reader; - return reader.ReadDeprecated(value->ToString().Utf8()); +base::Optional<base::Value> ParseTracedValue( + std::unique_ptr<TracedValueJSON> value) { + return base::JSONReader::Read(value->ToJSON().Utf8()); } TEST(TracedValueTest, FlatDictionary) { - auto value = std::make_unique<TracedValue>(); + auto value = std::make_unique<TracedValueJSON>(); value->SetIntegerWithCopiedName("int", 2014); value->SetDoubleWithCopiedName("double", 0.0); value->SetBooleanWithCopiedName("bool", true); value->SetStringWithCopiedName("string", "string"); - std::unique_ptr<base::Value> parsed = ParseTracedValue(std::move(value)); + base::Optional<base::Value> parsed = ParseTracedValue(std::move(value)); base::DictionaryValue* dictionary; ASSERT_TRUE(parsed->GetAsDictionary(&dictionary)); int int_value; @@ -39,7 +39,7 @@ TEST(TracedValueTest, FlatDictionary) { } TEST(TracedValueTest, Hierarchy) { - auto value = std::make_unique<TracedValue>(); + auto value = std::make_unique<TracedValueJSON>(); value->SetIntegerWithCopiedName("i0", 2014); value->BeginDictionaryWithCopiedName("dict1"); value->SetIntegerWithCopiedName("i1", 2014); @@ -59,7 +59,7 @@ TEST(TracedValueTest, Hierarchy) { value->EndArray(); value->SetStringWithCopiedName("s0", "foo"); - std::unique_ptr<base::Value> parsed = ParseTracedValue(std::move(value)); + base::Optional<base::Value> parsed = ParseTracedValue(std::move(value)); base::DictionaryValue* dictionary; ASSERT_TRUE(parsed->GetAsDictionary(&dictionary)); int i0; @@ -99,14 +99,14 @@ TEST(TracedValueTest, Hierarchy) { } TEST(TracedValueTest, Escape) { - auto value = std::make_unique<TracedValue>(); + auto value = std::make_unique<TracedValueJSON>(); value->SetStringWithCopiedName("s0", "value0\\"); value->SetStringWithCopiedName("s1", "value\n1"); value->SetStringWithCopiedName("s2", "\"value2\""); value->SetStringWithCopiedName("s3\\", "value3"); value->SetStringWithCopiedName("\"s4\"", "value4"); - std::unique_ptr<base::Value> parsed = ParseTracedValue(std::move(value)); + base::Optional<base::Value> parsed = ParseTracedValue(std::move(value)); base::DictionaryValue* dictionary; ASSERT_TRUE(parsed->GetAsDictionary(&dictionary)); std::string s0; @@ -127,7 +127,7 @@ TEST(TracedValueTest, Escape) { } TEST(TracedValueTest, NonCopiedNames) { - auto value = std::make_unique<TracedValue>(); + auto value = std::make_unique<TracedValueJSON>(); const char* int_str = "int"; const char* double_str = "double"; const char* bool_str = "bool"; @@ -142,7 +142,7 @@ TEST(TracedValueTest, NonCopiedNames) { value->PushInteger(2); value->EndArray(); - std::unique_ptr<base::Value> parsed = ParseTracedValue(std::move(value)); + base::Optional<base::Value> parsed = ParseTracedValue(std::move(value)); base::DictionaryValue* dictionary; ASSERT_TRUE(parsed->GetAsDictionary(&dictionary)); int int_value; diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.cc b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.cc index e1e24170879..54c702cb6e2 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.cc +++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.cc @@ -14,8 +14,6 @@ WebMemoryAllocatorDump::WebMemoryAllocatorDump( : memory_allocator_dump_(memory_allocator_dump), guid_(memory_allocator_dump->guid().ToUint64()) {} -WebMemoryAllocatorDump::~WebMemoryAllocatorDump() = default; - void WebMemoryAllocatorDump::AddScalar(const char* name, const char* units, uint64_t value) { diff --git a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h index a9af22ee206..75180ada990 100644 --- a/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h +++ b/chromium/third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h @@ -30,7 +30,6 @@ class PLATFORM_EXPORT WebMemoryAllocatorDump final { public: explicit WebMemoryAllocatorDump( base::trace_event::MemoryAllocatorDump* memory_allocator_dump); - ~WebMemoryAllocatorDump(); // Adds a scalar attribute to the dump. // Arguments: diff --git a/chromium/third_party/blink/renderer/platform/lifecycle_context_test.cc b/chromium/third_party/blink/renderer/platform/lifecycle_context_test.cc deleted file mode 100644 index 94a9886d12b..00000000000 --- a/chromium/third_party/blink/renderer/platform/lifecycle_context_test.cc +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2013 Google Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "base/test/scoped_feature_list.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/common/features.h" -#include "third_party/blink/renderer/platform/heap/handle.h" -#include "third_party/blink/renderer/platform/heap/heap.h" -#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" -#include "third_party/blink/renderer/platform/heap/persistent.h" -#include "third_party/blink/renderer/platform/heap/thread_state.h" -#include "third_party/blink/renderer/platform/lifecycle_notifier.h" -#include "third_party/blink/renderer/platform/lifecycle_observer.h" -#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" - -namespace blink { - -class TestingObserver; - -class DummyContext final - : public GarbageCollected<DummyContext>, - public LifecycleNotifier<DummyContext, TestingObserver> { - USING_GARBAGE_COLLECTED_MIXIN(DummyContext); - - public: - void Trace(blink::Visitor* visitor) override { - LifecycleNotifier<DummyContext, TestingObserver>::Trace(visitor); - } - - // Make the protected method public for testing. - using LifecycleNotifier<DummyContext, TestingObserver>::ForEachObserver; -}; - -class TestingObserver final - : public GarbageCollected<TestingObserver>, - public LifecycleObserver<DummyContext, TestingObserver> { - USING_GARBAGE_COLLECTED_MIXIN(TestingObserver); - - public: - explicit TestingObserver(DummyContext* context) - : LifecycleObserver(context), context_destroyed_called_(false) {} - - void ContextDestroyed(DummyContext* destroyed_context) { - if (observer_to_remove_on_destruct_) { - destroyed_context->RemoveObserver(observer_to_remove_on_destruct_); - observer_to_remove_on_destruct_.Clear(); - } - context_destroyed_called_ = true; - } - - void Trace(blink::Visitor* visitor) override { - visitor->Trace(observer_to_remove_on_destruct_); - LifecycleObserver::Trace(visitor); - } - - void Unobserve() { SetContext(nullptr); } - - void SetObserverToRemoveAndDestroy( - TestingObserver* observer_to_remove_on_destruct) { - DCHECK(!observer_to_remove_on_destruct_); - observer_to_remove_on_destruct_ = observer_to_remove_on_destruct; - } - - TestingObserver* InnerObserver() const { - return observer_to_remove_on_destruct_; - } - bool ContextDestroyedCalled() const { return context_destroyed_called_; } - - private: - Member<TestingObserver> observer_to_remove_on_destruct_; - bool context_destroyed_called_; -}; - -TEST(LifecycleContextTest, ShouldObserveContextDestroyed) { - auto* context = MakeGarbageCollected<DummyContext>(); - Persistent<TestingObserver> observer = - MakeGarbageCollected<TestingObserver>(context); - - EXPECT_EQ(observer->LifecycleContext(), context); - EXPECT_FALSE(observer->ContextDestroyedCalled()); - context->NotifyContextDestroyed(); - context = nullptr; - ThreadState::Current()->CollectAllGarbageForTesting(); - EXPECT_EQ(observer->LifecycleContext(), static_cast<DummyContext*>(nullptr)); - EXPECT_TRUE(observer->ContextDestroyedCalled()); -} - -TEST(LifecycleContextTest, ShouldNotObserveContextDestroyedIfUnobserve) { - auto* context = MakeGarbageCollected<DummyContext>(); - Persistent<TestingObserver> observer = - MakeGarbageCollected<TestingObserver>(context); - observer->Unobserve(); - context->NotifyContextDestroyed(); - context = nullptr; - ThreadState::Current()->CollectAllGarbageForTesting(); - EXPECT_EQ(observer->LifecycleContext(), static_cast<DummyContext*>(nullptr)); - EXPECT_FALSE(observer->ContextDestroyedCalled()); -} - -TEST(LifecycleContextTest, ObserverRemovedDuringNotifyDestroyed) { - auto* context = MakeGarbageCollected<DummyContext>(); - Persistent<TestingObserver> observer = - MakeGarbageCollected<TestingObserver>(context); - auto* inner_observer = MakeGarbageCollected<TestingObserver>(context); - // Attach the observer to the other. When 'observer' is notified - // of destruction, it will remove & destroy 'innerObserver'. - observer->SetObserverToRemoveAndDestroy(inner_observer); - - EXPECT_EQ(observer->LifecycleContext(), context); - EXPECT_EQ(observer->InnerObserver()->LifecycleContext(), context); - EXPECT_FALSE(observer->ContextDestroyedCalled()); - EXPECT_FALSE(observer->InnerObserver()->ContextDestroyedCalled()); - - context->NotifyContextDestroyed(); - EXPECT_EQ(observer->InnerObserver(), nullptr); - context = nullptr; - ThreadState::Current()->CollectAllGarbageForTesting(); - EXPECT_EQ(observer->LifecycleContext(), static_cast<DummyContext*>(nullptr)); - EXPECT_TRUE(observer->ContextDestroyedCalled()); -} - -// This is a regression test for http://crbug.com/854639. -TEST(LifecycleContextTest, ShouldNotHitCFICheckOnIncrementalMarking) { - base::test::ScopedFeatureList scoped_feature_list; - // Disable concurrent marking and concurrent sweeping as worker_pool task - // environment is not set. - scoped_feature_list.InitWithFeatures( - {blink::features::kBlinkHeapIncrementalMarking}, - {blink::features::kBlinkHeapConcurrentMarking, - blink::features::kBlinkHeapConcurrentSweeping}); - IncrementalMarkingTestDriver driver(ThreadState::Current()); - driver.Start(); - - auto* context = MakeGarbageCollected<DummyContext>(); - - // This should not cause a CFI check failure. - Persistent<TestingObserver> observer = - MakeGarbageCollected<TestingObserver>(context); - - EXPECT_FALSE(observer->ContextDestroyedCalled()); - context->NotifyContextDestroyed(); - EXPECT_TRUE(observer->ContextDestroyedCalled()); - context = nullptr; - - driver.FinishGC(); -} - -TEST(LifecycleContextTest, ForEachObserver) { - Persistent<DummyContext> context = MakeGarbageCollected<DummyContext>(); - Persistent<TestingObserver> observer = - MakeGarbageCollected<TestingObserver>(context); - - HeapVector<Member<TestingObserver>> seen_observers; - context->ForEachObserver( - [&](TestingObserver* observer) { seen_observers.push_back(observer); }); - - ASSERT_EQ(1u, seen_observers.size()); - EXPECT_EQ(observer.Get(), seen_observers[0].Get()); - - seen_observers.clear(); - observer.Clear(); - ThreadState::Current()->CollectAllGarbageForTesting(); - - context->ForEachObserver( - [&](TestingObserver* observer) { seen_observers.push_back(observer); }); - ASSERT_EQ(0u, seen_observers.size()); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/lifecycle_notifier.h b/chromium/third_party/blink/renderer/platform/lifecycle_notifier.h deleted file mode 100644 index d105761b4b8..00000000000 --- a/chromium/third_party/blink/renderer/platform/lifecycle_notifier.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * Copyright (C) 2013 Google Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_NOTIFIER_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_NOTIFIER_H_ - -#include "base/auto_reset.h" -#include "third_party/blink/renderer/platform/heap/handle.h" -#include "third_party/blink/renderer/platform/heap/heap_allocator.h" - -namespace blink { - -class LifecycleObserverBase; - -template <typename T, typename Observer> -class LifecycleNotifier : public GarbageCollectedMixin { - public: - virtual ~LifecycleNotifier(); - - void AddObserver(LifecycleObserverBase*); - void RemoveObserver(LifecycleObserverBase*); - - // NotifyContextDestroyed() should be explicitly dispatched from an - // observed context to detach its observers and, if the observer kind - // requires it, notify each observer by invoking ContextDestroyed(). - // - // When ContextDestroyed() is called, it is supplied the context as - // an argument, but the observer's LifecycleContext() is still valid - // and safe to use while handling the notification. - virtual void NotifyContextDestroyed(); - - void Trace(blink::Visitor* visitor) override { visitor->Trace(observers_); } - - bool IsIteratingOverObservers() const { - return iteration_state_ != kNotIterating; - } - - protected: - LifecycleNotifier() : iteration_state_(kNotIterating) {} - - T* Context() { return static_cast<T*>(this); } - - // Safely iterate over the registered lifecycle observers. - // - // Adding or removing observers is not allowed during iteration. The callable - // will only be called synchronously inside ForEachObserver(). - // - // Sample usage: - // ForEachObserver([](ObserverType* observer) { - // observer->SomeMethod(); - // }); - template <typename ForEachCallable> - void ForEachObserver(const ForEachCallable& callable) const { - base::AutoReset<IterationState> scope(&iteration_state_, kAllowingNone); - for (LifecycleObserverBase* observer_base : observers_) { - Observer* observer = static_cast<Observer*>(observer_base); - callable(observer); - } - } - - private: - using ObserverSet = HeapLinkedHashSet<WeakMember<LifecycleObserverBase>>; - - enum IterationState { - kAllowingNone = 0, - kAllowingAddition = 1, - kAllowingRemoval = 2, - kNotIterating = kAllowingAddition | kAllowingRemoval, - }; - - // Iteration state is recorded while iterating the observer set, - // optionally barring add or remove mutations. - mutable IterationState iteration_state_; - ObserverSet observers_; -}; - -template <typename T, typename Observer> -inline LifecycleNotifier<T, Observer>::~LifecycleNotifier() { - // FIXME: Enable the following ASSERT. Also see a FIXME in - // Document::detachLayoutTree(). - // DCHECK(!m_observers.size()); -} - -// Determine if |contextDestroyed(Observer*) is a public method on -// class type |Observer|, or any of the class types it derives from. -template <typename Observer, typename T> -class HasContextDestroyed { - using YesType = char; - using NoType = int; - - template <typename V> - static YesType CheckHasContextDestroyedMethod( - V* observer, - T* context = nullptr, - typename std::enable_if< - std::is_same<decltype(observer->ContextDestroyed(context)), - void>::value>::type* g = nullptr); - template <typename V> - static NoType CheckHasContextDestroyedMethod(...); - - public: - static_assert(sizeof(Observer), "Observer's class declaration not in scope"); - static const bool value = - sizeof(YesType) == - sizeof(CheckHasContextDestroyedMethod<Observer>(nullptr)); -}; - -// If |Observer::contextDestroyed()| is present, invoke it. -template <typename Observer, - typename T, - bool = HasContextDestroyed<Observer, T>::value> -class ContextDestroyedNotifier { - STATIC_ONLY(ContextDestroyedNotifier); - - public: - static void Call(Observer* observer, T* context) { - observer->ContextDestroyed(context); - } -}; - -template <typename Observer, typename T> -class ContextDestroyedNotifier<Observer, T, false> { - STATIC_ONLY(ContextDestroyedNotifier); - - public: - static void Call(Observer*, T*) {} -}; - -template <typename T, typename Observer> -inline void LifecycleNotifier<T, Observer>::NotifyContextDestroyed() { - // Observer unregistration is allowed, but effectively a no-op. - base::AutoReset<IterationState> scope(&iteration_state_, kAllowingRemoval); - ObserverSet observers; - observers_.Swap(observers); - for (LifecycleObserverBase* observer_base : observers) { - Observer* observer = static_cast<Observer*>(observer_base); - DCHECK(observer->LifecycleContext() == Context()); - ContextDestroyedNotifier<Observer, T>::Call(observer, Context()); - observer->ClearContext(); - } - // Explicitly free the backing store to avoid memory regressions. - // TODO(bikineev): Revisit after young generation is done. - observers.clear(); -} - -template <typename T, typename Observer> -inline void LifecycleNotifier<T, Observer>::AddObserver( - LifecycleObserverBase* observer) { - CHECK(iteration_state_ & kAllowingAddition); - observers_.insert(observer); -} - -template <typename T, typename Observer> -inline void LifecycleNotifier<T, Observer>::RemoveObserver( - LifecycleObserverBase* observer) { - CHECK(iteration_state_ & kAllowingRemoval); - observers_.erase(observer); -} - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_NOTIFIER_H_ diff --git a/chromium/third_party/blink/renderer/platform/lifecycle_observer.h b/chromium/third_party/blink/renderer/platform/lifecycle_observer.h deleted file mode 100644 index 491550573f6..00000000000 --- a/chromium/third_party/blink/renderer/platform/lifecycle_observer.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_OBSERVER_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_OBSERVER_H_ - -#include "third_party/blink/renderer/platform/heap/handle.h" - -namespace blink { - -template <typename Context, typename Observer> -class LifecycleNotifier; - -class LifecycleObserverBase : public GarbageCollectedMixin {}; - -template <typename Context, typename Observer> -class LifecycleObserver : public LifecycleObserverBase { - public: - void Trace(blink::Visitor* visitor) override { - visitor->Trace(lifecycle_context_); - } - - Context* LifecycleContext() const { return lifecycle_context_; } - - void ClearContext() { SetContext(nullptr); } - - protected: - explicit LifecycleObserver(Context* context) : lifecycle_context_(nullptr) { - SetContext(context); - } - - void SetContext(Context*); - - private: - WeakMember<Context> lifecycle_context_; -}; - -template <typename Context, typename Observer> -inline void LifecycleObserver<Context, Observer>::SetContext(Context* context) { - using Notifier = LifecycleNotifier<Context, Observer>; - - if (lifecycle_context_ == context) - return; - - if (lifecycle_context_) { - static_cast<Notifier*>(lifecycle_context_)->RemoveObserver(this); - } - - lifecycle_context_ = context; - - if (lifecycle_context_) { - static_cast<Notifier*>(lifecycle_context_)->AddObserver(this); - } -} - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LIFECYCLE_OBSERVER_H_ diff --git a/chromium/third_party/blink/renderer/platform/loader/BUILD.gn b/chromium/third_party/blink/renderer/platform/loader/BUILD.gn index 731514c2914..c050053f89d 100644 --- a/chromium/third_party/blink/renderer/platform/loader/BUILD.gn +++ b/chromium/third_party/blink/renderer/platform/loader/BUILD.gn @@ -103,8 +103,12 @@ blink_platform_sources("loader") { "fetch/stale_revalidation_resource_client.h", "fetch/text_resource_decoder_options.cc", "fetch/text_resource_decoder_options.h", + "fetch/trust_token_params_conversion.cc", + "fetch/trust_token_params_conversion.h", "fetch/unique_identifier.cc", "fetch/unique_identifier.h", + "fetch/url_loader/request_conversion.cc", + "fetch/url_loader/request_conversion.h", "fetch/worker_resource_timing_notifier.h", "ftp_directory_listing.cc", "ftp_directory_listing.h", diff --git a/chromium/third_party/blink/renderer/platform/loader/DEPS b/chromium/third_party/blink/renderer/platform/loader/DEPS index bea7ce0ccd6..e99fddc126d 100644 --- a/chromium/third_party/blink/renderer/platform/loader/DEPS +++ b/chromium/third_party/blink/renderer/platform/loader/DEPS @@ -63,4 +63,13 @@ specific_include_rules = { "replaying_web_data_consumer_handle.h": [ "+third_party/blink/renderer/platform/waitable_event.h", ], + "request_conversion.cc": [ + # This file consists of conversion functions and hence needs to access + # both the blink and chromium mojom variants. + "+net/base/request_priority.h", + "+net/http/http_request_headers.h", + "+net/http/http_util.h", + "+third_party/blink/public/mojom/blob/blob.mojom.h", + "+services/network/public/mojom/data_pipe_getter.mojom.h", + ] } diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc b/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc index 42c442fb1d9..4b4c2cc6c9d 100644 --- a/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc +++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc @@ -25,6 +25,7 @@ #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" #include "third_party/blink/renderer/platform/wtf/thread_specific.h" #include "url/gurl.h" +#include "url/origin.h" namespace blink { @@ -89,7 +90,7 @@ class HTTPHeaderNameListParser { // in |output| when successful. Otherwise, returns with |output| kept empty. // // |output| must be empty. - void Parse(WebHTTPHeaderSet& output) { + void Parse(HTTPHeaderSet& output) { DCHECK(output.empty()); while (true) { @@ -160,12 +161,11 @@ namespace cors { base::Optional<network::CorsErrorStatus> CheckAccess( const KURL& response_url, - const int response_status_code, const HTTPHeaderMap& response_header, network::mojom::CredentialsMode credentials_mode, const SecurityOrigin& origin) { return network::cors::CheckAccess( - response_url, response_status_code, + response_url, GetHeaderValue(response_header, http_names::kAccessControlAllowOrigin), GetHeaderValue(response_header, http_names::kAccessControlAllowCredentials), @@ -201,11 +201,6 @@ base::Optional<network::CorsErrorStatus> CheckRedirectLocation( url, request_mode, origin_to_pass, cors_flag == CorsFlag::Set, false); } -base::Optional<network::mojom::CorsError> CheckPreflight( - const int preflight_response_status_code) { - return network::cors::CheckPreflight(preflight_response_status_code); -} - base::Optional<network::CorsErrorStatus> CheckExternalPreflight( const HTTPHeaderMap& response_header) { return network::cors::CheckExternalPreflight( @@ -253,8 +248,9 @@ base::Optional<network::CorsErrorStatus> EnsurePreflightResultAndCacheOnSuccess( if (status) return status; - GetPerThreadPreflightCache().AppendEntry(origin.Ascii(), request_url, - std::move(result)); + GetPerThreadPreflightCache().AppendEntry( + url::Origin::Create(GURL(origin.Ascii())), request_url, + net::NetworkIsolationKey(), std::move(result)); return base::nullopt; } @@ -270,7 +266,8 @@ bool CheckIfRequestCanSkipPreflight( // |is_revalidating| is not needed for blink-side CORS. constexpr bool is_revalidating = false; return GetPerThreadPreflightCache().CheckIfRequestCanSkipPreflight( - origin.Ascii(), url, credentials_mode, method.Ascii(), + url::Origin::Create(GURL(origin.Ascii())), url, + net::NetworkIsolationKey(), credentials_mode, method.Ascii(), *CreateNetHttpRequestHeaders(request_header_map), is_revalidating); } @@ -392,7 +389,7 @@ bool CalculateCorsFlag(const KURL& url, const SecurityOrigin* initiator_origin, const SecurityOrigin* isolated_world_origin, network::mojom::RequestMode request_mode) { - if (network::IsNavigationRequestMode(request_mode) || + if (request_mode == network::mojom::RequestMode::kNavigate || request_mode == network::mojom::RequestMode::kNoCors) { return false; } @@ -410,7 +407,7 @@ bool CalculateCorsFlag(const KURL& url, return true; } -WebHTTPHeaderSet ExtractCorsExposedHeaderNamesList( +HTTPHeaderSet ExtractCorsExposedHeaderNamesList( network::mojom::CredentialsMode credentials_mode, const ResourceResponse& response) { // If a response was fetched via a service worker, it will always have @@ -418,13 +415,13 @@ WebHTTPHeaderSet ExtractCorsExposedHeaderNamesList( // For requests that didn't come from a service worker, just parse the CORS // header. if (response.WasFetchedViaServiceWorker()) { - WebHTTPHeaderSet header_set; + HTTPHeaderSet header_set; for (const auto& header : response.CorsExposedHeaderNames()) header_set.insert(header.Ascii()); return header_set; } - WebHTTPHeaderSet header_set; + HTTPHeaderSet header_set; HTTPHeaderNameListParser parser( response.HttpHeaderField(http_names::kAccessControlExposeHeaders)); parser.Parse(header_set); @@ -441,7 +438,7 @@ WebHTTPHeaderSet ExtractCorsExposedHeaderNamesList( bool IsCorsSafelistedResponseHeader(const String& name) { // https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name // TODO(dcheng): Consider using a flat_set here with a transparent comparator. - DEFINE_THREAD_SAFE_STATIC_LOCAL(WebHTTPHeaderSet, + DEFINE_THREAD_SAFE_STATIC_LOCAL(HTTPHeaderSet, allowed_cross_origin_response_headers, ({ "cache-control", diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors.h b/chromium/third_party/blink/renderer/platform/loader/cors/cors.h index 84998f9c4b2..610c2c75c3f 100644 --- a/chromium/third_party/blink/renderer/platform/loader/cors/cors.h +++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors.h @@ -10,7 +10,7 @@ #include "services/network/public/mojom/cors.mojom-blink-forward.h" #include "services/network/public/mojom/fetch_api.mojom-blink-forward.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" -#include "third_party/blink/public/platform/web_http_header_set.h" +#include "third_party/blink/renderer/platform/network/http_header_set.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -35,7 +35,6 @@ namespace cors { // be removed. PLATFORM_EXPORT base::Optional<network::CorsErrorStatus> CheckAccess( const KURL&, - const int response_status_code, const HTTPHeaderMap&, network::mojom::CredentialsMode, const SecurityOrigin&); @@ -53,9 +52,6 @@ PLATFORM_EXPORT base::Optional<network::CorsErrorStatus> CheckRedirectLocation( const SecurityOrigin*, CorsFlag); -PLATFORM_EXPORT base::Optional<network::mojom::CorsError> CheckPreflight( - const int preflight_response_status_code); - PLATFORM_EXPORT base::Optional<network::CorsErrorStatus> CheckExternalPreflight( const HTTPHeaderMap&); @@ -124,7 +120,7 @@ PLATFORM_EXPORT bool CalculateCorsFlag( const SecurityOrigin* isolated_world_origin, network::mojom::RequestMode request_mode); -PLATFORM_EXPORT WebHTTPHeaderSet +PLATFORM_EXPORT HTTPHeaderSet ExtractCorsExposedHeaderNamesList(network::mojom::CredentialsMode, const ResourceResponse&); diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors_test.cc b/chromium/third_party/blink/renderer/platform/loader/cors/cors_test.cc index e68e93c56b7..173f852a577 100644 --- a/chromium/third_party/blink/renderer/platform/loader/cors/cors_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors_test.cc @@ -17,8 +17,8 @@ class CorsExposedHeadersTest : public testing::Test { public: using CredentialsMode = network::mojom::CredentialsMode; - WebHTTPHeaderSet Parse(CredentialsMode credentials_mode, - const AtomicString& header) const { + HTTPHeaderSet Parse(CredentialsMode credentials_mode, + const AtomicString& header) const { ResourceResponse response; response.AddHttpHeaderField("access-control-expose-headers", header); @@ -27,25 +27,24 @@ class CorsExposedHeadersTest : public testing::Test { }; TEST_F(CorsExposedHeadersTest, ValidInput) { - EXPECT_EQ(Parse(CredentialsMode::kOmit, "valid"), - WebHTTPHeaderSet({"valid"})); + EXPECT_EQ(Parse(CredentialsMode::kOmit, "valid"), HTTPHeaderSet({"valid"})); - EXPECT_EQ(Parse(CredentialsMode::kOmit, "a,b"), WebHTTPHeaderSet({"a", "b"})); + EXPECT_EQ(Parse(CredentialsMode::kOmit, "a,b"), HTTPHeaderSet({"a", "b"})); EXPECT_EQ(Parse(CredentialsMode::kOmit, " a , b "), - WebHTTPHeaderSet({"a", "b"})); + HTTPHeaderSet({"a", "b"})); EXPECT_EQ(Parse(CredentialsMode::kOmit, " \t \t\t a"), - WebHTTPHeaderSet({"a"})); + HTTPHeaderSet({"a"})); - EXPECT_EQ(Parse(CredentialsMode::kOmit, "a , "), WebHTTPHeaderSet({"a", ""})); + EXPECT_EQ(Parse(CredentialsMode::kOmit, "a , "), HTTPHeaderSet({"a", ""})); } TEST_F(CorsExposedHeadersTest, DuplicatedEntries) { - EXPECT_EQ(Parse(CredentialsMode::kOmit, "a, a"), WebHTTPHeaderSet{"a"}); + EXPECT_EQ(Parse(CredentialsMode::kOmit, "a, a"), HTTPHeaderSet{"a"}); EXPECT_EQ(Parse(CredentialsMode::kOmit, "a, a, b"), - WebHTTPHeaderSet({"a", "b"})); + HTTPHeaderSet({"a", "b"})); } TEST_F(CorsExposedHeadersTest, InvalidInput) { @@ -81,12 +80,12 @@ TEST_F(CorsExposedHeadersTest, Wildcard) { EXPECT_EQ( cors::ExtractCorsExposedHeaderNamesList(CredentialsMode::kOmit, response), - WebHTTPHeaderSet({"access-control-expose-headers", "b", "c", "d", "*"})); + HTTPHeaderSet({"access-control-expose-headers", "b", "c", "d", "*"})); EXPECT_EQ( cors::ExtractCorsExposedHeaderNamesList(CredentialsMode::kSameOrigin, response), - WebHTTPHeaderSet({"access-control-expose-headers", "b", "c", "d", "*"})); + HTTPHeaderSet({"access-control-expose-headers", "b", "c", "d", "*"})); } TEST_F(CorsExposedHeadersTest, Asterisk) { @@ -99,7 +98,7 @@ TEST_F(CorsExposedHeadersTest, Asterisk) { EXPECT_EQ(cors::ExtractCorsExposedHeaderNamesList(CredentialsMode::kInclude, response), - WebHTTPHeaderSet({"a", "b", "*"})); + HTTPHeaderSet({"a", "b", "*"})); } // Keep this in sync with the CalculateResponseTainting test in diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/DEPS b/chromium/third_party/blink/renderer/platform/loader/fetch/DEPS index d430e11a2d9..35694fdca61 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/DEPS +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/DEPS @@ -1,3 +1,5 @@ include_rules = [ + "+net/dns/public", "+services/network/public/cpp/fetch_api_utils.h", + "+services/network/public/cpp/optional_trust_token_params.h", ] diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc index 923c1903f67..e8411eddd6c 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc @@ -6,23 +6,22 @@ #include "base/feature_list.h" #include "base/metrics/field_trial_params.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" namespace blink { +namespace { +constexpr int32_t kDelayMilliseconds = 50; +} // namespace + // static BufferingBytesConsumer* BufferingBytesConsumer::CreateWithDelay( BytesConsumer* bytes_consumer, scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner) { - if (!base::FeatureList::IsEnabled(features::kBufferingBytesConsumerDelay)) - return Create(bytes_consumer); - return MakeGarbageCollected<BufferingBytesConsumer>( util::PassKey<BufferingBytesConsumer>(), bytes_consumer, std::move(timer_task_runner), - base::TimeDelta::FromMilliseconds( - features::kBufferingBytesConsumerDelayMilliseconds.Get())); + base::TimeDelta::FromMilliseconds(kDelayMilliseconds)); } // static diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h index 194590efe87..49155275231 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h @@ -79,7 +79,7 @@ class PLATFORM_EXPORT BufferingBytesConsumer final Error GetError() const override; String DebugName() const override { return "BufferingBytesConsumer"; } - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: void OnTimerFired(TimerBase*); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc index 1f17f0aadfc..8c298b58301 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc @@ -7,7 +7,6 @@ #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" #include "third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h" #include "third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h" @@ -74,10 +73,6 @@ TEST_F(BufferingBytesConsumerTest, ReadWithDelay) { auto* replaying_bytes_consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner); - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeatureWithParameters( - features::kBufferingBytesConsumerDelay, {{"milliseconds", "10"}}); - replaying_bytes_consumer->Add(Command(Command::kWait)); replaying_bytes_consumer->Add(Command(Command::kData, "1")); replaying_bytes_consumer->Add(Command(Command::kWait)); @@ -143,10 +138,6 @@ TEST_F(BufferingBytesConsumerTest, Buffering) { } TEST_F(BufferingBytesConsumerTest, BufferingWithDelay) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeatureWithParameters( - features::kBufferingBytesConsumerDelay, {{"milliseconds", "10"}}); - auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); auto* replaying_bytes_consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner); @@ -175,7 +166,7 @@ TEST_F(BufferingBytesConsumerTest, BufferingWithDelay) { EXPECT_EQ(PublicState::kReadableOrWaiting, replaying_bytes_consumer->GetPublicState()); - task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(11)); + task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(51)); task_runner->RunUntilIdle(); // After the delay expires the underlying consumer should be completely read. @@ -242,10 +233,6 @@ TEST_F(BufferingBytesConsumerTest, DrainAsDataPipeFailsWithoutDelay) { } TEST_F(BufferingBytesConsumerTest, DrainAsDataPipeSucceedsWithDelay) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeatureWithParameters( - features::kBufferingBytesConsumerDelay, {{"milliseconds", "10"}}); - auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; @@ -262,10 +249,6 @@ TEST_F(BufferingBytesConsumerTest, DrainAsDataPipeSucceedsWithDelay) { } TEST_F(BufferingBytesConsumerTest, DrainAsDataPipeFailsWithExpiredDelay) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeatureWithParameters( - features::kBufferingBytesConsumerDelay, {{"milliseconds", "10"}}); - auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; @@ -276,7 +259,7 @@ TEST_F(BufferingBytesConsumerTest, DrainAsDataPipeFailsWithExpiredDelay) { auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelay( data_pipe_consumer, scheduler::GetSingleThreadTaskRunnerForTesting()); - task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(11)); + task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(51)); EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); auto drained_consumer_handle = bytes_consumer->DrainAsDataPipe(); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h index c557ed1495d..5ec8bba5dd9 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h @@ -177,7 +177,7 @@ class PLATFORM_EXPORT BytesConsumer : public GarbageCollected<BytesConsumer> { // Returns a BytesConsumer whose state is Errored. static BytesConsumer* CreateErrored(const Error&); - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} protected: // This InternalState directly corresponds to the states in the class diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h index 3aea5dc8784..c0a819c63b0 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h @@ -20,8 +20,9 @@ class CachedMetadata; class ResourceResponse; class WebProcessMemoryDump; -// A callback for sending the serialized data of cached metadata back to the -// platform. +// A callback for sending the serialized data of cached metadata to the +// persistent storage. +// TODO(pasko): rename this class to CachedMetadataPersister. class PLATFORM_EXPORT CachedMetadataSender { USING_FAST_MALLOC(CachedMetadataSender); @@ -46,9 +47,19 @@ PLATFORM_EXPORT bool ShouldUseIsolatedCodeCache(mojom::RequestContextType, // Handler class for caching operations. class CachedMetadataHandler : public GarbageCollected<CachedMetadataHandler> { public: - enum CacheType { - kSendToPlatform, // send cache data to blink::Platform::cacheMetadata - kCacheLocally // cache only in Resource's member variables + enum ClearCacheType { + // Clears the in-memory cache, but doesn't update persistent storage. The + // old cached metadata is considered invalid. + kClearLocally, + + // Discards the in-memory cache for memory reduction, preventing any further + // uses or updates. The cached metadata will no longer be available, but + // should not be considered invalid. + kDiscardLocally, + + // Clears the metadata in both memory and persistent storage via + // blink::Platform::CacheMetadata. + kClearPersistentStorage }; // Enum for marking serialized cached metadatas so that the deserializers @@ -60,10 +71,11 @@ class CachedMetadataHandler : public GarbageCollected<CachedMetadataHandler> { }; virtual ~CachedMetadataHandler() = default; - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} - // Reset existing metadata, to allow setting new data. - virtual void ClearCachedMetadata(CacheType = kCacheLocally) = 0; + // Reset existing metadata. Subclasses can ignore setting new metadata after + // clearing with |kDiscardLocally| to save memory. + virtual void ClearCachedMetadata(ClearCacheType) = 0; // Returns the encoding to which the cache is specific. virtual String Encoding() const = 0; @@ -88,8 +100,13 @@ class SingleCachedMetadataHandler : public CachedMetadataHandler { // identifier that is used to distinguish data generated by the caller. virtual void SetCachedMetadata(uint32_t data_type_id, const uint8_t*, - size_t, - CacheType = kSendToPlatform) = 0; + size_t) = 0; + + // Permanently disable persisting CachedMetadata in the platform only when it + // is set. + void DisableSendToPlatformForTesting() { + disable_send_to_platform_for_testing_ = true; + } // Returns cached metadata of the given type associated with this resource. // This cached metadata can be pruned at any time. @@ -98,6 +115,7 @@ class SingleCachedMetadataHandler : public CachedMetadataHandler { protected: SingleCachedMetadataHandler() = default; + bool disable_send_to_platform_for_testing_ = false; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc index 3af7c4975b2..b49dc9e74a1 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc @@ -14,64 +14,6 @@ namespace blink { -namespace { - -void ParseAcceptChHeader(const String& header_value, - WebEnabledClientHints& enabled_hints) { - CommaDelimitedHeaderSet accept_client_hints_header; - ParseCommaDelimitedHeader(header_value, accept_client_hints_header); - - for (size_t i = 0; - i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) { - enabled_hints.SetIsEnabled( - static_cast<mojom::WebClientHintsType>(i), - accept_client_hints_header.Contains(kClientHintsNameMapping[i])); - } - - enabled_hints.SetIsEnabled( - mojom::WebClientHintsType::kDeviceMemory, - enabled_hints.IsEnabled(mojom::WebClientHintsType::kDeviceMemory)); - - enabled_hints.SetIsEnabled( - mojom::WebClientHintsType::kRtt, - enabled_hints.IsEnabled(mojom::WebClientHintsType::kRtt)); - - enabled_hints.SetIsEnabled( - mojom::WebClientHintsType::kDownlink, - enabled_hints.IsEnabled(mojom::WebClientHintsType::kDownlink)); - - enabled_hints.SetIsEnabled( - mojom::WebClientHintsType::kEct, - enabled_hints.IsEnabled(mojom::WebClientHintsType::kEct)); - - enabled_hints.SetIsEnabled( - mojom::WebClientHintsType::kLang, - enabled_hints.IsEnabled(mojom::WebClientHintsType::kLang) && - RuntimeEnabledFeatures::LangClientHintHeaderEnabled()); - - enabled_hints.SetIsEnabled( - mojom::WebClientHintsType::kUA, - enabled_hints.IsEnabled(mojom::WebClientHintsType::kUA) && - RuntimeEnabledFeatures::UserAgentClientHintEnabled()); - - enabled_hints.SetIsEnabled( - mojom::WebClientHintsType::kUAArch, - enabled_hints.IsEnabled(mojom::WebClientHintsType::kUAArch) && - RuntimeEnabledFeatures::UserAgentClientHintEnabled()); - - enabled_hints.SetIsEnabled( - mojom::WebClientHintsType::kUAPlatform, - enabled_hints.IsEnabled(mojom::WebClientHintsType::kUAPlatform) && - RuntimeEnabledFeatures::UserAgentClientHintEnabled()); - - enabled_hints.SetIsEnabled( - mojom::WebClientHintsType::kUAModel, - enabled_hints.IsEnabled(mojom::WebClientHintsType::kUAModel) && - RuntimeEnabledFeatures::UserAgentClientHintEnabled()); -} - -} // namespace - ClientHintsPreferences::ClientHintsPreferences() { DCHECK_EQ(static_cast<size_t>(mojom::WebClientHintsType::kMaxValue) + 1, kClientHintsMappingsCount); @@ -97,16 +39,24 @@ void ClientHintsPreferences::UpdateFromAcceptClientHintsHeader( if (!IsClientHintsAllowed(url)) return; - WebEnabledClientHints new_enabled_types; + // 8-bit conversions from String can turn non-ASCII characters into ?, + // turning syntax errors into "correct" syntax, so reject those first. + // (.Utf8() doesn't have this problem, but it does a lot of expensive + // work that would be wasted feeding to an ASCII-only syntax). + if (!header_value.ContainsOnlyASCIIOrEmpty()) + return; - ParseAcceptChHeader(header_value, new_enabled_types); + // Note: .Ascii() would convert tab to ?, which is undesirable. + base::Optional<std::vector<blink::mojom::WebClientHintsType>> parsed_ch = + ParseAcceptCH(header_value.Latin1(), + RuntimeEnabledFeatures::LangClientHintHeaderEnabled(), + RuntimeEnabledFeatures::UserAgentClientHintEnabled()); + if (!parsed_ch.has_value()) + return; - for (size_t i = 0; - i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) { - mojom::WebClientHintsType type = static_cast<mojom::WebClientHintsType>(i); - enabled_hints_.SetIsEnabled(type, enabled_hints_.IsEnabled(type) || - new_enabled_types.IsEnabled(type)); - } + // Note: this keeps previously enabled hints. + for (blink::mojom::WebClientHintsType newly_enabled : parsed_ch.value()) + enabled_hints_.SetIsEnabled(newly_enabled, true); if (context) { for (size_t i = 0; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc index e3e328e565d..1b6afccf66a 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc @@ -28,31 +28,33 @@ TEST_F(ClientHintsPreferencesTest, BasicSecure) { bool expectation_ua_arch; bool expectation_ua_platform; bool expectation_ua_model; + bool expectation_ua_full_version; } cases[] = { {"width, dpr, viewportWidth", true, true, false, false, false, false, - false, false, false, false, false}, + false, false, false, false, false, false}, {"WiDtH, dPr, viewport-width, rtt, downlink, ect, lang", true, true, true, - true, true, true, true, false, false, false, false}, + true, true, true, true, false, false, false, false, false}, {"WiDtH, dPr, viewport-width, rtt, downlink, effective-connection-type", - true, true, true, true, true, false, false, false, false, false, false}, + true, true, true, true, true, false, false, false, false, false, false, + false}, {"WIDTH, DPR, VIWEPROT-Width", true, true, false, false, false, false, - false, false, false, false, false}, + false, false, false, false, false, false}, {"VIewporT-Width, wutwut, width", true, false, true, false, false, false, - false, false, false, false, false}, + false, false, false, false, false, false}, {"dprw", false, false, false, false, false, false, false, false, false, - false, false}, + false, false, false}, {"DPRW", false, false, false, false, false, false, false, false, false, - false, false}, + false, false, false}, {"ua", false, false, false, false, false, false, false, true, false, - false, false}, - {"arch", false, false, false, false, false, false, false, false, true, - false, false}, - {"platform", false, false, false, false, false, false, false, false, - false, true, false}, - {"model", false, false, false, false, false, false, false, false, false, - false, true}, - {"ua, arch, platform, model", false, false, false, false, false, false, - false, true, true, true, true}, + false, false, false}, + {"ua-arch", false, false, false, false, false, false, false, false, true, + false, false, false}, + {"ua-platform", false, false, false, false, false, false, false, false, + false, true, false, false}, + {"ua-model", false, false, false, false, false, false, false, false, + false, false, true, false}, + {"ua, ua-arch, ua-platform, ua-model, ua-full-version", false, false, + false, false, false, false, false, true, true, true, true, true}, }; for (const auto& test_case : cases) { @@ -214,23 +216,25 @@ TEST_F(ClientHintsPreferencesTest, ParseHeaders) { bool expect_ua_arch; bool expect_ua_platform; bool expect_ua_model; + bool expect_ua_full_version; } test_cases[] = { {"width, dpr, viewportWidth, lang", "", 0, false, true, true, false, - false, false, false, true, false, false, false, false}, + false, false, false, true, false, false, false, false, false}, {"width, dpr, viewportWidth", "-1000", 0, false, true, true, false, false, - false, false, false, false, false, false, false}, + false, false, false, false, false, false, false, false}, {"width, dpr, viewportWidth", "1000s", 0, false, true, true, false, false, - false, false, false, false, false, false, false}, - {"width, dpr, viewportWidth", "1000.5", 0, false, true, true, false, false, false, false, false, false, false, false, false}, + {"width, dpr, viewportWidth", "1000.5", 0, false, true, true, false, + false, false, false, false, false, false, false, false, false}, {"width, dpr, rtt, downlink, ect", "1000", 1000, false, true, true, false, - true, true, true, false, false, false, false, false}, + true, true, true, false, false, false, false, false, false}, {"device-memory", "-1000", 0, true, false, false, false, false, false, - false, false, false, false, false, false}, + false, false, false, false, false, false, false}, {"dpr rtt", "1000", 1000, false, false, false, false, false, false, false, - false, false, false, false, false}, - {"ua, arch, platform, model", "1000", 1000, false, false, false, false, - false, false, false, false, true, true, true, true}, + false, false, false, false, false, false}, + {"ua, ua-arch, ua-platform, ua-model, ua-full-version", "1000", 1000, + false, false, false, false, false, false, false, false, true, true, true, + true, true}, }; for (const auto& test : test_cases) { diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.cc index 42054403a7b..c36b32b18fc 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.cc @@ -153,7 +153,7 @@ BytesConsumer::PublicState DataPipeBytesConsumer::GetPublicState() const { return GetPublicStateFromInternalState(state_); } -void DataPipeBytesConsumer::Trace(blink::Visitor* visitor) { +void DataPipeBytesConsumer::Trace(Visitor* visitor) { visitor->Trace(client_); BytesConsumer::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h index 4d84e2b8205..9bfc9775f21 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h @@ -40,7 +40,7 @@ class PLATFORM_EXPORT DataPipeBytesConsumer final : public BytesConsumer { // written into the pipe. void SignalComplete(); void SignalError(const BytesConsumer::Error& error); - void Trace(blink::Visitor*); + void Trace(Visitor*); private: const WeakMember<DataPipeBytesConsumer> bytes_consumer_; @@ -65,7 +65,7 @@ class PLATFORM_EXPORT DataPipeBytesConsumer final : public BytesConsumer { } String DebugName() const override { return "DataPipeBytesConsumer"; } - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: bool IsReadableOrWaiting() const; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h index 3ffd87b8aba..9f5f505218e 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h @@ -8,7 +8,7 @@ #include "base/optional.h" #include "services/network/public/mojom/ip_address_space.mojom-blink-forward.h" #include "services/network/public/mojom/referrer_policy.mojom-blink.h" -#include "third_party/blink/public/platform/web_insecure_request_policy.h" +#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink-forward.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/loader/allowed_by_nosniff.h" @@ -81,7 +81,8 @@ class PLATFORM_EXPORT FetchClientSettingsObject virtual network::mojom::IPAddressSpace GetAddressSpace() const = 0; // https://w3c.github.io/webappsec-upgrade-insecure-requests/#insecure-requests-policy - virtual WebInsecureRequestPolicy GetInsecureRequestsPolicy() const = 0; + virtual mojom::blink::InsecureRequestPolicy GetInsecureRequestsPolicy() + const = 0; // https://w3c.github.io/webappsec-upgrade-insecure-requests/#upgrade-insecure-navigations-set using InsecureNavigationsSet = HashSet<unsigned, WTF::AlreadyHashed>; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.cc index ad064d1dba4..e11f91fc064 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.cc @@ -4,6 +4,7 @@ #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h" +#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h" #include "third_party/blink/renderer/platform/heap/trace_traits.h" namespace blink { @@ -45,7 +46,7 @@ FetchClientSettingsObjectSnapshot::FetchClientSettingsObjectSnapshot( HttpsState https_state, AllowedByNosniff::MimeTypeCheck mime_type_check_for_classic_worker_script, network::mojom::IPAddressSpace address_space, - WebInsecureRequestPolicy insecure_requests_policy, + mojom::blink::InsecureRequestPolicy insecure_requests_policy, InsecureNavigationsSet insecure_navigations_set) : global_object_url_(global_object_url), base_url_(base_url), diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h index 84a9a1b9cc1..b4ae59e5550 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h @@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_CLIENT_SETTINGS_OBJECT_SNAPSHOT_H_ #include "services/network/public/mojom/referrer_policy.mojom-blink.h" +#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink-forward.h" #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" @@ -37,7 +38,7 @@ struct CrossThreadFetchClientSettingsObjectData { HttpsState https_state, AllowedByNosniff::MimeTypeCheck mime_type_check_for_classic_worker_script, network::mojom::IPAddressSpace address_space, - WebInsecureRequestPolicy insecure_requests_policy, + mojom::blink::InsecureRequestPolicy insecure_requests_policy, FetchClientSettingsObject::InsecureNavigationsSet insecure_navigations_set) : global_object_url(std::move(global_object_url)), @@ -61,7 +62,7 @@ struct CrossThreadFetchClientSettingsObjectData { const AllowedByNosniff::MimeTypeCheck mime_type_check_for_classic_worker_script; const network::mojom::IPAddressSpace address_space; - const WebInsecureRequestPolicy insecure_requests_policy; + const mojom::blink::InsecureRequestPolicy insecure_requests_policy; const FetchClientSettingsObject::InsecureNavigationsSet insecure_navigations_set; @@ -94,7 +95,7 @@ class PLATFORM_EXPORT FetchClientSettingsObjectSnapshot final HttpsState https_state, AllowedByNosniff::MimeTypeCheck, network::mojom::IPAddressSpace, - WebInsecureRequestPolicy, + mojom::blink::InsecureRequestPolicy, InsecureNavigationsSet); ~FetchClientSettingsObjectSnapshot() override = default; @@ -116,7 +117,8 @@ class PLATFORM_EXPORT FetchClientSettingsObjectSnapshot final return address_space_; } - WebInsecureRequestPolicy GetInsecureRequestsPolicy() const override { + mojom::blink::InsecureRequestPolicy GetInsecureRequestsPolicy() + const override { return insecure_requests_policy_; } @@ -151,7 +153,7 @@ class PLATFORM_EXPORT FetchClientSettingsObjectSnapshot final mime_type_check_for_classic_worker_script_; const network::mojom::IPAddressSpace address_space_; - const WebInsecureRequestPolicy insecure_requests_policy_; + const mojom::blink::InsecureRequestPolicy insecure_requests_policy_; const InsecureNavigationsSet insecure_navigations_set_; }; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h index cb44fc15e85..916041c76e1 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h @@ -47,7 +47,7 @@ #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h" #include "third_party/blink/renderer/platform/network/content_security_policy_parsers.h" #include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h" +#include "third_party/blink/renderer/platform/weborigin/reporting_disposition.h" #include "third_party/blink/renderer/platform/wtf/forward.h" namespace blink { @@ -75,7 +75,7 @@ class PLATFORM_EXPORT FetchContext : public GarbageCollected<FetchContext> { virtual ~FetchContext() = default; - virtual void Trace(blink::Visitor*) {} + virtual void Trace(Visitor*) {} virtual void AddAdditionalRequestHeaders(ResourceRequest&); @@ -112,7 +112,7 @@ class PLATFORM_EXPORT FetchContext : public GarbageCollected<FetchContext> { const ResourceRequest&, const KURL&, const ResourceLoaderOptions&, - SecurityViolationReportingPolicy, + ReportingDisposition, ResourceRequest::RedirectStatus) const { return ResourceRequestBlockedReason::kOther; } @@ -120,7 +120,7 @@ class PLATFORM_EXPORT FetchContext : public GarbageCollected<FetchContext> { mojom::RequestContextType, const KURL&, const ResourceLoaderOptions&, - SecurityViolationReportingPolicy, + ReportingDisposition, ResourceRequest::RedirectStatus) const { return ResourceRequestBlockedReason::kOther; } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc index eea543633a1..c57dfd3b668 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc @@ -33,16 +33,16 @@ namespace blink { -FetchParameters::FetchParameters(const ResourceRequest& resource_request) - : resource_request_(resource_request), +FetchParameters::FetchParameters(ResourceRequest resource_request) + : resource_request_(std::move(resource_request)), decoder_options_(TextResourceDecoderOptions::kPlainTextContent), speculative_preload_type_(SpeculativePreloadType::kNotSpeculative), defer_(kNoDefer), image_request_optimization_(kNone) {} -FetchParameters::FetchParameters(const ResourceRequest& resource_request, +FetchParameters::FetchParameters(ResourceRequest resource_request, const ResourceLoaderOptions& options) - : resource_request_(resource_request), + : resource_request_(std::move(resource_request)), decoder_options_(TextResourceDecoderOptions::kPlainTextContent), options_(options), speculative_preload_type_(SpeculativePreloadType::kNotSpeculative), diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h index de0d27f225b..a1e29fbe9b6 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h @@ -71,8 +71,8 @@ class PLATFORM_EXPORT FetchParameters { ResourceWidth() : width(0), is_set(false) {} }; - explicit FetchParameters(const ResourceRequest&); - FetchParameters(const ResourceRequest&, const ResourceLoaderOptions&); + explicit FetchParameters(ResourceRequest); + FetchParameters(ResourceRequest, const ResourceLoaderOptions&); FetchParameters(const FetchParameters&) = delete; FetchParameters& operator=(const FetchParameters&) = delete; FetchParameters(FetchParameters&&); @@ -88,6 +88,10 @@ class PLATFORM_EXPORT FetchParameters { resource_request_.SetRequestContext(context); } + void SetRequestDestination(network::mojom::RequestDestination destination) { + resource_request_.SetRequestDestination(destination); + } + void SetFetchImportanceMode(mojom::FetchImportanceMode importance_mode) { resource_request_.SetFetchImportanceMode(importance_mode); } @@ -139,7 +143,7 @@ class PLATFORM_EXPORT FetchParameters { } void SetContentSecurityCheck( - ContentSecurityPolicyDisposition content_security_policy_option) { + network::mojom::CSPDisposition content_security_policy_option) { options_.content_security_policy_option = content_security_policy_option; } // Configures the request to use the "cors" mode and the credentials mode diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc index 216c0f02ba1..f43766d2fcd 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc @@ -66,7 +66,7 @@ MemoryCache* ReplaceMemoryCacheForTesting(MemoryCache* cache) { return old_cache; } -void MemoryCacheEntry::Trace(blink::Visitor* visitor) { +void MemoryCacheEntry::Trace(Visitor* visitor) { visitor->template RegisterWeakCallbackMethod< MemoryCacheEntry, &MemoryCacheEntry::ClearResourceWeak>(this); } @@ -93,7 +93,7 @@ MemoryCache::MemoryCache( MemoryCache::~MemoryCache() = default; -void MemoryCache::Trace(blink::Visitor* visitor) { +void MemoryCache::Trace(Visitor* visitor) { visitor->Trace(resource_maps_); MemoryCacheDumpClient::Trace(visitor); MemoryPressureListener::Trace(visitor); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h index bc8e09b6442..d797ba16873 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h @@ -51,7 +51,7 @@ class MemoryCacheEntry final : public GarbageCollected<MemoryCacheEntry> { public: explicit MemoryCacheEntry(Resource* resource) : resource_(resource) {} - void Trace(blink::Visitor*); + void Trace(Visitor*); Resource* GetResource() const { return resource_; } private: @@ -71,7 +71,7 @@ class PLATFORM_EXPORT MemoryCache final : public GarbageCollected<MemoryCache>, explicit MemoryCache(scoped_refptr<base::SingleThreadTaskRunner> task_runner); ~MemoryCache() override; - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; struct TypeStatistic { STACK_ALLOCATED(); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc index 814e5c4b38f..96a01d2843c 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc @@ -95,13 +95,13 @@ class MemoryCacheCorrectnessTest : public testing::Test { ResourceRequest resource_request{KURL(kResourceURL)}; resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL); resource_request.SetRequestorOrigin(GetSecurityOrigin()); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); return RawResource::Fetch(fetch_params, Fetcher(), nullptr); } MockResource* FetchMockResource() { ResourceRequest resource_request{KURL(kResourceURL)}; resource_request.SetRequestorOrigin(GetSecurityOrigin()); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); return MockResource::Fetch(fetch_params, Fetcher(), nullptr); } ResourceFetcher* Fetcher() const { return fetcher_.Get(); } @@ -318,7 +318,7 @@ TEST_F(MemoryCacheCorrectnessTest, RequestWithNoCache) { no_cache_request.SetHttpHeaderField(http_names::kCacheControl, "no-cache"); no_cache_request.SetRequestorOrigin(GetSecurityOrigin()); MockResource* no_cache_resource = - ResourceFromResourceRequest(no_cache_request); + ResourceFromResourceRequest(std::move(no_cache_request)); MockResource* fetched = FetchMockResource(); EXPECT_NE(no_cache_resource, fetched); } @@ -349,7 +349,7 @@ TEST_F(MemoryCacheCorrectnessTest, RequestWithNoStore) { no_store_request.SetHttpHeaderField(http_names::kCacheControl, "no-store"); no_store_request.SetRequestorOrigin(GetSecurityOrigin()); MockResource* no_store_resource = - ResourceFromResourceRequest(no_store_request); + ResourceFromResourceRequest(std::move(no_store_request)); MockResource* fetched = FetchMockResource(); EXPECT_NE(no_store_resource, fetched); } @@ -469,7 +469,7 @@ TEST_F(MemoryCacheCorrectnessTest, PostToSameURLTwice) { ResourceRequest request2{KURL(kResourceURL)}; request2.SetHttpMethod(http_names::kPOST); request2.SetRequestorOrigin(GetSecurityOrigin()); - FetchParameters fetch2(request2); + FetchParameters fetch2(std::move(request2)); RawResource* resource2 = RawResource::FetchSynchronously(fetch2, Fetcher()); EXPECT_NE(resource1, resource2); } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc index fe682869223..73ce6bdb973 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc @@ -30,7 +30,6 @@ #include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h" -#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/heap/heap.h" @@ -126,13 +125,7 @@ TEST_F(MemoryCacheTest, CapacityAccounting) { EXPECT_EQ(kTotalCapacity, GetMemoryCache()->Capacity()); } -// TODO(crbug.com/850788): Reenable this. -#if defined(OS_ANDROID) -#define MAYBE_VeryLargeResourceAccounting DISABLED_VeryLargeResourceAccounting -#else -#define MAYBE_VeryLargeResourceAccounting VeryLargeResourceAccounting -#endif -TEST_F(MemoryCacheTest, MAYBE_VeryLargeResourceAccounting) { +TEST_F(MemoryCacheTest, VeryLargeResourceAccounting) { const size_t kSizeMax = ~static_cast<size_t>(0); const size_t kTotalCapacity = kSizeMax / 4; const size_t kResourceSize1 = kSizeMax / 16; @@ -140,7 +133,12 @@ TEST_F(MemoryCacheTest, MAYBE_VeryLargeResourceAccounting) { GetMemoryCache()->SetCapacity(kTotalCapacity); Persistent<MockResourceClient> client = MakeGarbageCollected<MockResourceClient>(); - FetchParameters params(ResourceRequest("data:text/html,")); + // Here and below, use an image MIME type. This is because on Android + // non-image MIME types trigger a query to Java to check which video codecs + // are supported. This fails in tests. The solution is either to use an image + // type, or disable the tests on Android. + // crbug.com/850788. + FetchParameters params(ResourceRequest("data:image/jpeg,")); FakeDecodedResource* cached_resource = FakeDecodedResource::Fetch(params, fetcher_, client); cached_resource->FakeEncodedSize(kResourceSize1); @@ -200,14 +198,14 @@ static void TestResourcePruningLater(ResourceFetcher* fetcher, EXPECT_EQ(0u, GetMemoryCache()->size()); const char kData[6] = "abcde"; - FetchParameters params1(ResourceRequest("data:text/html,resource1")); + FetchParameters params1(ResourceRequest("data:image/jpeg,resource1")); Resource* resource1 = FakeDecodedResource::Fetch(params1, fetcher, nullptr); GetMemoryCache()->Remove(resource1); if (!identifier1.IsEmpty()) resource1->SetCacheIdentifier(identifier1); resource1->AppendData(kData, 3u); resource1->FinishForTest(); - FetchParameters params2(ResourceRequest("data:text/html,resource2")); + FetchParameters params2(ResourceRequest("data:image/jpeg,resource2")); Persistent<MockResourceClient> client = MakeGarbageCollected<MockResourceClient>(); Resource* resource2 = FakeDecodedResource::Fetch(params2, fetcher, client); @@ -230,25 +228,11 @@ static void TestResourcePruningLater(ResourceFetcher* fetcher, } // Verified that when ordering a prune in a runLoop task, the prune is deferred. -// TODO(crbug.com/850788): Reenable this. -#if defined(OS_ANDROID) -#define MAYBE_ResourcePruningLater_Basic DISABLED_ResourcePruningLater_Basic -#else -#define MAYBE_ResourcePruningLater_Basic ResourcePruningLater_Basic -#endif -TEST_F(MemoryCacheTest, MAYBE_ResourcePruningLater_Basic) { +TEST_F(MemoryCacheTest, ResourcePruningLater_Basic) { TestResourcePruningLater(fetcher_, "", ""); } -// TODO(crbug.com/850788): Reenable this. -#if defined(OS_ANDROID) -#define MAYBE_ResourcePruningLater_MultipleResourceMaps \ - DISABLED_ResourcePruningLater_MultipleResourceMaps -#else -#define MAYBE_ResourcePruningLater_MultipleResourceMaps \ - ResourcePruningLater_MultipleResourceMaps -#endif -TEST_F(MemoryCacheTest, MAYBE_ResourcePruningLater_MultipleResourceMaps) { +TEST_F(MemoryCacheTest, ResourcePruningLater_MultipleResourceMaps) { { TestResourcePruningLater(fetcher_, "foo", ""); GetMemoryCache()->EvictResources(); @@ -272,9 +256,9 @@ static void TestClientRemoval(ResourceFetcher* fetcher, MakeGarbageCollected<MockResourceClient>(); Persistent<MockResourceClient> client2 = MakeGarbageCollected<MockResourceClient>(); - FetchParameters params1(ResourceRequest("data:text/html,foo")); + FetchParameters params1(ResourceRequest("data:image/jpeg,foo")); Resource* resource1 = FakeDecodedResource::Fetch(params1, fetcher, client1); - FetchParameters params2(ResourceRequest("data:text/html,bar")); + FetchParameters params2(ResourceRequest("data:image/jpeg,bar")); Resource* resource2 = FakeDecodedResource::Fetch(params2, fetcher, client2); resource1->AppendData(kData, 4u); resource2->AppendData(kData, 4u); @@ -327,25 +311,11 @@ static void TestClientRemoval(ResourceFetcher* fetcher, EXPECT_EQ(0u, GetMemoryCache()->size()); } -// TODO(crbug.com/850788): Reenable this. -#if defined(OS_ANDROID) -#define MAYBE_ClientRemoval_Basic DISABLED_ClientRemoval_Basic -#else -#define MAYBE_ClientRemoval_Basic ClientRemoval_Basic -#endif -TEST_F(MemoryCacheTest, MAYBE_ClientRemoval_Basic) { +TEST_F(MemoryCacheTest, ClientRemoval_Basic) { TestClientRemoval(fetcher_, "", ""); } -// TODO(crbug.com/850788): Reenable this. -#if defined(OS_ANDROID) -#define MAYBE_ClientRemoval_MultipleResourceMaps \ - DISABLED_ClientRemoval_MultipleResourceMaps -#else -#define MAYBE_ClientRemoval_MultipleResourceMaps \ - ClientRemoval_MultipleResourceMaps -#endif -TEST_F(MemoryCacheTest, MAYBE_ClientRemoval_MultipleResourceMaps) { +TEST_F(MemoryCacheTest, ClientRemoval_MultipleResourceMaps) { { TestClientRemoval(fetcher_, "foo", ""); GetMemoryCache()->EvictResources(); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc index 8a00bb48575..3a93692ce8b 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/null_resource_fetcher_properties.cc @@ -6,6 +6,7 @@ #include "services/network/public/mojom/ip_address_space.mojom-blink.h" #include "services/network/public/mojom/referrer_policy.mojom-blink.h" +#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h" #include "third_party/blink/renderer/platform/loader/allowed_by_nosniff.h" #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h" #include "third_party/blink/renderer/platform/loader/fetch/https_state.h" @@ -25,7 +26,7 @@ NullResourceFetcherProperties::NullResourceFetcherProperties() HttpsState::kNone, AllowedByNosniff::MimeTypeCheck::kStrict, network::mojom::IPAddressSpace::kPublic, - kLeaveInsecureRequestsAlone, + mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone, FetchClientSettingsObject::InsecureNavigationsSet())) {} void NullResourceFetcherProperties::Trace(Visitor* visitor) { diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc index 4d15c82e932..449d18c456b 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc @@ -55,6 +55,7 @@ RawResource* RawResource::FetchImport(FetchParameters& params, ResourceFetcher* fetcher, RawResourceClient* client) { params.SetRequestContext(mojom::RequestContextType::IMPORT); + params.SetRequestDestination(network::mojom::RequestDestination::kEmpty); return ToRawResource(fetcher->RequestResource( params, RawResourceFactory(ResourceType::kImportResource), client)); } @@ -85,6 +86,7 @@ RawResource* RawResource::FetchTextTrack(FetchParameters& params, ResourceFetcher* fetcher, RawResourceClient* client) { params.SetRequestContext(mojom::RequestContextType::TRACK); + params.SetRequestDestination(network::mojom::RequestDestination::kTrack); return ToRawResource(fetcher->RequestResource( params, RawResourceFactory(ResourceType::kTextTrack), client)); } @@ -171,8 +173,8 @@ void RawResource::DidAddClient(ResourceClient* c) { RevalidationStartForbiddenScope revalidation_start_forbidden_scope(this); RawResourceClient* client = static_cast<RawResourceClient*>(c); for (const auto& redirect : RedirectChain()) { - ResourceRequest request(redirect.request_); - client->RedirectReceived(this, request, redirect.redirect_response_); + client->RedirectReceived(this, ResourceRequest(redirect.request_), + redirect.redirect_response_); if (!HasClient(c)) return; } @@ -406,8 +408,6 @@ void RawResourceClient::DidDownloadToBlob(Resource*, RawResourceClientStateChecker::RawResourceClientStateChecker() : state_(kNotAddedAsClient) {} -RawResourceClientStateChecker::~RawResourceClientStateChecker() = default; - NOINLINE void RawResourceClientStateChecker::WillAddClient() { SECURITY_CHECK(state_ == kNotAddedAsClient); state_ = kStarted; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h index 11aa7531c9a..3a22921c1f3 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h @@ -63,7 +63,7 @@ class PLATFORM_EXPORT RawResource final : public Resource { RawResourceClient*); // Exposed for testing - static RawResource* CreateForTest(ResourceRequest request, + static RawResource* CreateForTest(const ResourceRequest& request, ResourceType type) { ResourceLoaderOptions options; return MakeGarbageCollected<RawResource>(request, type, options); @@ -207,7 +207,6 @@ class PLATFORM_EXPORT RawResourceClientStateChecker final { public: RawResourceClientStateChecker(); - ~RawResourceClientStateChecker(); // Call before addClient()/removeClient() is called. void WillAddClient(); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc index 4a1276b11b0..a469fe6ae20 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc @@ -92,7 +92,7 @@ TEST_F(RawResourceTest, DontIgnoreAcceptForCacheReuse) { ResourceRequest png_request; png_request.SetHTTPAccept("image/png"); png_request.SetRequestorOrigin(source_origin); - EXPECT_NE(jpeg_resource->CanReuse(FetchParameters(png_request)), + EXPECT_NE(jpeg_resource->CanReuse(FetchParameters(std::move(png_request))), Resource::MatchStatus::kOk); } @@ -124,9 +124,7 @@ class DummyClient final : public GarbageCollected<DummyClient>, return number_of_redirects_received_; } const Vector<char>& Data() { return data_; } - void Trace(blink::Visitor* visitor) override { - RawResourceClient::Trace(visitor); - } + void Trace(Visitor* visitor) override { RawResourceClient::Trace(visitor); } private: bool called_; @@ -162,7 +160,7 @@ class AddingClient final : public GarbageCollected<AddingClient>, void RemoveClient() { resource_->RemoveClient(dummy_client_); } - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { visitor->Trace(dummy_client_); visitor->Trace(resource_); RawResourceClient::Trace(visitor); @@ -207,7 +205,7 @@ class RemovingClient : public GarbageCollected<RemovingClient>, resource->RemoveClient(this); } String DebugName() const override { return "RemovingClient"; } - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { visitor->Trace(dummy_client_); RawResourceClient::Trace(visitor); } @@ -255,7 +253,7 @@ TEST_F(RawResourceTest, PreloadWithAsynchronousAddClient) { // Set the response first to make ResourceClient addition asynchronous. raw->SetResponse(ResourceResponse(KURL("http://600.613/"))); - FetchParameters params(request); + FetchParameters params(std::move(request)); params.MutableResourceRequest().SetUseStreamOnResponse(false); raw->MatchPreload(params, platform_->test_task_runner().get()); raw->AddClient(dummy_client, platform_->test_task_runner().get()); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc index a94e9665eaf..d7c24f32f9e 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc @@ -122,8 +122,7 @@ const char* const kHeaderPrefixesToIgnoreAfterRevalidation[] = { static inline bool ShouldUpdateHeaderAfterRevalidation( const AtomicString& header) { for (size_t i = 0; i < base::size(kHeadersToIgnoreAfterRevalidation); i++) { - if (DeprecatedEqualIgnoringCase(header, - kHeadersToIgnoreAfterRevalidation[i])) + if (EqualIgnoringASCIICase(header, kHeadersToIgnoreAfterRevalidation[i])) return false; } for (size_t i = 0; i < base::size(kHeaderPrefixesToIgnoreAfterRevalidation); @@ -146,7 +145,7 @@ static inline base::Time Now() { return clock->Now(); } -Resource::Resource(const ResourceRequest& request, +Resource::Resource(const ResourceRequestHead& request, ResourceType type, const ResourceLoaderOptions& options) : type_(type), @@ -174,7 +173,7 @@ Resource::~Resource() { InstanceCounters::DecrementCounter(InstanceCounters::kResourceCounter); } -void Resource::Trace(blink::Visitor* visitor) { +void Resource::Trace(Visitor* visitor) { visitor->Trace(loader_); visitor->Trace(cache_handler_); visitor->Trace(clients_); @@ -472,7 +471,7 @@ static bool CanUseResponse(const ResourceResponse& response, return CurrentAge(response, response_timestamp) <= max_life; } -const ResourceRequest& Resource::LastResourceRequest() const { +const ResourceRequestHead& Resource::LastResourceRequest() const { if (!redirect_chain_.size()) return GetResourceRequest(); return redirect_chain_.back().request_; @@ -484,7 +483,7 @@ const ResourceResponse* Resource::LastResourceResponse() const { return &redirect_chain_.back().redirect_response_; } -void Resource::SetRevalidatingRequest(const ResourceRequest& request) { +void Resource::SetRevalidatingRequest(const ResourceRequestHead& request) { SECURITY_CHECK(redirect_chain_.IsEmpty()); SECURITY_CHECK(!is_unused_preload_); DCHECK(!request.IsNull()); @@ -567,22 +566,23 @@ String Resource::ReasonNotDeletable() const { return builder.ToString(); } -void Resource::DidAddClient(ResourceClient* c) { +void Resource::DidAddClient(ResourceClient* client) { if (scoped_refptr<SharedBuffer> data = Data()) { for (const auto& span : *data) { - c->DataReceived(this, span.data(), span.size()); + client->DataReceived(this, span.data(), span.size()); // Stop pushing data if the client removed itself. - if (!HasClient(c)) + if (!HasClient(client)) break; } } - if (!HasClient(c)) + if (!HasClient(client)) return; if (IsFinishedInternal()) { - c->NotifyFinished(this); - if (clients_.Contains(c)) { - finished_clients_.insert(c); - clients_.erase(c); + client->SetHasFinishedFromMemoryCache(); + client->NotifyFinished(this); + if (clients_.Contains(client)) { + finished_clients_.insert(client); + clients_.erase(client); } } } @@ -823,12 +823,13 @@ Resource::MatchStatus Resource::CanReuse(const FetchParameters& params) const { if (resource_request_.GetKeepalive() || new_request.GetKeepalive()) return MatchStatus::kKeepaliveSet; - if (GetResourceRequest().HttpMethod() != new_request.HttpMethod()) + if (GetResourceRequest().HttpMethod() != http_names::kGET || + new_request.HttpMethod() != http_names::kGET) { return MatchStatus::kRequestMethodDoesNotMatch; + } - if (GetResourceRequest().HttpBody() != new_request.HttpBody()) - return MatchStatus::kUnknownFailure; - + // A GET request doesn't have a request body. + DCHECK(!new_request.HttpBody()); // Don't reuse an existing resource when the source origin is different. if (!existing_origin->IsSameOriginWith(new_origin.get())) @@ -851,8 +852,6 @@ Resource::MatchStatus Resource::CanReuse(const FetchParameters& params) const { switch (new_mode) { case network::mojom::RequestMode::kNoCors: case network::mojom::RequestMode::kNavigate: - case network::mojom::RequestMode::kNavigateNestedFrame: - case network::mojom::RequestMode::kNavigateNestedObject: break; case network::mojom::RequestMode::kCors: @@ -887,7 +886,7 @@ void Resource::OnPurgeMemory() { Prune(); if (!cache_handler_) return; - cache_handler_->ClearCachedMetadata(CachedMetadataHandler::kCacheLocally); + cache_handler_->ClearCachedMetadata(CachedMetadataHandler::kClearLocally); } void Resource::OnMemoryDump(WebMemoryDumpLevelOfDetail level_of_detail, @@ -1068,8 +1067,7 @@ bool Resource::MustRevalidateDueToCacheHeaders(bool allow_stale) const { GetResourceRequest().CacheControlContainsNoStore(); } -static bool ShouldRevalidateStaleResponse(const ResourceRequest& request, - const ResourceResponse& response, +static bool ShouldRevalidateStaleResponse(const ResourceResponse& response, base::Time response_timestamp) { base::TimeDelta staleness = response.CacheControlStaleWhileRevalidate(); if (staleness.is_zero()) @@ -1083,15 +1081,14 @@ bool Resource::ShouldRevalidateStaleResponse() const { for (auto& redirect : redirect_chain_) { // Use |response_timestamp_| since we don't store the timestamp // of each redirect response. - if (blink::ShouldRevalidateStaleResponse(redirect.request_, - redirect.redirect_response_, + if (blink::ShouldRevalidateStaleResponse(redirect.redirect_response_, response_timestamp_)) { return true; } } - return blink::ShouldRevalidateStaleResponse( - GetResourceRequest(), GetResponse(), response_timestamp_); + return blink::ShouldRevalidateStaleResponse(GetResponse(), + response_timestamp_); } bool Resource::StaleRevalidationRequested() const { diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h index 9b83d433ec0..4dc121198b8 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h @@ -157,7 +157,7 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, ~Resource() override; - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; virtual WTF::TextEncoding Encoding() const { return WTF::TextEncoding(); } virtual void AppendData(const char*, size_t); @@ -176,13 +176,13 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, virtual bool ShouldIgnoreHTTPStatusCodeErrors() const { return false; } - const ResourceRequest& GetResourceRequest() const { + const ResourceRequestHead& GetResourceRequest() const { return resource_request_; } - const ResourceRequest& LastResourceRequest() const; + const ResourceRequestHead& LastResourceRequest() const; const ResourceResponse* LastResourceResponse() const; - virtual void SetRevalidatingRequest(const ResourceRequest&); + virtual void SetRevalidatingRequest(const ResourceRequestHead&); // This url can have a fragment, but it can match resources that differ by the // fragment only. @@ -374,13 +374,6 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, // be triggered right away in ResourceLoader. virtual void WillReloadAfterDiskCacheMiss() {} - // TODO(shaochuan): This is for saving back the actual ResourceRequest sent - // in ResourceFetcher::StartLoad() for retry in cache-aware loading, remove - // once ResourceRequest is not modified in StartLoad(). crbug.com/632580 - void SetResourceRequest(const ResourceRequest& resource_request) { - resource_request_ = resource_request; - } - // Used by the MemoryCache to reduce the memory consumption of the entry. void Prune(); @@ -426,8 +419,14 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, // The caller owns the |clock| which must outlive the Resource. static void SetClockForTesting(const base::Clock* clock); + size_t CalculateOverheadSizeForTest() const { + return CalculateOverheadSize(); + } + protected: - Resource(const ResourceRequest&, ResourceType, const ResourceLoaderOptions&); + Resource(const ResourceRequestHead&, + ResourceType, + const ResourceLoaderOptions&); // Returns true if the resource has finished any processing it wanted to do // after loading. Should only be used to decide whether to call @@ -473,11 +472,11 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, DISALLOW_NEW(); public: - explicit RedirectPair(const ResourceRequest& request, + explicit RedirectPair(const ResourceRequestHead& request, const ResourceResponse& redirect_response) : request_(request), redirect_response_(redirect_response) {} - ResourceRequest request_; + ResourceRequestHead request_; ResourceResponse redirect_response_; }; const Vector<RedirectPair>& RedirectChain() const { return redirect_chain_; } @@ -566,7 +565,7 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, TaskHandle async_finish_pending_clients_task_; - ResourceRequest resource_request_; + ResourceRequestHead resource_request_; // Resource::CalculateOverheadSize() is affected by changes in // |m_resourceRequest.url()|, but |m_overheadSize| is not updated after diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h index dced85b7646..a4ea28e5e0f 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h @@ -64,6 +64,9 @@ class PLATFORM_EXPORT ResourceClient : public GarbageCollectedMixin { Resource* GetResource() const { return resource_; } + bool FinishedFromMemoryCache() const { return finished_from_memory_cache_; } + void SetHasFinishedFromMemoryCache() { finished_from_memory_cache_ = true; } + // Name for debugging, e.g. shown in memory-infra. virtual String DebugName() const = 0; @@ -86,6 +89,10 @@ class PLATFORM_EXPORT ResourceClient : public GarbageCollectedMixin { base::SingleThreadTaskRunner* task_runner); Member<Resource> resource_; + + // If true, the Resource was already available from the memory cache when this + // ResourceClient was setup, so that the request finished immediately. + bool finished_from_memory_cache_ = false; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc index 8451bf18965..a6706847ef4 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc @@ -96,10 +96,12 @@ ResourceError::ResourceError(const KURL& url, ResourceError::ResourceError(const WebURLError& error) : error_code_(error.reason()), extended_error_code_(error.extended_reason()), + resolve_error_info_(error.resolve_error_info()), failing_url_(error.url()), is_access_check_(error.is_web_security_violation()), has_copy_in_cache_(error.has_copy_in_cache()), - cors_error_status_(error.cors_error_status()) { + cors_error_status_(error.cors_error_status()), + blocked_by_response_reason_(error.blocked_by_response_reason()) { DCHECK_NE(error_code_, 0); InitializeDescription(); } @@ -108,6 +110,7 @@ ResourceError ResourceError::Copy() const { ResourceError error_copy(error_code_, failing_url_.Copy(), cors_error_status_); error_copy.extended_error_code_ = extended_error_code_; + error_copy.resolve_error_info_ = resolve_error_info_; error_copy.has_copy_in_cache_ = has_copy_in_cache_; error_copy.localized_description_ = localized_description_.IsolatedCopy(); error_copy.is_access_check_ = is_access_check_; @@ -124,11 +127,11 @@ ResourceError::operator WebURLError() const { return WebURLError(*cors_error_status_, has_copy_in_cache, failing_url_); } - return WebURLError(error_code_, extended_error_code_, has_copy_in_cache, - is_access_check_ - ? WebURLError::IsWebSecurityViolation::kTrue - : WebURLError::IsWebSecurityViolation::kFalse, - failing_url_); + return WebURLError( + error_code_, extended_error_code_, resolve_error_info_, has_copy_in_cache, + is_access_check_ ? WebURLError::IsWebSecurityViolation::kTrue + : WebURLError::IsWebSecurityViolation::kFalse, + failing_url_); } bool ResourceError::Compare(const ResourceError& a, const ResourceError& b) { @@ -153,6 +156,9 @@ bool ResourceError::Compare(const ResourceError& a, const ResourceError& b) { if (a.extended_error_code_ != b.extended_error_code_) return false; + if (a.resolve_error_info_ != b.resolve_error_info_) + return false; + return true; } @@ -178,12 +184,45 @@ bool ResourceError::ShouldCollapseInitiator() const { ResourceRequestBlockedReason::kCollapsedByClient; } +namespace { + +blink::ResourceRequestBlockedReason +BlockedByResponseReasonToResourceRequestBlockedReason( + network::BlockedByResponseReason reason) { + switch (reason) { + case network::BlockedByResponseReason::kCoepFrameResourceNeedsCoepHeader: + return blink::ResourceRequestBlockedReason:: + kCoepFrameResourceNeedsCoepHeader; + case network::BlockedByResponseReason:: + kCoopSandboxedIFrameCannotNavigateToCoopPage: + return blink::ResourceRequestBlockedReason:: + kCoopSandboxedIFrameCannotNavigateToCoopPage; + case network::BlockedByResponseReason::kCorpNotSameOrigin: + return blink::ResourceRequestBlockedReason::kCorpNotSameOrigin; + case network::BlockedByResponseReason:: + kCorpNotSameOriginAfterDefaultedToSameOriginByCoep: + return blink::ResourceRequestBlockedReason:: + kCorpNotSameOriginAfterDefaultedToSameOriginByCoep; + break; + case network::BlockedByResponseReason::kCorpNotSameSite: + return blink::ResourceRequestBlockedReason::kCorpNotSameSite; + break; + } + NOTREACHED(); + return blink::ResourceRequestBlockedReason::kOther; +} + +} // namespace base::Optional<ResourceRequestBlockedReason> ResourceError::GetResourceRequestBlockedReason() const { if (error_code_ != net::ERR_BLOCKED_BY_CLIENT && error_code_ != net::ERR_BLOCKED_BY_RESPONSE) { return base::nullopt; } + if (blocked_by_response_reason_) { + return BlockedByResponseReasonToResourceRequestBlockedReason( + *blocked_by_response_reason_); + } return static_cast<ResourceRequestBlockedReason>(extended_error_code_); } @@ -217,6 +256,23 @@ String DescriptionForBlockedByClientOrResponse(int error, int extended_error) { case ResourceRequestBlockedReason::kCollapsedByClient: detail = "Collapsed"; break; + case ResourceRequestBlockedReason::kCoepFrameResourceNeedsCoepHeader: + detail = "ResponseNeedsCrossOriginEmbedderPolicy"; + break; + case ResourceRequestBlockedReason:: + kCoopSandboxedIFrameCannotNavigateToCoopPage: + detail = "SandboxedIFrameCannotNavigateToOriginIsolatedPage"; + break; + case ResourceRequestBlockedReason::kCorpNotSameOrigin: + detail = "NotSameOrigin"; + break; + case ResourceRequestBlockedReason:: + kCorpNotSameOriginAfterDefaultedToSameOriginByCoep: + detail = "NotSameOriginAfterDefaultedToSameOriginByCoep"; + break; + case ResourceRequestBlockedReason::kCorpNotSameSite: + detail = "NotSameSite"; + break; } return WebString::FromASCII(net::ErrorToString(error) + "." + detail); } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h index 592174eae92..19c60919c24 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h @@ -29,6 +29,8 @@ #include <iosfwd> #include "base/optional.h" +#include "net/dns/public/resolve_error_info.h" +#include "services/network/public/cpp/blocked_by_response_reason.h" #include "services/network/public/cpp/cors/cors_error_status.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" @@ -99,12 +101,15 @@ class PLATFORM_EXPORT ResourceError final { int error_code_; int extended_error_code_ = 0; + net::ResolveErrorInfo resolve_error_info_; KURL failing_url_; String localized_description_; bool is_access_check_ = false; bool has_copy_in_cache_ = false; bool blocked_by_subresource_filter_ = false; base::Optional<network::CorsErrorStatus> cors_error_status_; + + base::Optional<network::BlockedByResponseReason> blocked_by_response_reason_; }; inline bool operator==(const ResourceError& a, const ResourceError& b) { diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc index d55eaa3ac98..04ed5036553 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc @@ -34,15 +34,15 @@ #include "base/auto_reset.h" #include "base/debug/dump_without_crashing.h" #include "base/feature_list.h" +#include "base/metrics/histogram_macros.h" #include "base/time/time.h" #include "services/network/public/cpp/request_mode.h" #include "third_party/blink/public/common/features.h" -#include "third_party/blink/public/common/loader/request_destination.h" #include "third_party/blink/public/common/mime_util/mime_util.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h" #include "third_party/blink/public/platform/web_url.h" @@ -78,10 +78,10 @@ #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" #include "third_party/blink/renderer/platform/weborigin/known_ports.h" #include "third_party/blink/renderer/platform/weborigin/origin_access_entry.h" +#include "third_party/blink/renderer/platform/weborigin/reporting_disposition.h" #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" #include "third_party/blink/renderer/platform/weborigin/security_policy.h" -#include "third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -98,13 +98,12 @@ namespace { constexpr base::TimeDelta kKeepaliveLoadersTimeout = base::TimeDelta::FromSeconds(30); -#define DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, name) \ - case ResourceType::k##name: { \ - DEFINE_THREAD_SAFE_STATIC_LOCAL( \ - EnumerationHistogram, _single_resource_histogram, \ - ("Blink.MemoryCache.RevalidationPolicy." prefix #name, kLoad + 1)); \ - _single_resource_histogram.Count(policy); \ - break; \ +#define RESOURCE_HISTOGRAM_PREFIX "Blink.MemoryCache.RevalidationPolicy." + +#define DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, name) \ + case ResourceType::k##name: { \ + UMA_HISTOGRAM_ENUMERATION(RESOURCE_HISTOGRAM_PREFIX prefix #name, policy); \ + break; \ } #define DEFINE_RESOURCE_HISTOGRAM(prefix) \ @@ -159,24 +158,12 @@ ResourceLoadPriority TypeToPriority(ResourceType type) { return ResourceLoadPriority::kUnresolved; } -static bool IsCacheableHTTPMethod(const AtomicString& method) { - // Per http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10, - // these methods always invalidate the cache entry. - return method != http_names::kPOST && method != http_names::kPUT && - method != "DELETE"; -} - bool ShouldResourceBeAddedToMemoryCache(const FetchParameters& params, Resource* resource) { - if (!IsMainThread()) - return false; - if (params.Options().data_buffering_policy == kDoNotBufferData) - return false; - if (IsRawResource(*resource)) - return false; - if (!IsCacheableHTTPMethod(params.GetResourceRequest().HttpMethod())) - return false; - return true; + return IsMainThread() && + params.GetResourceRequest().HttpMethod() == http_names::kGET && + params.Options().data_buffering_policy != kDoNotBufferData && + !IsRawResource(*resource); } static ResourceFetcher::ResourceFetcherSet& MainThreadFetchersSet() { @@ -204,7 +191,7 @@ static ThreadSpecific<PriorityObserverMap>& PriorityObservers() { ResourceLoadPriority AdjustPriorityWithPriorityHint( ResourceLoadPriority priority_so_far, ResourceType type, - const ResourceRequest& resource_request, + const ResourceRequestHead& resource_request, FetchParameters::DeferOption defer_option, bool is_link_preload) { mojom::FetchImportanceMode importance_mode = @@ -254,7 +241,7 @@ ResourceLoadPriority AdjustPriorityWithDeferScriptIntervention( const FetchContext& fetch_context, ResourceLoadPriority priority_so_far, ResourceType type, - const ResourceRequest& resource_request, + const ResourceRequestHead& resource_request, FetchParameters::DeferOption defer_option, bool is_link_preload) { if (!base::FeatureList::IsEnabled( @@ -307,65 +294,20 @@ std::unique_ptr<TracedValue> ResourcePrioritySetData( void SetReferrer( ResourceRequest& request, const FetchClientSettingsObject& fetch_client_settings_object) { - if (!request.DidSetHttpReferrer()) { - String referrer_to_use = request.ReferrerString(); - network::mojom::ReferrerPolicy referrer_policy_to_use = - request.GetReferrerPolicy(); - - if (referrer_to_use == Referrer::ClientReferrerString()) - referrer_to_use = fetch_client_settings_object.GetOutgoingReferrer(); - - if (referrer_policy_to_use == network::mojom::ReferrerPolicy::kDefault) - referrer_policy_to_use = fetch_client_settings_object.GetReferrerPolicy(); - - request.SetReferrerString(referrer_to_use); - request.SetReferrerPolicy(referrer_policy_to_use); - // TODO(domfarolino): Stop storing ResourceRequest's referrer as a header - // and store it elsewhere. See https://crbug.com/850813. - request.SetHttpReferrer(SecurityPolicy::GenerateReferrer( - referrer_policy_to_use, request.Url(), referrer_to_use)); - } else { - // In the case of stale requests that are being revalidated, these requests - // will already have their HttpReferrer set, and we will end up here. We - // won't regenerate the referrer, but instead check that it's still correct. - CHECK_EQ(SecurityPolicy::GenerateReferrer(request.GetReferrerPolicy(), - request.Url(), - request.ReferrerString()) - .referrer, - request.HttpReferrer()); - } -} + String referrer_to_use = request.ReferrerString(); + network::mojom::ReferrerPolicy referrer_policy_to_use = + request.GetReferrerPolicy(); -void SetSecFetchHeaders( - ResourceRequest& request, - const FetchClientSettingsObject& fetch_client_settings_object) { - scoped_refptr<SecurityOrigin> url_origin = - SecurityOrigin::Create(request.Url()); - if (blink::RuntimeEnabledFeatures::FetchMetadataEnabled() && - url_origin->IsPotentiallyTrustworthy()) { - const char* destination_value = - GetRequestDestinationFromContext(request.GetRequestContext()); - - // If the request's destination is the empty string (e.g. `fetch()`), then - // we'll use the identifier "empty" instead. - if (strlen(destination_value) == 0) - destination_value = "empty"; - - // We'll handle adding these headers to navigations outside of Blink. - if ((strncmp(destination_value, "document", 8) != 0 || - strncmp(destination_value, "iframe", 6) != 0 || - strncmp(destination_value, "frame", 5) != 0) && - request.GetRequestContext() != mojom::RequestContextType::INTERNAL) { - if (blink::RuntimeEnabledFeatures::FetchMetadataDestinationEnabled()) { - request.SetHttpHeaderField("Sec-Fetch-Dest", destination_value); - } + if (referrer_to_use == Referrer::ClientReferrerString()) + referrer_to_use = fetch_client_settings_object.GetOutgoingReferrer(); - // Note that the `Sec-Fetch-User` header is always false (and therefore - // omitted) for subresource requests. Likewise, note that we rely on - // Blink's embedder to set `Sec-Fetch-Site`, as we don't want to trust the - // renderer to assert its own origin. Ditto for `Sec-Fetch-Mode`. - } - } + if (referrer_policy_to_use == network::mojom::ReferrerPolicy::kDefault) + referrer_policy_to_use = fetch_client_settings_object.GetReferrerPolicy(); + + Referrer generated_referrer = SecurityPolicy::GenerateReferrer( + referrer_policy_to_use, request.Url(), referrer_to_use); + request.SetReferrerString(generated_referrer.referrer); + request.SetReferrerPolicy(generated_referrer.referrer_policy); } } // namespace @@ -375,7 +317,7 @@ ResourceFetcherInit::ResourceFetcherInit( FetchContext* context, scoped_refptr<base::SingleThreadTaskRunner> task_runner, ResourceFetcher::LoaderFactory* loader_factory) - : properties(properties), + : properties(&properties), context(context), task_runner(std::move(task_runner)), loader_factory(loader_factory) { @@ -426,6 +368,40 @@ mojom::RequestContextType ResourceFetcher::DetermineRequestContext( return mojom::RequestContextType::SUBRESOURCE; } +network::mojom::RequestDestination ResourceFetcher::DetermineRequestDestination( + ResourceType type) { + switch (type) { + case ResourceType::kXSLStyleSheet: + DCHECK(RuntimeEnabledFeatures::XSLTEnabled()); + FALLTHROUGH; + case ResourceType::kCSSStyleSheet: + return network::mojom::RequestDestination::kStyle; + case ResourceType::kScript: + return network::mojom::RequestDestination::kScript; + case ResourceType::kFont: + return network::mojom::RequestDestination::kFont; + case ResourceType::kImage: + return network::mojom::RequestDestination::kImage; + case ResourceType::kTextTrack: + return network::mojom::RequestDestination::kTrack; + case ResourceType::kSVGDocument: + return network::mojom::RequestDestination::kImage; + case ResourceType::kAudio: + return network::mojom::RequestDestination::kAudio; + case ResourceType::kVideo: + return network::mojom::RequestDestination::kVideo; + case ResourceType::kManifest: + return network::mojom::RequestDestination::kManifest; + case ResourceType::kRaw: + case ResourceType::kImportResource: + case ResourceType::kLinkPrefetch: + case ResourceType::kMock: + return network::mojom::RequestDestination::kEmpty; + } + NOTREACHED(); + return network::mojom::RequestDestination::kEmpty; +} + // static void ResourceFetcher::AddPriorityObserverForTesting( const KURL& resource_url, @@ -441,7 +417,7 @@ void ResourceFetcher::AddPriorityObserverForTesting( // images, which may need to be reprioritized. ResourceLoadPriority ResourceFetcher::ComputeLoadPriority( ResourceType type, - const ResourceRequest& resource_request, + const ResourceRequestHead& resource_request, ResourcePriority::VisibilityStatus visibility, FetchParameters::DeferOption defer_option, FetchParameters::SpeculativePreloadType speculative_preload_type, @@ -524,17 +500,13 @@ ResourceLoadPriority ResourceFetcher::ComputeLoadPriority( if (properties_->IsSubframeDeprioritizationEnabled()) { if (properties_->IsMainFrame()) { - DEFINE_STATIC_LOCAL( - EnumerationHistogram, main_frame_priority_histogram, - ("LowPriorityIframes.MainFrameRequestPriority", - static_cast<int>(ResourceLoadPriority::kHighest) + 1)); - main_frame_priority_histogram.Count(static_cast<int>(priority)); + UMA_HISTOGRAM_ENUMERATION( + "LowPriorityIframes.MainFrameRequestPriority", priority, + static_cast<int>(ResourceLoadPriority::kHighest) + 1); } else { - DEFINE_STATIC_LOCAL( - EnumerationHistogram, iframe_priority_histogram, - ("LowPriorityIframes.IframeRequestPriority", - static_cast<int>(ResourceLoadPriority::kHighest) + 1)); - iframe_priority_histogram.Count(static_cast<int>(priority)); + UMA_HISTOGRAM_ENUMERATION( + "LowPriorityIframes.IframeRequestPriority", priority, + static_cast<int>(ResourceLoadPriority::kHighest) + 1); // When enabled, the priority of all resources in subframe is dropped. // Non-delayable resources are assigned a priority of kLow, and the rest // of them are assigned a priority of kLowest. This ensures that if the @@ -556,10 +528,10 @@ ResourceFetcher::ResourceFetcher(const ResourceFetcherInit& init) context_(init.context), task_runner_(init.task_runner), use_counter_(init.use_counter - ? init.use_counter.Get() + ? init.use_counter : MakeGarbageCollected<DetachableUseCounter>(nullptr)), console_logger_(init.console_logger - ? init.console_logger.Get() + ? init.console_logger : MakeGarbageCollected<DetachableConsoleLogger>()), loader_factory_(init.loader_factory), scheduler_(MakeGarbageCollected<ResourceLoadScheduler>( @@ -622,7 +594,7 @@ bool ResourceFetcher::ResourceNeedsLoad(Resource* resource, FetchParameters::kDeferImageLoad)) { return false; } - return policy != kUse || resource->StillNeedsLoad(); + return policy != RevalidationPolicy::kUse || resource->StillNeedsLoad(); } void ResourceFetcher::DidLoadResourceFromMemoryCache( @@ -653,7 +625,7 @@ void ResourceFetcher::DidLoadResourceFromMemoryCache( // they're used. scoped_refptr<ResourceTimingInfo> info = ResourceTimingInfo::Create( resource->Options().initiator_info.name, base::TimeTicks::Now(), - request.GetRequestContext()); + request.GetRequestContext(), request.GetRequestDestination()); // TODO(yoav): GetInitialUrlForResourceTiming() is only needed until // Out-of-Blink CORS lands: https://crbug.com/736308 info->SetInitialURL( @@ -772,11 +744,6 @@ void ResourceFetcher::UpdateMemoryCacheStats(Resource* resource, if (is_static_data) return; - DEFINE_THREAD_SAFE_STATIC_LOCAL( - BooleanHistogram, resource_histogram, - ("Blink.ResourceFetcher.StaleWhileRevalidate")); - resource_histogram.Count(params.IsStaleRevalidation()); - if (params.IsSpeculativePreload() || params.IsLinkPreload()) { DEFINE_RESOURCE_HISTOGRAM("Preload."); } else { @@ -790,6 +757,13 @@ void ResourceFetcher::UpdateMemoryCacheStats(Resource* resource, if (resource && !resource->IsAlive() && !ContainsAsPreload(resource)) { DEFINE_RESOURCE_HISTOGRAM("Dead."); } + + // Async (and defer) scripts may have more cache misses, track them + // separately. See https://crbug.com/1043679 for context. + if (params.Defer() != FetchParameters::DeferOption::kNoDefer && + factory.GetType() == ResourceType::kScript) { + UMA_HISTOGRAM_ENUMERATION(RESOURCE_HISTOGRAM_PREFIX "AsyncScript", policy); + } } bool ResourceFetcher::ContainsAsPreload(Resource* resource) const { @@ -821,10 +795,9 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest( params.OverrideContentType(factory.ContentType()); // Don't send security violation reports for speculative preloads. - SecurityViolationReportingPolicy reporting_policy = - params.IsSpeculativePreload() - ? SecurityViolationReportingPolicy::kSuppressReporting - : SecurityViolationReportingPolicy::kReport; + ReportingDisposition reporting_disposition = + params.IsSpeculativePreload() ? ReportingDisposition::kSuppressReporting + : ReportingDisposition::kReport; // Note that resource_request.GetRedirectStatus() may return kFollowedRedirect // here since e.g. ThreadableLoader may create a new Resource from @@ -837,7 +810,7 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest( Context().CheckCSPForRequest( resource_request.GetRequestContext(), MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url()), options, - reporting_policy, resource_request.GetRedirectStatus()); + reporting_disposition, resource_request.GetRedirectStatus()); // This may modify params.Url() (via the resource_request argument). Context().PopulateResourceRequest( @@ -868,6 +841,8 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest( mojom::RequestContextType::UNSPECIFIED) { resource_request.SetRequestContext( DetermineRequestContext(resource_type, kImageNotImageSet)); + resource_request.SetRequestDestination( + DetermineRequestDestination(resource_type)); } if (resource_type == ResourceType::kLinkPrefetch) resource_request.SetPurposeHeader("prefetch"); @@ -885,9 +860,6 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest( resource_request.SetExternalRequestStateFromRequestorAddressSpace( properties_->GetFetchClientSettingsObject().GetAddressSpace()); - SetSecFetchHeaders(resource_request, - properties_->GetFetchClientSettingsObject()); - Context().AddAdditionalRequestHeaders(resource_request); TRACE_EVENT_NESTABLE_ASYNC_INSTANT1( @@ -898,7 +870,7 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest( KURL url = MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url()); base::Optional<ResourceRequestBlockedReason> blocked_reason = Context().CanRequest(resource_type, resource_request, url, options, - reporting_policy, + reporting_disposition, resource_request.GetRedirectStatus()); if (Context().CalculateIfAdSubresource(resource_request, resource_type)) @@ -952,10 +924,6 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, if (should_log_request_as_invalid_in_imported_document_) { DCHECK(properties_->IsDetached()); - // We don't expect the fetcher to be used, so count such unexpected use. - UMA_HISTOGRAM_ENUMERATION("HTMLImport.UnexpectedRequest", - factory.GetType()); - base::debug::DumpWithoutCrashing(); } @@ -1012,7 +980,7 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, } Resource* resource = nullptr; - RevalidationPolicy policy = kLoad; + RevalidationPolicy policy = RevalidationPolicy::kLoad; bool is_data_url = resource_request.Url().ProtocolIsData(); bool is_static_data = is_data_url || archive_; @@ -1035,7 +1003,7 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, if (!is_stale_revalidation && !resource) { resource = MatchPreload(params, resource_type); if (resource) { - policy = kUse; + policy = RevalidationPolicy::kUse; // If |params| is for a blocking resource and a preloaded resource is // found, we may need to make it block the onload event. MakePreloadedResourceBlockOnloadIfNeeded(resource, params); @@ -1052,16 +1020,16 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, UpdateMemoryCacheStats(resource, policy, params, factory, is_static_data); switch (policy) { - case kReload: + case RevalidationPolicy::kReload: GetMemoryCache()->Remove(resource); FALLTHROUGH; - case kLoad: + case RevalidationPolicy::kLoad: resource = CreateResourceForLoading(params, factory); break; - case kRevalidate: + case RevalidationPolicy::kRevalidate: InitializeRevalidation(resource_request, resource); break; - case kUse: + case RevalidationPolicy::kUse: if (resource_request.AllowsStaleResponse() && resource->ShouldRevalidateStaleResponse()) { ScheduleStaleRevalidate(resource); @@ -1072,7 +1040,7 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, DCHECK(resource); DCHECK_EQ(resource->GetType(), resource_type); - if (policy != kUse) + if (policy != RevalidationPolicy::kUse) resource->VirtualTimePauser() = std::move(pauser); if (client) @@ -1080,7 +1048,7 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, // TODO(yoav): It is not clear why preloads are exempt from this check. Can we // remove the exemption? - if (!params.IsSpeculativePreload() || policy != kUse) { + if (!params.IsSpeculativePreload() || policy != RevalidationPolicy::kUse) { // When issuing another request for a resource that is already in-flight // make sure to not demote the priority of the in-flight request. If the new // request isn't at the same priority as the in-flight request, only allow @@ -1095,7 +1063,8 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, // If only the fragment identifiers differ, it is the same resource. DCHECK(EqualIgnoringFragmentIdentifier(resource->Url(), params.Url())); - if (policy == kUse && resource->GetStatus() == ResourceStatus::kCached && + if (policy == RevalidationPolicy::kUse && + resource->GetStatus() == ResourceStatus::kCached && !cached_resources_map_.Contains( MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url()))) { // Loaded from MemoryCache. @@ -1124,13 +1093,14 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, // the resource was already initialized for the revalidation here, but won't // start loading. if (ResourceNeedsLoad(resource, params, policy)) { - if (!StartLoad(resource)) { + if (!StartLoad(resource, + std::move(params.MutableResourceRequest().MutableBody()))) { resource->FinishAsError(ResourceError::CancelledError(params.Url()), task_runner_.get()); } } - if (policy != kUse) + if (policy != RevalidationPolicy::kUse) InsertAsPreloadIfNecessary(resource, params, resource_type); if (resource->InspectorId() != identifier || @@ -1240,7 +1210,8 @@ void ResourceFetcher::StorePerformanceTimingInitiatorInformation( scoped_refptr<ResourceTimingInfo> info = ResourceTimingInfo::Create( fetch_initiator, base::TimeTicks::Now(), - resource->GetResourceRequest().GetRequestContext()); + resource->GetResourceRequest().GetRequestContext(), + resource->GetResourceRequest().GetRequestDestination()); resource_timing_info_map_.insert(resource, std::move(info)); } @@ -1430,13 +1401,13 @@ ResourceFetcher::DetermineRevalidationPolicy( const char* ResourceFetcher::GetNameFor(RevalidationPolicy policy) { switch (policy) { - case kUse: + case RevalidationPolicy::kUse: return "use"; - case kRevalidate: + case RevalidationPolicy::kRevalidate: return "revalidate"; - case kReload: + case RevalidationPolicy::kReload: return "reload"; - case kLoad: + case RevalidationPolicy::kLoad: return "load"; } NOTREACHED(); @@ -1451,11 +1422,13 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( const ResourceRequest& request = fetch_params.GetResourceRequest(); if (IsDownloadOrStreamRequest(request)) { - return std::make_pair(kReload, "It is for download or for streaming."); + return {RevalidationPolicy::kReload, + "It is for download or for streaming."}; } if (IsImageResourceDisallowedToBeReused(existing_resource)) { - return std::make_pair(kReload, "Reload due to 'allow image' settings."); + return {RevalidationPolicy::kReload, + "Reload due to 'allow image' settings."}; } // If the existing resource is loading and the associated fetcher is not equal @@ -1463,8 +1436,8 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( // happen in redirect handling. if (existing_resource.Loader() && existing_resource.Loader()->Fetcher() != this) { - return std::make_pair( - kReload, "The existing resource is loading in a foreign fetcher."); + return {RevalidationPolicy::kReload, + "The existing resource is loading in a foreign fetcher."}; } // It's hard to share a not-yet-referenced preloads via MemoryCache correctly. @@ -1472,9 +1445,9 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( // the memory cache could be used without this block. if ((fetch_params.IsLinkPreload() || fetch_params.IsSpeculativePreload()) && existing_resource.IsUnusedPreload()) { - return std::make_pair(kReload, - "The existing resource is an unused preload made " - "from a foreign fetcher."); + return {RevalidationPolicy::kReload, + "The existing resource is an unused preload made " + "from a foreign fetcher."}; } // Checks if the resource has an explicit policy about integrity metadata. @@ -1493,7 +1466,7 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( // uncommon case, however, as it implies two same-origin requests to the same // resource, but with different integrity metadata. if (existing_resource.MustRefetchDueToIntegrityMetadata(fetch_params)) { - return std::make_pair(kReload, "Reload due to resource integrity."); + return {RevalidationPolicy::kReload, "Reload due to resource integrity."}; } // If the same URL has been loaded as a different type, we need to reload. @@ -1502,35 +1475,36 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( // We really should discard the new prefetch since the preload has more // specific type information! crbug.com/379893 // fast/dom/HTMLLinkElement/link-and-subresource-test hits this case. - return std::make_pair(kReload, "Reload due to type mismatch."); + return {RevalidationPolicy::kReload, "Reload due to type mismatch."}; } // If resource was populated from archive or data: url, use it. // This doesn't necessarily mean that |resource| was just created by using // ResourceForStaticData(). if (is_static_data) { - return std::make_pair(kUse, "Use the existing static resource."); + return {RevalidationPolicy::kUse, "Use the existing static resource."}; } if (existing_resource.CanReuse(fetch_params) != Resource::MatchStatus::kOk) { - return std::make_pair(kReload, "Reload due to Resource::CanReuse."); + return {RevalidationPolicy::kReload, "Reload due to Resource::CanReuse."}; } // Don't reload resources while pasting. if (allow_stale_resources_) { - return std::make_pair( - kUse, "Use the existing resource due to |allow_stale_resources_|."); + return {RevalidationPolicy::kUse, + "Use the existing resource due to |allow_stale_resources_|."}; } // FORCE_CACHE uses the cache no matter what. if (request.GetCacheMode() == mojom::FetchCacheMode::kForceCache) { - return std::make_pair( - kUse, "Use the existing resource due to cache-mode: 'force-cache'."); + return {RevalidationPolicy::kUse, + "Use the existing resource due to cache-mode: 'force-cache'."}; } // Don't reuse resources with Cache-control: no-store. if (existing_resource.HasCacheControlNoStoreHeader()) { - return std::make_pair(kReload, "Reload due to cache-control: no-sotre."); + return {RevalidationPolicy::kReload, + "Reload due to cache-control: no-sotre."}; } // During the initial load, avoid loading the same resource multiple times for @@ -1543,25 +1517,25 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( cached_resources_map_.Contains( MemoryCache::RemoveFragmentIdentifierIfNeeded( existing_resource.Url()))) { - return std::make_pair(kUse, - "Avoid making multiple requests for the same URL " - "during the initial load."); + return {RevalidationPolicy::kUse, + "Avoid making multiple requests for the same URL " + "during the initial load."}; } if (existing_resource.IsLoading()) { - return std::make_pair( - kUse, "Use the existing resource because it's being loaded."); + return {RevalidationPolicy::kUse, + "Use the existing resource because it's being loaded."}; } } // RELOAD always reloads if (request.GetCacheMode() == mojom::FetchCacheMode::kBypassCache) { - return std::make_pair(kReload, "Reload due to cache-mode: 'reload'."); + return {RevalidationPolicy::kReload, "Reload due to cache-mode: 'reload'."}; } // We'll try to reload the resource if it failed last time. if (existing_resource.ErrorOccurred()) { - return std::make_pair( - kReload, "Reload because the existing resource has failed loading."); + return {RevalidationPolicy::kReload, + "Reload because the existing resource has failed loading."}; } // List of available images logic allows images to be re-used without cache @@ -1569,18 +1543,19 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( // same as the version in the current document. if (type == ResourceType::kImage && &existing_resource == CachedResource(request.Url())) { - return std::make_pair(kUse, - "Images can be reused without cache validation."); + return {RevalidationPolicy::kUse, + "Images can be reused without cache validation."}; } if (existing_resource.MustReloadDueToVaryHeader(request)) { - return std::make_pair(kReload, "Reload due to vary header."); + return {RevalidationPolicy::kReload, "Reload due to vary header."}; } // If any of the redirects in the chain to loading the resource were not // cacheable, we cannot reuse our cached resource. if (!existing_resource.CanReuseRedirectChain()) { - return std::make_pair(kReload, "Reload due to an uncacheable redirect."); + return {RevalidationPolicy::kReload, + "Reload due to an uncacheable redirect."}; } // Check if the cache headers requires us to revalidate (cache expiration for @@ -1592,8 +1567,8 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( // Revalidation is harmful for non-matched preloads because it may lead to // sharing one preloaded resource among multiple ResourceFetchers. if (existing_resource.IsUnusedPreload()) { - return std::make_pair( - kReload, "Revalidation is harmful for non-matched preloads."); + return {RevalidationPolicy::kReload, + "Revalidation is harmful for non-matched preloads."}; } // See if the resource has usable ETag or Last-modified headers. If the page @@ -1612,20 +1587,19 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( // |Use| policy should be applied to subsequent requests. if (existing_resource.IsCacheValidator()) { DCHECK(existing_resource.StillNeedsLoad()); - return std::make_pair( - kUse, - "Merged to the revalidate request which has not yet started."); + return {RevalidationPolicy::kUse, + "Merged to the revalidate request which has not yet started."}; } - return std::make_pair(kRevalidate, ""); + return {RevalidationPolicy::kRevalidate, ""}; } // No, must reload. - return std::make_pair(kReload, "Reload due to missing cache validators."); + return {RevalidationPolicy::kReload, + "Reload due to missing cache validators."}; } - return std::make_pair( - kUse, - "Use the existing resource because there is no reason not to do so."); + return {RevalidationPolicy::kUse, + "Use the existing resource because there is no reason not to do so."}; } void ResourceFetcher::SetAutoLoadImages(bool enable) { @@ -1877,6 +1851,11 @@ void ResourceFetcher::MoveResourceLoaderToNonBlocking(ResourceLoader* loader) { } bool ResourceFetcher::StartLoad(Resource* resource) { + return StartLoad(resource, ResourceRequestBody()); +} + +bool ResourceFetcher::StartLoad(Resource* resource, + ResourceRequestBody request_body) { DCHECK(resource); DCHECK(resource->StillNeedsLoad()); @@ -1894,11 +1873,13 @@ bool ResourceFetcher::StartLoad(Resource* resource) { return false; } - const auto& request = resource->GetResourceRequest(); - ResourceResponse response; + const ResourceRequestHead& request_head = resource->GetResourceRequest(); if (resource_load_observer_) { DCHECK(!IsDetached()); + ResourceRequest request(request_head); + request.SetHttpBody(request_body.FormBody()); + ResourceResponse response; resource_load_observer_->WillSendRequest( resource->InspectorId(), request, response, resource->GetType(), resource->Options().initiator_info); @@ -1906,8 +1887,8 @@ bool ResourceFetcher::StartLoad(Resource* resource) { using QuotaType = decltype(inflight_keepalive_bytes_); QuotaType size = 0; - if (request.GetKeepalive() && request.HttpBody()) { - auto original_size = request.HttpBody()->SizeInBytes(); + if (request_head.GetKeepalive() && request_body.FormBody()) { + auto original_size = request_body.FormBody()->SizeInBytes(); DCHECK_LE(inflight_keepalive_bytes_, kKeepaliveInflightBytesQuota); if (original_size > std::numeric_limits<QuotaType>::max()) return false; @@ -1918,8 +1899,8 @@ bool ResourceFetcher::StartLoad(Resource* resource) { inflight_keepalive_bytes_ += size; } - loader = - MakeGarbageCollected<ResourceLoader>(this, scheduler_, resource, size); + loader = MakeGarbageCollected<ResourceLoader>( + this, scheduler_, resource, std::move(request_body), size); // Preload requests should not block the load event. IsLinkPreload() // actually continues to return true for Resources matched from the preload // cache that must block the load event, but that is OK because this method @@ -2037,12 +2018,14 @@ void ResourceFetcher::EmulateLoadStartedForInspector( Resource* resource, const KURL& url, mojom::RequestContextType request_context, + network::mojom::RequestDestination request_destination, const AtomicString& initiator_name) { base::AutoReset<bool> r(&is_in_request_resource_, true); if (CachedResource(url)) return; ResourceRequest resource_request(url); resource_request.SetRequestContext(request_context); + resource_request.SetRequestDestination(request_destination); if (!resource_request.PriorityHasBeenSet()) { resource_request.SetPriority(ComputeLoadPriority( resource->GetType(), resource_request, ResourcePriority::kNotVisible, @@ -2050,14 +2033,17 @@ void ResourceFetcher::EmulateLoadStartedForInspector( FetchParameters::SpeculativePreloadType::kNotSpeculative, false /* is_link_preload */)); } + resource_request.SetReferrerString(Referrer::NoReferrer()); + resource_request.SetReferrerPolicy(network::mojom::ReferrerPolicy::kNever); ResourceLoaderOptions options = resource->Options(); options.initiator_info.name = initiator_name; - FetchParameters params(resource_request, options); - Context().CanRequest(resource->GetType(), resource->LastResourceRequest(), - resource->LastResourceRequest().Url(), params.Options(), - SecurityViolationReportingPolicy::kReport, - resource->LastResourceRequest().GetRedirectStatus()); + FetchParameters params(std::move(resource_request), options); + ResourceRequest last_resource_request(resource->LastResourceRequest()); + Context().CanRequest(resource->GetType(), last_resource_request, + last_resource_request.Url(), params.Options(), + ReportingDisposition::kReport, + last_resource_request.GetRedirectStatus()); DidLoadResourceFromMemoryCache(resource->InspectorId(), resource, params.GetResourceRequest(), false /* is_static_data */); @@ -2113,7 +2099,9 @@ void ResourceFetcher::RevalidateStaleResource(Resource* stale_resource) { // purpose this is probably fine. // TODO(dtapuska): revisit this when we have a better way to re-dispatch // requests. - FetchParameters params(stale_resource->GetResourceRequest()); + ResourceRequest request; + request.CopyHeadFrom(stale_resource->GetResourceRequest()); + FetchParameters params(std::move(request)); params.SetStaleRevalidation(true); params.MutableResourceRequest().SetSkipServiceWorker(true); // Stale revalidation resource requests should be very low regardless of @@ -2126,7 +2114,7 @@ void ResourceFetcher::RevalidateStaleResource(Resource* stale_resource) { mojom::blink::BlobRegistry* ResourceFetcher::GetBlobRegistry() { if (!blob_registry_remote_) { - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( blob_registry_remote_.BindNewPipeAndPassReceiver(task_runner_)); } return blob_registry_remote_.get(); @@ -2136,7 +2124,7 @@ FrameScheduler* ResourceFetcher::GetFrameScheduler() { return frame_scheduler_.get(); } -void ResourceFetcher::Trace(blink::Visitor* visitor) { +void ResourceFetcher::Trace(Visitor* visitor) { visitor->Trace(context_); visitor->Trace(properties_); visitor->Trace(resource_load_observer_); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h index 9c3ad282fc6..75ac2446bee 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h @@ -102,7 +102,7 @@ class PLATFORM_EXPORT ResourceFetcher // in ResourceFetcherInit to ensure correctness of this ResourceFetcher. explicit ResourceFetcher(const ResourceFetcherInit&); virtual ~ResourceFetcher(); - virtual void Trace(blink::Visitor*); + virtual void Trace(Visitor*); // - This function returns the same object throughout this fetcher's // entire life. @@ -168,6 +168,7 @@ class PLATFORM_EXPORT ResourceFetcher // call this method explicitly on cases such as ResourceNeedsLoad() returning // false. bool StartLoad(Resource*); + bool StartLoad(Resource*, ResourceRequestBody); void SetAutoLoadImages(bool); void SetImagesEnabled(bool); @@ -223,6 +224,9 @@ class PLATFORM_EXPORT ResourceFetcher ResourceType, IsImageSet); + static network::mojom::RequestDestination DetermineRequestDestination( + ResourceType); + void UpdateAllImageResourcePriorities(); // Returns whether the given resource is contained as a preloaded resource. @@ -237,6 +241,7 @@ class PLATFORM_EXPORT ResourceFetcher void EmulateLoadStartedForInspector(Resource*, const KURL&, mojom::RequestContextType, + network::mojom::RequestDestination, const AtomicString& initiator_name); // This is called from leak detectors (Real-world leak detector & web test @@ -253,7 +258,7 @@ class PLATFORM_EXPORT ResourceFetcher ResourceLoadPriority ComputeLoadPriorityForTesting( ResourceType type, - const ResourceRequest& request, + const ResourceRequestHead& request, ResourcePriority::VisibilityStatus visibility_statue, FetchParameters::DeferOption defer_option, FetchParameters::SpeculativePreloadType speculative_preload_type, @@ -284,7 +289,7 @@ class PLATFORM_EXPORT ResourceFetcher void StorePerformanceTimingInitiatorInformation(Resource*); ResourceLoadPriority ComputeLoadPriority( ResourceType, - const ResourceRequest&, + const ResourceRequestHead&, ResourcePriority::VisibilityStatus, FetchParameters::DeferOption = FetchParameters::kNoDefer, FetchParameters::SpeculativePreloadType = @@ -319,7 +324,13 @@ class PLATFORM_EXPORT ResourceFetcher void StopFetchingIncludingKeepaliveLoaders(); // RevalidationPolicy enum values are used in UMAs https://crbug.com/579496. - enum RevalidationPolicy { kUse, kRevalidate, kReload, kLoad }; + enum class RevalidationPolicy { + kUse, + kRevalidate, + kReload, + kLoad, + kMaxValue = kLoad + }; // A wrapper just for placing a trace_event macro. RevalidationPolicy DetermineRevalidationPolicy( @@ -445,7 +456,7 @@ class ResourceCacheValidationSuppressor { } private: - Member<ResourceFetcher> loader_; + ResourceFetcher* loader_; bool previous_state_; DISALLOW_COPY_AND_ASSIGN(ResourceCacheValidationSuppressor); @@ -465,15 +476,15 @@ struct PLATFORM_EXPORT ResourceFetcherInit final { scoped_refptr<base::SingleThreadTaskRunner> task_runner, ResourceFetcher::LoaderFactory* loader_factory); - const Member<DetachableResourceFetcherProperties> properties; - const Member<FetchContext> context; + DetachableResourceFetcherProperties* const properties; + FetchContext* const context; const scoped_refptr<base::SingleThreadTaskRunner> task_runner; - const Member<ResourceFetcher::LoaderFactory> loader_factory; - Member<DetachableUseCounter> use_counter; - Member<DetachableConsoleLogger> console_logger; + ResourceFetcher::LoaderFactory* const loader_factory; + DetachableUseCounter* use_counter = nullptr; + DetachableConsoleLogger* console_logger = nullptr; ResourceLoadScheduler::ThrottlingPolicy initial_throttling_policy = ResourceLoadScheduler::ThrottlingPolicy::kNormal; - Member<MHTMLArchive> archive; + MHTMLArchive* archive = nullptr; FrameScheduler* frame_scheduler = nullptr; DISALLOW_COPY_AND_ASSIGN(ResourceFetcherInit); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc index 3b14d081582..8890de7be67 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties_test.cc @@ -6,6 +6,7 @@ #include "services/network/public/mojom/ip_address_space.mojom-blink.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h" #include "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom-blink.h" #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h" #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h" @@ -26,7 +27,7 @@ class DetachableResourceFetcherPropertiesTest : public testing::Test { network::mojom::ReferrerPolicy::kDefault, "https://example.com/foo.html", HttpsState::kModern, AllowedByNosniff::MimeTypeCheck::kStrict, address_space, - kLeaveInsecureRequestsAlone, + mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone, FetchClientSettingsObject::InsecureNavigationsSet()); } }; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc index bb529c83964..980d15a3fd0 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc @@ -37,6 +37,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h" +#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_url_loader.h" #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" @@ -103,10 +104,26 @@ const FetchClientSettingsObjectSnapshot& CreateFetchClientSettingsObject( SecurityOrigin::Create(KURL("https://example.com/")), network::mojom::ReferrerPolicy::kDefault, "https://example.com/foo.html", HttpsState::kModern, AllowedByNosniff::MimeTypeCheck::kStrict, - address_space, kLeaveInsecureRequestsAlone, + address_space, + mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone, FetchClientSettingsObject::InsecureNavigationsSet()); } +class PartialResourceRequest { + public: + PartialResourceRequest() : PartialResourceRequest(ResourceRequest()) {} + PartialResourceRequest(const ResourceRequest& request) + : is_ad_resource_(request.IsAdResource()), + priority_(request.Priority()) {} + + bool IsAdResource() const { return is_ad_resource_; } + ResourceLoadPriority Priority() const { return priority_; } + + private: + bool is_ad_resource_; + ResourceLoadPriority priority_; +}; + } // namespace class ResourceFetcherTest : public testing::Test { @@ -123,7 +140,7 @@ class ResourceFetcherTest : public testing::Test { const ResourceResponse& redirect_response, ResourceType, const FetchInitiatorInfo&) override { - request_ = request; + request_ = PartialResourceRequest(request); } void DidChangePriority(uint64_t identifier, ResourceLoadPriority, @@ -149,12 +166,12 @@ class ResourceFetcherTest : public testing::Test { int64_t encoded_data_length, IsInternalRequest is_internal_request) override {} - const base::Optional<ResourceRequest>& GetLastRequest() const { + const base::Optional<PartialResourceRequest>& GetLastRequest() const { return request_; } private: - base::Optional<ResourceRequest> request_; + base::Optional<PartialResourceRequest> request_; }; protected: @@ -194,7 +211,7 @@ TEST_F(ResourceFetcherTest, StartLoadAfterFrameDetach) { fetcher->ClearContext(); ResourceRequest resource_request(secure_url); resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr); ASSERT_TRUE(resource); EXPECT_TRUE(resource->ErrorOccurred()); @@ -256,12 +273,13 @@ TEST_F(ResourceFetcherTest, WillSendRequestAdBit) { ResourceRequest resource_request(url); resource_request.SetIsAdResource(); resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), ""); Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr); EXPECT_EQ(resource, new_resource); - base::Optional<ResourceRequest> new_request = observer->GetLastRequest(); + base::Optional<PartialResourceRequest> new_request = + observer->GetLastRequest(); EXPECT_TRUE(new_request.has_value()); EXPECT_TRUE(new_request.value().IsAdResource()); } @@ -286,7 +304,7 @@ TEST_F(ResourceFetcherTest, Vary) { *MakeGarbageCollected<TestResourceFetcherProperties>(source_origin)); ResourceRequest resource_request(url); resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), ""); Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr); EXPECT_NE(resource, new_resource); @@ -296,7 +314,8 @@ TEST_F(ResourceFetcherTest, Vary) { TEST_F(ResourceFetcherTest, ResourceTimingInfo) { auto info = ResourceTimingInfo::Create( fetch_initiator_type_names::kDocument, base::TimeTicks::Now(), - mojom::RequestContextType::UNSPECIFIED); + mojom::RequestContextType::UNSPECIFIED, + network::mojom::RequestDestination::kEmpty); info->AddFinalTransferSize(5); EXPECT_EQ(info->TransferSize(), static_cast<uint64_t>(5)); ResourceResponse redirect_response(KURL("https://example.com/original")); @@ -328,7 +347,7 @@ TEST_F(ResourceFetcherTest, VaryOnBack) { ResourceRequest resource_request(url); resource_request.SetCacheMode(mojom::FetchCacheMode::kForceCache); resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr); EXPECT_EQ(resource, new_resource); } @@ -383,7 +402,7 @@ class RequestSameResourceOnComplete MakeGarbageCollected<TestLoaderFactory>())); ResourceRequest resource_request2(GetResource()->Url()); resource_request2.SetCacheMode(mojom::FetchCacheMode::kValidateCache); - FetchParameters fetch_params2(resource_request2); + FetchParameters fetch_params2(std::move(resource_request2)); Resource* resource2 = MockResource::Fetch(fetch_params2, fetcher2, nullptr); EXPECT_EQ(GetResource(), resource2); notify_finished_called_ = true; @@ -391,9 +410,7 @@ class RequestSameResourceOnComplete } bool NotifyFinishedCalled() const { return notify_finished_called_; } - void Trace(blink::Visitor* visitor) override { - RawResourceClient::Trace(visitor); - } + void Trace(Visitor* visitor) override { RawResourceClient::Trace(visitor); } String DebugName() const override { return "RequestSameResourceOnComplete"; } @@ -419,7 +436,7 @@ TEST_F(ResourceFetcherTest, RevalidateWhileFinishingLoading) { *MakeGarbageCollected<TestResourceFetcherProperties>(source_origin)); ResourceRequest request1(url); request1.SetHttpHeaderField(http_names::kCacheControl, "no-cache"); - FetchParameters fetch_params1(request1); + FetchParameters fetch_params1(std::move(request1)); Persistent<RequestSameResourceOnComplete> client = MakeGarbageCollected<RequestSameResourceOnComplete>(fetch_params1, fetcher1); @@ -440,7 +457,7 @@ TEST_F(ResourceFetcherTest, MAYBE_DontReuseMediaDataUrl) { ResourceLoaderOptions options; options.data_buffering_policy = kDoNotBufferData; options.initiator_info.name = fetch_initiator_type_names::kInternal; - FetchParameters fetch_params(request, options); + FetchParameters fetch_params(std::move(request), options); Resource* resource1 = RawResource::FetchMedia(fetch_params, fetcher, nullptr); Resource* resource2 = RawResource::FetchMedia(fetch_params, fetcher, nullptr); EXPECT_NE(resource1, resource2); @@ -477,9 +494,7 @@ class ServeRequestsOnCompleteClient final } void DataDownloaded(Resource*, uint64_t) override { ASSERT_TRUE(false); } - void Trace(blink::Visitor* visitor) override { - RawResourceClient::Trace(visitor); - } + void Trace(Visitor* visitor) override { RawResourceClient::Trace(visitor); } String DebugName() const override { return "ServeRequestsOnCompleteClient"; } }; @@ -496,7 +511,7 @@ TEST_F(ResourceFetcherTest, ResponseOnCancel) { auto* fetcher = CreateFetcher(); ResourceRequest resource_request(url); resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); Persistent<ServeRequestsOnCompleteClient> client = MakeGarbageCollected<ServeRequestsOnCompleteClient>(); Resource* resource = RawResource::Fetch(fetch_params, fetcher, client); @@ -536,13 +551,13 @@ class ScopedMockRedirectRequester { MakeGarbageCollected<TestLoaderFactory>())); ResourceRequest resource_request(url); resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); RawResource::Fetch(fetch_params, fetcher, nullptr); url_test_helpers::ServeAsynchronousRequests(); } private: - Member<MockFetchContext> context_; + MockFetchContext* context_; const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; DISALLOW_COPY_AND_ASSIGN(ScopedMockRedirectRequester); @@ -596,7 +611,7 @@ TEST_F(ResourceFetcherTest, SynchronousRequest) { auto* fetcher = CreateFetcher(); ResourceRequest resource_request(url); resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); fetch_params.MakeSynchronous(); Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr); EXPECT_TRUE(resource->IsLoaded()); @@ -611,7 +626,7 @@ TEST_F(ResourceFetcherTest, PingPriority) { auto* fetcher = CreateFetcher(); ResourceRequest resource_request(url); resource_request.SetRequestContext(mojom::RequestContextType::PING); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr); EXPECT_EQ(ResourceLoadPriority::kVeryLow, resource->GetResourceRequest().Priority()); @@ -849,7 +864,7 @@ TEST_F(ResourceFetcherTest, Revalidate304) { *MakeGarbageCollected<TestResourceFetcherProperties>(source_origin)); ResourceRequest resource_request(url); resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), ""); Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr); fetcher->StopFetching(); @@ -918,7 +933,7 @@ TEST_F(ResourceFetcherTest, ContentIdURL) { { ResourceRequest resource_request(url); resource_request.SetRequestContext(mojom::RequestContextType::VIDEO); - FetchParameters fetch_params(resource_request); + FetchParameters fetch_params(std::move(resource_request)); RawResource* resource = RawResource::FetchMedia(fetch_params, fetcher, nullptr); ASSERT_NE(nullptr, resource); @@ -956,7 +971,7 @@ TEST_F(ResourceFetcherTest, StaleWhileRevalidate) { ResourceRequest resource_request(url); resource_request.SetRequestContext(mojom::RequestContextType::INTERNAL); - FetchParameters fetch_params2 = FetchParameters(resource_request); + FetchParameters fetch_params2 = FetchParameters(std::move(resource_request)); Resource* new_resource = MockResource::Fetch(fetch_params2, fetcher, nullptr); EXPECT_EQ(resource, new_resource); platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); @@ -976,7 +991,8 @@ TEST_F(ResourceFetcherTest, StaleWhileRevalidate) { EXPECT_TRUE(GetMemoryCache()->Contains(resource)); static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()) ->RunUntilIdle(); - base::Optional<ResourceRequest> swr_request = observer->GetLastRequest(); + base::Optional<PartialResourceRequest> swr_request = + observer->GetLastRequest(); ASSERT_TRUE(swr_request.has_value()); EXPECT_EQ(ResourceLoadPriority::kVeryLow, swr_request->Priority()); platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h index 72cfbda8334..376800c68cd 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h @@ -14,12 +14,13 @@ namespace blink { // ResourceFinishObserver is different from ResourceClient in several ways. // - NotifyFinished is dispatched asynchronously. // - ResourceFinishObservers will be removed from Resource when the load -// finishes. - This class is not intended to be "subclassed" per each Resource -// subclass. +// finishes. +// - This class is not intended to be "subclassed" per each Resource subclass. // There is no ImageResourceFinishObserver, for example. // ResourceFinishObserver should be quite simple. All notifications must be // notified AFTER the loading finishes. -class PLATFORM_EXPORT ResourceFinishObserver : public GarbageCollectedMixin { +class PLATFORM_EXPORT ResourceFinishObserver + : public GarbageCollected<ResourceFinishObserver> { public: virtual ~ResourceFinishObserver() = default; @@ -30,7 +31,7 @@ class PLATFORM_EXPORT ResourceFinishObserver : public GarbageCollectedMixin { // Name for debugging virtual String DebugName() const = 0; - void Trace(blink::Visitor* visitor) override {} + virtual void Trace(Visitor* visitor) {} }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc index 6496f9fe7e8..c79595b9005 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc @@ -235,7 +235,7 @@ ResourceLoadScheduler::ResourceLoadScheduler( ResourceLoadScheduler::~ResourceLoadScheduler() = default; -void ResourceLoadScheduler::Trace(blink::Visitor* visitor) { +void ResourceLoadScheduler::Trace(Visitor* visitor) { visitor->Trace(pending_request_map_); visitor->Trace(resource_fetcher_properties_); visitor->Trace(console_logger_); @@ -275,7 +275,7 @@ void ResourceLoadScheduler::Request(ResourceLoadSchedulerClient* client, // Check if the request can be throttled. ClientIdWithPriority request_info(*id, priority, intra_priority); - if (!IsClientDelayable(request_info, option)) { + if (!IsClientDelayable(option)) { Run(*id, client, false); return; } @@ -356,27 +356,15 @@ void ResourceLoadScheduler::SetOutstandingLimitForTesting(size_t tight_limit, MaybeRun(); } -bool ResourceLoadScheduler::IsClientDelayable(const ClientIdWithPriority& info, - ThrottleOption option) const { - const bool throttleable = option == ThrottleOption::kThrottleable && - info.priority < ResourceLoadPriority::kHigh; - const bool stoppable = option != ThrottleOption::kCanNotBeStoppedOrThrottled; - - // Also takes the lifecycle state of the associated FrameScheduler - // into account to determine if the request should be throttled - // regardless of the priority. +bool ResourceLoadScheduler::IsClientDelayable(ThrottleOption option) const { switch (frame_scheduler_lifecycle_state_) { case scheduler::SchedulingLifecycleState::kNotThrottled: - return throttleable; case scheduler::SchedulingLifecycleState::kHidden: case scheduler::SchedulingLifecycleState::kThrottled: return option == ThrottleOption::kThrottleable; case scheduler::SchedulingLifecycleState::kStopped: - return stoppable; + return option != ThrottleOption::kCanNotBeStoppedOrThrottled; } - - NOTREACHED() << static_cast<int>(frame_scheduler_lifecycle_state_); - return throttleable; } void ResourceLoadScheduler::OnLifecycleStateChanged( @@ -419,9 +407,6 @@ bool ResourceLoadScheduler::IsPendingRequestEffectivelyEmpty( } bool ResourceLoadScheduler::GetNextPendingRequest(ClientId* id) { - bool needs_throttling = - running_throttleable_requests_.size() >= GetOutstandingLimit(); - auto& stoppable_queue = pending_requests_[ThrottleOption::kStoppable]; auto& throttleable_queue = pending_requests_[ThrottleOption::kThrottleable]; @@ -429,14 +414,16 @@ bool ResourceLoadScheduler::GetNextPendingRequest(ClientId* id) { auto stoppable_it = stoppable_queue.begin(); bool has_runnable_stoppable_request = stoppable_it != stoppable_queue.end() && - (!IsClientDelayable(*stoppable_it, ThrottleOption::kStoppable) || - !needs_throttling); + (!IsClientDelayable(ThrottleOption::kStoppable) || + running_throttleable_requests_.size() < + GetOutstandingLimit(stoppable_it->priority)); auto throttleable_it = throttleable_queue.begin(); bool has_runnable_throttleable_request = throttleable_it != throttleable_queue.end() && - (!IsClientDelayable(*throttleable_it, ThrottleOption::kThrottleable) || - !needs_throttling); + (!IsClientDelayable(ThrottleOption::kThrottleable) || + running_throttleable_requests_.size() < + GetOutstandingLimit(throttleable_it->priority)); if (!has_runnable_throttleable_request && !has_runnable_stoppable_request) return false; @@ -489,7 +476,8 @@ void ResourceLoadScheduler::Run(ResourceLoadScheduler::ClientId id, client->Run(); } -size_t ResourceLoadScheduler::GetOutstandingLimit() const { +size_t ResourceLoadScheduler::GetOutstandingLimit( + ResourceLoadPriority priority) const { size_t limit = kOutstandingUnlimited; switch (frame_scheduler_lifecycle_state_) { @@ -506,7 +494,9 @@ size_t ResourceLoadScheduler::GetOutstandingLimit() const { switch (policy_) { case ThrottlingPolicy::kTight: - limit = std::min(limit, tight_outstanding_limit_); + limit = std::min(limit, priority < ResourceLoadPriority::kHigh + ? tight_outstanding_limit_ + : normal_outstanding_limit_); break; case ThrottlingPolicy::kNormal: limit = std::min(limit, normal_outstanding_limit_); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h index b951ecfd303..eb66476c349 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h @@ -33,7 +33,7 @@ class PLATFORM_EXPORT ResourceLoadSchedulerClient // Called when the request is granted to run. virtual void Run() = 0; - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} }; // ResourceLoadScheduler provides a unified per-frame infrastructure to schedule @@ -157,7 +157,7 @@ class PLATFORM_EXPORT ResourceLoadScheduler final DetachableConsoleLogger& console_logger); ~ResourceLoadScheduler() override; - void Trace(blink::Visitor*); + void Trace(Visitor*); // Changes the policy from |kTight| to |kNormal|. This function can be called // multiple times, and does nothing when the scheduler is already working with @@ -245,7 +245,7 @@ class PLATFORM_EXPORT ResourceLoadScheduler final priority(priority), intra_priority(intra_priority) {} - void Trace(blink::Visitor* visitor) { visitor->Trace(client); } + void Trace(Visitor* visitor) { visitor->Trace(client); } Member<ResourceLoadSchedulerClient> client; ThrottleOption option; @@ -261,10 +261,9 @@ class PLATFORM_EXPORT ResourceLoadScheduler final // Gets the highest priority pending request that is allowed to be run. bool GetNextPendingRequest(ClientId* id); - // Returns whether we can throttle a request with the given client info based + // Returns whether we can throttle a request with the given option based // on life cycle state. - bool IsClientDelayable(const ClientIdWithPriority& info, - ThrottleOption option) const; + bool IsClientDelayable(ThrottleOption option) const; // Generates the next ClientId. ClientId GenerateClientId(); @@ -275,7 +274,7 @@ class PLATFORM_EXPORT ResourceLoadScheduler final // Grants a client to run, void Run(ClientId, ResourceLoadSchedulerClient*, bool throttleable); - size_t GetOutstandingLimit() const; + size_t GetOutstandingLimit(ResourceLoadPriority priority) const; void ShowConsoleMessageIfNeeded(); @@ -283,7 +282,6 @@ class PLATFORM_EXPORT ResourceLoadScheduler final resource_fetcher_properties_; // A flag to indicate an internal running state. - // TODO(toyoshim): We may want to use enum once we start to have more states. bool is_shutdown_ = false; ThrottlingPolicy policy_ = ThrottlingPolicy::kNormal; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc index a5056c1becf..ffe0d245cfc 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc @@ -38,7 +38,7 @@ class MockClient final : public GarbageCollected<MockClient>, return client_order_; } - void Trace(blink::Visitor* visitor) { visitor->Trace(client_order_); } + void Trace(Visitor* visitor) { visitor->Trace(client_order_); } private: HeapVector<Member<MockClient>> client_order_; @@ -56,7 +56,7 @@ class MockClient final : public GarbageCollected<MockClient>, } bool WasRun() { return was_run_; } - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { ResourceLoadSchedulerClient::Trace(visitor); visitor->Trace(console_logger_); } @@ -349,7 +349,8 @@ TEST_F(ResourceLoadSchedulerTest, PriorityIsConsidered) { // Push three requests. MockClient* client1 = MakeGarbageCollected<MockClient>(); - Scheduler()->SetOutstandingLimitForTesting(0); + // Allows one kHigh priority request by limits below. + Scheduler()->SetOutstandingLimitForTesting(0, 1); ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId; Scheduler()->Request(client1, ThrottleOption::kThrottleable, @@ -383,23 +384,21 @@ TEST_F(ResourceLoadSchedulerTest, PriorityIsConsidered) { EXPECT_FALSE(client3->WasRun()); EXPECT_TRUE(client4->WasRun()); - // Client 4 does not count against the limit as it was not delayable when it - // was created. - Scheduler()->SetOutstandingLimitForTesting(1); + Scheduler()->SetOutstandingLimitForTesting(2); EXPECT_FALSE(client1->WasRun()); EXPECT_FALSE(client2->WasRun()); EXPECT_TRUE(client3->WasRun()); EXPECT_TRUE(client4->WasRun()); - Scheduler()->SetOutstandingLimitForTesting(2); + Scheduler()->SetOutstandingLimitForTesting(3); EXPECT_FALSE(client1->WasRun()); EXPECT_TRUE(client2->WasRun()); EXPECT_TRUE(client3->WasRun()); EXPECT_TRUE(client4->WasRun()); - Scheduler()->SetOutstandingLimitForTesting(3); + Scheduler()->SetOutstandingLimitForTesting(4); EXPECT_TRUE(client1->WasRun()); EXPECT_TRUE(client2->WasRun()); @@ -407,7 +406,6 @@ TEST_F(ResourceLoadSchedulerTest, PriorityIsConsidered) { EXPECT_TRUE(client4->WasRun()); // Release the rest. - EXPECT_TRUE(Release(id4)); EXPECT_TRUE(Release(id3)); EXPECT_TRUE(Release(id2)); EXPECT_TRUE(Release(id1)); @@ -511,12 +509,11 @@ TEST_F(ResourceLoadSchedulerTest, StoppableRequestResumesWhenThrottled) { } TEST_F(ResourceLoadSchedulerTest, SetPriority) { - // Start with the normal scheduling policy. - Scheduler()->LoosenThrottlingPolicy(); // Push three requests. MockClient* client1 = MakeGarbageCollected<MockClient>(); - Scheduler()->SetOutstandingLimitForTesting(0); + // Allows one kHigh priority request by limits below. + Scheduler()->SetOutstandingLimitForTesting(0, 1); ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId; Scheduler()->Request(client1, ThrottleOption::kThrottleable, @@ -554,7 +551,16 @@ TEST_F(ResourceLoadSchedulerTest, SetPriority) { EXPECT_FALSE(client2->WasRun()); EXPECT_FALSE(client3->WasRun()); - Scheduler()->SetOutstandingLimitForTesting(2); + // Loosen the policy to adopt the normal limit for all. + Scheduler()->LoosenThrottlingPolicy(); + Scheduler()->SetOutstandingLimitForTesting(0, 2); + + EXPECT_TRUE(client1->WasRun()); + EXPECT_TRUE(client2->WasRun()); + EXPECT_FALSE(client3->WasRun()); + + // kHigh priority does not help here. + Scheduler()->SetPriority(id3, ResourceLoadPriority::kHigh, 0); EXPECT_TRUE(client1->WasRun()); EXPECT_TRUE(client2->WasRun()); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc index 838094ab45a..d09e6c051fb 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc @@ -4,35 +4,79 @@ #include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h" +#include "services/network/public/mojom/load_timing_info.mojom-blink.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" namespace blink { -ResourceLoadTiming::ResourceLoadTiming() {} +ResourceLoadTiming::ResourceLoadTiming() = default; + +ResourceLoadTiming::ResourceLoadTiming(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_start, + base::TimeTicks receive_headers_end, + base::TimeTicks ssl_start, + base::TimeTicks ssl_end, + base::TimeTicks push_start, + base::TimeTicks push_end) + : request_time_(request_time), + proxy_start_(proxy_start), + proxy_end_(proxy_end), + dns_start_(dns_start), + dns_end_(dns_end), + connect_start_(connect_start), + connect_end_(connect_end), + worker_start_(worker_start), + worker_ready_(worker_ready), + send_start_(send_start), + send_end_(send_end), + receive_headers_start_(receive_headers_start), + receive_headers_end_(receive_headers_end), + ssl_start_(ssl_start), + ssl_end_(ssl_end), + push_start_(push_start), + push_end_(push_end) {} scoped_refptr<ResourceLoadTiming> ResourceLoadTiming::Create() { return base::AdoptRef(new ResourceLoadTiming); } -scoped_refptr<ResourceLoadTiming> ResourceLoadTiming::DeepCopy() { - scoped_refptr<ResourceLoadTiming> timing = Create(); - timing->request_time_ = request_time_; - timing->proxy_start_ = proxy_start_; - timing->proxy_end_ = proxy_end_; - timing->dns_start_ = dns_start_; - timing->dns_end_ = dns_end_; - timing->connect_start_ = connect_start_; - timing->connect_end_ = connect_end_; - timing->worker_start_ = worker_start_; - timing->worker_ready_ = worker_ready_; - timing->send_start_ = send_start_; - timing->send_end_ = send_end_; - timing->receive_headers_start_ = receive_headers_start_; - timing->receive_headers_end_ = receive_headers_end_; - timing->ssl_start_ = ssl_start_; - timing->ssl_end_ = ssl_end_; - timing->push_start_ = push_start_; - timing->push_end_ = push_end_; +scoped_refptr<ResourceLoadTiming> ResourceLoadTiming::FromMojo( + const network::mojom::blink::LoadTimingInfo* mojo_timing) { + if (!mojo_timing) + return ResourceLoadTiming::Create(); + return base::AdoptRef(new ResourceLoadTiming( + mojo_timing->request_start, mojo_timing->proxy_resolve_start, + mojo_timing->proxy_resolve_end, mojo_timing->connect_timing->dns_start, + mojo_timing->connect_timing->dns_end, + mojo_timing->connect_timing->connect_start, + mojo_timing->connect_timing->connect_end, + mojo_timing->service_worker_start_time, + mojo_timing->service_worker_ready_time, mojo_timing->send_start, + mojo_timing->send_end, mojo_timing->receive_headers_start, + mojo_timing->receive_headers_end, mojo_timing->connect_timing->ssl_start, + mojo_timing->connect_timing->ssl_end, mojo_timing->push_start, + mojo_timing->push_end)); +} + +network::mojom::blink::LoadTimingInfoPtr ResourceLoadTiming::ToMojo() const { + network::mojom::blink::LoadTimingInfoPtr timing = + network::mojom::blink::LoadTimingInfo::New( + false, 0, base::Time(), request_time_, proxy_start_, proxy_end_, + network::mojom::blink::LoadTimingInfoConnectTiming::New( + dns_start_, dns_end_, connect_start_, connect_end_, ssl_start_, + ssl_end_), + send_start_, send_end_, receive_headers_start_, receive_headers_end_, + push_start_, push_end_, worker_start_, worker_ready_); return timing; } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h index ef8321289d3..b87da1d183f 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h @@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_TIMING_H_ #include "base/memory/scoped_refptr.h" +#include "services/network/public/mojom/load_timing_info.mojom-blink-forward.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" @@ -37,10 +38,13 @@ class PLATFORM_EXPORT ResourceLoadTiming public: static scoped_refptr<ResourceLoadTiming> Create(); - scoped_refptr<ResourceLoadTiming> DeepCopy(); bool operator==(const ResourceLoadTiming&) const; bool operator!=(const ResourceLoadTiming&) const; + static scoped_refptr<ResourceLoadTiming> FromMojo( + const network::mojom::blink::LoadTimingInfo*); + network::mojom::blink::LoadTimingInfoPtr ToMojo() const; + void SetDnsStart(base::TimeTicks); void SetRequestTime(base::TimeTicks); void SetProxyStart(base::TimeTicks); @@ -81,6 +85,23 @@ class PLATFORM_EXPORT ResourceLoadTiming private: ResourceLoadTiming(); + ResourceLoadTiming(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_start, + base::TimeTicks receive_headers_end, + base::TimeTicks ssl_start, + base::TimeTicks ssl_end, + base::TimeTicks push_start, + base::TimeTicks push_end); // We want to present a unified timeline to Javascript. Using walltime is // problematic, because the clock may skew while resources load. To prevent diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc index 58399f33fd1..7f1a04c6fe4 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc @@ -41,6 +41,7 @@ #include "services/metrics/public/cpp/ukm_builders.h" #include "services/network/public/cpp/features.h" #include "services/network/public/mojom/fetch_api.mojom-blink.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h" #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h" @@ -67,6 +68,7 @@ #include "third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h" #include "third_party/blink/renderer/platform/loader/fetch/response_body_loader.h" #include "third_party/blink/renderer/platform/loader/fetch/shared_buffer_bytes_consumer.h" +#include "third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h" #include "third_party/blink/renderer/platform/loader/mixed_content_autoupgrade_status.h" #include "third_party/blink/renderer/platform/network/http_names.h" #include "third_party/blink/renderer/platform/network/http_parsers.h" @@ -74,8 +76,8 @@ #include "third_party/blink/renderer/platform/network/network_utils.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "third_party/blink/renderer/platform/weborigin/reporting_disposition.h" #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h" -#include "third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/shared_buffer.h" @@ -147,7 +149,7 @@ void LogMixedAutoupgradeMetrics(blink::MixedContentAutoupgradeStatus status, builder.Record(recorder); } -bool CanHandleDataURLRequestLocally(const ResourceRequest& request) { +bool CanHandleDataURLRequestLocally(const ResourceRequestHead& request) { if (!request.Url().ProtocolIsData()) return false; @@ -156,11 +158,6 @@ bool CanHandleDataURLRequestLocally(const ResourceRequest& request) { if (request.DownloadToBlob()) return false; - // Data url requests from object tags may need to be intercepted as streams - // and so need to be sent to the browser. - if (request.GetRequestContext() == mojom::RequestContextType::OBJECT) - return false; - // Main resources are handled in the browser, so we can handle data url // subresources locally. return true; @@ -178,6 +175,18 @@ bool RequestContextObserveResponse(mojom::RequestContextType type) { } } +SchedulingPolicy::Feature GetFeatureFromRequestContextType( + mojom::RequestContextType type) { + switch (type) { + case mojom::RequestContextType::FETCH: + return SchedulingPolicy::Feature::kOutstandingNetworkRequestFetch; + case mojom::RequestContextType::XML_HTTP_REQUEST: + return SchedulingPolicy::Feature::kOutstandingNetworkRequestXHR; + default: + return SchedulingPolicy::Feature::kOutstandingNetworkRequestOthers; + } +} + } // namespace // CodeCacheRequest handles the requests to fetch data from code cache. @@ -372,11 +381,13 @@ void ResourceLoader::CodeCacheRequest::MaybeSendCachedCode( ResourceLoader::ResourceLoader(ResourceFetcher* fetcher, ResourceLoadScheduler* scheduler, Resource* resource, + ResourceRequestBody request_body, uint32_t inflight_keepalive_bytes) : scheduler_client_id_(ResourceLoadScheduler::kInvalidClientId), fetcher_(fetcher), scheduler_(scheduler), resource_(resource), + request_body_(std::move(request_body)), inflight_keepalive_bytes_(inflight_keepalive_bytes), is_cache_aware_loading_activated_(false), cancel_timer_(fetcher_->GetTaskRunner(), @@ -389,11 +400,12 @@ ResourceLoader::ResourceLoader(ResourceFetcher* fetcher, // If they are keepalive request && their responses are not observable to web // content, we can have them survive without breaking web content when the // page is put into BackForwardCache. - auto request = resource_->GetResourceRequest(); - if (!RequestContextObserveResponse(request.GetRequestContext())) { + auto& request = resource_->GetResourceRequest(); + auto request_context = request.GetRequestContext(); + if (!RequestContextObserveResponse(request_context)) { if (FrameScheduler* frame_scheduler = fetcher->GetFrameScheduler()) { feature_handle_for_scheduler_ = frame_scheduler->RegisterFeature( - SchedulingPolicy::Feature::kOutstandingNetworkRequest, + GetFeatureFromRequestContextType(request_context), {SchedulingPolicy::RecordMetricsForBackForwardCache()}); } } @@ -403,7 +415,7 @@ ResourceLoader::ResourceLoader(ResourceFetcher* fetcher, ResourceLoader::~ResourceLoader() = default; -void ResourceLoader::Trace(blink::Visitor* visitor) { +void ResourceLoader::Trace(Visitor* visitor) { visitor->Trace(fetcher_); visitor->Trace(scheduler_); visitor->Trace(resource_); @@ -415,11 +427,8 @@ void ResourceLoader::Trace(blink::Visitor* visitor) { bool ResourceLoader::ShouldFetchCodeCache() { if (!RuntimeEnabledFeatures::IsolatedCodeCacheEnabled()) return false; - if (resource_->GetType() == ResourceType::kRaw && - !RuntimeEnabledFeatures::WasmCodeCacheEnabled()) - return false; - const ResourceRequest& request = resource_->GetResourceRequest(); + const ResourceRequestHead& request = resource_->GetResourceRequest(); if (!request.Url().ProtocolIsInHTTPFamily()) return false; // When loading the service worker scripts, we don't need to check the @@ -448,9 +457,12 @@ bool ResourceLoader::ShouldFetchCodeCache() { } void ResourceLoader::Start() { - const ResourceRequest& request = resource_->GetResourceRequest(); + const ResourceRequestHead& request = resource_->GetResourceRequest(); ActivateCacheAwareLoadingIfNeeded(request); - loader_ = fetcher_->CreateURLLoader(request, resource_->Options()); + // TODO(yoichio): Have CreateURLLoader take a ResourceRequestHead, not + // ResourceRequest. + loader_ = + fetcher_->CreateURLLoader(ResourceRequest(request), resource_->Options()); task_runner_for_body_loader_ = loader_->GetTaskRunner(); DCHECK_EQ(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_); auto throttle_option = ResourceLoadScheduler::ThrottleOption::kThrottleable; @@ -547,7 +559,7 @@ void ResourceLoader::DidCancelLoadingBody() { Cancel(); } -void ResourceLoader::StartWith(const ResourceRequest& request) { +void ResourceLoader::StartWith(const ResourceRequestHead& request) { DCHECK_NE(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_); DCHECK(loader_); @@ -570,7 +582,7 @@ void ResourceLoader::StartWith(const ResourceRequest& request) { if (is_cache_aware_loading_activated_) { // Override cache policy for cache-aware loading. If this request fails, a // reload with original request will be triggered in DidFail(). - ResourceRequest cache_aware_request(request); + ResourceRequestHead cache_aware_request(request); cache_aware_request.SetCacheMode( mojom::FetchCacheMode::kUnspecifiedOnlyIfCachedStrict); RequestAsynchronously(cache_aware_request); @@ -594,9 +606,10 @@ void ResourceLoader::Release( feature_handle_for_scheduler_.reset(); } -void ResourceLoader::Restart(const ResourceRequest& request) { +void ResourceLoader::Restart(const ResourceRequestHead& request) { CHECK_EQ(resource_->Options().synchronous_policy, kRequestAsynchronously); - loader_ = fetcher_->CreateURLLoader(request, resource_->Options()); + loader_ = + fetcher_->CreateURLLoader(ResourceRequest(request), resource_->Options()); task_runner_for_body_loader_ = loader_->GetTaskRunner(); StartWith(request); } @@ -678,14 +691,14 @@ void ResourceLoader::CancelForRedirectAccessCheckError( } } -static bool IsManualRedirectFetchRequest(const ResourceRequest& request) { +static bool IsManualRedirectFetchRequest(const ResourceRequestHead& request) { return request.GetRedirectMode() == network::mojom::RedirectMode::kManual && request.GetRequestContext() == mojom::RequestContextType::FETCH; } bool ResourceLoader::WillFollowRedirect( const WebURL& new_url, - const WebURL& new_site_for_cookies, + const net::SiteForCookies& new_site_for_cookies, const WebString& new_referrer, network::mojom::ReferrerPolicy new_referrer_policy, const WebString& new_method, @@ -704,12 +717,16 @@ bool ResourceLoader::WillFollowRedirect( resource_->LastResourceRequest().CreateRedirectRequest( new_url, new_method, new_site_for_cookies, new_referrer, new_referrer_policy, - !passed_redirect_response.WasFetchedViaServiceWorker()); + if (!RuntimeEnabledFeatures::OutOfBlinkCorsEnabled() && + (new_request->HttpMethod() != http_names::kGET && + new_request->HttpMethod() != http_names::kHEAD)) { + new_request->SetHttpBody(request_body_.FormBody()); + } ResourceType resource_type = resource_->GetType(); - const ResourceRequest& initial_request = resource_->GetResourceRequest(); + const ResourceRequestHead& initial_request = resource_->GetResourceRequest(); // The following parameters never change during the lifetime of a request. mojom::RequestContextType request_context = initial_request.GetRequestContext(); @@ -726,19 +743,20 @@ bool ResourceLoader::WillFollowRedirect( bool unused_preload = resource_->IsUnusedPreload(); // Don't send security violation reports for unused preloads. - SecurityViolationReportingPolicy reporting_policy = - unused_preload ? SecurityViolationReportingPolicy::kSuppressReporting - : SecurityViolationReportingPolicy::kReport; + ReportingDisposition reporting_disposition = + unused_preload ? ReportingDisposition::kSuppressReporting + : ReportingDisposition::kReport; // CanRequest() checks only enforced CSP, so check report-only here to // ensure that violations are sent. Context().CheckCSPForRequest( - request_context, new_url, options, reporting_policy, + request_context, new_url, options, reporting_disposition, ResourceRequest::RedirectStatus::kFollowedRedirect); base::Optional<ResourceRequestBlockedReason> blocked_reason = Context().CanRequest( - resource_type, *new_request, new_url, options, reporting_policy, + resource_type, *new_request, new_url, options, + reporting_disposition, ResourceRequest::RedirectStatus::kFollowedRedirect); if (Context().CalculateIfAdSubresource(*new_request, resource_type)) @@ -756,9 +774,9 @@ bool ResourceLoader::WillFollowRedirect( new_url, request_mode, origin.get(), GetCorsFlag() ? CorsFlag::Set : CorsFlag::Unset); if (!cors_error && GetCorsFlag()) { - cors_error = cors::CheckAccess( - new_url, redirect_response.HttpStatusCode(), - redirect_response.HttpHeaderFields(), credentials_mode, *origin); + cors_error = + cors::CheckAccess(new_url, redirect_response.HttpHeaderFields(), + credentials_mode, *origin); } if (cors_error) { HandleError( @@ -809,18 +827,20 @@ bool ResourceLoader::WillFollowRedirect( redirect_response_with_type ? *redirect_response_with_type : redirect_response; - // The following two calls may rewrite the new_request.Url() to + // The following two calls may rewrite the new_request->Url() to // something else not for rejecting redirect but for other reasons. // E.g. WebFrameTestClient::WillSendRequest() and // RenderFrameImpl::WillSendRequest(). We should reflect the - // rewriting but currently we cannot. So, compare new_request.Url() and + // rewriting but currently we cannot. So, compare new_request->Url() and // new_url after calling them, and return false to make the redirect fail on // mismatch. WebScopedVirtualTimePauser unused_virtual_time_pauser; + // TODO(yoichio): Have PrepareRequest use ResourceRequestHead. Context().PrepareRequest(*new_request, resource_->Options().initiator_info, - unused_virtual_time_pauser, - resource_->GetType()); + unused_virtual_time_pauser, resource_->GetType()); + if (RuntimeEnabledFeatures::OutOfBlinkCorsEnabled()) + DCHECK(!new_request->HttpBody()); if (auto* observer = fetcher_->GetResourceLoadObserver()) { observer->WillSendRequest(resource_->InspectorId(), *new_request, redirect_response_to_pass, resource_->GetType(), @@ -830,7 +850,7 @@ bool ResourceLoader::WillFollowRedirect( // First-party cookie logic moved from DocumentLoader in Blink to // net::URLRequest in the browser. Assert that Blink didn't try to change it // to something else. - DCHECK(KURL(new_site_for_cookies) == new_request->SiteForCookies()); + DCHECK(new_request->SiteForCookies().IsEquivalent(new_site_for_cookies)); // The following parameters never change during the lifetime of a request. DCHECK_EQ(new_request->GetRequestContext(), request_context); @@ -909,7 +929,7 @@ void ResourceLoader::DidReceiveResponse(const WebURLResponse& response) { void ResourceLoader::DidReceiveResponseInternal( const ResourceResponse& response) { - const ResourceRequest& request = resource_->GetResourceRequest(); + const ResourceRequestHead& request = resource_->GetResourceRequest(); if (request.IsAutomaticUpgrade()) { mojo::PendingRemote<ukm::mojom::UkmRecorderInterface> pending_recorder; @@ -932,7 +952,7 @@ void ResourceLoader::DidReceiveResponseInternal( ResourceType resource_type = resource_->GetType(); - const ResourceRequest& initial_request = resource_->GetResourceRequest(); + const ResourceRequestHead& initial_request = resource_->GetResourceRequest(); // The following parameters never change during the lifetime of a request. mojom::RequestContextType request_context = initial_request.GetRequestContext(); @@ -957,12 +977,25 @@ void ResourceLoader::DidReceiveResponseInternal( return; } + // https://wicg.github.io/cross-origin-embedder-policy/#integration-html + // TODO(crbug.com/1064920): Remove this once PlzDedicatedWorker ships. + if (options.reject_coep_unsafe_none && + response.GetCrossOriginEmbedderPolicy() != + network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp && + !response.CurrentRequestUrl().ProtocolIsData() && + !response.CurrentRequestUrl().ProtocolIs("blob")) { + DCHECK(!base::FeatureList::IsEnabled(features::kPlzDedicatedWorker)); + HandleError(ResourceError::Failure(response.CurrentRequestUrl())); + return; + } + if (response.WasFetchedViaServiceWorker()) { if (options.cors_handling_by_resource_fetcher == kEnableCorsHandlingByResourceFetcher && request_mode == network::mojom::RequestMode::kCors && response.WasFallbackRequiredByServiceWorker()) { - ResourceRequest last_request = resource_->LastResourceRequest(); + DCHECK(resource_->RedirectChain().IsEmpty()); + ResourceRequestHead last_request(resource_->GetResourceRequest()); DCHECK(!last_request.GetSkipServiceWorker()); // This code handles the case when a controlling service worker doesn't // handle a cross origin request. @@ -999,14 +1032,13 @@ void ResourceLoader::DidReceiveResponseInternal( // CanRequest() below only checks enforced policies: check report-only // here to ensure violations are sent. Context().CheckCSPForRequest( - request_context, response_url, options, - SecurityViolationReportingPolicy::kReport, + request_context, response_url, options, ReportingDisposition::kReport, ResourceRequest::RedirectStatus::kFollowedRedirect); base::Optional<ResourceRequestBlockedReason> blocked_reason = Context().CanRequest( - resource_type, initial_request, response_url, options, - SecurityViolationReportingPolicy::kReport, + resource_type, ResourceRequest(initial_request), response_url, + options, ReportingDisposition::kReport, ResourceRequest::RedirectStatus::kFollowedRedirect); if (blocked_reason) { HandleError(ResourceError::CancelledDueToAccessCheckError( @@ -1021,9 +1053,8 @@ void ResourceLoader::DidReceiveResponseInternal( !(resource_->IsCacheValidator() && response.HttpStatusCode() == 304)) { if (GetCorsFlag()) { base::Optional<network::CorsErrorStatus> cors_error = cors::CheckAccess( - response.CurrentRequestUrl(), response.HttpStatusCode(), - response.HttpHeaderFields(), initial_request.GetCredentialsMode(), - *resource_->GetOrigin()); + response.CurrentRequestUrl(), response.HttpHeaderFields(), + initial_request.GetCredentialsMode(), *resource_->GetOrigin()); if (cors_error) { HandleError(ResourceError(response.CurrentRequestUrl(), *cors_error)); return; @@ -1040,9 +1071,12 @@ void ResourceLoader::DidReceiveResponseInternal( // FrameType never changes during the lifetime of a request. if (auto* observer = fetcher_->GetResourceLoadObserver()) { + ResourceRequest request_for_obserber(initial_request); + // TODO(yoichio): Have DidReceiveResponse take a ResourceResponseHead, not + // ResourceRequest. observer->DidReceiveResponse( - resource_->InspectorId(), initial_request, response_to_pass, resource_, - ResourceLoadObserver::ResponseSource::kNotFromMemoryCache); + resource_->InspectorId(), request_for_obserber, response_to_pass, + resource_, ResourceLoadObserver::ResponseSource::kNotFromMemoryCache); } resource_->ResponseReceived(response_to_pass); @@ -1182,7 +1216,7 @@ void ResourceLoader::DidFail(const WebURLError& error, int64_t encoded_data_length, int64_t encoded_body_length, int64_t decoded_body_length) { - const ResourceRequest& request = resource_->GetResourceRequest(); + const ResourceRequestHead& request = resource_->GetResourceRequest(); if (request.IsAutomaticUpgrade()) { mojo::PendingRemote<ukm::mojom::UkmRecorderInterface> pending_recorder; @@ -1242,11 +1276,16 @@ void ResourceLoader::HandleError(const ResourceError& error) { inflight_keepalive_bytes_); } -void ResourceLoader::RequestSynchronously(const ResourceRequest& request) { +void ResourceLoader::RequestSynchronously(const ResourceRequestHead& request) { DCHECK(loader_); DCHECK_EQ(request.Priority(), ResourceLoadPriority::kHighest); - WrappedResourceRequest request_in(request); + auto network_resource_request = std::make_unique<network::ResourceRequest>(); + scoped_refptr<EncodedFormData> form_body = request_body_.FormBody(); + PopulateResourceRequest(request, std::move(request_body_), + network_resource_request.get()); + if (form_body) + request_body_ = ResourceRequestBody(std::move(form_body)); WebURLResponse response_out; base::Optional<WebURLError> error_out; WebData data_out; @@ -1271,9 +1310,15 @@ void ResourceLoader::RequestSynchronously(const ResourceRequest& request) { data_out = WebData(std::move(data)); } } else { - loader_->LoadSynchronously(request_in, this, response_out, error_out, - data_out, encoded_data_length, - encoded_body_length, downloaded_blob); + // Don't do mime sniffing for fetch (crbug.com/2016) + bool no_mime_sniffing = + request.GetRequestContext() == blink::mojom::RequestContextType::FETCH; + loader_->LoadSynchronously( + std::move(network_resource_request), request.GetExtraData(), + request.RequestorID(), request.IsDownloadToNetworkCacheOnly(), + request.DownloadToBlob(), no_mime_sniffing, request.TimeoutInterval(), + this, response_out, error_out, data_out, encoded_data_length, + encoded_body_length, downloaded_blob); } // A message dispatched while synchronously fetching the resource // can bring about the cancellation of this load. @@ -1312,7 +1357,7 @@ void ResourceLoader::RequestSynchronously(const ResourceRequest& request) { encoded_body_length, decoded_body_length, false); } -void ResourceLoader::RequestAsynchronously(const ResourceRequest& request) { +void ResourceLoader::RequestAsynchronously(const ResourceRequestHead& request) { DCHECK(loader_); if (CanHandleDataURLRequestLocally(request)) { DCHECK(!code_cache_request_); @@ -1323,7 +1368,19 @@ void ResourceLoader::RequestAsynchronously(const ResourceRequest& request) { return; } - loader_->LoadAsynchronously(WrappedResourceRequest(request), this); + auto network_resource_request = std::make_unique<network::ResourceRequest>(); + // Don't do mime sniffing for fetch (crbug.com/2016) + bool no_mime_sniffing = + request.GetRequestContext() == blink::mojom::RequestContextType::FETCH; + scoped_refptr<EncodedFormData> form_body = request_body_.FormBody(); + PopulateResourceRequest(request, std::move(request_body_), + network_resource_request.get()); + if (form_body) + request_body_ = ResourceRequestBody(std::move(form_body)); + loader_->LoadAsynchronously(std::move(network_resource_request), + request.GetExtraData(), request.RequestorID(), + request.IsDownloadToNetworkCacheOnly(), + no_mime_sniffing, this); if (code_cache_request_) { // Sets defers loading and initiates a fetch from code cache. code_cache_request_->FetchFromCodeCache(loader_.get(), this); @@ -1346,7 +1403,7 @@ void ResourceLoader::Dispose() { } void ResourceLoader::ActivateCacheAwareLoadingIfNeeded( - const ResourceRequest& request) { + const ResourceRequestHead& request) { DCHECK(!is_cache_aware_loading_activated_); if (resource_->Options().cache_aware_loading_enabled != diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h index 0046f323f2e..ba73d21ca9d 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h @@ -75,9 +75,10 @@ class PLATFORM_EXPORT ResourceLoader final ResourceLoader(ResourceFetcher*, ResourceLoadScheduler*, Resource*, + ResourceRequestBody request_body = ResourceRequestBody(), uint32_t inflight_keepalive_bytes = 0); ~ResourceLoader() override; - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; void Start(); @@ -90,7 +91,7 @@ class PLATFORM_EXPORT ResourceLoader final // Called before start() to activate cache-aware loading if enabled in // |m_resource->options()| and applicable. - void ActivateCacheAwareLoadingIfNeeded(const ResourceRequest&); + void ActivateCacheAwareLoadingIfNeeded(const ResourceRequestHead&); bool IsCacheAwareLoadingActivated() const { return is_cache_aware_loading_activated_; @@ -113,14 +114,13 @@ class PLATFORM_EXPORT ResourceLoader final // A failed load is indicated by 1 DidFail(), which can occur at any time // before DidFinishLoading(), including synchronous inside one of the other // callbacks via ResourceLoader::cancel() - bool WillFollowRedirect( - const WebURL& new_url, - const WebURL& new_site_for_cookies, - const WebString& new_referrer, - network::mojom::ReferrerPolicy new_referrer_policy, - const WebString& new_method, - const WebURLResponse& passed_redirect_response, - bool& report_raw_headers) override; + bool WillFollowRedirect(const WebURL& new_url, + const net::SiteForCookies& new_site_for_cookies, + const WebString& new_referrer, + network::mojom::ReferrerPolicy new_referrer_policy, + const WebString& new_method, + const WebURLResponse& passed_redirect_response, + bool& report_raw_headers) override; void DidSendData(uint64_t bytes_sent, uint64_t total_bytes_to_be_sent) override; void DidReceiveResponse(const WebURLResponse&) override; @@ -166,7 +166,7 @@ class PLATFORM_EXPORT ResourceLoader final void DidCancelLoadingBody() override; bool ShouldFetchCodeCache(); - void StartWith(const ResourceRequest&); + void StartWith(const ResourceRequestHead&); void Release(ResourceLoadScheduler::ReleaseOption, const ResourceLoadScheduler::TrafficReportHints&); @@ -174,7 +174,7 @@ class PLATFORM_EXPORT ResourceLoader final // This method is currently only used for service worker fallback request and // cache-aware loading, other users should be careful not to break // ResourceLoader state. - void Restart(const ResourceRequest&); + void Restart(const ResourceRequestHead&); FetchContext& Context() const; @@ -184,8 +184,8 @@ class PLATFORM_EXPORT ResourceLoader final void CancelForRedirectAccessCheckError(const KURL&, ResourceRequestBlockedReason); - void RequestSynchronously(const ResourceRequest&); - void RequestAsynchronously(const ResourceRequest&); + void RequestSynchronously(const ResourceRequestHead&); + void RequestAsynchronously(const ResourceRequestHead&); void Dispose(); void DidReceiveResponseInternal(const ResourceResponse&); @@ -211,6 +211,7 @@ class PLATFORM_EXPORT ResourceLoader final Member<ResourceFetcher> fetcher_; Member<ResourceLoadScheduler> scheduler_; Member<Resource> resource_; + ResourceRequestBody request_body_; Member<ResponseBodyLoader> response_body_loader_; Member<DataPipeBytesConsumer::CompletionNotifier> data_pipe_completion_notifier_; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc index 16769297a2d..b074e42e062 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc @@ -91,17 +91,30 @@ class ResourceLoaderDefersLoadingTest::TestWebURLLoader final : defers_flag_ptr_(defers_flag_ptr) {} ~TestWebURLLoader() override = default; - void LoadSynchronously(const WebURLRequest&, - WebURLLoaderClient*, - WebURLResponse&, - base::Optional<WebURLError>&, - WebData&, - int64_t& encoded_data_length, - int64_t& encoded_body_length, - WebBlobInfo& downloaded_blob) override { + void LoadSynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequest::ExtraData> request_extra_data, + int requestor_id, + bool download_to_network_cache_only, + bool pass_response_pipe_to_client, + bool no_mime_sniffing, + base::TimeDelta timeout_interval, + WebURLLoaderClient*, + WebURLResponse&, + base::Optional<WebURLError>&, + WebData&, + int64_t& encoded_data_length, + int64_t& encoded_body_length, + WebBlobInfo& downloaded_blob) override { NOTREACHED(); } - void LoadAsynchronously(const WebURLRequest&, WebURLLoaderClient*) override {} + void LoadAsynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequest::ExtraData> request_extra_data, + int requestor_id, + bool download_to_network_cache_only, + bool no_mime_sniffing, + WebURLLoaderClient*) override {} void SetDefersLoading(bool defers) override { *defers_flag_ptr_ = defers; } void DidChangePriority(WebURLRequest::Priority, int) override { @@ -177,7 +190,7 @@ TEST_F(ResourceLoaderDefersLoadingTest, CodeCacheFetchCheckDefers) { ResourceRequest request; request.SetUrl(test_url_); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters fetch_parameters(request); + FetchParameters fetch_parameters(std::move(request)); Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr); @@ -200,7 +213,7 @@ TEST_F(ResourceLoaderDefersLoadingTest, CodeCacheFetchSyncReturn) { ResourceRequest request; request.SetUrl(test_url_); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters fetch_parameters(request); + FetchParameters fetch_parameters(std::move(request)); Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr); DCHECK(resource); @@ -214,7 +227,7 @@ TEST_F(ResourceLoaderDefersLoadingTest, ChangeDefersToFalse) { ResourceRequest request; request.SetUrl(test_url_); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters fetch_parameters(request); + FetchParameters fetch_parameters(std::move(request)); Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr); DCHECK(web_url_loader_defers_); @@ -232,7 +245,7 @@ TEST_F(ResourceLoaderDefersLoadingTest, ChangeDefersToTrue) { ResourceRequest request; request.SetUrl(test_url_); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters fetch_parameters(request); + FetchParameters fetch_parameters(std::move(request)); Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr); DCHECK(web_url_loader_defers_); @@ -254,7 +267,7 @@ TEST_F(ResourceLoaderDefersLoadingTest, ChangeDefersMultipleTimes) { request.SetUrl(test_url_); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters fetch_parameters(request); + FetchParameters fetch_parameters(std::move(request)); Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr); DCHECK(web_url_loader_defers_); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.cc index df25cab69b1..cb2c29d1c77 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.cc @@ -36,7 +36,7 @@ namespace blink { ResourceLoaderOptions::ResourceLoaderOptions() : data_buffering_policy(kBufferData), - content_security_policy_option(kCheckContentSecurityPolicy), + content_security_policy_option(network::mojom::CSPDisposition::CHECK), request_initiator_context(kDocumentContext), synchronous_policy(kRequestAsynchronously), cors_handling_by_resource_fetcher(kEnableCorsHandlingByResourceFetcher), diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h index b233ca5deed..8cc09706c37 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h @@ -32,7 +32,9 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_OPTIONS_H_ #include "base/memory/scoped_refptr.h" +#include "base/util/type_safety/strong_alias.h" #include "mojo/public/cpp/bindings/pending_remote.h" +#include "services/network/public/mojom/content_security_policy.mojom-blink-forward.h" #include "services/network/public/mojom/url_loader_factory.mojom-blink-forward.h" #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h" #include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h" @@ -44,11 +46,6 @@ namespace blink { enum DataBufferingPolicy : uint8_t { kBufferData, kDoNotBufferData }; -enum ContentSecurityPolicyDisposition : uint8_t { - kCheckContentSecurityPolicy, - kDoNotCheckContentSecurityPolicy -}; - enum RequestInitiatorContext : uint8_t { kDocumentContext, kWorkerContext, @@ -75,6 +72,12 @@ enum CacheAwareLoadingEnabled : uint8_t { kIsCacheAwareLoadingEnabled }; +// https://github.com/WICG/cross-origin-embedder-policy/pull/13 +// When true, a response is blocked unless it has +// cross-origin-embedder-policy: require-corp. +using RejectCoepUnsafeNone = + util::StrongAlias<class RejectCoepUnsafeNoneTag, bool>; + // This class is thread-bound. Do not copy/pass an instance across threads. struct PLATFORM_EXPORT ResourceLoaderOptions { USING_FAST_MALLOC(ResourceLoaderOptions); @@ -93,7 +96,7 @@ struct PLATFORM_EXPORT ResourceLoaderOptions { DataBufferingPolicy data_buffering_policy; - ContentSecurityPolicyDisposition content_security_policy_option; + network::mojom::CSPDisposition content_security_policy_option; RequestInitiatorContext request_initiator_context; SynchronousPolicy synchronous_policy; @@ -105,6 +108,9 @@ struct PLATFORM_EXPORT ResourceLoaderOptions { // Corresponds to the CORS flag in the Fetch spec. bool cors_flag; + // TODO(crbug.com/1064920): Remove this once PlzDedicatedWorker ships. + RejectCoepUnsafeNone reject_coep_unsafe_none = RejectCoepUnsafeNone(false); + String content_security_policy_nonce; IntegrityMetadataSet integrity_metadata; ParserDisposition parser_disposition; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc index 9a88c03ab81..7d307cefc7c 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc @@ -75,21 +75,34 @@ class ResourceLoaderTest : public testing::Test { private: class NoopWebURLLoader final : public WebURLLoader { public: - NoopWebURLLoader(scoped_refptr<base::SingleThreadTaskRunner> task_runner) + explicit NoopWebURLLoader( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) : task_runner_(task_runner) {} ~NoopWebURLLoader() override = default; - void LoadSynchronously(const WebURLRequest&, - WebURLLoaderClient*, - WebURLResponse&, - base::Optional<WebURLError>&, - WebData&, - int64_t& encoded_data_length, - int64_t& encoded_body_length, - WebBlobInfo& downloaded_blob) override { + void LoadSynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequest::ExtraData> request_extra_data, + int requestor_id, + bool download_to_network_cache_only, + bool pass_response_pipe_to_client, + bool no_mime_sniffing, + base::TimeDelta timeout_interval, + WebURLLoaderClient*, + WebURLResponse&, + base::Optional<WebURLError>&, + WebData&, + int64_t& encoded_data_length, + int64_t& encoded_body_length, + WebBlobInfo& downloaded_blob) override { NOTREACHED(); } - void LoadAsynchronously(const WebURLRequest&, - WebURLLoaderClient*) override {} + void LoadAsynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequest::ExtraData> request_extra_data, + int requestor_id, + bool download_to_network_cache_only, + bool no_mime_sniffing, + WebURLLoaderClient*) override {} void SetDefersLoading(bool) override {} void DidChangePriority(WebURLRequest::Priority, int) override { @@ -182,7 +195,7 @@ TEST_F(ResourceLoaderTest, ResponseType) { request.SetMode(test.request_mode); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters fetch_parameters(request); + FetchParameters fetch_parameters(std::move(request)); if (test.request_mode == network::mojom::RequestMode::kCors) { fetch_parameters.SetCrossOriginAccessControl( origin.get(), network::mojom::CredentialsMode::kOmit); @@ -216,7 +229,7 @@ TEST_F(ResourceLoaderTest, LoadResponseBody) { ResourceRequest request(url); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters params(request); + FetchParameters params(std::move(request)); Resource* resource = RawResource::Fetch(params, fetcher, nullptr); ResourceLoader* loader = resource->Loader(); @@ -280,7 +293,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_AsyncAndNonStream) { KURL url("data:text/plain,Hello%20World!"); ResourceRequest request(url); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters params(request); + FetchParameters params(std::move(request)); Resource* resource = RawResource::Fetch(params, fetcher, nullptr); EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()) @@ -338,7 +351,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_AsyncAndStream) { ResourceRequest request(url); request.SetRequestContext(mojom::RequestContextType::FETCH); request.SetUseStreamOnResponse(true); - FetchParameters params(request); + FetchParameters params(std::move(request)); auto* raw_resource_client = MakeGarbageCollected<TestRawResourceClient>(); Resource* resource = RawResource::Fetch(params, fetcher, raw_resource_client); EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); @@ -374,7 +387,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_AsyncEmptyData) { KURL url("data:text/html,"); ResourceRequest request(url); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters params(request); + FetchParameters params(std::move(request)); Resource* resource = RawResource::Fetch(params, fetcher, nullptr); EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()) @@ -397,7 +410,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_Sync) { KURL url("data:text/plain,Hello%20World!"); ResourceRequest request(url); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters params(request); + FetchParameters params(std::move(request)); Resource* resource = RawResource::FetchSynchronously(params, fetcher, nullptr); @@ -422,7 +435,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_SyncEmptyData) { KURL url("data:text/html,"); ResourceRequest request(url); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters params(request); + FetchParameters params(std::move(request)); Resource* resource = RawResource::FetchSynchronously(params, fetcher, nullptr); @@ -445,7 +458,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_DefersAsyncAndNonStream) { KURL url("data:text/plain,Hello%20World!"); ResourceRequest request(url); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters params(request); + FetchParameters params(std::move(request)); Resource* resource = RawResource::Fetch(params, fetcher, nullptr); EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); @@ -492,7 +505,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_DefersAsyncAndStream) { ResourceRequest request(url); request.SetRequestContext(mojom::RequestContextType::FETCH); request.SetUseStreamOnResponse(true); - FetchParameters params(request); + FetchParameters params(std::move(request)); auto* raw_resource_client = MakeGarbageCollected<TestRawResourceClient>(); Resource* resource = RawResource::Fetch(params, fetcher, raw_resource_client); EXPECT_EQ(resource->GetStatus(), ResourceStatus::kPending); @@ -561,7 +574,7 @@ class ResourceLoaderIsolatedCodeCacheTest : public ResourceLoaderTest { request.SetUrl(foo_url_); request.SetRequestContext(mojom::RequestContextType::FETCH); - FetchParameters fetch_parameters(request); + FetchParameters fetch_parameters(std::move(request)); Resource* resource = RawResource::Fetch(fetch_parameters, fetcher, nullptr); ResourceLoader* loader = resource->Loader(); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc index b36dbcf738b..8ee020a6df7 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc @@ -40,15 +40,12 @@ namespace blink { -const base::TimeDelta ResourceRequest::default_timeout_interval_ = +const base::TimeDelta ResourceRequestHead::default_timeout_interval_ = base::TimeDelta::Max(); -ResourceRequest::ResourceRequest() : ResourceRequest(NullURL()) {} +ResourceRequestHead::ResourceRequestHead() : ResourceRequestHead(NullURL()) {} -ResourceRequest::ResourceRequest(const String& url_string) - : ResourceRequest(KURL(url_string)) {} - -ResourceRequest::ResourceRequest(const KURL& url) +ResourceRequestHead::ResourceRequestHead(const KURL& url) : url_(url), timeout_interval_(default_timeout_interval_), http_method_(http_names::kGET), @@ -64,33 +61,86 @@ ResourceRequest::ResourceRequest(const KURL& url) cache_mode_(mojom::FetchCacheMode::kDefault), skip_service_worker_(false), download_to_cache_only_(false), + site_for_cookies_set_(false), priority_(ResourceLoadPriority::kUnresolved), intra_priority_value_(0), requestor_id_(0), previews_state_(WebURLRequest::kPreviewsUnspecified), request_context_(mojom::RequestContextType::UNSPECIFIED), + destination_(network::mojom::RequestDestination::kEmpty), mode_(network::mojom::RequestMode::kNoCors), fetch_importance_mode_(mojom::FetchImportanceMode::kImportanceAuto), credentials_mode_(network::mojom::CredentialsMode::kInclude), redirect_mode_(network::mojom::RedirectMode::kFollow), referrer_string_(Referrer::ClientReferrerString()), referrer_policy_(network::mojom::ReferrerPolicy::kDefault), - did_set_http_referrer_(false), is_external_request_(false), cors_preflight_policy_( network::mojom::CorsPreflightPolicy::kConsiderPreflight), redirect_status_(RedirectStatus::kNoRedirect) {} -ResourceRequest::ResourceRequest(const ResourceRequest&) = default; +ResourceRequestHead::ResourceRequestHead(const ResourceRequestHead&) = default; + +ResourceRequestHead& ResourceRequestHead::operator=( + const ResourceRequestHead&) = default; + +ResourceRequestHead::ResourceRequestHead(ResourceRequestHead&&) = default; + +ResourceRequestHead& ResourceRequestHead::operator=(ResourceRequestHead&&) = + default; + +ResourceRequestHead::~ResourceRequestHead() = default; + +ResourceRequestBody::ResourceRequestBody() : ResourceRequestBody(nullptr) {} + +ResourceRequestBody::ResourceRequestBody( + scoped_refptr<EncodedFormData> form_body) + : form_body_(form_body) {} + +ResourceRequestBody::ResourceRequestBody(ResourceRequestBody&& src) + : ResourceRequestBody(std::move(src.form_body_)) {} + +ResourceRequestBody& ResourceRequestBody::operator=(ResourceRequestBody&& src) { + form_body_ = std::move(src.form_body_); + return *this; +} + +ResourceRequestBody::~ResourceRequestBody() = default; + +ResourceRequest::ResourceRequest() : ResourceRequestHead(NullURL()) {} + +ResourceRequest::ResourceRequest(const String& url_string) + : ResourceRequestHead(KURL(url_string)) {} + +ResourceRequest::ResourceRequest(const KURL& url) : ResourceRequestHead(url) {} + +ResourceRequest::ResourceRequest(const ResourceRequestHead& head) + : ResourceRequestHead(head) {} + +ResourceRequest& ResourceRequest::operator=(const ResourceRequest& src) { + this->ResourceRequestHead::operator=(src); + body_.SetFormBody(src.body_.FormBody()); + return *this; +} + +ResourceRequest::ResourceRequest(ResourceRequest&&) = default; + +ResourceRequest& ResourceRequest::operator=(ResourceRequest&&) = default; ResourceRequest::~ResourceRequest() = default; -ResourceRequest& ResourceRequest::operator=(const ResourceRequest&) = default; +void ResourceRequest::CopyFrom(const ResourceRequest& src) { + *this = src; +} -std::unique_ptr<ResourceRequest> ResourceRequest::CreateRedirectRequest( +void ResourceRequest::CopyHeadFrom(const ResourceRequestHead& src) { + this->ResourceRequestHead::operator=(src); +} + +std::unique_ptr<ResourceRequest> ResourceRequestHead::CreateRedirectRequest( const KURL& new_url, const AtomicString& new_method, - const KURL& new_site_for_cookies, + const net::SiteForCookies& new_site_for_cookies, const String& new_referrer, network::mojom::ReferrerPolicy new_referrer_policy, bool skip_service_worker) const { @@ -102,9 +152,8 @@ std::unique_ptr<ResourceRequest> ResourceRequest::CreateRedirectRequest( request->SetSiteForCookies(new_site_for_cookies); String referrer = new_referrer.IsEmpty() ? Referrer::NoReferrer() : String(new_referrer); - // TODO(domfarolino): Stop storing ResourceRequest's generated referrer as a - // header and instead use a separate member. See https://crbug.com/850813. - request->SetHttpReferrer(Referrer(referrer, new_referrer_policy)); + request->SetReferrerString(referrer); + request->SetReferrerPolicy(new_referrer_policy); request->SetSkipServiceWorker(skip_service_worker); request->SetRedirectStatus(RedirectStatus::kFollowedRedirect); @@ -118,8 +167,6 @@ std::unique_ptr<ResourceRequest> ResourceRequest::CreateRedirectRequest( request->SetKeepalive(GetKeepalive()); request->SetPriority(Priority()); - if (request->HttpMethod() == HttpMethod()) - request->SetHttpBody(HttpBody()); request->SetCorsPreflightPolicy(CorsPreflightPolicy()); if (IsAdResource()) request->SetIsAdResource(); @@ -138,27 +185,27 @@ std::unique_ptr<ResourceRequest> ResourceRequest::CreateRedirectRequest( return request; } -bool ResourceRequest::IsNull() const { +bool ResourceRequestHead::IsNull() const { return url_.IsNull(); } -const KURL& ResourceRequest::Url() const { +const KURL& ResourceRequestHead::Url() const { return url_; } -void ResourceRequest::SetUrl(const KURL& url) { +void ResourceRequestHead::SetUrl(const KURL& url) { url_ = url; } -const KURL& ResourceRequest::GetInitialUrlForResourceTiming() const { +const KURL& ResourceRequestHead::GetInitialUrlForResourceTiming() const { return initial_url_for_resource_timing_; } -void ResourceRequest::SetInitialUrlForResourceTiming(const KURL& url) { +void ResourceRequestHead::SetInitialUrlForResourceTiming(const KURL& url) { initial_url_for_resource_timing_ = url; } -void ResourceRequest::RemoveUserAndPassFromURL() { +void ResourceRequestHead::RemoveUserAndPassFromURL() { if (url_.User().IsEmpty() && url_.Pass().IsEmpty()) return; @@ -166,155 +213,146 @@ void ResourceRequest::RemoveUserAndPassFromURL() { url_.SetPass(String()); } -mojom::FetchCacheMode ResourceRequest::GetCacheMode() const { +mojom::FetchCacheMode ResourceRequestHead::GetCacheMode() const { return cache_mode_; } -void ResourceRequest::SetCacheMode(mojom::FetchCacheMode cache_mode) { +void ResourceRequestHead::SetCacheMode(mojom::FetchCacheMode cache_mode) { cache_mode_ = cache_mode; } -base::TimeDelta ResourceRequest::TimeoutInterval() const { +base::TimeDelta ResourceRequestHead::TimeoutInterval() const { return timeout_interval_; } -void ResourceRequest::SetTimeoutInterval( +void ResourceRequestHead::SetTimeoutInterval( base::TimeDelta timout_interval_seconds) { timeout_interval_ = timout_interval_seconds; } -const KURL& ResourceRequest::SiteForCookies() const { +const net::SiteForCookies& ResourceRequestHead::SiteForCookies() const { return site_for_cookies_; } -void ResourceRequest::SetSiteForCookies(const KURL& site_for_cookies) { +void ResourceRequestHead::SetSiteForCookies( + const net::SiteForCookies& site_for_cookies) { site_for_cookies_ = site_for_cookies; + site_for_cookies_set_ = true; } -const SecurityOrigin* ResourceRequest::TopFrameOrigin() const { +const SecurityOrigin* ResourceRequestHead::TopFrameOrigin() const { return top_frame_origin_.get(); } -void ResourceRequest::SetTopFrameOrigin( +void ResourceRequestHead::SetTopFrameOrigin( scoped_refptr<const SecurityOrigin> origin) { top_frame_origin_ = std::move(origin); } -const AtomicString& ResourceRequest::HttpMethod() const { +const AtomicString& ResourceRequestHead::HttpMethod() const { return http_method_; } -void ResourceRequest::SetHttpMethod(const AtomicString& http_method) { +void ResourceRequestHead::SetHttpMethod(const AtomicString& http_method) { http_method_ = http_method; } -const HTTPHeaderMap& ResourceRequest::HttpHeaderFields() const { +const HTTPHeaderMap& ResourceRequestHead::HttpHeaderFields() const { return http_header_fields_; } -const AtomicString& ResourceRequest::HttpHeaderField( +const AtomicString& ResourceRequestHead::HttpHeaderField( const AtomicString& name) const { return http_header_fields_.Get(name); } -void ResourceRequest::SetHttpHeaderField(const AtomicString& name, - const AtomicString& value) { +void ResourceRequestHead::SetHttpHeaderField(const AtomicString& name, + const AtomicString& value) { http_header_fields_.Set(name, value); } -void ResourceRequest::SetHttpReferrer(const Referrer& referrer) { - if (referrer.referrer.IsEmpty()) - http_header_fields_.Remove(http_names::kReferer); - else - SetHttpHeaderField(http_names::kReferer, referrer.referrer); - referrer_policy_ = referrer.referrer_policy; - did_set_http_referrer_ = true; -} - -void ResourceRequest::ClearHTTPReferrer() { - http_header_fields_.Remove(http_names::kReferer); - referrer_policy_ = network::mojom::ReferrerPolicy::kDefault; - did_set_http_referrer_ = false; -} - -void ResourceRequest::SetHTTPOrigin(const SecurityOrigin* origin) { +void ResourceRequestHead::SetHTTPOrigin(const SecurityOrigin* origin) { SetHttpHeaderField(http_names::kOrigin, origin->ToAtomicString()); } -void ResourceRequest::ClearHTTPOrigin() { +void ResourceRequestHead::ClearHTTPOrigin() { http_header_fields_.Remove(http_names::kOrigin); } -void ResourceRequest::SetHttpOriginIfNeeded(const SecurityOrigin* origin) { +void ResourceRequestHead::SetHttpOriginIfNeeded(const SecurityOrigin* origin) { if (NeedsHTTPOrigin()) SetHTTPOrigin(origin); } -void ResourceRequest::SetHTTPOriginToMatchReferrerIfNeeded() { +void ResourceRequestHead::SetHTTPOriginToMatchReferrerIfNeeded() { if (NeedsHTTPOrigin()) { - SetHTTPOrigin( - SecurityOrigin::CreateFromString(HttpHeaderField(http_names::kReferer)) - .get()); + SetHTTPOrigin(SecurityOrigin::CreateFromString(ReferrerString()).get()); } } -void ResourceRequest::ClearHTTPUserAgent() { +void ResourceRequestHead::ClearHTTPUserAgent() { http_header_fields_.Remove(http_names::kUserAgent); } -EncodedFormData* ResourceRequest::HttpBody() const { - return http_body_.get(); +void ResourceRequestBody::SetFormBody( + scoped_refptr<EncodedFormData> form_body) { + form_body_ = std::move(form_body); +} + +const scoped_refptr<EncodedFormData>& ResourceRequest::HttpBody() const { + return body_.FormBody(); } void ResourceRequest::SetHttpBody(scoped_refptr<EncodedFormData> http_body) { - http_body_ = std::move(http_body); + body_.SetFormBody(std::move(http_body)); } -bool ResourceRequest::AllowStoredCredentials() const { +bool ResourceRequestHead::AllowStoredCredentials() const { return allow_stored_credentials_; } -void ResourceRequest::SetAllowStoredCredentials(bool allow_credentials) { +void ResourceRequestHead::SetAllowStoredCredentials(bool allow_credentials) { allow_stored_credentials_ = allow_credentials; } -ResourceLoadPriority ResourceRequest::Priority() const { +ResourceLoadPriority ResourceRequestHead::Priority() const { return priority_; } -int ResourceRequest::IntraPriorityValue() const { +int ResourceRequestHead::IntraPriorityValue() const { return intra_priority_value_; } -bool ResourceRequest::PriorityHasBeenSet() const { +bool ResourceRequestHead::PriorityHasBeenSet() const { return priority_ != ResourceLoadPriority::kUnresolved; } -void ResourceRequest::SetPriority(ResourceLoadPriority priority, - int intra_priority_value) { +void ResourceRequestHead::SetPriority(ResourceLoadPriority priority, + int intra_priority_value) { priority_ = priority; intra_priority_value_ = intra_priority_value; } -void ResourceRequest::AddHttpHeaderField(const AtomicString& name, - const AtomicString& value) { +void ResourceRequestHead::AddHttpHeaderField(const AtomicString& name, + const AtomicString& value) { HTTPHeaderMap::AddResult result = http_header_fields_.Add(name, value); if (!result.is_new_entry) result.stored_value->value = result.stored_value->value + ", " + value; } -void ResourceRequest::AddHTTPHeaderFields(const HTTPHeaderMap& header_fields) { +void ResourceRequestHead::AddHTTPHeaderFields( + const HTTPHeaderMap& header_fields) { HTTPHeaderMap::const_iterator end = header_fields.end(); for (HTTPHeaderMap::const_iterator it = header_fields.begin(); it != end; ++it) AddHttpHeaderField(it->key, it->value); } -void ResourceRequest::ClearHttpHeaderField(const AtomicString& name) { +void ResourceRequestHead::ClearHttpHeaderField(const AtomicString& name) { http_header_fields_.Remove(name); } -void ResourceRequest::SetExternalRequestStateFromRequestorAddressSpace( +void ResourceRequestHead::SetExternalRequestStateFromRequestorAddressSpace( network::mojom::IPAddressSpace requestor_space) { static_assert(network::mojom::IPAddressSpace::kLocal < network::mojom::IPAddressSpace::kPrivate, @@ -344,7 +382,7 @@ void ResourceRequest::SetExternalRequestStateFromRequestorAddressSpace( is_external_request_ = requestor_space > target_space; } -bool ResourceRequest::IsConditional() const { +bool ResourceRequestHead::IsConditional() const { return (http_header_fields_.Contains(http_names::kIfMatch) || http_header_fields_.Contains(http_names::kIfModifiedSince) || http_header_fields_.Contains(http_names::kIfNoneMatch) || @@ -352,11 +390,11 @@ bool ResourceRequest::IsConditional() const { http_header_fields_.Contains(http_names::kIfUnmodifiedSince)); } -void ResourceRequest::SetHasUserGesture(bool has_user_gesture) { +void ResourceRequestHead::SetHasUserGesture(bool has_user_gesture) { has_user_gesture_ |= has_user_gesture; } -bool ResourceRequest::CanDisplay(const KURL& url) const { +bool ResourceRequestHead::CanDisplay(const KURL& url) const { if (RequestorOrigin()->CanDisplay(url)) return true; @@ -366,7 +404,7 @@ bool ResourceRequest::CanDisplay(const KURL& url) const { return false; } -const CacheControlHeader& ResourceRequest::GetCacheControlHeader() const { +const CacheControlHeader& ResourceRequestHead::GetCacheControlHeader() const { if (!cache_control_header_cache_.parsed) { cache_control_header_cache_ = ParseCacheControlDirectives( http_header_fields_.Get(http_names::kCacheControl), @@ -375,20 +413,20 @@ const CacheControlHeader& ResourceRequest::GetCacheControlHeader() const { return cache_control_header_cache_; } -bool ResourceRequest::CacheControlContainsNoCache() const { +bool ResourceRequestHead::CacheControlContainsNoCache() const { return GetCacheControlHeader().contains_no_cache; } -bool ResourceRequest::CacheControlContainsNoStore() const { +bool ResourceRequestHead::CacheControlContainsNoStore() const { return GetCacheControlHeader().contains_no_store; } -bool ResourceRequest::HasCacheValidatorFields() const { +bool ResourceRequestHead::HasCacheValidatorFields() const { return !http_header_fields_.Get(http_names::kLastModified).IsEmpty() || !http_header_fields_.Get(http_names::kETag).IsEmpty(); } -bool ResourceRequest::NeedsHTTPOrigin() const { +bool ResourceRequestHead::NeedsHTTPOrigin() const { if (!HttpOrigin().IsEmpty()) return false; // Request already has an Origin header. diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h index 4148460ef54..71139b093a3 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h @@ -34,10 +34,12 @@ #include "base/optional.h" #include "base/time/time.h" #include "base/unguessable_token.h" +#include "net/cookies/site_for_cookies.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "services/network/public/mojom/cors.mojom-blink-forward.h" #include "services/network/public/mojom/fetch_api.mojom-blink-forward.h" #include "services/network/public/mojom/ip_address_space.mojom-blink-forward.h" +#include "services/network/public/mojom/trust_tokens.mojom-blink.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" #include "third_party/blink/public/platform/resource_request_blocked_reason.h" #include "third_party/blink/public/platform/web_url_request.h" @@ -52,36 +54,34 @@ namespace blink { class EncodedFormData; -struct Referrer; -// A ResourceRequest is a "request" object for ResourceLoader. Conceptually -// it is https://fetch.spec.whatwg.org/#concept-request, but it contains -// a lot of blink specific fields. WebURLRequest is the "public version" -// of this class and WebURLLoader needs it. See WebURLRequest and -// WrappedResourceRequest. -// -// This class is thread-bound. Do not copy/pass an instance across threads. -class PLATFORM_EXPORT ResourceRequest final { - USING_FAST_MALLOC(ResourceRequest); +// ResourceRequestHead represents request without request body. +// See ResourceRequest below to see what request is. +// ResourceRequestHead is implicitly copyable while ResourceRequest is not. +// TODO(yoichio) : Migrate existing ResourceRequest occurrence not using request +// body to ResourceRequestHead. +class PLATFORM_EXPORT ResourceRequestHead { + DISALLOW_NEW(); public: enum class RedirectStatus : uint8_t { kFollowedRedirect, kNoRedirect }; - ResourceRequest(); - explicit ResourceRequest(const String& url_string); - explicit ResourceRequest(const KURL&); + ResourceRequestHead(); + explicit ResourceRequestHead(const KURL&); - // TODO(toyoshim): Use std::unique_ptr as much as possible, and hopefully - // make ResourceRequest DISALLOW_COPY_AND_ASSIGN. See crbug.com/787704. - ResourceRequest(const ResourceRequest&); - ResourceRequest& operator=(const ResourceRequest&); + ResourceRequestHead(const ResourceRequestHead&); + ResourceRequestHead& operator=(const ResourceRequestHead&); + ResourceRequestHead(ResourceRequestHead&&); + ResourceRequestHead& operator=(ResourceRequestHead&&); - ~ResourceRequest(); + ~ResourceRequestHead(); // Constructs a new ResourceRequest for a redirect from this instance. + // Since body for a redirect request is kept and handled in the network + // service, the returned instance here in blink side doesn't contain body. std::unique_ptr<ResourceRequest> CreateRedirectRequest( const KURL& new_url, const AtomicString& new_method, - const KURL& new_site_for_cookies, + const net::SiteForCookies& new_site_for_cookies, const String& new_referrer, network::mojom::ReferrerPolicy new_referrer_policy, bool skip_service_worker) const; @@ -107,8 +107,12 @@ class PLATFORM_EXPORT ResourceRequest final { base::TimeDelta TimeoutInterval() const; void SetTimeoutInterval(base::TimeDelta); - const KURL& SiteForCookies() const; - void SetSiteForCookies(const KURL&); + const net::SiteForCookies& SiteForCookies() const; + void SetSiteForCookies(const net::SiteForCookies&); + + // Returns true if SiteForCookies() was set either via SetSiteForCookies or + // CreateRedirectRequest. + bool SiteForCookiesSet() const { return site_for_cookies_set_; } const SecurityOrigin* TopFrameOrigin() const; void SetTopFrameOrigin(scoped_refptr<const SecurityOrigin>); @@ -151,16 +155,6 @@ class PLATFORM_EXPORT ResourceRequest final { SetHttpHeaderField(http_names::kContentType, http_content_type); } - // TODO(domfarolino): Remove this once we stop storing the generated referrer - // as a header, and instead use a separate member. See - // https://crbug.com/850813. - const AtomicString& HttpReferrer() const { - return HttpHeaderField(http_names::kReferer); - } - void SetHttpReferrer(const Referrer&); - bool DidSetHttpReferrer() const { return did_set_http_referrer_; } - void ClearHTTPReferrer(); - void SetReferrerPolicy(network::mojom::ReferrerPolicy referrer_policy) { referrer_policy_ = referrer_policy; } @@ -190,9 +184,6 @@ class PLATFORM_EXPORT ResourceRequest final { SetHttpHeaderField(http_names::kAccept, http_accept); } - EncodedFormData* HttpBody() const; - void SetHttpBody(scoped_refptr<EncodedFormData>); - bool AllowStoredCredentials() const; void SetAllowStoredCredentials(bool allow_credentials); @@ -256,16 +247,11 @@ class PLATFORM_EXPORT ResourceRequest final { } // Extra data associated with this request. - WebURLRequest::ExtraData* GetExtraData() const { - return sharable_extra_data_ ? sharable_extra_data_->data.get() : nullptr; + const scoped_refptr<WebURLRequest::ExtraData>& GetExtraData() const { + return extra_data_; } - void SetExtraData(std::unique_ptr<WebURLRequest::ExtraData> extra_data) { - if (extra_data) { - sharable_extra_data_ = - base::MakeRefCounted<SharableExtraData>(std::move(extra_data)); - } else { - sharable_extra_data_ = nullptr; - } + void SetExtraData(scoped_refptr<WebURLRequest::ExtraData> extra_data) { + extra_data_ = extra_data; } bool IsDownloadToNetworkCacheOnly() const { return download_to_cache_only_; } @@ -281,6 +267,13 @@ class PLATFORM_EXPORT ResourceRequest final { request_context_ = context; } + network::mojom::RequestDestination GetRequestDestination() const { + return destination_; + } + void SetRequestDestination(network::mojom::RequestDestination destination) { + destination_ = destination; + } + network::mojom::RequestMode GetMode() const { return mode_; } void SetMode(network::mojom::RequestMode mode) { mode_ = mode; } @@ -448,14 +441,20 @@ class PLATFORM_EXPORT ResourceRequest final { prefetch_maybe_for_top_level_navigation; } + const base::Optional<network::mojom::blink::TrustTokenParams>& + TrustTokenParams() const { + return trust_token_params_; + } + void SetTrustTokenParams( + base::Optional<network::mojom::blink::TrustTokenParams> params) { + trust_token_params_ = std::move(params); + } + // Whether either RequestorOrigin or IsolatedWorldOrigin can display the // |url|, bool CanDisplay(const KURL&) const; private: - using SharableExtraData = - base::RefCountedData<std::unique_ptr<WebURLRequest::ExtraData>>; - const CacheControlHeader& GetCacheControlHeader() const; bool NeedsHTTPOrigin() const; @@ -467,7 +466,7 @@ class PLATFORM_EXPORT ResourceRequest final { // base::TimeDelta::Max() represents the default timeout on platforms that // have one. base::TimeDelta timeout_interval_; - KURL site_for_cookies_; + net::SiteForCookies site_for_cookies_; scoped_refptr<const SecurityOrigin> top_frame_origin_; scoped_refptr<const SecurityOrigin> requestor_origin_; @@ -475,7 +474,6 @@ class PLATFORM_EXPORT ResourceRequest final { AtomicString http_method_; HTTPHeaderMap http_header_fields_; - scoped_refptr<EncodedFormData> http_body_; bool allow_stored_credentials_ : 1; bool report_upload_progress_ : 1; bool report_raw_headers_ : 1; @@ -488,12 +486,14 @@ class PLATFORM_EXPORT ResourceRequest final { mojom::FetchCacheMode cache_mode_; bool skip_service_worker_ : 1; bool download_to_cache_only_ : 1; + bool site_for_cookies_set_ : 1; ResourceLoadPriority priority_; int intra_priority_value_; int requestor_id_; WebURLRequest::PreviewsState previews_state_; - scoped_refptr<SharableExtraData> sharable_extra_data_; + scoped_refptr<WebURLRequest::ExtraData> extra_data_; mojom::RequestContextType request_context_; + network::mojom::RequestDestination destination_; network::mojom::RequestMode mode_; mojom::FetchImportanceMode fetch_importance_mode_; network::mojom::CredentialsMode credentials_mode_; @@ -501,10 +501,10 @@ class PLATFORM_EXPORT ResourceRequest final { String fetch_integrity_; String referrer_string_; network::mojom::ReferrerPolicy referrer_policy_; - bool did_set_http_referrer_; bool is_external_request_; network::mojom::CorsPreflightPolicy cors_preflight_policy_; RedirectStatus redirect_status_; + base::Optional<network::mojom::blink::TrustTokenParams> trust_token_params_; base::Optional<String> suggested_filename_; @@ -547,6 +547,70 @@ class PLATFORM_EXPORT ResourceRequest final { base::Optional<base::UnguessableToken> recursive_prefetch_token_; }; +class PLATFORM_EXPORT ResourceRequestBody { + public: + ResourceRequestBody(); + explicit ResourceRequestBody(scoped_refptr<EncodedFormData> form_body); + ResourceRequestBody(const ResourceRequestBody&) = delete; + ResourceRequestBody(ResourceRequestBody&&); + + ResourceRequestBody& operator=(const ResourceRequestBody&) = delete; + ResourceRequestBody& operator=(ResourceRequestBody&&); + + ~ResourceRequestBody(); + + const scoped_refptr<EncodedFormData>& FormBody() const { return form_body_; } + void SetFormBody(scoped_refptr<EncodedFormData>); + + private: + scoped_refptr<EncodedFormData> form_body_; +}; + +// A ResourceRequest is a "request" object for ResourceLoader. Conceptually +// it is https://fetch.spec.whatwg.org/#concept-request, but it contains +// a lot of blink specific fields. WebURLRequest is the "public version" +// of this class and WebURLLoader needs it. See WebURLRequest and +// WrappedResourceRequest. +// +// This class is thread-bound. Do not copy/pass an instance across threads. +// +// Although request consists head and body, ResourceRequest is implemented by +// inheriting ResourceRequestHead due in order to make it possible to use +// property accessors through both ResourceRequestHead and ResourceRequest while +// avoiding duplicate accessor definitions. +// For those who want to add a new property in request, please implement its +// member and accessors in ResourceRequestHead instead of ResourceRequest. +class PLATFORM_EXPORT ResourceRequest final : public ResourceRequestHead { + USING_FAST_MALLOC(ResourceRequest); + + public: + ResourceRequest(); + explicit ResourceRequest(const String& url_string); + explicit ResourceRequest(const KURL&); + explicit ResourceRequest(const ResourceRequestHead&); + + ResourceRequest(const ResourceRequest&) = delete; + ResourceRequest(ResourceRequest&&); + ResourceRequest& operator=(ResourceRequest&&); + + ~ResourceRequest(); + + // TODO(yoichio): Use move semantics as much as possible. + // See crbug.com/787704. + void CopyFrom(const ResourceRequest&); + void CopyHeadFrom(const ResourceRequestHead&); + + const scoped_refptr<EncodedFormData>& HttpBody() const; + void SetHttpBody(scoped_refptr<EncodedFormData>); + + ResourceRequestBody& MutableBody() { return body_; } + + private: + ResourceRequest& operator=(const ResourceRequest&); + + ResourceRequestBody body_; +}; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_REQUEST_H_ diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc index ffb5fbcea4d..cde5b01c787 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc @@ -33,7 +33,7 @@ TEST(ResourceRequestTest, SetIsAdResource) { std::unique_ptr<ResourceRequest> redirect_request = original.CreateRedirectRequest( KURL("https://example.test/redirect"), original.HttpMethod(), - original.SiteForCookies(), original.HttpReferrer(), + original.SiteForCookies(), original.ReferrerString(), original.GetReferrerPolicy(), original.GetSkipServiceWorker()); EXPECT_TRUE(redirect_request->IsAdResource()); } @@ -48,7 +48,7 @@ TEST(ResourceRequestTest, UpgradeIfInsecureAcrossRedirects) { std::unique_ptr<ResourceRequest> redirect_request = original.CreateRedirectRequest( KURL("https://example.test/redirect"), original.HttpMethod(), - original.SiteForCookies(), original.HttpReferrer(), + original.SiteForCookies(), original.ReferrerString(), original.GetReferrerPolicy(), original.GetSkipServiceWorker()); EXPECT_TRUE(redirect_request->UpgradeIfInsecure()); } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc index e7a8fbad67f..f7848a110e7 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc @@ -31,6 +31,7 @@ #include <memory> #include <string> +#include "net/http/structured_headers.h" #include "services/network/public/cpp/cors/cors.h" #include "services/network/public/mojom/fetch_api.mojom-blink.h" #include "third_party/blink/public/platform/web_url_response.h" @@ -207,16 +208,16 @@ void ResourceResponse::UpdateHeaderParsedState(const AtomicString& name) { static const char kExpiresHeader[] = "expires"; static const char kLastModifiedHeader[] = "last-modified"; - if (DeprecatedEqualIgnoringCase(name, kAgeHeader)) + if (EqualIgnoringASCIICase(name, kAgeHeader)) have_parsed_age_header_ = false; - else if (DeprecatedEqualIgnoringCase(name, kCacheControlHeader) || - DeprecatedEqualIgnoringCase(name, kPragmaHeader)) + else if (EqualIgnoringASCIICase(name, kCacheControlHeader) || + EqualIgnoringASCIICase(name, kPragmaHeader)) cache_control_header_ = CacheControlHeader(); - else if (DeprecatedEqualIgnoringCase(name, kDateHeader)) + else if (EqualIgnoringASCIICase(name, kDateHeader)) have_parsed_date_header_ = false; - else if (DeprecatedEqualIgnoringCase(name, kExpiresHeader)) + else if (EqualIgnoringASCIICase(name, kExpiresHeader)) have_parsed_expires_header_ = false; - else if (DeprecatedEqualIgnoringCase(name, kLastModifiedHeader)) + else if (EqualIgnoringASCIICase(name, kLastModifiedHeader)) have_parsed_last_modified_header_ = false; } @@ -415,7 +416,7 @@ bool ResourceResponse::IsAttachment() const { if (loc != kNotFound) value = value.Left(loc); value = value.StripWhiteSpace(); - return DeprecatedEqualIgnoringCase(value, kAttachmentString); + return EqualIgnoringASCIICase(value, kAttachmentString); } AtomicString ResourceResponse::HttpContentType() const { @@ -489,6 +490,19 @@ void ResourceResponse::SetDecodedBodyLength(int64_t value) { decoded_body_length_ = value; } +network::mojom::CrossOriginEmbedderPolicyValue +ResourceResponse::GetCrossOriginEmbedderPolicy() const { + static constexpr char kHeaderName[] = "cross-origin-embedder-policy"; + const std::string value = HttpHeaderField(kHeaderName).Utf8(); + using Item = net::structured_headers::Item; + const auto item = net::structured_headers::ParseItem(value); + if (!item || item->item.Type() != Item::kTokenType || + item->item.GetString() != "require-corp") { + return network::mojom::CrossOriginEmbedderPolicyValue::kNone; + } + return network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp; +} + STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersionUnknown, ResourceResponse::kHTTPVersionUnknown); STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersion_0_9, diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h index 9ba1090ee2e..4028c6341d2 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h @@ -33,6 +33,7 @@ #include "base/memory/scoped_refptr.h" #include "base/optional.h" #include "base/time/time.h" +#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-shared.h" #include "third_party/blink/public/platform/web_url_response.h" #include "third_party/blink/renderer/platform/network/http_header_map.h" #include "third_party/blink/renderer/platform/network/http_parsers.h" @@ -282,6 +283,9 @@ class PLATFORM_EXPORT ResourceResponse final { bool IsLegacyTLSVersion() const { return is_legacy_tls_version_; } void SetIsLegacyTLSVersion(bool value) { is_legacy_tls_version_ = value; } + bool TimingAllowPassed() const { return timing_allow_passed_; } + void SetTimingAllowPassed(bool value) { timing_allow_passed_ = value; } + SecurityStyle GetSecurityStyle() const { return security_style_; } void SetSecurityStyle(SecurityStyle security_style) { security_style_ = security_style; @@ -465,6 +469,9 @@ class PLATFORM_EXPORT ResourceResponse final { was_in_prefetch_cache_ = was_in_prefetch_cache; } + network::mojom::CrossOriginEmbedderPolicyValue GetCrossOriginEmbedderPolicy() + const; + private: void UpdateHeaderParsedState(const AtomicString& name); @@ -504,6 +511,10 @@ class PLATFORM_EXPORT ResourceResponse final { // will be removed in the future. bool is_legacy_tls_version_ = false; + // True if the Timing-Allow-Origin check passes. + // https://fetch.spec.whatwg.org/#concept-response-timing-allow-passed + bool timing_allow_passed_ = false; + // The time at which the resource's certificate expires. Null if there was no // certificate. base::Time cert_validity_start_; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc index 599ec997269..4353c202b8a 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc @@ -575,4 +575,11 @@ TEST(ResourceTest, StaleWhileRevalidateCacheControlWithRedirect) { EXPECT_TRUE(resource->StaleRevalidationRequested()); } +// This is a regression test for https://crbug.com/1062837. +TEST(ResourceTest, DefaultOverheadSize) { + const KURL url("http://127.0.0.1:8000/foo.html"); + auto* resource = MakeGarbageCollected<MockResource>(url); + EXPECT_EQ(resource->CalculateOverheadSizeForTest(), resource->OverheadSize()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h index 32f53487f19..9eaba1ab7fc 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h @@ -51,8 +51,10 @@ class PLATFORM_EXPORT ResourceTimingInfo static scoped_refptr<ResourceTimingInfo> Create( const AtomicString& type, const base::TimeTicks time, - mojom::RequestContextType context) { - return base::AdoptRef(new ResourceTimingInfo(type, time, context)); + mojom::RequestContextType context, + network::mojom::RequestDestination destination) { + return base::AdoptRef( + new ResourceTimingInfo(type, time, context, destination)); } base::TimeTicks InitialTime() const { return initial_time_; } @@ -91,6 +93,9 @@ class PLATFORM_EXPORT ResourceTimingInfo } bool NegativeAllowed() const { return negative_allowed_; } mojom::RequestContextType ContextType() const { return context_type_; } + network::mojom::RequestDestination RequestDestination() const { + return request_destination_; + } void SetWorkerTimingReceiver( mojo::PendingReceiver<mojom::blink::WorkerTimingContainer> @@ -106,12 +111,17 @@ class PLATFORM_EXPORT ResourceTimingInfo private: ResourceTimingInfo(const AtomicString& type, const base::TimeTicks time, - mojom::RequestContextType context_type) - : type_(type), initial_time_(time), context_type_(context_type) {} + mojom::RequestContextType context_type, + network::mojom::RequestDestination request_destination) + : type_(type), + initial_time_(time), + context_type_(context_type), + request_destination_(request_destination) {} AtomicString type_; base::TimeTicks initial_time_; mojom::RequestContextType context_type_; + network::mojom::RequestDestination request_destination_; base::TimeTicks load_response_end_; KURL initial_url_; ResourceResponse final_response_; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.cc index 07882629bc3..33cedf848d7 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.cc @@ -4,45 +4,73 @@ #include "third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.h" +#include "base/metrics/histogram_macros.h" #include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h" #include "third_party/blink/renderer/platform/loader/fetch/resource.h" namespace blink { +namespace { + +void RecordState(StateOnGet state) { + UMA_HISTOGRAM_ENUMERATION("Memory.Renderer.BlinkCachedMetadataGetResult", + state); +} + +} // namespace + ScriptCachedMetadataHandler::ScriptCachedMetadataHandler( const WTF::TextEncoding& encoding, std::unique_ptr<CachedMetadataSender> sender) : sender_(std::move(sender)), encoding_(encoding) {} -void ScriptCachedMetadataHandler::Trace(blink::Visitor* visitor) { +void ScriptCachedMetadataHandler::Trace(Visitor* visitor) { CachedMetadataHandler::Trace(visitor); } -void ScriptCachedMetadataHandler::SetCachedMetadata( - uint32_t data_type_id, - const uint8_t* data, - size_t size, - CachedMetadataHandler::CacheType cache_type) { - // Currently, only one type of cached metadata per resource is supported. If - // the need arises for multiple types of metadata per resource this could be - // enhanced to store types of metadata in a map. +void ScriptCachedMetadataHandler::SetCachedMetadata(uint32_t data_type_id, + const uint8_t* data, + size_t size) { DCHECK(!cached_metadata_); + // Having been discarded once, the further attempts to overwrite the + // CachedMetadata are ignored. This behavior is slightly easier to simulate in + // tests. Should happen rarely enough not to affect performance. The + // JSModuleScript behaves similarly by preventing the creation of the code + // cache. + if (cached_metadata_discarded_) + return; cached_metadata_ = CachedMetadata::Create(data_type_id, data, size); - if (cache_type == CachedMetadataHandler::kSendToPlatform) - SendToPlatform(); + if (!disable_send_to_platform_for_testing_) + CommitToPersistentStorage(); } void ScriptCachedMetadataHandler::ClearCachedMetadata( - CachedMetadataHandler::CacheType cache_type) { + ClearCacheType cache_type) { cached_metadata_ = nullptr; - if (cache_type == CachedMetadataHandler::kSendToPlatform) - SendToPlatform(); + switch (cache_type) { + case kClearLocally: + break; + case kDiscardLocally: + cached_metadata_discarded_ = true; + break; + case kClearPersistentStorage: + CommitToPersistentStorage(); + break; + } } scoped_refptr<CachedMetadata> ScriptCachedMetadataHandler::GetCachedMetadata( uint32_t data_type_id) const { - if (!cached_metadata_ || cached_metadata_->DataTypeID() != data_type_id) + if (!cached_metadata_) { + RecordState(cached_metadata_discarded_ ? StateOnGet::kWasDiscarded + : StateOnGet::kWasNeverPresent); + return nullptr; + } + if (cached_metadata_->DataTypeID() != data_type_id) { + RecordState(StateOnGet::kDataTypeMismatch); return nullptr; + } + RecordState(StateOnGet::kPresent); return cached_metadata_; } @@ -79,7 +107,7 @@ size_t ScriptCachedMetadataHandler::GetCodeCacheSize() const { return (cached_metadata_) ? cached_metadata_->SerializedData().size() : 0; } -void ScriptCachedMetadataHandler::SendToPlatform() { +void ScriptCachedMetadataHandler::CommitToPersistentStorage() { if (cached_metadata_) { base::span<const uint8_t> serialized_data = cached_metadata_->SerializedData(); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.h b/chromium/third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.h index 4d1099b6625..df5ca5535a1 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.h @@ -33,9 +33,9 @@ class PLATFORM_EXPORT ScriptCachedMetadataHandler final ScriptCachedMetadataHandler(const WTF::TextEncoding&, std::unique_ptr<CachedMetadataSender>); ~ScriptCachedMetadataHandler() override = default; - void Trace(blink::Visitor*) override; - void SetCachedMetadata(uint32_t, const uint8_t*, size_t, CacheType) override; - void ClearCachedMetadata(CacheType) override; + void Trace(Visitor*) override; + void SetCachedMetadata(uint32_t, const uint8_t*, size_t) override; + void ClearCachedMetadata(ClearCacheType) override; scoped_refptr<CachedMetadata> GetCachedMetadata(uint32_t) const override; // This returns the encoding at the time of ResponseReceived(). Therefore this @@ -56,14 +56,31 @@ class PLATFORM_EXPORT ScriptCachedMetadataHandler final size_t GetCodeCacheSize() const override; private: - void SendToPlatform(); + friend class ModuleScriptTest; + + void CommitToPersistentStorage(); scoped_refptr<CachedMetadata> cached_metadata_; + bool cached_metadata_discarded_ = false; std::unique_ptr<CachedMetadataSender> sender_; const WTF::TextEncoding encoding_; }; +// Describes a few interesting states of the ScriptCachedMetadataHandler when +// GetCachedMetadata() is called. 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 StateOnGet : int { + kPresent = 0, + kDataTypeMismatch = 1, + kWasNeverPresent = 2, + kWasDiscarded = 3, + + // Must be equal to the greatest among enumeraiton values. + kMaxValue = kWasDiscarded +}; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SCRIPT_CACHED_METADATA_HANDLER_H_ diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc index f5aebce8964..f2647226f07 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc @@ -23,8 +23,10 @@ FetchParameters ScriptFetchOptions::CreateFetchParameters( // Step 1. ... "script", ... [spec text] ResourceLoaderOptions resource_loader_options; resource_loader_options.initiator_info.name = "script"; - FetchParameters params(resource_request, resource_loader_options); + resource_loader_options.reject_coep_unsafe_none = reject_coep_unsafe_none_; + FetchParameters params(std::move(resource_request), resource_loader_options); params.SetRequestContext(mojom::RequestContextType::SCRIPT); + params.SetRequestDestination(network::mojom::RequestDestination::kScript); // Step 1. ... and CORS setting. [spec text] if (cross_origin != kCrossOriginAttributeNotSet) diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h index 24e2efe2f92..45977a46229 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h @@ -48,14 +48,17 @@ class PLATFORM_EXPORT ScriptFetchOptions final { ParserDisposition parser_state, network::mojom::CredentialsMode credentials_mode, network::mojom::ReferrerPolicy referrer_policy, - mojom::FetchImportanceMode importance) + mojom::FetchImportanceMode importance, + RejectCoepUnsafeNone reject_coep_unsafe_none = + RejectCoepUnsafeNone(false)) : nonce_(nonce), integrity_metadata_(integrity_metadata), integrity_attribute_(integrity_attribute), parser_state_(parser_state), credentials_mode_(credentials_mode), referrer_policy_(referrer_policy), - importance_(importance) {} + importance_(importance), + reject_coep_unsafe_none_(reject_coep_unsafe_none) {} ~ScriptFetchOptions() = default; const String& Nonce() const { return nonce_; } @@ -73,6 +76,9 @@ class PLATFORM_EXPORT ScriptFetchOptions final { return referrer_policy_; } mojom::FetchImportanceMode Importance() const { return importance_; } + RejectCoepUnsafeNone GetRejectCoepUnsafeNone() const { + return reject_coep_unsafe_none_; + } // https://html.spec.whatwg.org/C/#fetch-a-classic-script // Steps 1 and 3. @@ -104,6 +110,13 @@ class PLATFORM_EXPORT ScriptFetchOptions final { // https://github.com/whatwg/html/issues/3670 for some discussion on adding an // "importance" member to the script fetch options struct. const mojom::FetchImportanceMode importance_; + + // True when we should reject a response with COEP: none. + // https://wicg.github.io/cross-origin-embedder-policy/#integration-html + // This is for dedicated workers. + // TODO(crbug.com/1064920): Remove this once PlzDedicatedWorker ships. + const RejectCoepUnsafeNone reject_coep_unsafe_none_ = + RejectCoepUnsafeNone(false); }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc index db355819f3d..58cc557ec78 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc @@ -28,18 +28,17 @@ class SourceKeyedCachedMetadataHandler::SingleKeyHandler final void SetCachedMetadata(uint32_t data_type_id, const uint8_t* data, - size_t size, - CacheType cache_type) override { + size_t size) override { DCHECK(!parent_->cached_metadata_map_.Contains(key_)); parent_->cached_metadata_map_.insert( key_, CachedMetadata::Create(data_type_id, data, size)); - if (cache_type == CachedMetadataHandler::kSendToPlatform) + if (!disable_send_to_platform_for_testing_) parent_->SendToPlatform(); } - void ClearCachedMetadata(CacheType cache_type) override { + void ClearCachedMetadata(ClearCacheType cache_type) override { parent_->cached_metadata_map_.erase(key_); - if (cache_type == CachedMetadataHandler::kSendToPlatform) + if (cache_type == CachedMetadataHandler::kClearPersistentStorage) parent_->SendToPlatform(); } @@ -104,9 +103,9 @@ SingleCachedMetadataHandler* SourceKeyedCachedMetadataHandler::HandlerForSource( } void SourceKeyedCachedMetadataHandler::ClearCachedMetadata( - CachedMetadataHandler::CacheType cache_type) { + CachedMetadataHandler::ClearCacheType cache_type) { cached_metadata_map_.clear(); - if (cache_type == CachedMetadataHandler::kSendToPlatform) + if (cache_type == CachedMetadataHandler::kClearPersistentStorage) SendToPlatform(); } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h index 045a52d8670..4460985757c 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h @@ -32,7 +32,7 @@ class PLATFORM_EXPORT SourceKeyedCachedMetadataHandler final SingleCachedMetadataHandler* HandlerForSource(const String& source); void ClearCachedMetadata( - CachedMetadataHandler::CacheType cache_type) override; + CachedMetadataHandler::ClearCacheType cache_type) override; String Encoding() const override; bool IsServedFromCacheStorage() const override { return sender_->IsServedFromCacheStorage(); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc index 287d8ec84cf..eec248b7b95 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc @@ -236,7 +236,7 @@ TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_EmptyClearDoesSend) { WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url)); // Clear and send to the platform - handler->ClearCachedMetadata(CachedMetadataHandler::kSendToPlatform); + handler->ClearCachedMetadata(CachedMetadataHandler::kClearPersistentStorage); // Load from platform Vector<CacheMetadataEntry> cache_metadatas = @@ -293,8 +293,8 @@ TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_SetWithNoSendDoesNotSend) { handler->HandlerForSource(source2); Vector<uint8_t> data1 = {1, 2, 3}; - source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size(), - CachedMetadataHandler::kCacheLocally); + source1_handler->DisableSendToPlatformForTesting(); + source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size()); Vector<uint8_t> data2 = {3, 4, 5, 6}; source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size()); @@ -320,8 +320,9 @@ TEST(SourceKeyedCachedMetadataHandlerTest, WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url)); - // Clear and send to the platform - handler->ClearCachedMetadata(CachedMetadataHandler::kSendToPlatform); + // Clear and persist in the platform. + handler->ClearCachedMetadata( + CachedMetadataHandler::kClearPersistentStorage); } // Reload from platform diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc index c20cf6b3f02..bd1ead40055 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc @@ -29,7 +29,7 @@ void StaleRevalidationResourceClient::NotifyFinished(Resource* resource) { } } -void StaleRevalidationResourceClient::Trace(blink::Visitor* visitor) { +void StaleRevalidationResourceClient::Trace(Visitor* visitor) { visitor->Trace(stale_resource_); RawResourceClient::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.h b/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.h index 6ef734efb47..b1b9a3eb226 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.h @@ -24,7 +24,7 @@ class StaleRevalidationResourceClient // RawResourceClient overloads. void NotifyFinished(Resource* resource) override; - void Trace(blink::Visitor* visitor) override; + void Trace(Visitor* visitor) override; String DebugName() const override; private: diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.cc new file mode 100644 index 00000000000..6654f8e72d5 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.cc @@ -0,0 +1,33 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h" +#include "services/network/public/cpp/optional_trust_token_params.h" +#include "services/network/public/mojom/trust_tokens.mojom-blink.h" + +namespace blink { + +network::OptionalTrustTokenParams ConvertTrustTokenParams( + const base::Optional<network::mojom::blink::TrustTokenParams>& maybe_in) { + if (!maybe_in) + return base::nullopt; + const network::mojom::blink::TrustTokenParams& in = *maybe_in; + + network::mojom::TrustTokenParamsPtr out = + network::mojom::TrustTokenParams::New(); + out->type = in.type; + out->refresh_policy = in.refresh_policy; + out->sign_request_data = in.sign_request_data; + out->include_timestamp_header = in.include_timestamp_header; + // Optional value: + if (in.issuer) + out->issuer = in.issuer->ToUrlOrigin(); + for (const String& additional_header : in.additional_signed_headers) { + out->additional_signed_headers.push_back(additional_header.Latin1()); + } + + return network::OptionalTrustTokenParams(std::move(out)); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h b/chromium/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h new file mode 100644 index 00000000000..9ba5fe77806 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h @@ -0,0 +1,30 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_TRUST_TOKEN_PARAMS_CONVERSION_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_TRUST_TOKEN_PARAMS_CONVERSION_H_ + +#include "base/optional.h" +#include "services/network/public/cpp/optional_trust_token_params.h" +#include "third_party/blink/public/platform/web_common.h" + +namespace network { +namespace mojom { +namespace blink { +class TrustTokenParams; + +} // namespace blink +} // namespace mojom +} // namespace network + +namespace blink { + +// Converts a mojom::blink TrustTokenParams object to its non-Blink counterpart +// by directly copying all fields, converting types where necessary. +network::OptionalTrustTokenParams ConvertTrustTokenParams( + const base::Optional<network::mojom::blink::TrustTokenParams>& maybe_in); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_TRUST_TOKEN_PARAMS_CONVERSION_H_ diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc new file mode 100644 index 00000000000..e3fe5379677 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc @@ -0,0 +1,353 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h" + +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "net/base/request_priority.h" +#include "net/http/http_request_headers.h" +#include "net/http/http_util.h" +#include "services/network/public/cpp/constants.h" +#include "services/network/public/cpp/optional_trust_token_params.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/resource_request_body.h" +#include "services/network/public/mojom/data_pipe_getter.mojom-blink.h" +#include "services/network/public/mojom/data_pipe_getter.mojom.h" +#include "services/network/public/mojom/trust_tokens.mojom-blink.h" +#include "services/network/public/mojom/trust_tokens.mojom.h" +#include "third_party/blink/public/mojom/blob/blob.mojom.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h" +#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h" +#include "third_party/blink/public/platform/file_path_conversion.h" +#include "third_party/blink/public/platform/url_conversion.h" +#include "third_party/blink/public/platform/web_string.h" +#include "third_party/blink/renderer/platform/blob/blob_data.h" +#include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h" +#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h" +#include "third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h" +#include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h" + +namespace blink { + +namespace { + +constexpr char kStylesheetAcceptHeader[] = "text/css,*/*;q=0.1"; +constexpr char kImageAcceptHeader[] = "image/webp,image/apng,image/*,*/*;q=0.8"; + +// TODO(yhirano): Unify these with variables in +// content/public/common/content_constants.h. +constexpr char kCorsExemptPurposeHeaderName[] = "Purpose"; +constexpr char kCorsExemptRequestedWithHeaderName[] = "X-Requested-With"; + +// This is complementary to ConvertNetPriorityToWebKitPriority, defined in +// service_worker_context_client.cc. +net::RequestPriority ConvertWebKitPriorityToNetPriority( + WebURLRequest::Priority priority) { + switch (priority) { + case WebURLRequest::Priority::kVeryHigh: + return net::HIGHEST; + + case WebURLRequest::Priority::kHigh: + return net::MEDIUM; + + case WebURLRequest::Priority::kMedium: + return net::LOW; + + case WebURLRequest::Priority::kLow: + return net::LOWEST; + + case WebURLRequest::Priority::kVeryLow: + return net::IDLE; + + case WebURLRequest::Priority::kUnresolved: + default: + NOTREACHED(); + return net::LOW; + } +} + +// TODO(yhirano) Dedupe this and the same-name function in +// web_url_request_util.cc. +std::string TrimLWSAndCRLF(const base::StringPiece& input) { + base::StringPiece string = net::HttpUtil::TrimLWS(input); + const char* begin = string.data(); + const char* end = string.data() + string.size(); + while (begin < end && (end[-1] == '\r' || end[-1] == '\n')) + --end; + return std::string(base::StringPiece(begin, end - begin)); +} + +mojom::ResourceType RequestContextToResourceType( + mojom::RequestContextType request_context) { + switch (request_context) { + // CSP report + case mojom::RequestContextType::CSP_REPORT: + return mojom::ResourceType::kCspReport; + + // Favicon + case mojom::RequestContextType::FAVICON: + return mojom::ResourceType::kFavicon; + + // Font + case mojom::RequestContextType::FONT: + return mojom::ResourceType::kFontResource; + + // Image + case mojom::RequestContextType::IMAGE: + case mojom::RequestContextType::IMAGE_SET: + return mojom::ResourceType::kImage; + + // Media + case mojom::RequestContextType::AUDIO: + case mojom::RequestContextType::VIDEO: + return mojom::ResourceType::kMedia; + + // Object + case mojom::RequestContextType::EMBED: + case mojom::RequestContextType::OBJECT: + return mojom::ResourceType::kObject; + + // Ping + case mojom::RequestContextType::BEACON: + case mojom::RequestContextType::PING: + return mojom::ResourceType::kPing; + + // Subresource of plugins + case mojom::RequestContextType::PLUGIN: + return mojom::ResourceType::kPluginResource; + + // Prefetch + case mojom::RequestContextType::PREFETCH: + return mojom::ResourceType::kPrefetch; + + // Script + case mojom::RequestContextType::IMPORT: + case mojom::RequestContextType::SCRIPT: + return mojom::ResourceType::kScript; + + // Style + case mojom::RequestContextType::XSLT: + case mojom::RequestContextType::STYLE: + return mojom::ResourceType::kStylesheet; + + // Subresource + case mojom::RequestContextType::DOWNLOAD: + case mojom::RequestContextType::MANIFEST: + case mojom::RequestContextType::SUBRESOURCE: + return mojom::ResourceType::kSubResource; + + // TextTrack + case mojom::RequestContextType::TRACK: + return mojom::ResourceType::kMedia; + + // Workers + case mojom::RequestContextType::SERVICE_WORKER: + return mojom::ResourceType::kServiceWorker; + case mojom::RequestContextType::SHARED_WORKER: + return mojom::ResourceType::kSharedWorker; + case mojom::RequestContextType::WORKER: + return mojom::ResourceType::kWorker; + + // Unspecified + case mojom::RequestContextType::INTERNAL: + case mojom::RequestContextType::UNSPECIFIED: + return mojom::ResourceType::kSubResource; + + // XHR + case mojom::RequestContextType::EVENT_SOURCE: + case mojom::RequestContextType::FETCH: + case mojom::RequestContextType::XML_HTTP_REQUEST: + return mojom::ResourceType::kXhr; + + // Navigation requests should not go through WebURLLoader. + case mojom::RequestContextType::FORM: + case mojom::RequestContextType::HYPERLINK: + case mojom::RequestContextType::LOCATION: + case mojom::RequestContextType::FRAME: + case mojom::RequestContextType::IFRAME: + NOTREACHED(); + return mojom::ResourceType::kSubResource; + + default: + NOTREACHED(); + return mojom::ResourceType::kSubResource; + } +} + +} // namespace + +void PopulateResourceRequestBody(const EncodedFormData& src, + network::ResourceRequestBody* dest) { + for (const auto& element : src.Elements()) { + switch (element.type_) { + case FormDataElement::kData: + dest->AppendBytes(element.data_.data(), element.data_.size()); + break; + case FormDataElement::kEncodedFile: + if (element.file_length_ == -1) { + dest->AppendFileRange( + WebStringToFilePath(element.filename_), 0, + std::numeric_limits<uint64_t>::max(), + element.expected_file_modification_time_.value_or(base::Time())); + } else { + dest->AppendFileRange( + WebStringToFilePath(element.filename_), + static_cast<uint64_t>(element.file_start_), + static_cast<uint64_t>(element.file_length_), + element.expected_file_modification_time_.value_or(base::Time())); + } + break; + case FormDataElement::kEncodedBlob: { + DCHECK(element.optional_blob_data_handle_); + mojo::Remote<mojom::Blob> blob_remote(mojo::PendingRemote<mojom::Blob>( + element.optional_blob_data_handle_->CloneBlobRemote().PassPipe(), + mojom::Blob::Version_)); + mojo::PendingRemote<network::mojom::DataPipeGetter> + data_pipe_getter_remote; + blob_remote->AsDataPipeGetter( + data_pipe_getter_remote.InitWithNewPipeAndPassReceiver()); + dest->AppendDataPipe(std::move(data_pipe_getter_remote)); + break; + } + case FormDataElement::kDataPipe: { + // Convert network::mojom::blink::DataPipeGetter to + // network::mojom::DataPipeGetter through a raw message pipe. + mojo::PendingRemote<network::mojom::blink::DataPipeGetter> + pending_data_pipe_getter; + element.data_pipe_getter_->GetDataPipeGetter()->Clone( + pending_data_pipe_getter.InitWithNewPipeAndPassReceiver()); + dest->AppendDataPipe( + mojo::PendingRemote<network::mojom::DataPipeGetter>( + pending_data_pipe_getter.PassPipe(), 0u)); + break; + } + } + } +} + +void PopulateResourceRequest(const ResourceRequestHead& src, + ResourceRequestBody src_body, + network::ResourceRequest* dest) { + dest->method = src.HttpMethod().Latin1(); + dest->url = src.Url(); + dest->site_for_cookies = src.SiteForCookies(); + dest->upgrade_if_insecure = src.UpgradeIfInsecure(); + dest->is_revalidating = src.IsRevalidating(); + if (src.RequestorOrigin()->ToString() == "null") { + // "file:" origin is treated like an opaque unique origin when + // allow-file-access-from-files is not specified. Such origin is not + // opaque (i.e., IsOpaque() returns false) but still serializes to + // "null". + dest->request_initiator = url::Origin(); + } else { + dest->request_initiator = src.RequestorOrigin()->ToUrlOrigin(); + } + if (src.IsolatedWorldOrigin()) { + dest->isolated_world_origin = src.IsolatedWorldOrigin()->ToUrlOrigin(); + } + dest->referrer = WebStringToGURL(src.ReferrerString()); + + // "default" referrer policy has already been resolved. + DCHECK_NE(src.GetReferrerPolicy(), network::mojom::ReferrerPolicy::kDefault); + dest->referrer_policy = + network::ReferrerPolicyForUrlRequest(src.GetReferrerPolicy()); + + for (const auto& item : src.HttpHeaderFields()) { + const std::string name = item.key.Latin1(); + const std::string value = TrimLWSAndCRLF(item.value.Latin1()); + dest->headers.SetHeader(name, value); + } + // Set X-Requested-With header to cors_exempt_headers rather than headers to + // be exempted from CORS checks. + if (!src.GetRequestedWithHeader().IsEmpty()) { + dest->cors_exempt_headers.SetHeader(kCorsExemptRequestedWithHeaderName, + src.GetRequestedWithHeader().Utf8()); + } + // Set Purpose header to cors_exempt_headers rather than headers to be + // exempted from CORS checks. + if (!src.GetPurposeHeader().IsEmpty()) { + dest->cors_exempt_headers.SetHeader(kCorsExemptPurposeHeaderName, + src.GetPurposeHeader().Utf8()); + } + + // TODO(yhirano): Remove this WrappedResourceRequest. + dest->load_flags = WrappedResourceRequest(ResourceRequest(src)) + .GetLoadFlagsForWebUrlRequest(); + dest->recursive_prefetch_token = src.RecursivePrefetchToken(); + dest->priority = ConvertWebKitPriorityToNetPriority(src.Priority()); + dest->should_reset_appcache = src.ShouldResetAppCache(); + dest->is_external_request = src.IsExternalRequest(); + dest->cors_preflight_policy = src.CorsPreflightPolicy(); + dest->skip_service_worker = src.GetSkipServiceWorker(); + dest->mode = src.GetMode(); + dest->destination = src.GetRequestDestination(); + dest->credentials_mode = src.GetCredentialsMode(); + dest->redirect_mode = src.GetRedirectMode(); + dest->fetch_integrity = src.GetFetchIntegrity().Utf8(); + + mojom::ResourceType resource_type = + RequestContextToResourceType(src.GetRequestContext()); + + // TODO(kinuko): Deprecate these. + dest->fetch_request_context_type = static_cast<int>(src.GetRequestContext()); + dest->resource_type = static_cast<int>(resource_type); + + if (resource_type == mojom::ResourceType::kXhr && + (dest->url.has_username() || dest->url.has_password())) { + dest->do_not_prompt_for_login = true; + } + if (resource_type == mojom::ResourceType::kPrefetch || + resource_type == mojom::ResourceType::kFavicon) { + dest->do_not_prompt_for_login = true; + } + + dest->keepalive = src.GetKeepalive(); + dest->has_user_gesture = src.HasUserGesture(); + dest->enable_load_timing = true; + dest->enable_upload_progress = src.ReportUploadProgress(); + dest->report_raw_headers = src.ReportRawHeaders(); + // TODO(ryansturm): Remove dest->previews_state once it is no + // longer used in a network delegate. https://crbug.com/842233 + dest->previews_state = static_cast<int>(src.GetPreviewsState()); + dest->throttling_profile_id = src.GetDevToolsToken(); + dest->trust_token_params = ConvertTrustTokenParams(src.TrustTokenParams()); + + if (base::UnguessableToken window_id = src.GetFetchWindowId()) + dest->fetch_window_id = base::make_optional(window_id); + + if (src.GetDevToolsId().has_value()) { + dest->devtools_request_id = src.GetDevToolsId().value().Ascii(); + } + + if (src.IsSignedExchangePrefetchCacheEnabled()) { + DCHECK_EQ(src.GetRequestContext(), mojom::RequestContextType::PREFETCH); + dest->is_signed_exchange_prefetch_cache_enabled = true; + } + + if (const EncodedFormData* body = src_body.FormBody().get()) { + DCHECK_NE(dest->method, net::HttpRequestHeaders::kGetMethod); + DCHECK_NE(dest->method, net::HttpRequestHeaders::kHeadMethod); + dest->request_body = base::MakeRefCounted<network::ResourceRequestBody>(); + + PopulateResourceRequestBody(*body, dest->request_body.get()); + } + + if (resource_type == mojom::ResourceType::kStylesheet) { + dest->headers.SetHeader(net::HttpRequestHeaders::kAccept, + kStylesheetAcceptHeader); + } else if (resource_type == mojom::ResourceType::kImage || + resource_type == mojom::ResourceType::kFavicon) { + dest->headers.SetHeader(net::HttpRequestHeaders::kAccept, + kImageAcceptHeader); + } else { + // Calling SetHeaderIfMissing() instead of SetHeader() because JS can + // manually set an accept header on an XHR. + dest->headers.SetHeaderIfMissing(net::HttpRequestHeaders::kAccept, + network::kDefaultAcceptHeaderValue); + } +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h new file mode 100644 index 00000000000..a94fba68d4c --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h @@ -0,0 +1,29 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_REQUEST_CONVERSION_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_REQUEST_CONVERSION_H_ + +// This file consists of request conversion functions between blink and network. + +namespace network { +class ResourceRequestBody; +struct ResourceRequest; +} // namespace network + +namespace blink { + +class ResourceRequestHead; +class ResourceRequestBody; +class EncodedFormData; + +void PopulateResourceRequestBody(const EncodedFormData& src, + network::ResourceRequestBody* dest); + +void PopulateResourceRequest(const ResourceRequestHead& src, + ResourceRequestBody src_body, + network::ResourceRequest* dest); +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_REQUEST_CONVERSION_H_ diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/worker_resource_timing_notifier.h b/chromium/third_party/blink/renderer/platform/loader/fetch/worker_resource_timing_notifier.h index 918a3528df8..92271f1c6ed 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/worker_resource_timing_notifier.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/worker_resource_timing_notifier.h @@ -5,14 +5,13 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_WORKER_RESOURCE_TIMING_NOTIFIER_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_WORKER_RESOURCE_TIMING_NOTIFIER_H_ +#include "third_party/blink/public/mojom/timing/resource_timing.mojom-blink-forward.h" #include "third_party/blink/public/mojom/timing/worker_timing_container.mojom-blink-forward.h" #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/heap/persistent.h" namespace blink { -struct WebResourceTimingInfo; - // This class is used by WorkerFetchContext to add a resource timing to an // appropriate Performance Timeline. // https://w3c.github.io/performance-timeline/#performance-timeline @@ -27,7 +26,7 @@ class WorkerResourceTimingNotifier // Timeline which may be associated with a different thread from the current // running thread. virtual void AddResourceTiming( - const WebResourceTimingInfo&, + mojom::blink::ResourceTimingInfoPtr, const AtomicString& initiator_type, mojo::PendingReceiver<mojom::blink::WorkerTimingContainer> worker_timing_receiver) = 0; diff --git a/chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc index 5b3753c2fdb..cf9e2d5fbf2 100644 --- a/chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc @@ -220,18 +220,17 @@ class SubresourceIntegrityTest : public testing::Test { const KURL& url, network::mojom::RequestMode request_mode, network::mojom::FetchResponseType response_type) { - Resource* resource = RawResource::CreateForTest( - url, SecurityOrigin::CreateUniqueOpaque(), ResourceType::kRaw); - ResourceRequest request; request.SetUrl(url); request.SetMode(request_mode); + request.SetRequestorOrigin(SecurityOrigin::CreateUniqueOpaque()); + Resource* resource = + RawResource::CreateForTest(request, ResourceType::kRaw); ResourceResponse response(url); response.SetHttpStatusCode(200); response.SetType(response_type); - resource->SetResourceRequest(request); resource->SetResponse(response); return resource; } diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h b/chromium/third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h index 6a321b75317..5fd5c9b36ac 100644 --- a/chromium/third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h +++ b/chromium/third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h @@ -30,7 +30,7 @@ class BytesConsumerTestReader final std::pair<BytesConsumer::Result, Vector<char>> Run( scheduler::FakeTaskRunner*); - void Trace(blink::Visitor* visitor) override { + void Trace(Visitor* visitor) override { visitor->Trace(consumer_); BytesConsumer::Client::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h b/chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h index 7f634061276..d98ded9ee9f 100644 --- a/chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h +++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h @@ -39,7 +39,7 @@ class MockFetchContext : public FetchContext { const ResourceRequest&, const KURL&, const ResourceLoaderOptions&, - SecurityViolationReportingPolicy, + ReportingDisposition, ResourceRequest::RedirectStatus redirect_status) const override { return base::nullopt; } @@ -47,7 +47,7 @@ class MockFetchContext : public FetchContext { mojom::RequestContextType, const KURL& url, const ResourceLoaderOptions& options, - SecurityViolationReportingPolicy reporting_policy, + ReportingDisposition reporting_disposition, ResourceRequest::RedirectStatus redirect_status) const override { return base::nullopt; } diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc index 197807baa5d..773060d3bd5 100644 --- a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc +++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc @@ -80,8 +80,8 @@ void MockCacheHandler::Set(const uint8_t* data, size_t size) { } void MockCacheHandler::ClearCachedMetadata( - CachedMetadataHandler::CacheType cache_type) { - if (cache_type == CachedMetadataHandler::kSendToPlatform) { + CachedMetadataHandler::ClearCacheType cache_type) { + if (cache_type == CachedMetadataHandler::kClearPersistentStorage) { Send(); } data_.reset(); diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h index 5c1e0690529..991860b1079 100644 --- a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h +++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h @@ -23,7 +23,7 @@ class MockCacheHandler : public CachedMetadataHandler { MockCacheHandler(std::unique_ptr<CachedMetadataSender> send_callback); void Set(const uint8_t* data, size_t); - void ClearCachedMetadata(CachedMetadataHandler::CacheType) override; + void ClearCachedMetadata(CachedMetadataHandler::ClearCacheType) override; void Send(); String Encoding() const override { return "mock encoding"; } diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.cc b/chromium/third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.cc index 401bed1de75..d699ef377ea 100644 --- a/chromium/third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.cc +++ b/chromium/third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.cc @@ -137,7 +137,7 @@ void ReplayingBytesConsumer::MakeErrored(const Error& e) { ++notification_token_; } -void ReplayingBytesConsumer::Trace(blink::Visitor* visitor) { +void ReplayingBytesConsumer::Trace(Visitor* visitor) { visitor->Trace(client_); BytesConsumer::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h b/chromium/third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h index e3dc0ae9af2..de29497afc4 100644 --- a/chromium/third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h +++ b/chromium/third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h @@ -65,7 +65,7 @@ class ReplayingBytesConsumer final : public BytesConsumer { bool IsCancelled() const { return is_cancelled_; } - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: void NotifyAsReadable(int notification_token); diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc b/chromium/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc index 723d4c721e0..9b222e3c600 100644 --- a/chromium/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc +++ b/chromium/third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.cc @@ -6,6 +6,7 @@ #include "services/network/public/mojom/ip_address_space.mojom-blink.h" #include "services/network/public/mojom/referrer_policy.mojom-blink.h" +#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h" #include "third_party/blink/renderer/platform/loader/allowed_by_nosniff.h" #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h" #include "third_party/blink/renderer/platform/loader/fetch/https_state.h" @@ -29,7 +30,7 @@ TestResourceFetcherProperties::TestResourceFetcherProperties( HttpsState::kNone, AllowedByNosniff::MimeTypeCheck::kStrict, network::mojom::IPAddressSpace::kPublic, - kLeaveInsecureRequestsAlone, + mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone, FetchClientSettingsObject::InsecureNavigationsSet())) {} TestResourceFetcherProperties::TestResourceFetcherProperties( diff --git a/chromium/third_party/blink/renderer/platform/media/web_audio_source_provider_client.h b/chromium/third_party/blink/renderer/platform/media/web_audio_source_provider_client.h new file mode 100644 index 00000000000..748e189444e --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/media/web_audio_source_provider_client.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_WEB_AUDIO_SOURCE_PROVIDER_CLIENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_WEB_AUDIO_SOURCE_PROVIDER_CLIENT_H_ + +namespace blink { + +class WebAudioSourceProviderClient { + public: + virtual void SetFormat(uint32_t number_of_channels, float sample_rate) = 0; + + protected: + virtual ~WebAudioSourceProviderClient() = default; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_WEB_AUDIO_SOURCE_PROVIDER_CLIENT_H_ diff --git a/chromium/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl.cc b/chromium/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl.cc index 979fb044f71..a38efb3e55b 100644 --- a/chromium/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl.cc +++ b/chromium/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl.cc @@ -16,7 +16,7 @@ #include "media/base/audio_timestamp_helper.h" #include "media/base/bind_to_current_loop.h" #include "media/base/media_log.h" -#include "third_party/blink/public/platform/web_audio_source_provider_client.h" +#include "third_party/blink/renderer/platform/media/web_audio_source_provider_client.h" #include "third_party/blink/renderer/platform/wtf/functional.h" namespace blink { @@ -74,8 +74,34 @@ class WebAudioSourceProviderImpl::TeeFilter int Render(base::TimeDelta delay, base::TimeTicks delay_timestamp, int prior_frames_skipped, - media::AudioBus* dest) override; - void OnRenderError() override; + media::AudioBus* audio_bus) override { + DCHECK(initialized()); + + const int num_rendered_frames = renderer_->Render( + delay, delay_timestamp, prior_frames_skipped, audio_bus); + + // Avoid taking the copy lock for the vast majority of cases. + if (copy_required_) { + base::AutoLock auto_lock(copy_lock_); + if (!copy_audio_bus_callback_.is_null()) { + const int64_t frames_delayed = + media::AudioTimestampHelper::TimeToFrames(delay, sample_rate_); + std::unique_ptr<media::AudioBus> bus_copy = + media::AudioBus::Create(audio_bus->channels(), audio_bus->frames()); + audio_bus->CopyTo(bus_copy.get()); + copy_audio_bus_callback_.Run(std::move(bus_copy), + static_cast<uint32_t>(frames_delayed), + sample_rate_); + } + } + + return num_rendered_frames; + } + + void OnRenderError() override { + DCHECK(initialized()); + renderer_->OnRenderError(); + } bool initialized() const { return !!renderer_; } int channels() const { return channels_; } @@ -134,7 +160,7 @@ void WebAudioSourceProviderImpl::SetClient( // The client will now take control by calling provideInput() periodically. client_ = client; - set_format_cb_ = media::BindToCurrentLoop(WTF::Bind( + set_format_cb_ = media::BindToCurrentLoop(WTF::BindRepeating( &WebAudioSourceProviderImpl::OnSetFormat, weak_factory_.GetWeakPtr())); // If |tee_filter_| is Initialize()d - then run |set_format_cb_| to send @@ -142,13 +168,17 @@ void WebAudioSourceProviderImpl::SetClient( // called when Initialize() is called. Note: Always using |set_format_cb_| // ensures we have the same locking order when calling into |client_|. if (tee_filter_->initialized()) - std::move(set_format_cb_).Run(); + set_format_cb_.Run(); return; } // Drop client, but normal playback can't be restored. This is okay, the only // way to disconnect a client is internally at time of destruction. client_ = nullptr; + + // We need to invalidate WeakPtr references on the renderer thread. + set_format_cb_.Reset(); + weak_factory_.InvalidateWeakPtrs(); } void WebAudioSourceProviderImpl::ProvideInput( @@ -196,7 +226,7 @@ void WebAudioSourceProviderImpl::Initialize( sink_->Initialize(params, tee_filter_.get()); if (set_format_cb_) - std::move(set_format_cb_).Run(); + set_format_cb_.Run(); } void WebAudioSourceProviderImpl::Start() { @@ -310,37 +340,4 @@ void WebAudioSourceProviderImpl::OnSetFormat() { client_->SetFormat(tee_filter_->channels(), tee_filter_->sample_rate()); } -int WebAudioSourceProviderImpl::TeeFilter::Render( - base::TimeDelta delay, - base::TimeTicks delay_timestamp, - int prior_frames_skipped, - media::AudioBus* audio_bus) { - DCHECK(initialized()); - - const int num_rendered_frames = renderer_->Render( - delay, delay_timestamp, prior_frames_skipped, audio_bus); - - // Avoid taking the copy lock for the vast majority of cases. - if (copy_required_) { - base::AutoLock auto_lock(copy_lock_); - if (!copy_audio_bus_callback_.is_null()) { - const int64_t frames_delayed = - media::AudioTimestampHelper::TimeToFrames(delay, sample_rate_); - std::unique_ptr<media::AudioBus> bus_copy = - media::AudioBus::Create(audio_bus->channels(), audio_bus->frames()); - audio_bus->CopyTo(bus_copy.get()); - copy_audio_bus_callback_.Run(std::move(bus_copy), - static_cast<uint32_t>(frames_delayed), - sample_rate_); - } - } - - return num_rendered_frames; -} - -void WebAudioSourceProviderImpl::TeeFilter::OnRenderError() { - DCHECK(initialized()); - renderer_->OnRenderError(); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl_test.cc b/chromium/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl_test.cc index f45ebe3f666..1f0c69d7143 100644 --- a/chromium/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl_test.cc +++ b/chromium/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl_test.cc @@ -14,8 +14,8 @@ #include "media/base/mock_audio_renderer_sink.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/platform/web_audio_source_provider_client.h" #include "third_party/blink/public/platform/webaudiosourceprovider_impl.h" +#include "third_party/blink/renderer/platform/media/web_audio_source_provider_client.h" using ::testing::_; @@ -253,7 +253,7 @@ TEST_F(WebAudioSourceProviderImplTest, ProvideInput) { TEST_F(WebAudioSourceProviderImplTest, CopyAudioCB) { testing::InSequence s; wasp_impl_->Initialize(params_, &fake_callback_); - wasp_impl_->SetCopyAudioCallback(base::Bind( + wasp_impl_->SetCopyAudioCallback(WTF::BindRepeating( &WebAudioSourceProviderImplTest::OnAudioBus, base::Unretained(this))); const auto bus1 = media::AudioBus::Create(params_); @@ -267,4 +267,57 @@ TEST_F(WebAudioSourceProviderImplTest, CopyAudioCB) { testing::Mock::VerifyAndClear(mock_sink_.get()); } +TEST_F(WebAudioSourceProviderImplTest, MultipleInitializeWithSetClient) { + // setClient() with a nullptr client should do nothing if no client is set. + wasp_impl_->SetClient(nullptr); + + // When Initialize() is called after setClient(), the params should propagate + // to the client via setFormat() during the call. + EXPECT_TRUE(wasp_impl_->IsOptimizedForHardwareParameters()); + EXPECT_CALL(*this, SetFormat(params_.channels(), params_.sample_rate())); + wasp_impl_->Initialize(params_, &fake_callback_); + base::RunLoop().RunUntilIdle(); + + // If |mock_sink_| is not null, it should be stopped during setClient(this). + if (mock_sink_) + EXPECT_CALL(*mock_sink_.get(), Stop()); + + // setClient() with the same client should do nothing. + wasp_impl_->SetClient(this); + base::RunLoop().RunUntilIdle(); + + // Stop allows Initialize() to be called again. + wasp_impl_->Stop(); + + // It's possible that due to media change or just the change in the return + // value for IsOptimizedForHardwareParameters() that different params are + // given. Ensure this doesn't crash. + EXPECT_FALSE(wasp_impl_->IsOptimizedForHardwareParameters()); + auto stream_params = media::AudioParameters( + media::AudioParameters::AUDIO_PCM_LINEAR, media::CHANNEL_LAYOUT_MONO, + kTestSampleRate * 2, 64); + + EXPECT_CALL(*this, + SetFormat(stream_params.channels(), stream_params.sample_rate())); + wasp_impl_->Initialize(stream_params, &fake_callback_); + base::RunLoop().RunUntilIdle(); + + wasp_impl_->Start(); + wasp_impl_->Play(); + + auto bus1 = media::AudioBus::Create(stream_params); + auto bus2 = media::AudioBus::Create(stream_params); + + // Point the WebVector into memory owned by |bus1|. + WebVector<float*> audio_data(static_cast<size_t>(bus1->channels())); + for (size_t i = 0; i < audio_data.size(); ++i) + audio_data[i] = bus1->channel(static_cast<int>(i)); + + // Verify provideInput() doesn't return silence and doesn't crash. + bus1->channel(0)[0] = 1; + bus2->Zero(); + wasp_impl_->ProvideInput(audio_data, params_.frames_per_buffer()); + ASSERT_FALSE(CompareBusses(bus1.get(), bus2.get())); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/mediastream/DEPS b/chromium/third_party/blink/renderer/platform/mediastream/DEPS index 22e08dbd4d9..3298a7464b4 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/DEPS +++ b/chromium/third_party/blink/renderer/platform/mediastream/DEPS @@ -6,6 +6,7 @@ include_rules = [ "+third_party/blink/renderer/platform/mediastream", # Dependencies. + "+base/atomicops.h", "+media/base", "+media/webrtc/audio_processor_controls.h", "+third_party/blink/renderer/platform/audio", diff --git a/chromium/third_party/blink/renderer/platform/mediastream/aec_dump_agent_impl.cc b/chromium/third_party/blink/renderer/platform/mediastream/aec_dump_agent_impl.cc index d0254a6fdb1..13ade0a00cf 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/aec_dump_agent_impl.cc +++ b/chromium/third_party/blink/renderer/platform/mediastream/aec_dump_agent_impl.cc @@ -4,6 +4,7 @@ #include "third_party/blink/renderer/platform/mediastream/aec_dump_agent_impl.h" +#include "base/memory/ptr_util.h" #include "mojo/public/cpp/bindings/remote.h" #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/platform/platform.h" diff --git a/chromium/third_party/blink/renderer/platform/mediastream/audio_service_audio_processor_proxy.cc b/chromium/third_party/blink/renderer/platform/mediastream/audio_service_audio_processor_proxy.cc index 771bad73b57..aa8c19862a3 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/audio_service_audio_processor_proxy.cc +++ b/chromium/third_party/blink/renderer/platform/mediastream/audio_service_audio_processor_proxy.cc @@ -12,7 +12,6 @@ #include <utility> #include <vector> -#include "base/bind.h" #include "base/single_thread_task_runner.h" #include "base/task/post_task.h" #include "base/timer/timer.h" @@ -20,6 +19,7 @@ #include "third_party/blink/renderer/platform/mediastream/aec_dump_agent_impl.h" #include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" namespace blink { @@ -62,8 +62,7 @@ void AudioServiceAudioProcessorProxy::OnStartDump(base::File dump_file) { } else { // Post the file close to avoid blocking the main thread. worker_pool::PostTask( - FROM_HERE, - {base::ThreadPool(), base::TaskPriority::LOWEST, base::MayBlock()}, + FROM_HERE, {base::TaskPriority::LOWEST, base::MayBlock()}, CrossThreadBindOnce([](base::File) {}, std::move(dump_file))); } } @@ -119,16 +118,16 @@ void AudioServiceAudioProcessorProxy::RescheduleStatsUpdateTimer( // Unretained is safe since |this| owns |stats_update_timer_|. stats_update_timer_.Start( FROM_HERE, new_interval, - base::BindRepeating(&AudioServiceAudioProcessorProxy::RequestStats, - base::Unretained(this))); + WTF::BindRepeating(&AudioServiceAudioProcessorProxy::RequestStats, + WTF::Unretained(this))); } void AudioServiceAudioProcessorProxy::RequestStats() { DCHECK(main_thread_runner_->BelongsToCurrentThread()); if (processor_controls_) { processor_controls_->GetStats( - base::BindOnce(&AudioServiceAudioProcessorProxy::UpdateStats, - weak_ptr_factory_.GetWeakPtr())); + WTF::Bind(&AudioServiceAudioProcessorProxy::UpdateStats, + weak_ptr_factory_.GetWeakPtr())); } } diff --git a/chromium/third_party/blink/renderer/platform/exported/web_media_constraints.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.cc index dcad673ac67..e23da0b73e5 100644 --- a/chromium/third_party/blink/renderer/platform/exported/web_media_constraints.cc +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.cc @@ -28,7 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "third_party/blink/public/platform/web_media_constraints.h" +#include "third_party/blink/renderer/platform/mediastream/media_constraints.h" #include <math.h> #include "base/memory/scoped_refptr.h" @@ -76,66 +76,66 @@ const char kEchoCancellationTypeBrowser[] = "browser"; const char kEchoCancellationTypeAec3[] = "aec3"; const char kEchoCancellationTypeSystem[] = "system"; -class WebMediaConstraintsPrivate final - : public ThreadSafeRefCounted<WebMediaConstraintsPrivate> { +class MediaConstraintsPrivate final + : public ThreadSafeRefCounted<MediaConstraintsPrivate> { public: - static scoped_refptr<WebMediaConstraintsPrivate> Create(); - static scoped_refptr<WebMediaConstraintsPrivate> Create( - const WebMediaTrackConstraintSet& basic, - const WebVector<WebMediaTrackConstraintSet>& advanced); + static scoped_refptr<MediaConstraintsPrivate> Create(); + static scoped_refptr<MediaConstraintsPrivate> Create( + const MediaTrackConstraintSetPlatform& basic, + const Vector<MediaTrackConstraintSetPlatform>& advanced); bool IsEmpty() const; - const WebMediaTrackConstraintSet& Basic() const; - const WebVector<WebMediaTrackConstraintSet>& Advanced() const; + const MediaTrackConstraintSetPlatform& Basic() const; + const Vector<MediaTrackConstraintSetPlatform>& Advanced() const; const String ToString() const; private: - WebMediaConstraintsPrivate( - const WebMediaTrackConstraintSet& basic, - const WebVector<WebMediaTrackConstraintSet>& advanced); + MediaConstraintsPrivate( + const MediaTrackConstraintSetPlatform& basic, + const Vector<MediaTrackConstraintSetPlatform>& advanced); - WebMediaTrackConstraintSet basic_; - WebVector<WebMediaTrackConstraintSet> advanced_; + MediaTrackConstraintSetPlatform basic_; + Vector<MediaTrackConstraintSetPlatform> advanced_; }; -scoped_refptr<WebMediaConstraintsPrivate> WebMediaConstraintsPrivate::Create() { - WebMediaTrackConstraintSet basic; - WebVector<WebMediaTrackConstraintSet> advanced; - return base::AdoptRef(new WebMediaConstraintsPrivate(basic, advanced)); +scoped_refptr<MediaConstraintsPrivate> MediaConstraintsPrivate::Create() { + MediaTrackConstraintSetPlatform basic; + Vector<MediaTrackConstraintSetPlatform> advanced; + return base::AdoptRef(new MediaConstraintsPrivate(basic, advanced)); } -scoped_refptr<WebMediaConstraintsPrivate> WebMediaConstraintsPrivate::Create( - const WebMediaTrackConstraintSet& basic, - const WebVector<WebMediaTrackConstraintSet>& advanced) { - return base::AdoptRef(new WebMediaConstraintsPrivate(basic, advanced)); +scoped_refptr<MediaConstraintsPrivate> MediaConstraintsPrivate::Create( + const MediaTrackConstraintSetPlatform& basic, + const Vector<MediaTrackConstraintSetPlatform>& advanced) { + return base::AdoptRef(new MediaConstraintsPrivate(basic, advanced)); } -WebMediaConstraintsPrivate::WebMediaConstraintsPrivate( - const WebMediaTrackConstraintSet& basic, - const WebVector<WebMediaTrackConstraintSet>& advanced) +MediaConstraintsPrivate::MediaConstraintsPrivate( + const MediaTrackConstraintSetPlatform& basic, + const Vector<MediaTrackConstraintSetPlatform>& advanced) : basic_(basic), advanced_(advanced) {} -bool WebMediaConstraintsPrivate::IsEmpty() const { +bool MediaConstraintsPrivate::IsEmpty() const { // TODO(hta): When generating advanced constraints, make sure no empty // elements can be added to the m_advanced vector. - return basic_.IsEmpty() && advanced_.empty(); + return basic_.IsEmpty() && advanced_.IsEmpty(); } -const WebMediaTrackConstraintSet& WebMediaConstraintsPrivate::Basic() const { +const MediaTrackConstraintSetPlatform& MediaConstraintsPrivate::Basic() const { return basic_; } -const WebVector<WebMediaTrackConstraintSet>& -WebMediaConstraintsPrivate::Advanced() const { +const Vector<MediaTrackConstraintSetPlatform>& +MediaConstraintsPrivate::Advanced() const { return advanced_; } -const String WebMediaConstraintsPrivate::ToString() const { +const String MediaConstraintsPrivate::ToString() const { StringBuilder builder; if (!IsEmpty()) { builder.Append('{'); builder.Append(Basic().ToString()); - if (!Advanced().empty()) { + if (!Advanced().IsEmpty()) { if (builder.length() > 1) builder.Append(", "); builder.Append("advanced: ["); @@ -193,7 +193,7 @@ bool LongConstraint::IsEmpty() const { return !has_min_ && !has_max_ && !has_exact_ && !has_ideal_; } -WebString LongConstraint::ToString() const { +String LongConstraint::ToString() const { StringBuilder builder; builder.Append('{'); MaybeEmitNamedValue(builder, has_min_, "min", min_); @@ -235,7 +235,7 @@ bool DoubleConstraint::IsEmpty() const { return !has_min_ && !has_max_ && !has_exact_ && !has_ideal_; } -WebString DoubleConstraint::ToString() const { +String DoubleConstraint::ToString() const { StringBuilder builder; builder.Append('{'); MaybeEmitNamedValue(builder, has_min_, "min", min_); @@ -249,8 +249,8 @@ WebString DoubleConstraint::ToString() const { StringConstraint::StringConstraint(const char* name) : BaseConstraint(name), exact_(), ideal_() {} -bool StringConstraint::Matches(WebString value) const { - if (exact_.empty()) { +bool StringConstraint::Matches(String value) const { + if (exact_.IsEmpty()) { return true; } for (const auto& choice : exact_) { @@ -262,21 +262,21 @@ bool StringConstraint::Matches(WebString value) const { } bool StringConstraint::IsEmpty() const { - return exact_.empty() && ideal_.empty(); + return exact_.IsEmpty() && ideal_.IsEmpty(); } -const WebVector<WebString>& StringConstraint::Exact() const { +const Vector<String>& StringConstraint::Exact() const { return exact_; } -const WebVector<WebString>& StringConstraint::Ideal() const { +const Vector<String>& StringConstraint::Ideal() const { return ideal_; } -WebString StringConstraint::ToString() const { +String StringConstraint::ToString() const { StringBuilder builder; builder.Append('{'); - if (!ideal_.empty()) { + if (!ideal_.IsEmpty()) { builder.Append("ideal: ["); bool first = true; for (const auto& iter : ideal_) { @@ -289,7 +289,7 @@ WebString StringConstraint::ToString() const { } builder.Append(']'); } - if (!exact_.empty()) { + if (!exact_.IsEmpty()) { if (builder.length() > 1) builder.Append(", "); builder.Append("exact: ["); @@ -325,7 +325,7 @@ bool BooleanConstraint::IsEmpty() const { return !has_ideal_ && !has_exact_; } -WebString BooleanConstraint::ToString() const { +String BooleanConstraint::ToString() const { StringBuilder builder; builder.Append('{'); MaybeEmitNamedBoolean(builder, has_exact_, "exact", Exact()); @@ -334,7 +334,7 @@ WebString BooleanConstraint::ToString() const { return builder.ToString(); } -WebMediaTrackConstraintSet::WebMediaTrackConstraintSet() +MediaTrackConstraintSetPlatform::MediaTrackConstraintSetPlatform() : width("width"), height("height"), aspect_ratio("aspectRatio"), @@ -389,64 +389,62 @@ WebMediaTrackConstraintSet::WebMediaTrackConstraintSet() goog_payload_padding("googPayloadPadding"), goog_latency_ms("latencyMs") {} -std::vector<const BaseConstraint*> WebMediaTrackConstraintSet::AllConstraints() +Vector<const BaseConstraint*> MediaTrackConstraintSetPlatform::AllConstraints() const { - const BaseConstraint* temp[] = {&width, - &height, - &aspect_ratio, - &frame_rate, - &facing_mode, - &resize_mode, - &volume, - &sample_rate, - &sample_size, - &echo_cancellation, - &echo_cancellation_type, - &latency, - &channel_count, - &device_id, - &group_id, - &video_kind, - &media_stream_source, - &disable_local_echo, - &render_to_associated_sink, - &goog_echo_cancellation, - &goog_experimental_echo_cancellation, - &goog_auto_gain_control, - &goog_experimental_auto_gain_control, - &goog_noise_suppression, - &goog_highpass_filter, - &goog_experimental_noise_suppression, - &goog_audio_mirroring, - &goog_da_echo_cancellation, - &goog_noise_reduction, - &offer_to_receive_audio, - &offer_to_receive_video, - &voice_activity_detection, - &ice_restart, - &goog_use_rtp_mux, - &enable_dtls_srtp, - &enable_rtp_data_channels, - &enable_dscp, - &enable_i_pv6, - &goog_enable_video_suspend_below_min_bitrate, - &goog_num_unsignalled_recv_streams, - &goog_combined_audio_video_bwe, - &goog_screencast_min_bitrate, - &goog_cpu_overuse_detection, - &goog_cpu_underuse_threshold, - &goog_cpu_overuse_threshold, - &goog_cpu_underuse_encode_rsd_threshold, - &goog_cpu_overuse_encode_rsd_threshold, - &goog_cpu_overuse_encode_usage, - &goog_high_start_bitrate, - &goog_payload_padding, - &goog_latency_ms}; - const int element_count = sizeof(temp) / sizeof(temp[0]); - return std::vector<const BaseConstraint*>(&temp[0], &temp[element_count]); -} - -bool WebMediaTrackConstraintSet::IsEmpty() const { + return {&width, + &height, + &aspect_ratio, + &frame_rate, + &facing_mode, + &resize_mode, + &volume, + &sample_rate, + &sample_size, + &echo_cancellation, + &echo_cancellation_type, + &latency, + &channel_count, + &device_id, + &group_id, + &video_kind, + &media_stream_source, + &disable_local_echo, + &render_to_associated_sink, + &goog_echo_cancellation, + &goog_experimental_echo_cancellation, + &goog_auto_gain_control, + &goog_experimental_auto_gain_control, + &goog_noise_suppression, + &goog_highpass_filter, + &goog_experimental_noise_suppression, + &goog_audio_mirroring, + &goog_da_echo_cancellation, + &goog_noise_reduction, + &offer_to_receive_audio, + &offer_to_receive_video, + &voice_activity_detection, + &ice_restart, + &goog_use_rtp_mux, + &enable_dtls_srtp, + &enable_rtp_data_channels, + &enable_dscp, + &enable_i_pv6, + &goog_enable_video_suspend_below_min_bitrate, + &goog_num_unsignalled_recv_streams, + &goog_combined_audio_video_bwe, + &goog_screencast_min_bitrate, + &goog_cpu_overuse_detection, + &goog_cpu_underuse_threshold, + &goog_cpu_overuse_threshold, + &goog_cpu_underuse_encode_rsd_threshold, + &goog_cpu_overuse_encode_rsd_threshold, + &goog_cpu_overuse_encode_usage, + &goog_high_start_bitrate, + &goog_payload_padding, + &goog_latency_ms}; +} + +bool MediaTrackConstraintSetPlatform::IsEmpty() const { for (auto* const constraint : AllConstraints()) { if (!constraint->IsEmpty()) return false; @@ -454,9 +452,9 @@ bool WebMediaTrackConstraintSet::IsEmpty() const { return true; } -bool WebMediaTrackConstraintSet::HasMandatoryOutsideSet( - const std::vector<std::string>& good_names, - std::string& found_name) const { +bool MediaTrackConstraintSetPlatform::HasMandatoryOutsideSet( + const Vector<String>& good_names, + String& found_name) const { for (auto* const constraint : AllConstraints()) { if (constraint->HasMandatory()) { if (std::find(good_names.begin(), good_names.end(), @@ -469,12 +467,12 @@ bool WebMediaTrackConstraintSet::HasMandatoryOutsideSet( return false; } -bool WebMediaTrackConstraintSet::HasMandatory() const { - std::string dummy_string; - return HasMandatoryOutsideSet(std::vector<std::string>(), dummy_string); +bool MediaTrackConstraintSetPlatform::HasMandatory() const { + String dummy_string; + return HasMandatoryOutsideSet(Vector<String>(), dummy_string); } -bool WebMediaTrackConstraintSet::HasMin() const { +bool MediaTrackConstraintSetPlatform::HasMin() const { for (auto* const constraint : AllConstraints()) { if (constraint->HasMin()) return true; @@ -482,7 +480,7 @@ bool WebMediaTrackConstraintSet::HasMin() const { return false; } -bool WebMediaTrackConstraintSet::HasExact() const { +bool MediaTrackConstraintSetPlatform::HasExact() const { for (auto* const constraint : AllConstraints()) { if (constraint->HasExact()) return true; @@ -490,7 +488,7 @@ bool WebMediaTrackConstraintSet::HasExact() const { return false; } -WebString WebMediaTrackConstraintSet::ToString() const { +String MediaTrackConstraintSetPlatform::ToString() const { StringBuilder builder; bool first = true; for (auto* const constraint : AllConstraints()) { @@ -506,46 +504,46 @@ WebString WebMediaTrackConstraintSet::ToString() const { return builder.ToString(); } -// WebMediaConstraints +// MediaConstraints -void WebMediaConstraints::Assign(const WebMediaConstraints& other) { +void MediaConstraints::Assign(const MediaConstraints& other) { private_ = other.private_; } -void WebMediaConstraints::Reset() { +void MediaConstraints::Reset() { private_.Reset(); } -bool WebMediaConstraints::IsEmpty() const { +bool MediaConstraints::IsEmpty() const { return private_.IsNull() || private_->IsEmpty(); } -void WebMediaConstraints::Initialize() { +void MediaConstraints::Initialize() { DCHECK(IsNull()); - private_ = WebMediaConstraintsPrivate::Create(); + private_ = MediaConstraintsPrivate::Create(); } -void WebMediaConstraints::Initialize( - const WebMediaTrackConstraintSet& basic, - const WebVector<WebMediaTrackConstraintSet>& advanced) { +void MediaConstraints::Initialize( + const MediaTrackConstraintSetPlatform& basic, + const Vector<MediaTrackConstraintSetPlatform>& advanced) { DCHECK(IsNull()); - private_ = WebMediaConstraintsPrivate::Create(basic, advanced); + private_ = MediaConstraintsPrivate::Create(basic, advanced); } -const WebMediaTrackConstraintSet& WebMediaConstraints::Basic() const { +const MediaTrackConstraintSetPlatform& MediaConstraints::Basic() const { DCHECK(!IsNull()); return private_->Basic(); } -const WebVector<WebMediaTrackConstraintSet>& WebMediaConstraints::Advanced() +const Vector<MediaTrackConstraintSetPlatform>& MediaConstraints::Advanced() const { DCHECK(!IsNull()); return private_->Advanced(); } -const WebString WebMediaConstraints::ToString() const { +const String MediaConstraints::ToString() const { if (IsNull()) - return WebString(""); + return String(""); return private_->ToString(); } diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.h b/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.h new file mode 100644 index 00000000000..ed536a694d6 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_constraints.h @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_CONSTRAINTS_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_CONSTRAINTS_H_ + +#include "third_party/blink/public/platform/web_private_ptr.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +// Possible values of the echo canceller type constraint. +PLATFORM_EXPORT extern const char kEchoCancellationTypeBrowser[]; +PLATFORM_EXPORT extern const char kEchoCancellationTypeAec3[]; +PLATFORM_EXPORT extern const char kEchoCancellationTypeSystem[]; + +class MediaConstraintsPrivate; + +class PLATFORM_EXPORT BaseConstraint { + public: + explicit BaseConstraint(const char* name); + virtual ~BaseConstraint(); + virtual bool IsEmpty() const = 0; + bool HasMandatory() const; + virtual bool HasMin() const { return false; } + virtual bool HasMax() const { return false; } + virtual bool HasExact() const = 0; + const char* GetName() const { return name_; } + virtual String ToString() const = 0; + + private: + const char* name_; +}; + +// Note this class refers to the "long" WebIDL definition which is +// equivalent to int32_t. +class PLATFORM_EXPORT LongConstraint : public BaseConstraint { + public: + explicit LongConstraint(const char* name); + + void SetMin(int32_t value) { + min_ = value; + has_min_ = true; + } + + void SetMax(int32_t value) { + max_ = value; + has_max_ = true; + } + + void SetExact(int32_t value) { + exact_ = value; + has_exact_ = true; + } + + void SetIdeal(int32_t value) { + ideal_ = value; + has_ideal_ = true; + } + + bool Matches(int32_t value) const; + bool IsEmpty() const override; + bool HasMin() const override { return has_min_; } + bool HasMax() const override { return has_max_; } + bool HasExact() const override { return has_exact_; } + String ToString() const override; + int32_t Min() const { return min_; } + int32_t Max() const { return max_; } + int32_t Exact() const { return exact_; } + bool HasIdeal() const { return has_ideal_; } + int32_t Ideal() const { return ideal_; } + + private: + int32_t min_; + int32_t max_; + int32_t exact_; + int32_t ideal_; + unsigned has_min_ : 1; + unsigned has_max_ : 1; + unsigned has_exact_ : 1; + unsigned has_ideal_ : 1; +}; + +class PLATFORM_EXPORT DoubleConstraint : public BaseConstraint { + public: + // Permit a certain leeway when comparing floats. The offset of 0.00001 + // is chosen based on observed behavior of doubles formatted with + // rtc::ToString. + static const double kConstraintEpsilon; + + explicit DoubleConstraint(const char* name); + + void SetMin(double value) { + min_ = value; + has_min_ = true; + } + + void SetMax(double value) { + max_ = value; + has_max_ = true; + } + + void SetExact(double value) { + exact_ = value; + has_exact_ = true; + } + + void SetIdeal(double value) { + ideal_ = value; + has_ideal_ = true; + } + + bool Matches(double value) const; + bool IsEmpty() const override; + bool HasMin() const override { return has_min_; } + bool HasMax() const override { return has_max_; } + bool HasExact() const override { return has_exact_; } + String ToString() const override; + double Min() const { return min_; } + double Max() const { return max_; } + double Exact() const { return exact_; } + bool HasIdeal() const { return has_ideal_; } + double Ideal() const { return ideal_; } + + private: + double min_; + double max_; + double exact_; + double ideal_; + unsigned has_min_ : 1; + unsigned has_max_ : 1; + unsigned has_exact_ : 1; + unsigned has_ideal_ : 1; +}; + +class PLATFORM_EXPORT StringConstraint : public BaseConstraint { + public: + // String-valued options don't have min or max, but can have multiple + // values for ideal and exact. + explicit StringConstraint(const char* name); + + void SetExact(const String& exact) { exact_ = {exact}; } + + void SetExact(const Vector<String>& exact) { exact_ = exact; } + + void SetIdeal(const String& ideal) { ideal_ = {ideal}; } + + void SetIdeal(const Vector<String>& ideal) { ideal_ = ideal; } + + bool Matches(String value) const; + bool IsEmpty() const override; + bool HasExact() const override { return !exact_.IsEmpty(); } + String ToString() const override; + bool HasIdeal() const { return !ideal_.IsEmpty(); } + const Vector<String>& Exact() const; + const Vector<String>& Ideal() const; + + private: + Vector<String> exact_; + Vector<String> ideal_; +}; + +class PLATFORM_EXPORT BooleanConstraint : public BaseConstraint { + public: + explicit BooleanConstraint(const char* name); + + bool Exact() const { return exact_; } + bool Ideal() const { return ideal_; } + void SetIdeal(bool value) { + ideal_ = value; + has_ideal_ = true; + } + + void SetExact(bool value) { + exact_ = value; + has_exact_ = true; + } + + bool Matches(bool value) const; + bool IsEmpty() const override; + bool HasExact() const override { return has_exact_; } + String ToString() const override; + bool HasIdeal() const { return has_ideal_; } + + private: + unsigned ideal_ : 1; + unsigned exact_ : 1; + unsigned has_ideal_ : 1; + unsigned has_exact_ : 1; +}; + +struct MediaTrackConstraintSetPlatform { + public: + PLATFORM_EXPORT MediaTrackConstraintSetPlatform(); + + LongConstraint width; + LongConstraint height; + DoubleConstraint aspect_ratio; + DoubleConstraint frame_rate; + StringConstraint facing_mode; + StringConstraint resize_mode; + DoubleConstraint volume; + LongConstraint sample_rate; + LongConstraint sample_size; + BooleanConstraint echo_cancellation; + StringConstraint echo_cancellation_type; + DoubleConstraint latency; + LongConstraint channel_count; + StringConstraint device_id; + BooleanConstraint disable_local_echo; + StringConstraint group_id; + // https://w3c.github.io/mediacapture-depth/#mediatrackconstraints + StringConstraint video_kind; + // Constraints not exposed in Blink at the moment, only through + // the legacy name interface. + StringConstraint media_stream_source; // tab, screen, desktop, system + BooleanConstraint render_to_associated_sink; + BooleanConstraint goog_echo_cancellation; + BooleanConstraint goog_experimental_echo_cancellation; + BooleanConstraint goog_auto_gain_control; + BooleanConstraint goog_experimental_auto_gain_control; + BooleanConstraint goog_noise_suppression; + BooleanConstraint goog_highpass_filter; + BooleanConstraint goog_experimental_noise_suppression; + BooleanConstraint goog_audio_mirroring; + BooleanConstraint goog_da_echo_cancellation; + BooleanConstraint goog_noise_reduction; + LongConstraint offer_to_receive_audio; + LongConstraint offer_to_receive_video; + BooleanConstraint voice_activity_detection; + BooleanConstraint ice_restart; + BooleanConstraint goog_use_rtp_mux; + BooleanConstraint enable_dtls_srtp; + BooleanConstraint enable_rtp_data_channels; + BooleanConstraint enable_dscp; + BooleanConstraint enable_i_pv6; + BooleanConstraint goog_enable_video_suspend_below_min_bitrate; + LongConstraint goog_num_unsignalled_recv_streams; + BooleanConstraint goog_combined_audio_video_bwe; + LongConstraint goog_screencast_min_bitrate; + BooleanConstraint goog_cpu_overuse_detection; + LongConstraint goog_cpu_underuse_threshold; + LongConstraint goog_cpu_overuse_threshold; + LongConstraint goog_cpu_underuse_encode_rsd_threshold; + LongConstraint goog_cpu_overuse_encode_rsd_threshold; + BooleanConstraint goog_cpu_overuse_encode_usage; + LongConstraint goog_high_start_bitrate; + BooleanConstraint goog_payload_padding; + LongConstraint goog_latency_ms; + + PLATFORM_EXPORT bool IsEmpty() const; + PLATFORM_EXPORT bool HasMandatory() const; + PLATFORM_EXPORT bool HasMandatoryOutsideSet(const Vector<String>&, + String&) const; + PLATFORM_EXPORT bool HasMin() const; + PLATFORM_EXPORT bool HasExact() const; + PLATFORM_EXPORT String ToString() const; + + private: + Vector<const BaseConstraint*> AllConstraints() const; +}; + +class MediaConstraints { + public: + MediaConstraints() = default; + MediaConstraints(const MediaConstraints& other) { Assign(other); } + ~MediaConstraints() { Reset(); } + + MediaConstraints& operator=(const MediaConstraints& other) { + Assign(other); + return *this; + } + + PLATFORM_EXPORT void Assign(const MediaConstraints&); + + PLATFORM_EXPORT void Reset(); + bool IsNull() const { return private_.IsNull(); } + PLATFORM_EXPORT bool IsEmpty() const; + + PLATFORM_EXPORT void Initialize(); + PLATFORM_EXPORT void Initialize( + const MediaTrackConstraintSetPlatform& basic, + const Vector<MediaTrackConstraintSetPlatform>& advanced); + + PLATFORM_EXPORT const MediaTrackConstraintSetPlatform& Basic() const; + PLATFORM_EXPORT const Vector<MediaTrackConstraintSetPlatform>& Advanced() + const; + + PLATFORM_EXPORT const String ToString() const; + + private: + WebPrivatePtr<MediaConstraintsPrivate> private_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_CONSTRAINTS_H_ diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_deliverer.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_deliverer.h new file mode 100644 index 00000000000..9ff7615bb73 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_deliverer.h @@ -0,0 +1,158 @@ +// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_AUDIO_DELIVERER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_AUDIO_DELIVERER_H_ + +#include <algorithm> + +#include "base/synchronization/lock.h" +#include "base/threading/thread_checker.h" +#include "base/trace_event/trace_event.h" +#include "media/base/audio_parameters.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +// Template containing functionality common to both MediaStreamAudioSource and +// MediaStreamAudioTrack. This is used for managing the connections between +// objects through which audio data flows, and doing so in a thread-safe manner. +// +// The Consumer parameter of the template is the type of the objects to which +// audio data is delivered: MediaStreamAudioTrack or MediaStreamAudioSink. It's +// assumed the Consumer class defines methods named OnSetFormat() and OnData() +// that have the same signature as the ones defined in this template. +// MediaStreamAudioDeliverer will always guarantee the Consumer's OnSetFormat() +// and OnData() methods are called sequentially. +template <typename Consumer> +class MediaStreamAudioDeliverer { + public: + MediaStreamAudioDeliverer() {} + ~MediaStreamAudioDeliverer() {} + + // Returns the current audio parameters. These will be invalid before the + // first call to OnSetFormat(). This method is thread-safe. + media::AudioParameters GetAudioParameters() const { + base::AutoLock auto_lock(params_lock_); + return params_; + } + + // Begin delivering audio to |consumer|. The caller must guarantee |consumer| + // is not destroyed until after calling RemoveConsumer(consumer). This method + // must be called on the main thread. + void AddConsumer(Consumer* consumer) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(consumer); + base::AutoLock auto_lock(consumers_lock_); + DCHECK(!base::Contains(consumers_, consumer)); + DCHECK(!base::Contains(pending_consumers_, consumer)); + pending_consumers_.push_back(consumer); + } + + // Stop delivering audio to |consumer|. Returns true if |consumer| was the + // last consumer removed, false otherwise. When this method returns, no + // further calls will be made to OnSetFormat() or OnData() on any thread. + // This method must be called on the main thread. + bool RemoveConsumer(Consumer* consumer) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + base::AutoLock auto_lock(consumers_lock_); + const bool had_consumers = + !consumers_.IsEmpty() || !pending_consumers_.IsEmpty(); + auto it = std::find(consumers_.begin(), consumers_.end(), consumer); + if (it != consumers_.end()) { + consumers_.erase(it); + } else { + it = std::find(pending_consumers_.begin(), pending_consumers_.end(), + consumer); + if (it != pending_consumers_.end()) + pending_consumers_.erase(it); + } + return had_consumers && consumers_.IsEmpty() && + pending_consumers_.IsEmpty(); + } + + // Returns the current list of connected Consumers. This is normally used to + // send a notification to all consumers. This method must be called on the + // main thread. + void GetConsumerList(Vector<Consumer*>* consumer_list) const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + base::AutoLock auto_lock(consumers_lock_); + *consumer_list = consumers_; + consumer_list->AppendRange(pending_consumers_.begin(), + pending_consumers_.end()); + } + + // Change the format of the audio passed in the next call to OnData(). This + // method may be called on any thread but, logically, should only be called + // between calls to OnData(). + void OnSetFormat(const media::AudioParameters& params) { + DCHECK(params.IsValid()); + base::AutoLock auto_lock(consumers_lock_); + { + base::AutoLock auto_params_lock(params_lock_); + if (params_.Equals(params)) + return; + params_ = params; + } + pending_consumers_.AppendRange(consumers_.begin(), consumers_.end()); + consumers_.clear(); + } + + // Deliver data to all consumers. This method may be called on any thread. + void OnData(const media::AudioBus& audio_bus, + base::TimeTicks reference_time) { + TRACE_EVENT1("audio", "MediaStreamAudioDeliverer::OnData", + "reference time (ms)", + (reference_time - base::TimeTicks()).InMillisecondsF()); + base::AutoLock auto_lock(consumers_lock_); + + // Call OnSetFormat() for all pending consumers and move them to the + // active-delivery list. + if (!pending_consumers_.IsEmpty()) { + const media::AudioParameters params = GetAudioParameters(); + DCHECK(params.IsValid()); + for (Consumer* consumer : pending_consumers_) + consumer->OnSetFormat(params); + consumers_.AppendRange(pending_consumers_.begin(), + pending_consumers_.end()); + pending_consumers_.clear(); + } + + // Deliver the audio data to each consumer. + for (Consumer* consumer : consumers_) + consumer->OnData(audio_bus, reference_time); + } + + private: + // In debug builds, check that all methods that could cause object graph or + // data flow changes are being called on the main thread. + THREAD_CHECKER(thread_checker_); + + // Protects concurrent access to |pending_consumers_| and |consumers_|. + mutable base::Lock consumers_lock_; + + // Any consumers needing a call to OnSetFormat(), to be notified of the + // changed audio format, are placed in this list. This includes consumers + // added via AddConsumer() that need to have an initial OnSetFormat() call + // before audio data is first delivered. Consumers are moved from this list to + // |consumers_| on the audio thread. + Vector<Consumer*> pending_consumers_; + + // Consumers that are up to date on the current audio format and are receiving + // audio data are placed in this list. + Vector<Consumer*> consumers_; + + // Protects concurrent access to |params_|. + mutable base::Lock params_lock_; + + // Specifies the current format of the audio passing through this + // MediaStreamAudioDeliverer. + media::AudioParameters params_; + + DISALLOW_COPY_AND_ASSIGN(MediaStreamAudioDeliverer); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_AUDIO_DELIVERER_H_ diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h index 923bc2a2a47..f25d1e2abbe 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h @@ -15,7 +15,7 @@ #include "media/base/audio_point.h" #include "media/base/audio_processing.h" #include "third_party/blink/public/common/mediastream/media_stream_request.h" -#include "third_party/blink/public/platform/web_media_constraints.h" +#include "third_party/blink/renderer/platform/mediastream/media_constraints.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/webrtc/api/media_stream_interface.h" #include "third_party/webrtc/media/base/media_channel.h" diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc index 783587fdaee..4c60ce0cff0 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.cc @@ -6,17 +6,27 @@ #include "base/bind.h" #include "base/single_thread_task_runner.h" +#include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" -#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_track.h" +#include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" #include "third_party/blink/public/platform/web_media_stream_source.h" #include "third_party/blink/public/platform/web_string.h" +#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/blink/renderer/platform/wtf/functional.h" namespace blink { +namespace { + +void SendLogMessage(const std::string& message) { + blink::WebRtcLogMessage("MSAS::" + message); +} + +} // namespace + const int kMaxAudioLatencyMs = 5000; static_assert(std::numeric_limits<int>::max() / media::limits::kMaxSampleRate > kMaxAudioLatencyMs, @@ -45,8 +55,9 @@ MediaStreamAudioSource::MediaStreamAudioSource( disable_local_echo_(disable_local_echo), is_stopped_(false), task_runner_(std::move(task_runner)) { - DVLOG(1) << "MediaStreamAudioSource@" << this << "::MediaStreamAudioSource(" - << (is_local_source_ ? "local" : "remote") << " source)"; + SendLogMessage(base::StringPrintf( + "MediaStreamAudioSource([this=%p] {is_local_source=%s})", this, + (is_local_source ? "local" : "remote"))); } MediaStreamAudioSource::MediaStreamAudioSource( @@ -58,7 +69,8 @@ MediaStreamAudioSource::MediaStreamAudioSource( MediaStreamAudioSource::~MediaStreamAudioSource() { DCHECK(task_runner_->BelongsToCurrentThread()); - DVLOG(1) << "MediaStreamAudioSource@" << this << " is being destroyed."; + SendLogMessage( + base::StringPrintf("~MediaStreamAudioSource([this=%p])", this)); } // static @@ -74,6 +86,8 @@ bool MediaStreamAudioSource::ConnectToTrack( const WebMediaStreamTrack& blink_track) { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(!blink_track.IsNull()); + SendLogMessage(base::StringPrintf("ConnectToTrack({track_id=%s})", + blink_track.Id().Utf8().c_str())); // Sanity-check that there is not already a MediaStreamAudioTrack instance // associated with |blink_track|. @@ -167,6 +181,8 @@ void MediaStreamAudioSource::DoChangeSource( std::unique_ptr<MediaStreamAudioTrack> MediaStreamAudioSource::CreateMediaStreamAudioTrack(const std::string& id) { DCHECK(task_runner_->BelongsToCurrentThread()); + SendLogMessage( + base::StringPrintf("CreateMediaStreamAudioTrack({id=%s})", id.c_str())); return std::unique_ptr<MediaStreamAudioTrack>( new MediaStreamAudioTrack(is_local_source())); } @@ -190,9 +206,10 @@ void MediaStreamAudioSource::ChangeSourceImpl( } void MediaStreamAudioSource::SetFormat(const media::AudioParameters& params) { - DVLOG(1) << "MediaStreamAudioSource@" << this << "::SetFormat(" - << params.AsHumanReadableString() << "), was previously set to {" - << deliverer_.GetAudioParameters().AsHumanReadableString() << "}."; + SendLogMessage(base::StringPrintf( + "SetFormat([this=%p] {params=[%s]}, {old_params=[%s]})", this, + params.AsHumanReadableString().c_str(), + deliverer_.GetAudioParameters().AsHumanReadableString().c_str())); deliverer_.OnSetFormat(params); } @@ -210,6 +227,7 @@ void MediaStreamAudioSource::DoStopSource() { void MediaStreamAudioSource::StopAudioDeliveryTo(MediaStreamAudioTrack* track) { DCHECK(task_runner_->BelongsToCurrentThread()); + SendLogMessage(base::StringPrintf("StopAudioDeliveryTo([this=%p])", this)); const bool did_remove_last_track = deliverer_.RemoveConsumer(track); DVLOG(1) << "Removed MediaStreamAudioTrack@" << track @@ -222,8 +240,8 @@ void MediaStreamAudioSource::StopAudioDeliveryTo(MediaStreamAudioTrack* track) { } void MediaStreamAudioSource::StopSourceOnError(const std::string& why) { - VLOG(1) << why; - + SendLogMessage(base::StringPrintf("StopSourceOnError([this=%p] {why=%s})", + this, why.c_str())); // Stop source when error occurs. PostCrossThreadTask( *task_runner_, FROM_HERE, @@ -232,7 +250,8 @@ void MediaStreamAudioSource::StopSourceOnError(const std::string& why) { } void MediaStreamAudioSource::SetMutedState(bool muted_state) { - DVLOG(3) << "MediaStreamAudioSource::SetMutedState state=" << muted_state; + SendLogMessage(base::StringPrintf("SetMutedState([this=%p] {muted_state=%s})", + this, (muted_state ? "true" : "false"))); PostCrossThreadTask( *task_runner_, FROM_HERE, WTF::CrossThreadBindOnce(&WebPlatformMediaStreamSource::SetSourceMuted, diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h index 24a2227e573..1dfc87329a2 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h @@ -12,10 +12,10 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "media/base/limits.h" -#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_deliverer.h" #include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h" #include "third_party/blink/public/platform/web_media_stream_source.h" #include "third_party/blink/public/platform/web_media_stream_track.h" +#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_deliverer.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h" #include "third_party/blink/renderer/platform/platform_export.h" diff --git a/chromium/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_track.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.cc index 1b6c6e1578f..24f05b8fc4e 100644 --- a/chromium/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_track.cc +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.cc @@ -2,27 +2,37 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_track.h" +#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h" #include <utility> -#include <vector> #include "base/logging.h" +#include "base/strings/stringprintf.h" #include "media/base/audio_bus.h" #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_audio_sink.h" +#include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" #include "third_party/blink/public/platform/web_media_stream_source.h" namespace blink { +namespace { + +void SendLogMessage(const std::string& message) { + blink::WebRtcLogMessage("MSAT::" + message); +} + +} // namespace + MediaStreamAudioTrack::MediaStreamAudioTrack(bool is_local_track) : WebPlatformMediaStreamTrack(is_local_track), is_enabled_(1) { - DVLOG(1) << "MediaStreamAudioTrack@" << this << "::MediaStreamAudioTrack(" - << (is_local_track ? "local" : "remote") << " track)"; + SendLogMessage( + base::StringPrintf("MediaStreamAudioTrack([this=%p] {is_local_track=%s})", + this, (is_local_track ? "true" : "false"))); } MediaStreamAudioTrack::~MediaStreamAudioTrack() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DVLOG(1) << "MediaStreamAudioTrack@" << this << " is being destroyed."; + SendLogMessage(base::StringPrintf("~MediaStreamAudioTrack([this=%p])", this)); Stop(); } @@ -38,9 +48,7 @@ MediaStreamAudioTrack* MediaStreamAudioTrack::From( void MediaStreamAudioTrack::AddSink(WebMediaStreamAudioSink* sink) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - DVLOG(1) << "Adding WebMediaStreamAudioSink@" << sink - << " to MediaStreamAudioTrack@" << this << '.'; + SendLogMessage(base::StringPrintf("AddSink([this=%p])", this)); // If the track has already stopped, just notify the sink of this fact without // adding it. @@ -55,9 +63,8 @@ void MediaStreamAudioTrack::AddSink(WebMediaStreamAudioSink* sink) { void MediaStreamAudioTrack::RemoveSink(WebMediaStreamAudioSink* sink) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + SendLogMessage(base::StringPrintf("RemoveSink([this=%p])", this)); deliverer_.RemoveConsumer(sink); - DVLOG(1) << "Removed WebMediaStreamAudioSink@" << sink - << " from MediaStreamAudioTrack@" << this << '.'; } media::AudioParameters MediaStreamAudioTrack::GetOutputFormat() const { @@ -66,15 +73,15 @@ media::AudioParameters MediaStreamAudioTrack::GetOutputFormat() const { void MediaStreamAudioTrack::SetEnabled(bool enabled) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DVLOG(1) << "MediaStreamAudioTrack@" << this << "::SetEnabled(" - << (enabled ? 'Y' : 'N') << ')'; + SendLogMessage(base::StringPrintf("SetEnabled([this=%p] {enabled=%s})", this, + (enabled ? "true" : "false"))); const bool previously_enabled = !!base::subtle::NoBarrier_AtomicExchange(&is_enabled_, enabled ? 1 : 0); if (enabled == previously_enabled) return; - std::vector<WebMediaStreamAudioSink*> sinks_to_notify; + Vector<WebMediaStreamAudioSink*> sinks_to_notify; deliverer_.GetConsumerList(&sinks_to_notify); for (WebMediaStreamAudioSink* sink : sinks_to_notify) sink->OnEnabledChanged(enabled); @@ -84,7 +91,7 @@ void MediaStreamAudioTrack::SetContentHint( WebMediaStreamTrack::ContentHintType content_hint) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - std::vector<WebMediaStreamAudioSink*> sinks_to_notify; + Vector<WebMediaStreamAudioSink*> sinks_to_notify; deliverer_.GetConsumerList(&sinks_to_notify); for (WebMediaStreamAudioSink* sink : sinks_to_notify) sink->OnContentHintChanged(content_hint); @@ -98,18 +105,18 @@ void MediaStreamAudioTrack::Start(base::OnceClosure stop_callback) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(!stop_callback.is_null()); DCHECK(stop_callback_.is_null()); - DVLOG(1) << "Starting MediaStreamAudioTrack@" << this << '.'; + SendLogMessage(base::StringPrintf("Start([this=%p])", this)); stop_callback_ = std::move(stop_callback); } void MediaStreamAudioTrack::StopAndNotify(base::OnceClosure callback) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DVLOG(1) << "Stopping MediaStreamAudioTrack@" << this << '.'; + SendLogMessage(base::StringPrintf("StopAndNotify([this=%p])", this)); if (!stop_callback_.is_null()) std::move(stop_callback_).Run(); - std::vector<WebMediaStreamAudioSink*> sinks_to_end; + Vector<WebMediaStreamAudioSink*> sinks_to_end; deliverer_.GetConsumerList(&sinks_to_end); for (WebMediaStreamAudioSink* sink : sinks_to_end) { deliverer_.RemoveConsumer(sink); @@ -122,11 +129,22 @@ void MediaStreamAudioTrack::StopAndNotify(base::OnceClosure callback) { } void MediaStreamAudioTrack::OnSetFormat(const media::AudioParameters& params) { + SendLogMessage(base::StringPrintf("OnSetFormat([this=%p] {params: [%s]})", + this, + params.AsHumanReadableString().c_str())); deliverer_.OnSetFormat(params); } void MediaStreamAudioTrack::OnData(const media::AudioBus& audio_bus, base::TimeTicks reference_time) { + if (!received_audio_callback_) { + // Add log message with unique this pointer id to mark the audio track as + // alive at the first data callback. + SendLogMessage(base::StringPrintf( + "OnData([this=%p] => (audio track is alive))", this)); + received_audio_callback_ = true; + } + // Note: Using NoBarrier_Load because the timing of when the audio thread sees // a changed |is_enabled_| value can be relaxed. const bool deliver_data = !!base::subtle::NoBarrier_Load(&is_enabled_); diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h new file mode 100644 index 00000000000..5981a2aa271 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h @@ -0,0 +1,126 @@ +// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_AUDIO_TRACK_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_AUDIO_TRACK_H_ + +#include <memory> + +#include "base/atomicops.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/synchronization/lock.h" +#include "base/threading/thread_checker.h" +#include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_track.h" +#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_deliverer.h" +#include "third_party/blink/renderer/platform/platform_export.h" + +namespace blink { + +class WebMediaStreamAudioSink; +class MediaStreamAudioSource; + +// Provides the part of the audio pipeline delivering audio from a +// MediaStreamAudioSource to one or more WebMediaStreamAudioSinks. An instance +// of this class is owned by WebMediaStreamTrack, and clients should use +// From() to gain access to a MediaStreamAudioTrack. +class PLATFORM_EXPORT MediaStreamAudioTrack + : public WebPlatformMediaStreamTrack { + public: + explicit MediaStreamAudioTrack(bool is_local_track); + + ~MediaStreamAudioTrack() override; + + // Returns the MediaStreamAudioTrack instance owned by the given blink |track| + // or null. + static MediaStreamAudioTrack* From(const WebMediaStreamTrack& track); + + // Provides a weak reference to this MediaStreamAudioTrack which is + // invalidated when Stop() is called. The weak pointer may only be + // dereferenced on the main thread. + base::WeakPtr<MediaStreamAudioTrack> GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + + // Add a sink to the track. This function will trigger a OnSetFormat() + // call on the |sink| before the first chunk of audio is delivered. + void AddSink(WebMediaStreamAudioSink* sink); + + // Remove a sink from the track. When this method returns, the sink's + // OnSetFormat() and OnData() methods will not be called again on any thread. + void RemoveSink(WebMediaStreamAudioSink* sink); + + // Returns the output format of the capture source. May return an invalid + // AudioParameters if the format is not yet available. + // Called on the main render thread. + // TODO(tommi): This method appears to only be used by Pepper and in fact + // does not appear to be necessary there. We should remove it since it adds + // to the complexity of all types of audio tracks+source implementations. + // https://crbug.com/577874 + media::AudioParameters GetOutputFormat() const; + + // Halts the flow of audio data from the source (and to the sinks), and then + // notifies all sinks of the "ended" state. + void StopAndNotify(base::OnceClosure callback) final; + + // MediaStreamTrack override. + void SetEnabled(bool enabled) override; + void SetContentHint( + WebMediaStreamTrack::ContentHintType content_hint) override; + + // Returns a unique class identifier. Some subclasses override and use this + // method to provide safe down-casting to their type. + virtual void* GetClassIdentifier() const; + + private: + friend class MediaStreamAudioSource; + friend class MediaStreamAudioDeliverer<MediaStreamAudioTrack>; + + // Called by MediaStreamAudioSource to notify this track that the flow of + // audio data has started from the source. |stop_callback| is run by Stop() + // when the source must halt the flow of audio data to this track. + void Start(base::OnceClosure stop_callback); + + // Called by the MediaStreamAudioDeliverer to notify this track of an audio + // format change. In turn, all WebMediaStreamAudioSinks will be notified + // before the next chunk of audio is delivered to them. + void OnSetFormat(const media::AudioParameters& params); + + // Called by the MediaStreamAudioDeliverer to deliver audio data to this + // track, which in turn delivers the audio to one or more + // WebMediaStreamAudioSinks. While this track is disabled, silent audio will + // be delivered to the sinks instead of the content of |audio_bus|. + void OnData(const media::AudioBus& audio_bus, base::TimeTicks reference_time); + + private: + // In debug builds, check that all methods that could cause object graph + // or data flow changes are being called on the main thread. + THREAD_CHECKER(thread_checker_); + + // Callback provided to Start() which is run when the audio flow must halt. + base::OnceClosure stop_callback_; + + // Manages sinks connected to this track and the audio format and data flow. + MediaStreamAudioDeliverer<WebMediaStreamAudioSink> deliverer_; + + // While false (0), silent audio is delivered to the sinks. + base::subtle::Atomic32 is_enabled_; + + // Buffer used to deliver silent audio data while this track is disabled. + std::unique_ptr<media::AudioBus> silent_bus_; + + // Set to true once at first audio callback after calling Start(). + // Only used for logging purposes. + bool received_audio_callback_ = false; + + // Provides weak pointers that are valid until Stop() is called. + base::WeakPtrFactory<MediaStreamAudioTrack> weak_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(MediaStreamAudioTrack); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_AUDIO_TRACK_H_ diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.cc index 0e48a233069..bec6546fb21 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.cc +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.cc @@ -132,7 +132,7 @@ void MediaStreamComponent::AudioSourceProviderImpl::ProvideInput( web_audio_source_provider_->ProvideInput(web_audio_data, frames_to_process); } -void MediaStreamComponent::Trace(blink::Visitor* visitor) { +void MediaStreamComponent::Trace(Visitor* visitor) { visitor->Trace(source_); } diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.h index b4643ea7cb6..6515d8bb8df 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.h +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_component.h @@ -35,10 +35,10 @@ #include <memory> #include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_track.h" -#include "third_party/blink/public/platform/web_media_constraints.h" #include "third_party/blink/public/platform/web_media_stream_track.h" #include "third_party/blink/renderer/platform/audio/audio_source_provider.h" #include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/mediastream/media_constraints.h" #include "third_party/blink/renderer/platform/wtf/forward.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/threading_primitives.h" @@ -84,8 +84,8 @@ class PLATFORM_EXPORT MediaStreamComponent final void SetMuted(bool muted) { muted_ = muted; } WebMediaStreamTrack::ContentHintType ContentHint() { return content_hint_; } void SetContentHint(WebMediaStreamTrack::ContentHintType); - const WebMediaConstraints& Constraints() const { return constraints_; } - void SetConstraints(const WebMediaConstraints& constraints) { + const MediaConstraints& Constraints() const { return constraints_; } + void SetConstraints(const MediaConstraints& constraints) { constraints_ = constraints; } AudioSourceProvider* GetAudioSourceProvider() { return &source_provider_; } @@ -102,7 +102,7 @@ class PLATFORM_EXPORT MediaStreamComponent final } void GetSettings(WebMediaStreamTrack::Settings&); - void Trace(blink::Visitor*); + void Trace(Visitor*); private: // AudioSourceProviderImpl wraps a WebAudioSourceProvider::provideInput() @@ -135,7 +135,7 @@ class PLATFORM_EXPORT MediaStreamComponent final bool muted_ = false; WebMediaStreamTrack::ContentHintType content_hint_ = WebMediaStreamTrack::ContentHintType::kNone; - WebMediaConstraints constraints_; + MediaConstraints constraints_; std::unique_ptr<WebPlatformMediaStreamTrack> platform_track_; }; diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.cc index 0f3c348d4aa..970dd08e6f6 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.cc +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.cc @@ -170,7 +170,7 @@ MediaStreamDescriptor::MediaStreamDescriptor( video_components_.push_back((*iter)); } -void MediaStreamDescriptor::Trace(blink::Visitor* visitor) { +void MediaStreamDescriptor::Trace(Visitor* visitor) { visitor->Trace(audio_components_); visitor->Trace(video_components_); visitor->Trace(client_); diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h index ea3e04c6d18..80141ceea79 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h @@ -51,7 +51,7 @@ class PLATFORM_EXPORT MediaStreamDescriptorClient virtual void StreamEnded() = 0; virtual void AddTrackByComponentAndFireEvents(MediaStreamComponent*) = 0; virtual void RemoveTrackByComponentAndFireEvents(MediaStreamComponent*) = 0; - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} }; class PLATFORM_EXPORT MediaStreamDescriptor final @@ -111,7 +111,7 @@ class PLATFORM_EXPORT MediaStreamDescriptor final void AddObserver(WebMediaStreamObserver*); void RemoveObserver(WebMediaStreamObserver*); - void Trace(blink::Visitor*); + void Trace(Visitor*); private: Member<MediaStreamDescriptorClient> client_; diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.cc b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.cc index df7371c24a7..a80d97c398f 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.cc +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.cc @@ -30,6 +30,7 @@ #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h" +#include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" @@ -37,6 +38,36 @@ namespace blink { namespace { +void SendLogMessage(const std::string& message) { + blink::WebRtcLogMessage("MSS::" + message); +} + +const char* StreamTypeToString(MediaStreamSource::StreamType type) { + switch (type) { + case MediaStreamSource::kTypeAudio: + return "Audio"; + case MediaStreamSource::kTypeVideo: + return "Video"; + default: + NOTREACHED(); + } + return "Invalid"; +} + +const char* ReadyStateToString(MediaStreamSource::ReadyState state) { + switch (state) { + case MediaStreamSource::kReadyStateLive: + return "Live"; + case MediaStreamSource::kReadyStateMuted: + return "Muted"; + case MediaStreamSource::kReadyStateEnded: + return "Ended"; + default: + NOTREACHED(); + } + return "Invalid"; +} + void GetSourceSettings(const blink::WebMediaStreamSource& web_source, blink::WebMediaStreamTrack::Settings& settings) { blink::MediaStreamAudioSource* const source = @@ -68,13 +99,28 @@ MediaStreamSource::MediaStreamSource(const String& id, name_(name), remote_(remote), ready_state_(ready_state), - requires_consumer_(requires_consumer) {} + requires_consumer_(requires_consumer) { + SendLogMessage( + String::Format( + "MediaStreamSource({id=%s}, {type=%s}, {name=%s}, {remote=%d}, " + "{ready_state=%s}", + id.Utf8().c_str(), StreamTypeToString(type), name.Utf8().c_str(), + remote, ReadyStateToString(ready_state)) + .Utf8()); +} void MediaStreamSource::SetGroupId(const String& group_id) { + SendLogMessage( + String::Format("SetGroupId({group_id=%s})", group_id.Utf8().c_str()) + .Utf8()); group_id_ = group_id; } void MediaStreamSource::SetReadyState(ReadyState ready_state) { + SendLogMessage(String::Format("SetReadyState({id=%s}, {ready_state=%s})", + Id().Utf8().c_str(), + ReadyStateToString(ready_state)) + .Utf8()); if (ready_state_ != kReadyStateEnded && ready_state_ != ready_state) { ready_state_ = ready_state; @@ -171,6 +217,12 @@ void MediaStreamSource::GetSettings(WebMediaStreamTrack::Settings& settings) { void MediaStreamSource::SetAudioFormat(size_t number_of_channels, float sample_rate) { + SendLogMessage( + String::Format( + "SetAudioFormat({id=%s}, {number_of_channels=%d}, {sample_rate=%f})", + Id().Utf8().c_str(), static_cast<int>(number_of_channels), + sample_rate) + .Utf8()); DCHECK(requires_consumer_); MutexLocker locker(audio_consumers_lock_); for (AudioDestinationConsumer* consumer : audio_consumers_) @@ -184,7 +236,7 @@ void MediaStreamSource::ConsumeAudio(AudioBus* bus, size_t number_of_frames) { consumer->ConsumeAudio(bus, number_of_frames); } -void MediaStreamSource::Trace(blink::Visitor* visitor) { +void MediaStreamSource::Trace(Visitor* visitor) { visitor->Trace(observers_); } diff --git a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.h b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.h index 2e4edff6f0b..2cc1f159f49 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.h +++ b/chromium/third_party/blink/renderer/platform/mediastream/media_stream_source.h @@ -37,10 +37,10 @@ #include "base/optional.h" #include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h" -#include "third_party/blink/public/platform/web_media_constraints.h" #include "third_party/blink/public/platform/web_media_stream_source.h" #include "third_party/blink/public/platform/web_media_stream_track.h" #include "third_party/blink/renderer/platform/audio/audio_destination_consumer.h" +#include "third_party/blink/renderer/platform/mediastream/media_constraints.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -121,7 +121,7 @@ class PLATFORM_EXPORT MediaStreamSource final return audio_consumers_; } - void Trace(blink::Visitor*); + void Trace(Visitor*); void Dispose(); @@ -138,7 +138,7 @@ class PLATFORM_EXPORT MediaStreamSource final HashSet<AudioDestinationConsumer*> audio_consumers_ GUARDED_BY(audio_consumers_lock_); std::unique_ptr<WebPlatformMediaStreamSource> platform_source_; - WebMediaConstraints constraints_; + MediaConstraints constraints_; WebMediaStreamSource::Capabilities capabilities_; base::Optional<EchoCancellationMode> echo_cancellation_mode_; base::Optional<bool> auto_gain_control_; diff --git a/chromium/third_party/blink/renderer/platform/mediastream/webaudio_destination_consumer.h b/chromium/third_party/blink/renderer/platform/mediastream/webaudio_destination_consumer.h new file mode 100644 index 00000000000..d6184f918fb --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mediastream/webaudio_destination_consumer.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_WEBAUDIO_DESTINATION_CONSUMER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_WEBAUDIO_DESTINATION_CONSUMER_H_ + +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +class WebAudioDestinationConsumer { + public: + virtual ~WebAudioDestinationConsumer() = default; + + virtual void SetFormat(size_t number_of_channels, float sample_rate) = 0; + + // The size of the vector is the number of audio channels, and + // |number_of_frames| is the number of audio frames in the (possibly + // multi-channel) buffer in a planar format. + virtual void ConsumeAudio(const Vector<const float*>&, + size_t number_of_frames) = 0; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_WEBAUDIO_DESTINATION_CONSUMER_H_ diff --git a/chromium/third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.cc b/chromium/third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.cc index 8d294a3c5ed..7ae4e9ea83b 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.cc +++ b/chromium/third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.cc @@ -6,9 +6,9 @@ #include <utility> -#include "base/bind.h" #include "base/bind_helpers.h" #include "base/logging.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" namespace blink { @@ -17,8 +17,9 @@ WebAudioMediaStreamSource::WebAudioMediaStreamSource( scoped_refptr<base::SingleThreadTaskRunner> task_runner) : MediaStreamAudioSource(std::move(task_runner), false /* is_remote */), is_registered_consumer_(false), - fifo_(base::Bind(&WebAudioMediaStreamSource::DeliverRebufferedAudio, - base::Unretained(this))), + fifo_(ConvertToBaseRepeatingCallback(CrossThreadBindRepeating( + &WebAudioMediaStreamSource::DeliverRebufferedAudio, + WTF::CrossThreadUnretained(this)))), blink_source_(*blink_source) { DVLOG(1) << "WebAudioMediaStreamSource::WebAudioMediaStreamSource()"; } @@ -83,7 +84,7 @@ void WebAudioMediaStreamSource::EnsureSourceIsStopped() { } void WebAudioMediaStreamSource::ConsumeAudio( - const WebVector<const float*>& audio_data, + const Vector<const float*>& audio_data, size_t number_of_frames) { // TODO(miu): Plumbing is needed to determine the actual capture timestamp // of the audio, instead of just snapshotting base::TimeTicks::Now(), for diff --git a/chromium/third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.h b/chromium/third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.h index e5de09a72b2..afdfc6e7c58 100644 --- a/chromium/third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.h +++ b/chromium/third_party/blink/renderer/platform/mediastream/webaudio_media_stream_source.h @@ -10,10 +10,10 @@ #include "base/time/time.h" #include "media/base/audio_bus.h" #include "media/base/audio_push_fifo.h" -#include "third_party/blink/public/platform/web_audio_destination_consumer.h" #include "third_party/blink/public/platform/web_media_stream_source.h" #include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h" +#include "third_party/blink/renderer/platform/mediastream/webaudio_destination_consumer.h" #include "third_party/blink/renderer/platform/platform_export.h" namespace blink { @@ -39,7 +39,7 @@ class PLATFORM_EXPORT WebAudioMediaStreamSource final // concurrently across threads, but these methods could be called on any // thread. void SetFormat(size_t number_of_channels, float sample_rate) override; - void ConsumeAudio(const WebVector<const float*>& audio_data, + void ConsumeAudio(const Vector<const float*>& audio_data, size_t number_of_frames) override; // Called by AudioPushFifo zero or more times during the call to diff --git a/chromium/third_party/blink/renderer/platform/mhtml/archive_resource.h b/chromium/third_party/blink/renderer/platform/mhtml/archive_resource.h index 10b339edd88..fde61b92783 100644 --- a/chromium/third_party/blink/renderer/platform/mhtml/archive_resource.h +++ b/chromium/third_party/blink/renderer/platform/mhtml/archive_resource.h @@ -53,7 +53,7 @@ class PLATFORM_EXPORT ArchiveResource final const AtomicString& MimeType() const { return mime_type_; } const AtomicString& TextEncoding() const { return text_encoding_; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: KURL url_; diff --git a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc index 94d98b13a53..9021739bd00 100644 --- a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc +++ b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.cc @@ -440,7 +440,7 @@ ArchiveResource* MHTMLArchive::SubresourceForURL(const KURL& url) const { return subresources_.at(url.GetString()); } -void MHTMLArchive::Trace(blink::Visitor* visitor) { +void MHTMLArchive::Trace(Visitor* visitor) { visitor->Trace(main_resource_); visitor->Trace(subresources_); } diff --git a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.h b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.h index 985d346276c..c1edc8d5764 100644 --- a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.h +++ b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_archive.h @@ -105,7 +105,7 @@ class PLATFORM_EXPORT MHTMLArchive final // The purported creation date (as expressed by the Date: header). base::Time Date() const { return date_; } - void Trace(blink::Visitor*); + void Trace(Visitor*); blink::mojom::MHTMLLoadResult LoadResult() const { return load_result_; } private: diff --git a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc index f08d30dab9c..c055297ed18 100644 --- a/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc +++ b/chromium/third_party/blink/renderer/platform/mhtml/mhtml_parser.cc @@ -124,7 +124,7 @@ class MIMEHeader final : public GarbageCollected<MIMEHeader> { String EndOfPartBoundary() const { return end_of_part_boundary_; } String EndOfDocumentBoundary() const { return end_of_document_boundary_; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: static Encoding ParseContentTransferEncoding(const String&); @@ -234,7 +234,7 @@ MIMEHeader* MIMEHeader::ParseHeader(SharedBufferChunkReader* buffer) { MIMEHeader::Encoding MIMEHeader::ParseContentTransferEncoding( const String& text) { - String encoding = text.StripWhiteSpace().DeprecatedLower(); + String encoding = text.StripWhiteSpace().LowerASCII(); if (encoding == "base64") return Encoding::kBase64; if (encoding == "quoted-printable") diff --git a/chromium/third_party/blink/renderer/platform/mojo/DEPS b/chromium/third_party/blink/renderer/platform/mojo/DEPS index ee6991fbfd1..0520c841e8c 100644 --- a/chromium/third_party/blink/renderer/platform/mojo/DEPS +++ b/chromium/third_party/blink/renderer/platform/mojo/DEPS @@ -13,7 +13,6 @@ include_rules = [ "+base/strings/latin1_string_conversions.h", "+base/strings/string16.h", "+mojo/public/cpp/base/time_mojom_traits.h", - "+mojo/public/cpp/bindings/binding.h", "+mojo/public/mojom/base/string16.mojom-blink.h", "+skia/public/mojom/bitmap_skbitmap_mojom_traits.h", @@ -22,6 +21,10 @@ include_rules = [ "+third_party/blink/renderer/platform/platform_export.h", "+third_party/blink/renderer/platform/weborigin", "+third_party/blink/renderer/platform/wtf", + "+third_party/blink/renderer/platform/context_lifecycle_observer.h", + "+third_party/blink/renderer/platform/context_lifecycle_notifier.h", + "+third_party/blink/renderer/platform/heap_observer_list.h", + "+third_party/blink/renderer/platform/heap", ] specific_include_rules = { diff --git a/chromium/third_party/blink/renderer/platform/mojo/big_string.typemap b/chromium/third_party/blink/renderer/platform/mojo/big_string.typemap deleted file mode 100644 index bd4bac7b3f7..00000000000 --- a/chromium/third_party/blink/renderer/platform/mojo/big_string.typemap +++ /dev/null @@ -1,11 +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 = "//mojo/public/mojom/base/big_string.mojom" -public_headers = - [ "//third_party/blink/renderer/platform/wtf/text/wtf_string.h" ] -traits_headers = - [ "//third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h" ] -type_mappings = - [ "mojo_base.mojom.BigString=::WTF::String[nullable_is_same_type]" ] diff --git a/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h b/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h index dda183cb6c9..2b0f1388aaa 100644 --- a/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h +++ b/chromium/third_party/blink/renderer/platform/mojo/big_string_mojom_traits.h @@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BIG_STRING_MOJOM_TRAITS_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_BIG_STRING_MOJOM_TRAITS_H_ +#include "mojo/public/cpp/base/big_buffer.h" #include "mojo/public/cpp/bindings/struct_traits.h" #include "mojo/public/mojom/base/big_string.mojom-blink-forward.h" #include "third_party/blink/renderer/platform/platform_export.h" diff --git a/chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni b/chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni index 176545f7ecb..8af90056cf9 100644 --- a/chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni +++ b/chromium/third_party/blink/renderer/platform/mojo/blink_typemaps.gni @@ -3,33 +3,27 @@ # found in the LICENSE file. typemaps = [ - "//media/capture/mojom/video_capture_types_for_blink.typemap", + "//media/capture/mojom/video_capture_types.typemap", "//media/mojo/mojom/audio_parameters.typemap", - "//mojo/public/cpp/base/values.typemap", - "//services/network/public/cpp/http_request_headers.typemap", - "//services/network/public/cpp/ip_address_for_blink.typemap", - "//services/network/public/cpp/ip_endpoint_for_blink.typemap", - "//services/network/public/cpp/network_interface_for_blink.typemap", - "//services/network/public/cpp/mutable_network_traffic_annotation_tag.typemap", - "//services/network/public/cpp/p2p.typemap", + "//third_party/blink/common/feature_policy/feature_policy.typemap", + "//third_party/blink/common/frame/frame_policy.typemap", + "//third_party/blink/public/common/loader/url_loader_factory_bundle.typemap", + "//third_party/blink/public/common/messaging/message_port_descriptor.typemap", "//third_party/blink/renderer/core/messaging/blink_cloneable_message.typemap", "//third_party/blink/renderer/core/messaging/blink_transferable_message.typemap", "//third_party/blink/renderer/modules/indexeddb/indexed_db_blink.typemap", "//third_party/blink/renderer/platform/blob/serialized_blob.typemap", "//third_party/blink/renderer/platform/cookie/canonical_cookie.typemap", - "//third_party/blink/renderer/platform/mojo/big_string.typemap", "//third_party/blink/renderer/platform/mojo/fetch_api_request_headers.typemap", - "//third_party/blink/renderer/platform/mojo/geometry.typemap", - "//third_party/blink/renderer/platform/mojo/kurl.typemap", - "//third_party/blink/renderer/platform/mojo/security_origin.typemap", "//third_party/blink/renderer/platform/mojo/string.typemap", "//third_party/blink/renderer/platform/mojo/time.typemap", "//third_party/blink/renderer/platform/network/encoded_form_data_element.typemap", "//third_party/blink/renderer/platform/network/encoded_form_data.typemap", + "//third_party/blink/public/common/mediastream/media_devices.typemap", "//third_party/blink/public/common/mediastream/media_stream.typemap", "//third_party/blink/public/common/screen_orientation/screen_orientation_lock_types.typemap", "//third_party/blink/public/mojom/bluetooth/bluetooth.typemap", - "//ui/gfx/mojom/buffer_types_for_blink.typemap", - "//ui/gfx/mojom/color_space_for_blink.typemap", + "//ui/gfx/geometry/mojom/geometry.typemap", + "//ui/gfx/mojom/color_space.typemap", "//ui/gfx/mojom/transform.typemap", ] diff --git a/chromium/third_party/blink/renderer/platform/mojo/geometry.typemap b/chromium/third_party/blink/renderer/platform/mojo/geometry.typemap deleted file mode 100644 index 8029163748a..00000000000 --- a/chromium/third_party/blink/renderer/platform/mojo/geometry.typemap +++ /dev/null @@ -1,44 +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. - -mojom = "//ui/gfx/geometry/mojom/geometry.mojom" -public_headers = [ - "//ui/gfx/geometry/quaternion.h", - "//ui/gfx/geometry/vector3d_f.h", - "//third_party/blink/public/platform/web_float_rect.h", - "//third_party/blink/public/platform/web_float_point.h", - "//third_party/blink/public/platform/web_point.h", - "//third_party/blink/public/platform/web_rect.h", - "//third_party/blink/public/platform/web_size.h", - "//third_party/blink/renderer/platform/geometry/float_point_3d.h", -] -traits_headers = [ - "//third_party/blink/renderer/platform/mojo/geometry_mojom_traits.h", - "//ui/gfx/geometry/mojom/geometry_mojom_traits.h", -] - -# Note: consumers of this typemap must themselves depend on platform. -deps = [ - "//mojo/public/cpp/bindings", -] - -public_deps = [ - "//third_party/blink/public:blink_headers", - "//third_party/blink/renderer/platform:geometry_mojom_traits", - "//ui/gfx/geometry", - "//ui/gfx/geometry/mojom:mojom_traits", -] - -# TODO(zqzhang): ideally, gfx.mojom.Size should be mapped into ::blink::IntSize. -# However that introduces an link issue on Windows. See https://crbug.com/653323 -type_mappings = [ - "gfx.mojom.Point=::blink::WebPoint", - "gfx.mojom.PointF=::blink::WebFloatPoint", - "gfx.mojom.Point3F=::blink::FloatPoint3D", - "gfx.mojom.Quaternion=::gfx::Quaternion", - "gfx.mojom.RectF=::blink::WebFloatRect", - "gfx.mojom.Rect=::blink::WebRect", - "gfx.mojom.Size=::blink::WebSize", - "gfx.mojom.Vector3dF=::gfx::Vector3dF", -] diff --git a/chromium/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.cc b/chromium/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.cc deleted file mode 100644 index 29128e0055d..00000000000 --- a/chromium/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.cc +++ /dev/null @@ -1,73 +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 "third_party/blink/renderer/platform/mojo/geometry_mojom_traits.h" - -namespace mojo { - -// static -bool StructTraits<gfx::mojom::RectFDataView, ::blink::WebFloatRect>::Read( - gfx::mojom::RectFDataView data, - ::blink::WebFloatRect* out) { - if (data.width() < 0 || data.height() < 0) - return false; - out->x = data.x(); - out->y = data.y(); - out->width = data.width(); - out->height = data.height(); - return true; -} - -// static -bool StructTraits<gfx::mojom::RectDataView, ::blink::WebRect>::Read( - gfx::mojom::RectDataView data, - ::blink::WebRect* out) { - if (data.width() < 0 || data.height() < 0) - return false; - out->x = data.x(); - out->y = data.y(); - out->width = data.width(); - out->height = data.height(); - return true; -} - -// static -bool StructTraits<gfx::mojom::PointDataView, ::blink::WebPoint>::Read( - gfx::mojom::PointDataView data, - ::blink::WebPoint* out) { - out->x = data.x(); - out->y = data.y(); - return true; -} - -// static -bool StructTraits<gfx::mojom::PointFDataView, ::blink::WebFloatPoint>::Read( - gfx::mojom::PointFDataView data, - ::blink::WebFloatPoint* out) { - out->x = data.x(); - out->y = data.y(); - return true; -} - -bool StructTraits<gfx::mojom::Point3FDataView, ::blink::FloatPoint3D>::Read( - gfx::mojom::Point3FDataView data, - ::blink::FloatPoint3D* out) { - out->SetX(data.x()); - out->SetY(data.y()); - out->SetZ(data.z()); - return true; -} - -// static -bool StructTraits<gfx::mojom::SizeDataView, ::blink::WebSize>::Read( - gfx::mojom::SizeDataView data, - ::blink::WebSize* out) { - if (data.width() < 0 || data.height() < 0) - return false; - out->width = data.width(); - out->height = data.height(); - return true; -} - -} // namespace mojo diff --git a/chromium/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.h b/chromium/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.h deleted file mode 100644 index eb16fc06bb1..00000000000 --- a/chromium/third_party/blink/renderer/platform/mojo/geometry_mojom_traits.h +++ /dev/null @@ -1,68 +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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_GEOMETRY_MOJOM_TRAITS_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_GEOMETRY_MOJOM_TRAITS_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_point.h" -#include "third_party/blink/public/platform/web_rect.h" -#include "third_party/blink/public/platform/web_size.h" -#include "third_party/blink/renderer/platform/geometry/float_point_3d.h" -#include "ui/gfx/geometry/mojom/geometry.mojom-shared.h" - -namespace mojo { - -template <> -struct StructTraits<gfx::mojom::PointDataView, ::blink::WebPoint> { - static int x(const ::blink::WebPoint& point) { return point.x; } - static int y(const ::blink::WebPoint& point) { return point.y; } - static bool Read(gfx::mojom::PointDataView, ::blink::WebPoint* out); -}; - -template <> -struct StructTraits<gfx::mojom::PointFDataView, ::blink::WebFloatPoint> { - static float x(const ::blink::WebFloatPoint& point) { return point.x; } - static float y(const ::blink::WebFloatPoint& point) { return point.y; } - static bool Read(gfx::mojom::PointFDataView, ::blink::WebFloatPoint* out); -}; - -template <> -struct StructTraits<gfx::mojom::Point3FDataView, ::blink::FloatPoint3D> { - static float x(const gfx::Point3F& p) { return p.x(); } - static float y(const gfx::Point3F& p) { return p.y(); } - static float z(const gfx::Point3F& p) { return p.z(); } - static bool Read(gfx::mojom::Point3FDataView data, - ::blink::FloatPoint3D* out); -}; - -template <> -struct StructTraits<gfx::mojom::RectFDataView, ::blink::WebFloatRect> { - static float x(const ::blink::WebFloatRect& rect) { return rect.x; } - static float y(const ::blink::WebFloatRect& rect) { return rect.y; } - static float width(const ::blink::WebFloatRect& rect) { return rect.width; } - static float height(const ::blink::WebFloatRect& rect) { return rect.height; } - static bool Read(gfx::mojom::RectFDataView, ::blink::WebFloatRect* out); -}; - -template <> -struct StructTraits<gfx::mojom::RectDataView, ::blink::WebRect> { - static int x(const ::blink::WebRect& rect) { return rect.x; } - static int y(const ::blink::WebRect& rect) { return rect.y; } - static int width(const ::blink::WebRect& rect) { return rect.width; } - static int height(const ::blink::WebRect& rect) { return rect.height; } - static bool Read(gfx::mojom::RectDataView, ::blink::WebRect* out); -}; - -template <> -struct StructTraits<gfx::mojom::SizeDataView, ::blink::WebSize> { - static int width(const ::blink::WebSize& size) { return size.width; } - static int height(const ::blink::WebSize& size) { return size.height; } - static bool Read(gfx::mojom::SizeDataView, ::blink::WebSize* out); -}; - -} // namespace mojo - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_GEOMETRY_MOJOM_TRAITS_H_ diff --git a/chromium/third_party/blink/renderer/platform/mojo/geometry_mojom_traits_test.cc b/chromium/third_party/blink/renderer/platform/mojo/geometry_mojom_traits_test.cc deleted file mode 100644 index 0c1d8a69650..00000000000 --- a/chromium/third_party/blink/renderer/platform/mojo/geometry_mojom_traits_test.cc +++ /dev/null @@ -1,180 +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 <utility> - -#include "base/test/task_environment.h" -#include "mojo/public/cpp/bindings/receiver_set.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/geometry/float_point_3d.h" -#include "ui/gfx/geometry/mojom/geometry.mojom-blink.h" -#include "ui/gfx/geometry/mojom/geometry_traits_test_service.mojom-blink.h" - -namespace blink { - -namespace { - -class GeometryStructTraitsTest - : public testing::Test, - public gfx::mojom::blink::GeometryTraitsTestService { - public: - GeometryStructTraitsTest() {} - - protected: - mojo::Remote<gfx::mojom::blink::GeometryTraitsTestService> - GetTraitsTestProxy() { - mojo::Remote<gfx::mojom::blink::GeometryTraitsTestService> proxy; - traits_test_receivers_.Add(this, proxy.BindNewPipeAndPassReceiver()); - return proxy; - } - - private: - // GeometryTraitsTestService: - void EchoPoint(const WebPoint& p, EchoPointCallback callback) override { - std::move(callback).Run(p); - } - - void EchoPointF(const WebFloatPoint& p, - EchoPointFCallback callback) override { - std::move(callback).Run(p); - } - - void EchoPoint3F(const FloatPoint3D& p, - EchoPoint3FCallback callback) override { - std::move(callback).Run(p); - } - - void EchoSize(const WebSize& s, EchoSizeCallback callback) override { - std::move(callback).Run(s); - } - - void EchoSizeF(gfx::mojom::blink::SizeFPtr, EchoSizeFCallback) override { - // The type map is not specified. - NOTREACHED(); - } - - void EchoRect(const WebRect& r, EchoRectCallback callback) override { - std::move(callback).Run(r); - } - - void EchoRectF(const WebFloatRect& r, EchoRectFCallback callback) override { - std::move(callback).Run(r); - } - - void EchoInsets(gfx::mojom::blink::InsetsPtr, EchoInsetsCallback) override { - // The type map is not specified. - NOTREACHED(); - } - - void EchoInsetsF(gfx::mojom::blink::InsetsFPtr, - EchoInsetsFCallback) override { - // The type map is not specified. - NOTREACHED(); - } - - void EchoVector2d(gfx::mojom::blink::Vector2dPtr, - EchoVector2dCallback) override { - // The type map is not specified. - NOTREACHED(); - } - - void EchoVector2dF(gfx::mojom::blink::Vector2dFPtr, - EchoVector2dFCallback) override { - // The type map is not specified. - NOTREACHED(); - } - - void EchoVector3dF(const gfx::Vector3dF& v, - EchoVector3dFCallback callback) override { - std::move(callback).Run(v); - } - - void EchoQuaternion(const gfx::Quaternion& q, - EchoQuaternionCallback callback) override { - std::move(callback).Run(q); - } - - mojo::ReceiverSet<gfx::mojom::blink::GeometryTraitsTestService> - traits_test_receivers_; - - base::test::TaskEnvironment task_environment_; - - DISALLOW_COPY_AND_ASSIGN(GeometryStructTraitsTest); -}; - -} // namespace - -TEST_F(GeometryStructTraitsTest, Size) { - const int32_t kWidth = 1234; - const int32_t kHeight = 5678; - WebSize input(kWidth, kHeight); - mojo::Remote<gfx::mojom::blink::GeometryTraitsTestService> proxy = - GetTraitsTestProxy(); - WebSize output; - proxy->EchoSize(input, &output); - EXPECT_EQ(input, output); -} - -TEST_F(GeometryStructTraitsTest, Point) { - const float kX = 1234; - const float kY = 5678; - WebPoint input(kX, kY); - mojo::Remote<gfx::mojom::blink::GeometryTraitsTestService> proxy = - GetTraitsTestProxy(); - WebPoint output; - proxy->EchoPoint(input, &output); - EXPECT_EQ(input, output); -} - -TEST_F(GeometryStructTraitsTest, PointF) { - const float kX = 1.234; - const float kY = 5.678; - WebFloatPoint input(kX, kY); - mojo::Remote<gfx::mojom::blink::GeometryTraitsTestService> proxy = - GetTraitsTestProxy(); - WebFloatPoint output; - proxy->EchoPointF(input, &output); - EXPECT_EQ(input, output); -} - -TEST_F(GeometryStructTraitsTest, Point3D) { - const float kX = 1.234; - const float kY = 5.678; - const float kZ = 9.098; - FloatPoint3D input(kX, kY, kZ); - mojo::Remote<gfx::mojom::blink::GeometryTraitsTestService> proxy = - GetTraitsTestProxy(); - FloatPoint3D output; - proxy->EchoPoint3F(input, &output); - EXPECT_EQ(input, output); -} - -TEST_F(GeometryStructTraitsTest, Rect) { - const float kX = 1; - const float kY = 2; - const float kWidth = 3; - const float kHeight = 4; - WebRect input(kX, kY, kWidth, kHeight); - mojo::Remote<gfx::mojom::blink::GeometryTraitsTestService> proxy = - GetTraitsTestProxy(); - WebRect output; - proxy->EchoRect(input, &output); - EXPECT_EQ(input, output); -} - -TEST_F(GeometryStructTraitsTest, RectF) { - const float kX = 1.234; - const float kY = 2.345; - const float kWidth = 3.456; - const float kHeight = 4.567; - WebFloatRect input(kX, kY, kWidth, kHeight); - mojo::Remote<gfx::mojom::blink::GeometryTraitsTestService> proxy = - GetTraitsTestProxy(); - WebFloatRect output; - proxy->EchoRectF(input, &output); - EXPECT_EQ(input, output); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h new file mode 100644 index 00000000000..bfc774c962b --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h @@ -0,0 +1,94 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_RECEIVER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_RECEIVER_H_ + +#include "mojo/public/cpp/bindings/receiver.h" +#include "third_party/blink/renderer/platform/context_lifecycle_observer.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" + +namespace blink { + +// HeapMojoReceiver is a wrapper for mojo::Receiver to be owned by a +// garbage-collected object. Blink is expected to use HeapMojoReceiver by +// default. HeapMojoReceiver must be associated with context. +// HeapMojoReceiver's constructor takes context as a mandatory parameter. +// HeapMojoReceiver resets the mojo connection when 1) the owner object is +// garbage-collected and 2) the associated ExecutionContext is detached. + +// TODO(crbug.com/1058076) HeapMojoWrapperMode should be removed once we ensure +// that the interface is not used after ContextDestroyed(). +template <typename Interface, + HeapMojoWrapperMode Mode = HeapMojoWrapperMode::kWithContextObserver> +class HeapMojoReceiver { + DISALLOW_NEW(); + + public: + using ImplPointerType = typename mojo::Receiver<Interface>::ImplPointerType; + + HeapMojoReceiver(ImplPointerType impl, ContextLifecycleNotifier* context) + : wrapper_(MakeGarbageCollected<Wrapper>(std::move(impl), context)) {} + + // Methods to redirect to mojo::Receiver: + ImplPointerType operator->() const { return get(); } + ImplPointerType get() { return wrapper_->receiver().get(); } + bool is_bound() const { return wrapper_->receiver().is_bound(); } + void reset() { wrapper_->receiver().reset(); } + void set_disconnect_handler(base::OnceClosure handler) { + wrapper_->receiver().set_disconnect_handler(std::move(handler)); + } + mojo::PendingRemote<Interface> BindNewPipeAndPassRemote( + scoped_refptr<base::SequencedTaskRunner> task_runner) WARN_UNUSED_RESULT { + DCHECK(task_runner); + return wrapper_->receiver().BindNewPipeAndPassRemote( + std::move(task_runner)); + } + void Bind(mojo::PendingReceiver<Interface> pending_receiver, + scoped_refptr<base::SequencedTaskRunner> task_runner) { + DCHECK(task_runner); + wrapper_->receiver().Bind(std::move(pending_receiver), + std::move(task_runner)); + } + + void Trace(Visitor* visitor) { visitor->Trace(wrapper_); } + + private: + // Garbage collected wrapper class to add a prefinalizer. + class Wrapper final : public GarbageCollected<Wrapper>, + public ContextLifecycleObserver { + USING_PRE_FINALIZER(Wrapper, Dispose); + USING_GARBAGE_COLLECTED_MIXIN(Wrapper); + + public: + Wrapper(ImplPointerType impl, ContextLifecycleNotifier* notifier) + : receiver_(std::move(impl)) { + SetContextLifecycleNotifier(notifier); + } + + void Trace(Visitor* visitor) override { + ContextLifecycleObserver::Trace(visitor); + } + + void Dispose() { receiver_.reset(); } + + mojo::Receiver<Interface>& receiver() { return receiver_; } + + // ContextLifecycleObserver methods + void ContextDestroyed() override { + if (Mode == HeapMojoWrapperMode::kWithContextObserver) + receiver_.reset(); + } + + private: + mojo::Receiver<Interface> receiver_; + }; + + Member<Wrapper> wrapper_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_RECEIVER_H_ diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h new file mode 100644 index 00000000000..36b7a74aa76 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h @@ -0,0 +1,94 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_RECEIVER_SET_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_RECEIVER_SET_H_ + +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "third_party/blink/renderer/platform/context_lifecycle_observer.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" + +namespace blink { + +// HeapMojoReceiverSet is a wrapper for mojo::ReceiverSet to be owned by a +// garbage-collected object. Blink is expected to use HeapMojoReceiverSet by +// default. HeapMojoReceiverSet must be associated with context. +// HeapMojoReceiverSet's constructor takes context as a mandatory parameter. +// HeapMojoReceiverSet resets the mojo connection when 1) the owner object is +// garbage-collected or 2) the associated ExecutionContext is detached. + +// TODO(crbug.com/1058076) HeapMojoWrapperMode should be removed once we ensure +// that the interface is not used after ContextDestroyed(). +template <typename Interface, + HeapMojoWrapperMode Mode = HeapMojoWrapperMode::kWithContextObserver> +class HeapMojoReceiverSet { + DISALLOW_NEW(); + + public: + using ImplPointerType = typename mojo::Receiver<Interface>::ImplPointerType; + + explicit HeapMojoReceiverSet(ContextLifecycleNotifier* context) + : wrapper_(MakeGarbageCollected<Wrapper>(context)) { + DCHECK(context); + } + + // Methods to redirect to mojo::ReceiverSet: + mojo::ReceiverId Add(ImplPointerType impl, + mojo::PendingReceiver<Interface> receiver, + scoped_refptr<base::SequencedTaskRunner> task_runner) { + DCHECK(task_runner); + return wrapper_->receiver_set().Add(std::move(impl), std::move(receiver), + task_runner); + } + + bool Remove(mojo::ReceiverId id) { + return wrapper_->receiver_set().Remove(id); + } + + void Clear() { wrapper_->receiver_set().Clear(); } + + bool HasReceiver(mojo::ReceiverId id) { + return wrapper_->receiver_set().HasReceiver(id); + } + + void Trace(Visitor* visitor) { visitor->Trace(wrapper_); } + + private: + // Garbage collected wrapper class to add a prefinalizer. + class Wrapper final : public GarbageCollected<Wrapper>, + public ContextLifecycleObserver { + USING_PRE_FINALIZER(Wrapper, Dispose); + USING_GARBAGE_COLLECTED_MIXIN(Wrapper); + + public: + explicit Wrapper(ContextLifecycleNotifier* notifier) { + SetContextLifecycleNotifier(notifier); + } + + void Trace(Visitor* visitor) override { + ContextLifecycleObserver::Trace(visitor); + } + + void Dispose() { receiver_set_.Clear(); } + + mojo::ReceiverSet<Interface>& receiver_set() { return receiver_set_; } + + // ContextLifecycleObserver methods + void ContextDestroyed() override { + if (Mode == HeapMojoWrapperMode::kWithContextObserver) + receiver_set_.Clear(); + } + + private: + mojo::ReceiverSet<Interface> receiver_set_; + }; + + Member<Wrapper> wrapper_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_RECEIVER_SET_H_ diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set_test.cc b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set_test.cc new file mode 100644 index 00000000000..aa13bc2b39b --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set_test.cc @@ -0,0 +1,179 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h" + +#include "base/test/null_task_runner.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/interfaces/bindings/tests/sample_service.mojom-blink.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/context_lifecycle_notifier.h" +#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/heap_observer_list.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" + +namespace blink { + +namespace { + +class FakeContextNotifier final : public GarbageCollected<FakeContextNotifier>, + public ContextLifecycleNotifier { + USING_GARBAGE_COLLECTED_MIXIN(FakeContextNotifier); + + public: + FakeContextNotifier() = default; + + void AddContextLifecycleObserver( + ContextLifecycleObserver* observer) override { + observers_.AddObserver(observer); + } + void RemoveContextLifecycleObserver( + ContextLifecycleObserver* observer) override { + observers_.RemoveObserver(observer); + } + + void NotifyContextDestroyed() { + observers_.ForEachObserver([](ContextLifecycleObserver* observer) { + observer->ContextDestroyed(); + }); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(observers_); + ContextLifecycleNotifier::Trace(visitor); + } + + private: + HeapObserverList<ContextLifecycleObserver> observers_; +}; + +class MockService : public sample::blink::Service { + public: + MockService() = default; + + void Frobinate(sample::blink::FooPtr foo, + Service::BazOptions baz, + mojo::PendingRemote<sample::blink::Port> port, + FrobinateCallback callback) override {} + void GetPort(mojo::PendingReceiver<sample::blink::Port> receiver) override {} +}; + +template <HeapMojoWrapperMode Mode> +class GCOwner : public GarbageCollected<GCOwner<Mode>> { + public: + explicit GCOwner(FakeContextNotifier* context) : receiver_set_(context) {} + void Trace(Visitor* visitor) { visitor->Trace(receiver_set_); } + + HeapMojoReceiverSet<sample::blink::Service, Mode>& receiver_set() { + return receiver_set_; + } + + private: + HeapMojoReceiverSet<sample::blink::Service, Mode> receiver_set_; +}; + +template <HeapMojoWrapperMode Mode> +class HeapMojoReceiverSetGCBaseTest : public TestSupportingGC { + public: + FakeContextNotifier* context() { return context_; } + scoped_refptr<base::NullTaskRunner> task_runner() { + return null_task_runner_; + } + GCOwner<Mode>* owner() { return owner_; } + + void ClearOwner() { owner_ = nullptr; } + + protected: + void SetUp() override { + context_ = MakeGarbageCollected<FakeContextNotifier>(); + owner_ = MakeGarbageCollected<GCOwner<Mode>>(context()); + } + void TearDown() override {} + + Persistent<FakeContextNotifier> context_; + Persistent<GCOwner<Mode>> owner_; + scoped_refptr<base::NullTaskRunner> null_task_runner_ = + base::MakeRefCounted<base::NullTaskRunner>(); +}; + +} // namespace + +class HeapMojoReceiverSetGCWithContextObserverTest + : public HeapMojoReceiverSetGCBaseTest< + HeapMojoWrapperMode::kWithContextObserver> {}; +class HeapMojoReceiverSetGCWithoutContextObserverTest + : public HeapMojoReceiverSetGCBaseTest< + HeapMojoWrapperMode::kWithoutContextObserver> {}; + +// GC the HeapMojoReceiverSet with context observer and verify that the receiver +// is no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoReceiverSetGCWithContextObserverTest, RemovesReceiver) { + auto receiver_set = owner()->receiver_set(); + MockService service; + auto receiver = mojo::PendingReceiver<sample::blink::Service>( + mojo::MessagePipe().handle0); + + mojo::ReceiverId rid = + receiver_set.Add(&service, std::move(receiver), task_runner()); + EXPECT_TRUE(receiver_set.HasReceiver(rid)); + + receiver_set.Remove(rid); + + EXPECT_FALSE(receiver_set.HasReceiver(rid)); +} + +// GC the HeapMojoReceiverSet without context observer and verify that the +// receiver is no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoReceiverSetGCWithoutContextObserverTest, RemovesReceiver) { + auto receiver_set = owner()->receiver_set(); + MockService service; + auto receiver = mojo::PendingReceiver<sample::blink::Service>( + mojo::MessagePipe().handle0); + + mojo::ReceiverId rid = + receiver_set.Add(&service, std::move(receiver), task_runner()); + EXPECT_TRUE(receiver_set.HasReceiver(rid)); + + receiver_set.Remove(rid); + + EXPECT_FALSE(receiver_set.HasReceiver(rid)); +} + +// GC the HeapMojoReceiverSet with context observer and verify that the receiver +// is no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoReceiverSetGCWithContextObserverTest, ClearLeavesSetEmpty) { + auto receiver_set = owner()->receiver_set(); + MockService service; + auto receiver = mojo::PendingReceiver<sample::blink::Service>( + mojo::MessagePipe().handle0); + + mojo::ReceiverId rid = + receiver_set.Add(&service, std::move(receiver), task_runner()); + EXPECT_TRUE(receiver_set.HasReceiver(rid)); + + receiver_set.Clear(); + + EXPECT_FALSE(receiver_set.HasReceiver(rid)); +} + +// GC the HeapMojoReceiverSet without context observer and verify that the +// receiver is no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoReceiverSetGCWithoutContextObserverTest, ClearLeavesSetEmpty) { + auto receiver_set = owner()->receiver_set(); + MockService service; + auto receiver = mojo::PendingReceiver<sample::blink::Service>( + mojo::MessagePipe().handle0); + + mojo::ReceiverId rid = + receiver_set.Add(&service, std::move(receiver), task_runner()); + EXPECT_TRUE(receiver_set.HasReceiver(rid)); + + receiver_set.Clear(); + + EXPECT_FALSE(receiver_set.HasReceiver(rid)); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_test.cc b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_test.cc new file mode 100644 index 00000000000..2f348ab5b4f --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_receiver_test.cc @@ -0,0 +1,176 @@ + +#include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h" +#include "base/test/null_task_runner.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/interfaces/bindings/tests/sample_service.mojom-blink.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/context_lifecycle_notifier.h" +#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/heap_observer_list.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" + +namespace blink { + +namespace { + +class MockContext final : public GarbageCollected<MockContext>, + public ContextLifecycleNotifier { + USING_GARBAGE_COLLECTED_MIXIN(MockContext); + + public: + MockContext() = default; + + void AddContextLifecycleObserver( + ContextLifecycleObserver* observer) override { + observers_.AddObserver(observer); + } + void RemoveContextLifecycleObserver( + ContextLifecycleObserver* observer) override { + observers_.RemoveObserver(observer); + } + + void NotifyContextDestroyed() { + observers_.ForEachObserver([](ContextLifecycleObserver* observer) { + observer->ContextDestroyed(); + }); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(observers_); + ContextLifecycleNotifier::Trace(visitor); + } + + private: + HeapObserverList<ContextLifecycleObserver> observers_; +}; + +template <HeapMojoWrapperMode Mode> +class ReceiverOwner : public GarbageCollected<ReceiverOwner<Mode>>, + public sample::blink::Service { + public: + explicit ReceiverOwner(MockContext* context) : receiver_(this, context) {} + + HeapMojoReceiver<sample::blink::Service, Mode>& receiver() { + return receiver_; + } + + void Trace(Visitor* visitor) { visitor->Trace(receiver_); } + + private: + // sample::blink::Service implementation + void Frobinate(sample::blink::FooPtr foo, + sample::blink::Service::BazOptions options, + mojo::PendingRemote<sample::blink::Port> port, + sample::blink::Service::FrobinateCallback callback) override {} + void GetPort(mojo::PendingReceiver<sample::blink::Port> port) override {} + + HeapMojoReceiver<sample::blink::Service, Mode> receiver_; +}; + +template <HeapMojoWrapperMode Mode> +class HeapMojoReceiverGCBaseTest : public TestSupportingGC { + public: + base::RunLoop& run_loop() { return run_loop_; } + bool& disconnected() { return disconnected_; } + + void ClearOwner() { owner_ = nullptr; } + + protected: + void SetUp() override { + CHECK(!disconnected_); + context_ = MakeGarbageCollected<MockContext>(); + owner_ = MakeGarbageCollected<ReceiverOwner<Mode>>(context_); + scoped_refptr<base::NullTaskRunner> null_task_runner = + base::MakeRefCounted<base::NullTaskRunner>(); + remote_ = mojo::Remote<sample::blink::Service>( + owner_->receiver().BindNewPipeAndPassRemote(null_task_runner)); + remote_.set_disconnect_handler(WTF::Bind( + [](HeapMojoReceiverGCBaseTest* receiver_test) { + receiver_test->run_loop().Quit(); + receiver_test->disconnected() = true; + }, + WTF::Unretained(this))); + } + void TearDown() override { CHECK(disconnected_); } + + Persistent<MockContext> context_; + Persistent<ReceiverOwner<Mode>> owner_; + base::RunLoop run_loop_; + mojo::Remote<sample::blink::Service> remote_; + bool disconnected_ = false; +}; + +template <HeapMojoWrapperMode Mode> +class HeapMojoReceiverDestroyContextBaseTest : public TestSupportingGC { + protected: + void SetUp() override { + context_ = MakeGarbageCollected<MockContext>(); + owner_ = MakeGarbageCollected<ReceiverOwner<Mode>>(context_); + scoped_refptr<base::NullTaskRunner> null_task_runner = + base::MakeRefCounted<base::NullTaskRunner>(); + remote_ = mojo::Remote<sample::blink::Service>( + owner_->receiver().BindNewPipeAndPassRemote(null_task_runner)); + } + + Persistent<MockContext> context_; + Persistent<ReceiverOwner<Mode>> owner_; + mojo::Remote<sample::blink::Service> remote_; +}; + +} // namespace + +class HeapMojoReceiverGCWithContextObserverTest + : public HeapMojoReceiverGCBaseTest< + HeapMojoWrapperMode::kWithContextObserver> {}; +class HeapMojoReceiverGCWithoutContextObserverTest + : public HeapMojoReceiverGCBaseTest< + HeapMojoWrapperMode::kWithoutContextObserver> {}; +class HeapMojoReceiverDestroyContextWithContextObserverTest + : public HeapMojoReceiverDestroyContextBaseTest< + HeapMojoWrapperMode::kWithContextObserver> {}; +class HeapMojoReceiverDestroyContextWithoutContextObserverTest + : public HeapMojoReceiverDestroyContextBaseTest< + HeapMojoWrapperMode::kWithoutContextObserver> {}; + +// Make HeapMojoReceiver with context observer garbage collected and check that +// the connection is disconnected right after the marking phase. +TEST_F(HeapMojoReceiverGCWithContextObserverTest, ResetsOnGC) { + ClearOwner(); + EXPECT_FALSE(disconnected()); + PreciselyCollectGarbage(); + run_loop().Run(); + EXPECT_TRUE(disconnected()); + CompleteSweepingIfNeeded(); +} + +// Make HeapMojoReceiver without context observer garbage collected and check +// that the connection is disconnected right after the marking phase. +TEST_F(HeapMojoReceiverGCWithoutContextObserverTest, ResetsOnGC) { + ClearOwner(); + EXPECT_FALSE(disconnected()); + PreciselyCollectGarbage(); + run_loop().Run(); + EXPECT_TRUE(disconnected()); + CompleteSweepingIfNeeded(); +} + +// Destroy the context with context observer and check that the connection is +// disconnected. +TEST_F(HeapMojoReceiverDestroyContextWithContextObserverTest, + ResetsOnContextDestroyed) { + EXPECT_TRUE(owner_->receiver().is_bound()); + context_->NotifyContextDestroyed(); + EXPECT_FALSE(owner_->receiver().is_bound()); +} + +// Destroy the context without context observer and check that the connection is +// still connected. +TEST_F(HeapMojoReceiverDestroyContextWithoutContextObserverTest, + ResetsOnContextDestroyed) { + EXPECT_TRUE(owner_->receiver().is_bound()); + context_->NotifyContextDestroyed(); + EXPECT_TRUE(owner_->receiver().is_bound()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_remote.h b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_remote.h new file mode 100644 index 00000000000..dd5da3c5d23 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_remote.h @@ -0,0 +1,95 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_REMOTE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_REMOTE_H_ + +#include <utility> + +#include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/renderer/platform/context_lifecycle_observer.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" + +namespace blink { + +// HeapMojoRemote is a wrapper for mojo::Remote to be owned by a +// garbage-collected object. Blink is expected to use HeapMojoRemote by +// default. HeapMojoRemote must be associated with context. +// HeapMojoRemote's constructor takes context as a mandatory parameter. +// HeapMojoRemote resets the mojo connection when 1) the owner object is +// garbage-collected and 2) the associated ExecutionContext is detached. + +// TODO(crbug.com/1058076) HeapMojoWrapperMode should be removed once we ensure +// that the interface is not used after ContextDestroyed(). +template <typename Interface, + HeapMojoWrapperMode Mode = HeapMojoWrapperMode::kWithContextObserver> +class HeapMojoRemote { + DISALLOW_NEW(); + + public: + explicit HeapMojoRemote(ContextLifecycleNotifier* notifier) + : wrapper_(MakeGarbageCollected<Wrapper>(notifier)) {} + + // Methods to redirect to mojo::Remote. + using Proxy = typename Interface::Proxy_; + Proxy* operator->() const { return get(); } + Proxy* get() const { return wrapper_->remote().get(); } + bool is_bound() const { return wrapper_->remote().is_bound(); } + bool is_connected() const { return wrapper_->remote().is_connected(); } + void reset() { wrapper_->remote().reset(); } + void set_disconnect_handler(base::OnceClosure handler) { + wrapper_->remote().set_disconnect_handler(std::move(handler)); + } + mojo::PendingReceiver<Interface> BindNewPipeAndPassReceiver( + scoped_refptr<base::SequencedTaskRunner> task_runner) WARN_UNUSED_RESULT { + DCHECK(task_runner); + return wrapper_->remote().BindNewPipeAndPassReceiver( + std::move(task_runner)); + } + void Bind(mojo::PendingRemote<Interface> pending_remote, + scoped_refptr<base::SequencedTaskRunner> task_runner) { + DCHECK(task_runner); + wrapper_->remote().Bind(std::move(pending_remote), std::move(task_runner)); + } + void FlushForTesting() { return wrapper_->remote().FlushForTesting(); } + + void Trace(Visitor* visitor) { visitor->Trace(wrapper_); } + + private: + // Garbage collected wrapper class to add a prefinalizer. + class Wrapper final : public GarbageCollected<Wrapper>, + public ContextLifecycleObserver { + USING_PRE_FINALIZER(Wrapper, Dispose); + USING_GARBAGE_COLLECTED_MIXIN(Wrapper); + + public: + explicit Wrapper(ContextLifecycleNotifier* notifier) { + SetContextLifecycleNotifier(notifier); + } + + void Trace(Visitor* visitor) override { + ContextLifecycleObserver::Trace(visitor); + } + + void Dispose() { remote_.reset(); } + + mojo::Remote<Interface>& remote() { return remote_; } + + // ContextLifecycleObserver methods + void ContextDestroyed() override { + if (Mode == HeapMojoWrapperMode::kWithContextObserver) + remote_.reset(); + } + + private: + mojo::Remote<Interface> remote_; + }; + + Member<Wrapper> wrapper_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_REMOTE_H_ diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_remote_test.cc b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_remote_test.cc new file mode 100644 index 00000000000..5af34d07ac9 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_remote_test.cc @@ -0,0 +1,181 @@ + +#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" +#include "base/test/null_task_runner.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/interfaces/bindings/tests/sample_service.mojom-blink.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/context_lifecycle_notifier.h" +#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/heap_observer_list.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" + +namespace blink { + +namespace { + +class MockContext final : public GarbageCollected<MockContext>, + public ContextLifecycleNotifier { + USING_GARBAGE_COLLECTED_MIXIN(MockContext); + + public: + MockContext() = default; + + void AddContextLifecycleObserver( + ContextLifecycleObserver* observer) override { + observers_.AddObserver(observer); + } + void RemoveContextLifecycleObserver( + ContextLifecycleObserver* observer) override { + observers_.RemoveObserver(observer); + } + + void NotifyContextDestroyed() { + observers_.ForEachObserver([](ContextLifecycleObserver* observer) { + observer->ContextDestroyed(); + }); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(observers_); + ContextLifecycleNotifier::Trace(visitor); + } + + private: + HeapObserverList<ContextLifecycleObserver> observers_; +}; + +class ServiceImpl : public sample::blink::Service { + public: + explicit ServiceImpl() = default; + + mojo::Receiver<sample::blink::Service>& receiver() { return receiver_; } + + private: + // sample::blink::Service implementation + void Frobinate(sample::blink::FooPtr foo, + sample::blink::Service::BazOptions options, + mojo::PendingRemote<sample::blink::Port> port, + sample::blink::Service::FrobinateCallback callback) override {} + void GetPort(mojo::PendingReceiver<sample::blink::Port> port) override {} + + mojo::Receiver<sample::blink::Service> receiver_{this}; +}; + +template <HeapMojoWrapperMode Mode> +class RemoteOwner : public GarbageCollected<RemoteOwner<Mode>> { + public: + explicit RemoteOwner(MockContext* context) : remote_(context) {} + + HeapMojoRemote<sample::blink::Service, Mode>& remote() { return remote_; } + + void Trace(Visitor* visitor) { visitor->Trace(remote_); } + + HeapMojoRemote<sample::blink::Service, Mode> remote_; +}; + +template <HeapMojoWrapperMode Mode> +class HeapMojoRemoteGCBaseTest : public TestSupportingGC { + public: + base::RunLoop& run_loop() { return run_loop_; } + bool& disconnected() { return disconnected_; } + + void ClearOwner() { owner_ = nullptr; } + + protected: + void SetUp() override { + CHECK(!disconnected_); + context_ = MakeGarbageCollected<MockContext>(); + owner_ = MakeGarbageCollected<RemoteOwner<Mode>>(context_); + scoped_refptr<base::NullTaskRunner> null_task_runner = + base::MakeRefCounted<base::NullTaskRunner>(); + impl_.receiver().Bind( + owner_->remote().BindNewPipeAndPassReceiver(null_task_runner)); + impl_.receiver().set_disconnect_handler(WTF::Bind( + [](HeapMojoRemoteGCBaseTest* remote_test) { + remote_test->run_loop().Quit(); + remote_test->disconnected() = true; + }, + WTF::Unretained(this))); + } + + ServiceImpl impl_; + Persistent<MockContext> context_; + Persistent<RemoteOwner<Mode>> owner_; + base::RunLoop run_loop_; + bool disconnected_ = false; +}; + +template <HeapMojoWrapperMode Mode> +class HeapMojoRemoteDestroyContextBaseTest : public TestSupportingGC { + protected: + void SetUp() override { + context_ = MakeGarbageCollected<MockContext>(); + owner_ = MakeGarbageCollected<RemoteOwner<Mode>>(context_); + scoped_refptr<base::NullTaskRunner> null_task_runner = + base::MakeRefCounted<base::NullTaskRunner>(); + impl_.receiver().Bind( + owner_->remote().BindNewPipeAndPassReceiver(null_task_runner)); + } + + ServiceImpl impl_; + Persistent<MockContext> context_; + Persistent<RemoteOwner<Mode>> owner_; +}; + +} // namespace + +class HeapMojoRemoteGCWithContextObserverTest + : public HeapMojoRemoteGCBaseTest< + HeapMojoWrapperMode::kWithContextObserver> {}; +class HeapMojoRemoteGCWithoutContextObserverTest + : public HeapMojoRemoteGCBaseTest< + HeapMojoWrapperMode::kWithoutContextObserver> {}; +class HeapMojoRemoteDestroyContextWithContextObserverTest + : public HeapMojoRemoteDestroyContextBaseTest< + HeapMojoWrapperMode::kWithContextObserver> {}; +class HeapMojoRemoteDestroyContextWithoutContextObserverTest + : public HeapMojoRemoteDestroyContextBaseTest< + HeapMojoWrapperMode::kWithoutContextObserver> {}; + +// Make HeapMojoRemote with context observer garbage collected and check that +// the connection is disconnected right after the marking phase. +TEST_F(HeapMojoRemoteGCWithContextObserverTest, ResetsOnGC) { + ClearOwner(); + EXPECT_FALSE(disconnected()); + PreciselyCollectGarbage(); + run_loop().Run(); + EXPECT_TRUE(disconnected()); + CompleteSweepingIfNeeded(); +} + +// Make HeapMojoRemote without context observer garbage collected and check that +// the connection is disconnected right after the marking phase. +TEST_F(HeapMojoRemoteGCWithoutContextObserverTest, ResetsOnGC) { + ClearOwner(); + EXPECT_FALSE(disconnected()); + PreciselyCollectGarbage(); + run_loop().Run(); + EXPECT_TRUE(disconnected()); + CompleteSweepingIfNeeded(); +} + +// Destroy the context with context observer and check that the connection is +// disconnected. +TEST_F(HeapMojoRemoteDestroyContextWithContextObserverTest, + ResetsOnContextDestroyed) { + EXPECT_TRUE(owner_->remote().is_bound()); + context_->NotifyContextDestroyed(); + EXPECT_FALSE(owner_->remote().is_bound()); +} + +// Destroy the context without context observer and check that the connection is +// still connected. +TEST_F(HeapMojoRemoteDestroyContextWithoutContextObserverTest, + ResetsOnContextDestroyed) { + EXPECT_TRUE(owner_->remote().is_bound()); + context_->NotifyContextDestroyed(); + EXPECT_TRUE(owner_->remote().is_bound()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_unique_receiver_set.h b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_unique_receiver_set.h new file mode 100644 index 00000000000..8479bf54e25 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_unique_receiver_set.h @@ -0,0 +1,96 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_UNIQUE_RECEIVER_SET_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_UNIQUE_RECEIVER_SET_H_ + +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/unique_receiver_set.h" +#include "third_party/blink/renderer/platform/context_lifecycle_observer.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" + +namespace blink { + +// HeapMojoUniqueReceiverSet is a wrapper for mojo::UniqueReceiverSet to be +// owned by a garbage-collected object. Blink is expected to use +// HeapMojoUniqueReceiverSet by default. HeapMojoUniqueReceiverSet must be +// associated with context. HeapMojoUniqueReceiverSet's constructor takes +// context as a mandatory parameter. HeapMojoUniqueReceiverSet resets the mojo +// connection when 1) the owner object is garbage-collected or 2) the associated +// ExecutionContext is detached. +template <typename Interface, + typename Deleter = std::default_delete<Interface>, + HeapMojoWrapperMode Mode = HeapMojoWrapperMode::kWithContextObserver> +class HeapMojoUniqueReceiverSet { + DISALLOW_NEW(); + + public: + using ImplPointerType = typename mojo::Receiver< + Interface, + mojo::UniquePtrImplRefTraits<Interface, Deleter>>::ImplPointerType; + + explicit HeapMojoUniqueReceiverSet(ContextLifecycleNotifier* context) + : wrapper_(MakeGarbageCollected<Wrapper>(context)) { + DCHECK(context); + } + + // Methods to redirect to mojo::ReceiverSet: + mojo::ReceiverId Add(ImplPointerType impl, + mojo::PendingReceiver<Interface> receiver, + scoped_refptr<base::SequencedTaskRunner> task_runner) { + return wrapper_->receiver_set().Add(std::move(impl), std::move(receiver), + task_runner); + } + + bool Remove(mojo::ReceiverId id) { + return wrapper_->receiver_set().Remove(id); + } + + void Clear() { wrapper_->receiver_set().Clear(); } + + bool HasReceiver(mojo::ReceiverId id) { + return wrapper_->receiver_set().HasReceiver(id); + } + + void Trace(Visitor* visitor) { visitor->Trace(wrapper_); } + + private: + // Garbage collected wrapper class to add a prefinalizer. + class Wrapper final : public GarbageCollected<Wrapper>, + public ContextLifecycleObserver { + USING_PRE_FINALIZER(Wrapper, Dispose); + USING_GARBAGE_COLLECTED_MIXIN(Wrapper); + + public: + explicit Wrapper(ContextLifecycleNotifier* notifier) { + SetContextLifecycleNotifier(notifier); + } + + void Trace(Visitor* visitor) override { + ContextLifecycleObserver::Trace(visitor); + } + + void Dispose() { receiver_set_.Clear(); } + + mojo::UniqueReceiverSet<Interface, void, Deleter>& receiver_set() { + return receiver_set_; + } + + // ContextLifecycleObserver methods + void ContextDestroyed() override { + if (Mode == HeapMojoWrapperMode::kWithContextObserver) + receiver_set_.Clear(); + } + + private: + mojo::UniqueReceiverSet<Interface, void, Deleter> receiver_set_; + }; + + Member<Wrapper> wrapper_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_UNIQUE_RECEIVER_SET_H_ diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_unique_receiver_set_test.cc b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_unique_receiver_set_test.cc new file mode 100644 index 00000000000..5438160d598 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_unique_receiver_set_test.cc @@ -0,0 +1,214 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/mojo/heap_mojo_unique_receiver_set.h" +#include "base/test/null_task_runner.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/interfaces/bindings/tests/sample_service.mojom-blink.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/context_lifecycle_notifier.h" +#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/heap_observer_list.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" + +namespace blink { + +namespace { + +class FakeContextNotifier final : public GarbageCollected<FakeContextNotifier>, + public ContextLifecycleNotifier { + USING_GARBAGE_COLLECTED_MIXIN(FakeContextNotifier); + + public: + FakeContextNotifier() = default; + + void AddContextLifecycleObserver( + ContextLifecycleObserver* observer) override { + observers_.AddObserver(observer); + } + void RemoveContextLifecycleObserver( + ContextLifecycleObserver* observer) override { + observers_.RemoveObserver(observer); + } + + void NotifyContextDestroyed() { + observers_.ForEachObserver([](ContextLifecycleObserver* observer) { + observer->ContextDestroyed(); + }); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(observers_); + ContextLifecycleNotifier::Trace(visitor); + } + + private: + HeapObserverList<ContextLifecycleObserver> observers_; +}; + +template <HeapMojoWrapperMode Mode> +class GCOwner : public GarbageCollected<GCOwner<Mode>> { + public: + explicit GCOwner(FakeContextNotifier* context) : receiver_set_(context) {} + void Trace(Visitor* visitor) { visitor->Trace(receiver_set_); } + + HeapMojoUniqueReceiverSet<sample::blink::Service, + std::default_delete<sample::blink::Service>, + Mode>& + receiver_set() { + return receiver_set_; + } + + private: + HeapMojoUniqueReceiverSet<sample::blink::Service, + std::default_delete<sample::blink::Service>, + Mode> + receiver_set_; +}; + +template <HeapMojoWrapperMode Mode> +class HeapMojoUniqueReceiverSetBaseTest : public TestSupportingGC { + public: + FakeContextNotifier* context() { return context_; } + scoped_refptr<base::NullTaskRunner> task_runner() { + return null_task_runner_; + } + GCOwner<Mode>* owner() { return owner_; } + + void ClearOwner() { owner_ = nullptr; } + + void MarkServiceDeleted() { service_deleted_ = true; } + + protected: + void SetUp() override { + context_ = MakeGarbageCollected<FakeContextNotifier>(); + owner_ = MakeGarbageCollected<GCOwner<Mode>>(context()); + } + void TearDown() override {} + + Persistent<FakeContextNotifier> context_; + Persistent<GCOwner<Mode>> owner_; + scoped_refptr<base::NullTaskRunner> null_task_runner_ = + base::MakeRefCounted<base::NullTaskRunner>(); + bool service_deleted_ = false; +}; + +class HeapMojoUniqueReceiverSetWithContextObserverTest + : public HeapMojoUniqueReceiverSetBaseTest< + HeapMojoWrapperMode::kWithContextObserver> {}; +class HeapMojoUniqueReceiverSetWithoutContextObserverTest + : public HeapMojoUniqueReceiverSetBaseTest< + HeapMojoWrapperMode::kWithoutContextObserver> {}; + +} // namespace + +namespace { + +template <typename T> +class MockService : public sample::blink::Service { + public: + explicit MockService(T* test) : test_(test) {} + // Notify the test when the service is deleted by the UniqueReceiverSet. + ~MockService() override { test_->MarkServiceDeleted(); } + + void Frobinate(sample::blink::FooPtr foo, + Service::BazOptions baz, + mojo::PendingRemote<sample::blink::Port> port, + FrobinateCallback callback) override {} + void GetPort(mojo::PendingReceiver<sample::blink::Port> receiver) override {} + + private: + T* test_; +}; + +} // namespace + +// GC the HeapMojoUniqueReceiverSet with context observer and verify that the +// receiver is no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoUniqueReceiverSetWithContextObserverTest, ResetsOnGC) { + auto receiver_set = owner()->receiver_set(); + auto service = std::make_unique< + MockService<HeapMojoUniqueReceiverSetWithContextObserverTest>>(this); + auto receiver = mojo::PendingReceiver<sample::blink::Service>( + mojo::MessagePipe().handle0); + + mojo::ReceiverId rid = + receiver_set.Add(std::move(service), std::move(receiver), task_runner()); + EXPECT_TRUE(receiver_set.HasReceiver(rid)); + EXPECT_FALSE(service_deleted_); + + ClearOwner(); + PreciselyCollectGarbage(); + + EXPECT_TRUE(service_deleted_); + + CompleteSweepingIfNeeded(); +} + +// GC the HeapMojoUniqueReceiverSet without context observer and verify that the +// receiver is no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoUniqueReceiverSetWithoutContextObserverTest, ResetsOnGC) { + auto receiver_set = owner()->receiver_set(); + auto service = std::make_unique< + MockService<HeapMojoUniqueReceiverSetWithoutContextObserverTest>>(this); + auto receiver = mojo::PendingReceiver<sample::blink::Service>( + mojo::MessagePipe().handle0); + + mojo::ReceiverId rid = + receiver_set.Add(std::move(service), std::move(receiver), task_runner()); + EXPECT_TRUE(receiver_set.HasReceiver(rid)); + EXPECT_FALSE(service_deleted_); + + ClearOwner(); + PreciselyCollectGarbage(); + + EXPECT_TRUE(service_deleted_); + + CompleteSweepingIfNeeded(); +} + +// Destroy the context with context observer and verify that the receiver is no +// longer part of the set, and that the service was deleted. +TEST_F(HeapMojoUniqueReceiverSetWithContextObserverTest, + ResetsOnContextDestroyed) { + HeapMojoUniqueReceiverSet<sample::blink::Service> receiver_set(context()); + auto service = std::make_unique< + MockService<HeapMojoUniqueReceiverSetWithContextObserverTest>>(this); + auto receiver = mojo::PendingReceiver<sample::blink::Service>( + mojo::MessagePipe().handle0); + + mojo::ReceiverId rid = + receiver_set.Add(std::move(service), std::move(receiver), task_runner()); + EXPECT_TRUE(receiver_set.HasReceiver(rid)); + EXPECT_FALSE(service_deleted_); + + context_->NotifyContextDestroyed(); + + EXPECT_FALSE(receiver_set.HasReceiver(rid)); + EXPECT_TRUE(service_deleted_); +} + +// Destroy the context without context observer and verify that the receiver is +// no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoUniqueReceiverSetWithoutContextObserverTest, + ResetsOnContextDestroyed) { + HeapMojoUniqueReceiverSet<sample::blink::Service> receiver_set(context()); + auto service = std::make_unique< + MockService<HeapMojoUniqueReceiverSetWithoutContextObserverTest>>(this); + auto receiver = mojo::PendingReceiver<sample::blink::Service>( + mojo::MessagePipe().handle0); + + mojo::ReceiverId rid = + receiver_set.Add(std::move(service), std::move(receiver), task_runner()); + EXPECT_TRUE(receiver_set.HasReceiver(rid)); + EXPECT_FALSE(service_deleted_); + + context_->NotifyContextDestroyed(); + + EXPECT_FALSE(receiver_set.HasReceiver(rid)); + EXPECT_TRUE(service_deleted_); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h new file mode 100644 index 00000000000..f59f0e0e8dc --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h @@ -0,0 +1,25 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_WRAPPER_MODE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_WRAPPER_MODE_H_ + +namespace blink { + +// A list of modes for HeapMojo wrappers. +// TODO(crbug.com/1058076) This is just a temporary thing to keep the existing +// behavior during the release freeze. +enum class HeapMojoWrapperMode { + // Resets the mojo connection when 1) the owner object is garbage-collected + // and 2) the associated ExecutionContext is detached. + kWithContextObserver, + // Resets the mojo connection when the owner object is garbage-collected. + // But, it will not reset the mojo connection when the associated + // ExecutionContext is detached. + kWithoutContextObserver, +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_WRAPPER_MODE_H_ diff --git a/chromium/third_party/blink/renderer/platform/mojo/kurl.typemap b/chromium/third_party/blink/renderer/platform/mojo/kurl.typemap deleted file mode 100644 index 8a25d18986b..00000000000 --- a/chromium/third_party/blink/renderer/platform/mojo/kurl.typemap +++ /dev/null @@ -1,18 +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. - -mojom = "//url/mojom/url.mojom" -public_headers = [ - "//third_party/blink/renderer/platform/weborigin/kurl.h", - "//third_party/blink/renderer/platform/weborigin/kurl_hash.h", -] -traits_headers = - [ "//third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h" ] - -# Note: consumers of this typemap must themselves depend on platform. -deps = [ - "//mojo/public/cpp/bindings", - "//url", -] -type_mappings = [ "url.mojom.Url=::blink::KURL[force_serialize]" ] diff --git a/chromium/third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h b/chromium/third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h index bcd6a69e8df..f50dd8444e6 100644 --- a/chromium/third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h +++ b/chromium/third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h @@ -5,15 +5,16 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_KURL_MOJOM_TRAITS_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_KURL_MOJOM_TRAITS_H_ +#include "mojo/public/cpp/bindings/string_traits_wtf.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" -#include "url/mojom/url.mojom-blink-forward.h" +#include "url/mojom/url.mojom-shared.h" #include "url/url_constants.h" namespace mojo { template <> -struct StructTraits<url::mojom::blink::Url::DataView, ::blink::KURL> { +struct StructTraits<url::mojom::UrlDataView, ::blink::KURL> { static WTF::String url(const ::blink::KURL& blinkUrl) { if (!blinkUrl.IsValid() || blinkUrl.GetString().length() > url::kMaxURLChars) { @@ -22,7 +23,7 @@ struct StructTraits<url::mojom::blink::Url::DataView, ::blink::KURL> { return blinkUrl.GetString(); } - static bool Read(url::mojom::blink::Url::DataView data, ::blink::KURL* out) { + static bool Read(url::mojom::UrlDataView data, ::blink::KURL* out) { WTF::String urlString; if (!data.ReadUrl(&urlString)) return false; diff --git a/chromium/third_party/blink/renderer/platform/mojo/security_origin.typemap b/chromium/third_party/blink/renderer/platform/mojo/security_origin.typemap deleted file mode 100644 index f94a2bb7732..00000000000 --- a/chromium/third_party/blink/renderer/platform/mojo/security_origin.typemap +++ /dev/null @@ -1,17 +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. - -mojom = "//url/mojom/origin.mojom" -public_headers = - [ "//third_party/blink/renderer/platform/weborigin/security_origin.h" ] -traits_headers = [ - "//third_party/blink/renderer/platform/mojo/security_origin_mojom_traits.h", -] - -# Note: consumers of this typemap must themselves depend on platform. -deps = [ - "//mojo/public/cpp/bindings", - "//url", -] -type_mappings = [ "url.mojom.Origin=::scoped_refptr<const ::blink::SecurityOrigin>[nullable_is_same_type]" ] diff --git a/chromium/third_party/blink/renderer/platform/mojo/security_origin_mojom_traits.h b/chromium/third_party/blink/renderer/platform/mojo/security_origin_mojom_traits.h index 386bc631ecd..925965d09b5 100644 --- a/chromium/third_party/blink/renderer/platform/mojo/security_origin_mojom_traits.h +++ b/chromium/third_party/blink/renderer/platform/mojo/security_origin_mojom_traits.h @@ -5,10 +5,12 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_SECURITY_ORIGIN_MOJOM_TRAITS_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_SECURITY_ORIGIN_MOJOM_TRAITS_H_ +#include "mojo/public/cpp/base/unguessable_token_mojom_traits.h" +#include "mojo/public/cpp/bindings/string_traits_wtf.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" -#include "url/mojom/origin.mojom-blink-forward.h" +#include "url/mojom/origin.mojom-shared.h" #include "url/scheme_host_port.h" namespace mojo { @@ -22,7 +24,7 @@ struct UrlOriginAdapter { const url::SchemeHostPort& tuple, const base::Optional<base::UnguessableToken>& nonce_if_opaque) { scoped_refptr<blink::SecurityOrigin> tuple_origin; - if (!tuple.IsInvalid()) { + if (tuple.IsValid()) { // url::SchemeHostPort is percent encoded and SecurityOrigin is percent // decoded. String host = blink::DecodeURLEscapeSequences( @@ -45,7 +47,7 @@ struct UrlOriginAdapter { }; template <> -struct StructTraits<url::mojom::blink::Origin::DataView, +struct StructTraits<url::mojom::OriginDataView, scoped_refptr<const ::blink::SecurityOrigin>> { static WTF::String scheme( const scoped_refptr<const ::blink::SecurityOrigin>& origin) { @@ -65,7 +67,7 @@ struct StructTraits<url::mojom::blink::Origin::DataView, const scoped_refptr<const ::blink::SecurityOrigin>& origin) { return UrlOriginAdapter::nonce_if_opaque(origin); } - static bool Read(url::mojom::blink::Origin::DataView data, + static bool Read(url::mojom::OriginDataView data, scoped_refptr<const ::blink::SecurityOrigin>* out) { // This implementation is very close to // SecurityOrigin::CreateFromUrlOrigin, so keep in sync if modifications @@ -79,7 +81,7 @@ struct StructTraits<url::mojom::blink::Origin::DataView, const url::SchemeHostPort& tuple = url::SchemeHostPort(scheme, host, data.port()); - if (tuple.IsInvalid()) { + if (!tuple.IsValid()) { // If the tuple is invalid, it is a valid case if and only if it is an // opaque origin and the scheme, host, and port are empty. if (!nonce_if_opaque) diff --git a/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits_test.cc b/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits_test.cc index 07be8469c2d..9efcde17184 100644 --- a/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits_test.cc +++ b/chromium/third_party/blink/renderer/platform/mojo/string16_mojom_traits_test.cc @@ -7,7 +7,6 @@ #include "base/macros.h" #include "base/rand_util.h" #include "mojo/public/cpp/base/big_buffer_mojom_traits.h" -#include "mojo/public/cpp/bindings/binding_set.h" #include "mojo/public/cpp/test_support/test_utils.h" #include "mojo/public/mojom/base/string16.mojom-blink.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/third_party/blink/renderer/platform/network/BUILD.gn b/chromium/third_party/blink/renderer/platform/network/BUILD.gn index 2bc375f8c61..983c4aba408 100644 --- a/chromium/third_party/blink/renderer/platform/network/BUILD.gn +++ b/chromium/third_party/blink/renderer/platform/network/BUILD.gn @@ -18,9 +18,7 @@ group("make_generated") { "//third_party/blink/renderer/platform:*", ] - public_deps = [ - ":http_names", - ] + public_deps = [ ":http_names" ] } blink_platform_sources("network") { @@ -41,6 +39,7 @@ blink_platform_sources("network") { "header_field_tokenizer.h", "http_header_map.cc", "http_header_map.h", + "http_header_set.h", "http_parsers.cc", "http_parsers.h", "http_request_headers_mojom_traits.cc", @@ -69,9 +68,7 @@ blink_platform_sources("network") { sources += get_target_outputs(":http_names") - deps = [ - "//media", - ] + deps = [ "//media" ] } jumbo_source_set("unit_tests") { @@ -91,21 +88,15 @@ jumbo_source_set("unit_tests") { configs += [ "//third_party/blink/renderer/platform:blink_platform_config" ] - deps = [ - "//testing/gtest", - ] - public_deps = [ - "//third_party/blink/renderer/platform:platform", - ] + deps = [ "//testing/gtest" ] + public_deps = [ "//third_party/blink/renderer/platform:platform" ] } jumbo_source_set("test_support") { visibility = [ "//third_party/blink/renderer/platform:test_support" ] testonly = true - sources = [ - "mime/mock_mime_registry.h", - ] + sources = [ "mime/mock_mime_registry.h" ] configs += [ "//third_party/blink/renderer:non_test_config", diff --git a/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.cc b/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.cc index 3d106e98802..212c2b97594 100644 --- a/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.cc +++ b/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.cc @@ -5,7 +5,6 @@ #include "third_party/blink/renderer/platform/network/content_security_policy_parsers.h" #include "services/network/public/mojom/content_security_policy.mojom-blink.h" -#include "third_party/blink/public/platform/web_content_security_policy.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h" #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h" @@ -58,16 +57,4 @@ bool IsMediaTypeCharacter(UChar c) { return !IsASCIISpace(c) && c != '/'; } -STATIC_ASSERT_ENUM(network::mojom::ContentSecurityPolicyType::kReport, - kContentSecurityPolicyHeaderTypeReport); -STATIC_ASSERT_ENUM(network::mojom::ContentSecurityPolicyType::kEnforce, - kContentSecurityPolicyHeaderTypeEnforce); - -STATIC_ASSERT_ENUM(network::mojom::ContentSecurityPolicySource::kHTTP, - kContentSecurityPolicyHeaderSourceHTTP); -STATIC_ASSERT_ENUM(network::mojom::ContentSecurityPolicySource::kMeta, - kContentSecurityPolicyHeaderSourceMeta); -STATIC_ASSERT_ENUM(network::mojom::ContentSecurityPolicySource::kOriginPolicy, - kContentSecurityPolicyHeaderSourceOriginPolicy); - } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.h b/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.h index e352fa904e3..4165e180916 100644 --- a/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.h +++ b/chromium/third_party/blink/renderer/platform/network/content_security_policy_parsers.h @@ -13,17 +13,6 @@ namespace blink { typedef std::pair<unsigned, DigestValue> CSPHashValue; -enum ContentSecurityPolicyHeaderType { - kContentSecurityPolicyHeaderTypeReport, - kContentSecurityPolicyHeaderTypeEnforce -}; - -enum ContentSecurityPolicyHeaderSource { - kContentSecurityPolicyHeaderSourceHTTP, - kContentSecurityPolicyHeaderSourceMeta, - kContentSecurityPolicyHeaderSourceOriginPolicy -}; - enum ContentSecurityPolicyHashAlgorithm { kContentSecurityPolicyHashAlgorithmNone = 0, kContentSecurityPolicyHashAlgorithmSha256 = 1 << 2, diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data.cc b/chromium/third_party/blink/renderer/platform/network/encoded_form_data.cc index 03a6c6d3aac..eb21bafdb1f 100644 --- a/chromium/third_party/blink/renderer/platform/network/encoded_form_data.cc +++ b/chromium/third_party/blink/renderer/platform/network/encoded_form_data.cc @@ -175,9 +175,11 @@ void EncodedFormData::AppendData(const void* data, wtf_size_t size) { memcpy(e.data_.data() + old_size, data, size); } -void EncodedFormData::AppendFile(const String& filename) { - elements_.push_back( - FormDataElement(filename, 0, BlobData::kToEndOfFile, base::nullopt)); +void EncodedFormData::AppendFile( + const String& filename, + const base::Optional<base::Time>& expected_modification_time) { + elements_.push_back(FormDataElement(filename, 0, BlobData::kToEndOfFile, + expected_modification_time)); } void EncodedFormData::AppendFileRange( diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data.h b/chromium/third_party/blink/renderer/platform/network/encoded_form_data.h index a3de079f0ce..cbb1eed257e 100644 --- a/chromium/third_party/blink/renderer/platform/network/encoded_form_data.h +++ b/chromium/third_party/blink/renderer/platform/network/encoded_form_data.h @@ -110,7 +110,8 @@ class PLATFORM_EXPORT EncodedFormData : public RefCounted<EncodedFormData> { ~EncodedFormData(); void AppendData(const void* data, wtf_size_t); - void AppendFile(const String& file_path); + void AppendFile(const String& file_path, + const base::Optional<base::Time>& expected_modification_time); void AppendFileRange( const String& filename, int64_t start, diff --git a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc b/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc index 4807a6de7a7..d95ad2f00f0 100644 --- a/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc +++ b/chromium/third_party/blink/renderer/platform/network/encoded_form_data_element_mojom_traits.cc @@ -18,7 +18,6 @@ #include "third_party/blink/public/mojom/blob/blob.mojom-blink.h" #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h" #include "third_party/blink/public/platform/file_path_conversion.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h" diff --git a/chromium/third_party/blink/renderer/platform/network/http_header_set.h b/chromium/third_party/blink/renderer/platform/network/http_header_set.h new file mode 100644 index 00000000000..80a501f9dad --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/network/http_header_set.h @@ -0,0 +1,24 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_HEADER_SET_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_HEADER_SET_H_ + +#include <set> +#include <string> +#include "base/strings/string_util.h" + +namespace blink { + +struct CompareIgnoreCase { + bool operator()(const std::string& left, const std::string& right) const { + return base::CompareCaseInsensitiveASCII(left, right) < 0; + } +}; + +using HTTPHeaderSet = std::set<std::string, CompareIgnoreCase>; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_HEADER_SET_H_ diff --git a/chromium/third_party/blink/renderer/platform/network/http_names.json5 b/chromium/third_party/blink/renderer/platform/network/http_names.json5 index 248c5e8a11c..fa2fda6cc0d 100644 --- a/chromium/third_party/blink/renderer/platform/network/http_names.json5 +++ b/chromium/third_party/blink/renderer/platform/network/http_names.json5 @@ -31,6 +31,8 @@ "Content-Security-Policy", "Content-Security-Policy-Report-Only", "Content-Type", + "Document-Policy", + "Document-Policy-Report-Only", "ETag", "Expires", "Date", @@ -51,11 +53,11 @@ "Ping-To", "Pragma", "Range", - // TODO(domfarolino): Remove "Referer" as part of https://crbug.com/850813. "Referer", "Referrer-Policy", "Refresh", "Resource-Freshness", + "Require-Document-Policy", "Save-Data", "Sec-CH-Lang", "Sec-Required-CSP", diff --git a/chromium/third_party/blink/renderer/platform/network/http_parsers.cc b/chromium/third_party/blink/renderer/platform/network/http_parsers.cc index 9c376f04607..49218661ef4 100644 --- a/chromium/third_party/blink/renderer/platform/network/http_parsers.cc +++ b/chromium/third_party/blink/renderer/platform/network/http_parsers.cc @@ -409,17 +409,17 @@ CacheControlHeader ParseCacheControlDirectives( for (wtf_size_t i = 0; i < directives_size; ++i) { // RFC2616 14.9.1: A no-cache directive with a value is only meaningful // for proxy caches. It should be ignored by a browser level cache. - if (DeprecatedEqualIgnoringCase(directives[i].first, kNoCacheDirective) && + if (EqualIgnoringASCIICase(directives[i].first, kNoCacheDirective) && directives[i].second.IsEmpty()) { cache_control_header.contains_no_cache = true; - } else if (DeprecatedEqualIgnoringCase(directives[i].first, - kNoStoreDirective)) { + } else if (EqualIgnoringASCIICase(directives[i].first, + kNoStoreDirective)) { cache_control_header.contains_no_store = true; - } else if (DeprecatedEqualIgnoringCase(directives[i].first, - kMustRevalidateDirective)) { + } else if (EqualIgnoringASCIICase(directives[i].first, + kMustRevalidateDirective)) { cache_control_header.contains_must_revalidate = true; - } else if (DeprecatedEqualIgnoringCase(directives[i].first, - kMaxAgeDirective)) { + } else if (EqualIgnoringASCIICase(directives[i].first, + kMaxAgeDirective)) { if (cache_control_header.max_age) { // First max-age directive wins if there are multiple ones. continue; @@ -428,8 +428,8 @@ CacheControlHeader ParseCacheControlDirectives( double max_age = directives[i].second.ToDouble(&ok); if (ok) cache_control_header.max_age = base::TimeDelta::FromSecondsD(max_age); - } else if (DeprecatedEqualIgnoringCase(directives[i].first, - kStaleWhileRevalidateDirective)) { + } else if (EqualIgnoringASCIICase(directives[i].first, + kStaleWhileRevalidateDirective)) { if (cache_control_header.stale_while_revalidate) { // First stale-while-revalidate directive wins if there are multiple // ones. diff --git a/chromium/third_party/blink/renderer/platform/network/mime/content_type.cc b/chromium/third_party/blink/renderer/platform/network/mime/content_type.cc index 95072e109fa..5a3c8b0fa5e 100644 --- a/chromium/third_party/blink/renderer/platform/network/mime/content_type.cc +++ b/chromium/third_party/blink/renderer/platform/network/mime/content_type.cc @@ -32,35 +32,29 @@ namespace blink { ContentType::ContentType(const String& content_type) : type_(content_type) {} +static bool IsASCIIQuote(UChar c) { + return c == '"'; +} + String ContentType::Parameter(const String& parameter_name) const { - String parameter_value; - String stripped_type = type_.StripWhiteSpace(); + Vector<String> parameters; + ParseParameters(parameters); - // a MIME type can have one or more "param=value" after a semi-colon, and - // separated from each other by semi-colons - wtf_size_t semi = stripped_type.find(';'); - if (semi != kNotFound) { - wtf_size_t start = - stripped_type.FindIgnoringASCIICase(parameter_name, semi + 1); - if (start != kNotFound) { - start = stripped_type.find('=', start + parameter_name.length()); - if (start != kNotFound) { - wtf_size_t quote = stripped_type.find('\"', start + 1); - wtf_size_t end = stripped_type.find('\"', start + 2); - if (quote != kNotFound && end != kNotFound) { - start = quote; - } else { - end = stripped_type.find(';', start + 1); - if (end == kNotFound) - end = stripped_type.length(); - } - parameter_value = stripped_type.Substring(start + 1, end - (start + 1)) - .StripWhiteSpace(); + for (auto& parameter : parameters) { + String stripped_parameter = parameter.StripWhiteSpace(); + wtf_size_t separator_pos = stripped_parameter.find('='); + if (separator_pos != kNotFound) { + String attribute = + stripped_parameter.Left(separator_pos).StripWhiteSpace(); + if (EqualIgnoringASCIICase(attribute, parameter_name)) { + return stripped_parameter.Substring(separator_pos + 1) + .StripWhiteSpace() + .RemoveCharacters(IsASCIIQuote); } } } - return parameter_value; + return String(); } String ContentType::GetType() const { @@ -74,4 +68,29 @@ String ContentType::GetType() const { return stripped_type; } +void ContentType::ParseParameters(Vector<String>& result) const { + String stripped_type = type_.StripWhiteSpace(); + + unsigned cur_pos = 0; + unsigned end_pos = stripped_type.length(); + unsigned start_pos = 0; + bool is_quote = false; + + while (cur_pos < end_pos) { + if (!is_quote && stripped_type[cur_pos] == ';') { + if (cur_pos != start_pos) { + result.push_back( + stripped_type.Substring(start_pos, cur_pos - start_pos)); + } + start_pos = cur_pos + 1; + } else if (stripped_type[cur_pos] == '"') { + is_quote = !is_quote; + } + cur_pos++; + } + + if (start_pos != end_pos) + result.push_back(stripped_type.Substring(start_pos)); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/network/mime/content_type.h b/chromium/third_party/blink/renderer/platform/network/mime/content_type.h index e1d5bbc6a75..e02b0986537 100644 --- a/chromium/third_party/blink/renderer/platform/network/mime/content_type.h +++ b/chromium/third_party/blink/renderer/platform/network/mime/content_type.h @@ -44,6 +44,8 @@ class PLATFORM_EXPORT ContentType { const String& Raw() const { return type_; } private: + void ParseParameters(Vector<String>& result) const; + String type_; }; diff --git a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc index 9f56c4b2da6..e35ee692fe3 100644 --- a/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc +++ b/chromium/third_party/blink/renderer/platform/network/mime/mime_type_registry.cc @@ -11,9 +11,9 @@ #include "mojo/public/cpp/bindings/remote.h" #include "net/base/mime_util.h" #include "third_party/blink/public/common/mime_util/mime_util.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/mime/mime_registry.mojom-blink.h" #include "third_party/blink/public/platform/file_path_conversion.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -25,7 +25,7 @@ namespace { struct MimeRegistryPtrHolder { public: MimeRegistryPtrHolder() { - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( mime_registry.BindNewPipeAndPassReceiver()); } ~MimeRegistryPtrHolder() = default; @@ -193,7 +193,7 @@ bool MIMETypeRegistry::IsSupportedFontMIMEType(const String& mime_type) { static const unsigned kFontLen = 5; if (!mime_type.StartsWithIgnoringASCIICase("font/")) return false; - String sub_type = mime_type.Substring(kFontLen).DeprecatedLower(); + String sub_type = mime_type.Substring(kFontLen).LowerASCII(); return sub_type == "woff" || sub_type == "woff2" || sub_type == "otf" || sub_type == "ttf" || sub_type == "sfnt"; } diff --git a/chromium/third_party/blink/renderer/platform/p2p/OWNERS b/chromium/third_party/blink/renderer/platform/p2p/OWNERS index 70573c449d9..eabc2b276ea 100644 --- a/chromium/third_party/blink/renderer/platform/p2p/OWNERS +++ b/chromium/third_party/blink/renderer/platform/p2p/OWNERS @@ -1,4 +1,5 @@ +guidou@chromium.org sergeyu@chromium.org -juberti@chromium.org +steveanton@chromium.org # COMPONENT: Blink>WebRTC diff --git a/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc b/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc index c5131f81207..d2407625650 100644 --- a/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc +++ b/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager.cc @@ -6,22 +6,20 @@ #include <utility> -#include "base/bind.h" #include "base/location.h" #include "base/logging.h" #include "base/threading/thread_task_runner_handle.h" #include "media/base/media_permission.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" namespace blink { FilteringNetworkManager::FilteringNetworkManager( rtc::NetworkManager* network_manager, - const GURL& requesting_origin, media::MediaPermission* media_permission, bool allow_mdns_obfuscation) : network_manager_(network_manager), media_permission_(media_permission), - requesting_origin_(requesting_origin), allow_mdns_obfuscation_(allow_mdns_obfuscation) { DETACH_FROM_THREAD(thread_checker_); set_enumeration_permission(ENUMERATION_BLOCKED); @@ -117,12 +115,10 @@ void FilteringNetworkManager::CheckPermission() { // Request for media permission asynchronously. media_permission_->HasPermission( media::MediaPermission::AUDIO_CAPTURE, - base::BindOnce(&FilteringNetworkManager::OnPermissionStatus, - GetWeakPtr())); + WTF::Bind(&FilteringNetworkManager::OnPermissionStatus, GetWeakPtr())); media_permission_->HasPermission( media::MediaPermission::VIDEO_CAPTURE, - base::BindOnce(&FilteringNetworkManager::OnPermissionStatus, - GetWeakPtr())); + WTF::Bind(&FilteringNetworkManager::OnPermissionStatus, GetWeakPtr())); } void FilteringNetworkManager::OnPermissionStatus(bool granted) { @@ -210,9 +206,8 @@ void FilteringNetworkManager::FireEventIfStarted() { // // TODO(crbug.com/787254): Use Frame-based TaskRunner here. base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&FilteringNetworkManager::SendNetworksChangedSignal, - GetWeakPtr())); + FROM_HERE, WTF::Bind(&FilteringNetworkManager::SendNetworksChangedSignal, + GetWeakPtr())); sent_first_update_ = true; } diff --git a/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager.h b/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager.h index 1bc21566bab..8243e2f0046 100644 --- a/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager.h +++ b/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager.h @@ -13,7 +13,6 @@ #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/webrtc/rtc_base/network.h" #include "third_party/webrtc/rtc_base/third_party/sigslot/sigslot.h" -#include "url/gurl.h" namespace media { class MediaPermission; @@ -34,8 +33,6 @@ namespace blink { // rtc::NetworkManagerBase to have the same implementation of // GetAnyAddressNetworks(). We can't mark the whole class PLATFORM_EXPORT // as it requires all super classes to be PLATFORM_EXPORT as well. -// -// TODO(crbug.com/787254): Also, move it away from url/gurl.h. class FilteringNetworkManager : public rtc::NetworkManagerBase, public sigslot::has_slots<> { public: @@ -43,7 +40,6 @@ class FilteringNetworkManager : public rtc::NetworkManagerBase, // worker thread |task_runner|. PLATFORM_EXPORT FilteringNetworkManager( rtc::NetworkManager* network_manager, - const GURL& requesting_origin, media::MediaPermission* media_permission, bool allow_mdns_obfuscation); @@ -116,8 +112,6 @@ class FilteringNetworkManager : public rtc::NetworkManagerBase, // the setup time. base::TimeTicks start_updating_time_; - GURL requesting_origin_; - // When the mDNS obfuscation is allowed, access to the mDNS responder provided // by the base network manager is provided to conceal IPs with mDNS hostnames. bool allow_mdns_obfuscation_ = true; diff --git a/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc b/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc index 6b2da1ad524..177722fa4ae 100644 --- a/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc +++ b/chromium/third_party/blink/renderer/platform/p2p/filtering_network_manager_test.cc @@ -171,7 +171,7 @@ class FilteringNetworkManagerTest : public testing::Test, SetNewNetworkForBaseNetworkManager(); if (multiple_routes_requested) { network_manager_ = std::make_unique<FilteringNetworkManager>( - base_network_manager_.get(), GURL(), media_permission_.get(), + base_network_manager_.get(), media_permission_.get(), allow_mdns_obfuscation_); network_manager_->Initialize(); } else { diff --git a/chromium/third_party/blink/renderer/platform/p2p/host_address_request.cc b/chromium/third_party/blink/renderer/platform/p2p/host_address_request.cc index eca457b7ee7..1e36c27d132 100644 --- a/chromium/third_party/blink/renderer/platform/p2p/host_address_request.cc +++ b/chromium/third_party/blink/renderer/platform/p2p/host_address_request.cc @@ -6,12 +6,12 @@ #include <utility> -#include "base/bind.h" #include "base/feature_list.h" #include "base/location.h" #include "jingle/glue/utils.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/renderer/platform/p2p/socket_dispatcher.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace blink { @@ -19,7 +19,6 @@ namespace blink { P2PAsyncAddressResolver::P2PAsyncAddressResolver( P2PSocketDispatcher* dispatcher) : dispatcher_(dispatcher), state_(STATE_CREATED) { - AddRef(); // Balanced in Destroy(). } P2PAsyncAddressResolver::~P2PAsyncAddressResolver() { @@ -37,8 +36,8 @@ void P2PAsyncAddressResolver::Start(const rtc::SocketAddress& host_name, blink::features::kWebRtcHideLocalIpsWithMdns); dispatcher_->GetP2PSocketManager()->GetHostAddress( String(host_name.hostname().data()), enable_mdns, - base::BindOnce(&P2PAsyncAddressResolver::OnResponse, - base::Unretained(this))); + WTF::Bind(&P2PAsyncAddressResolver::OnResponse, + scoped_refptr<P2PAsyncAddressResolver>(this))); } void P2PAsyncAddressResolver::Cancel() { diff --git a/chromium/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc b/chromium/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc index 1ac806df97a..8a125da49e4 100644 --- a/chromium/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc +++ b/chromium/third_party/blink/renderer/platform/p2p/ipc_network_manager.cc @@ -8,7 +8,6 @@ #include <utility> #include <vector> -#include "base/bind.h" #include "base/location.h" #include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" @@ -19,6 +18,7 @@ #include "net/base/network_change_notifier.h" #include "net/base/network_interfaces.h" #include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/webrtc/rtc_base/socket_address.h" namespace blink { @@ -62,8 +62,8 @@ void IpcNetworkManager::StartUpdating() { if (network_list_received_) { // Post a task to avoid reentrancy. base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&IpcNetworkManager::SendNetworksChangedSignal, - weak_factory_.GetWeakPtr())); + FROM_HERE, WTF::Bind(&IpcNetworkManager::SendNetworksChangedSignal, + weak_factory_.GetWeakPtr())); } else { VLOG(1) << "IpcNetworkManager::StartUpdating called; still waiting for " "network list from browser process."; diff --git a/chromium/third_party/blink/renderer/platform/p2p/ipc_socket_factory.cc b/chromium/third_party/blink/renderer/platform/p2p/ipc_socket_factory.cc index a28777e90c0..57c834e7e52 100644 --- a/chromium/third_party/blink/renderer/platform/p2p/ipc_socket_factory.cc +++ b/chromium/third_party/blink/renderer/platform/p2p/ipc_socket_factory.cc @@ -9,12 +9,10 @@ #include <algorithm> #include <list> -#include "base/bind.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/macros.h" #include "base/metrics/histogram_macros.h" -#include "base/sequence_checker.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_checker.h" #include "base/trace_event/trace_event.h" @@ -25,6 +23,7 @@ #include "third_party/blink/renderer/platform/p2p/socket_client_delegate.h" #include "third_party/blink/renderer/platform/p2p/socket_client_impl.h" #include "third_party/blink/renderer/platform/p2p/socket_dispatcher.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/webrtc/rtc_base/async_packet_socket.h" @@ -226,7 +225,7 @@ class AsyncAddressResolverImpl : public rtc::AsyncResolverInterface { scoped_refptr<P2PAsyncAddressResolver> resolver_; - SEQUENCE_CHECKER(sequence_checker_); + THREAD_CHECKER(thread_checker_); rtc::SocketAddress addr_; // Address to resolve. std::vector<rtc::IPAddress> addresses_; // Resolved addresses. @@ -423,9 +422,8 @@ int IpcPacketSocket::SendTo(const void* data, send_bytes_available_ -= data_size; - const int8_t* data_char = reinterpret_cast<const int8_t*>(data); Vector<int8_t> data_vector; - data_vector.AppendRange(data_char, data_char + data_size); + data_vector.Append(reinterpret_cast<const int8_t*>(data), data_size); uint64_t packet_id = client_->Send(address_chrome, data_vector, options); // Ensure packet_id is not 0. It can't be the case according to @@ -651,27 +649,26 @@ void IpcPacketSocket::OnDataReceived(const net::IPEndPoint& address, AsyncAddressResolverImpl::AsyncAddressResolverImpl( P2PSocketDispatcher* dispatcher) - : resolver_(new P2PAsyncAddressResolver(dispatcher)) {} + : resolver_(base::MakeRefCounted<P2PAsyncAddressResolver>(dispatcher)) {} AsyncAddressResolverImpl::~AsyncAddressResolverImpl() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); } void AsyncAddressResolverImpl::Start(const rtc::SocketAddress& addr) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // Port and hostname must be copied to the resolved address returned from // GetResolvedAddress. addr_ = addr; - resolver_->Start(addr, - base::BindOnce(&AsyncAddressResolverImpl::OnAddressResolved, - base::Unretained(this))); + resolver_->Start(addr, WTF::Bind(&AsyncAddressResolverImpl::OnAddressResolved, + WTF::Unretained(this))); } bool AsyncAddressResolverImpl::GetResolvedAddress( int family, rtc::SocketAddress* addr) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (addresses_.empty()) return false; @@ -687,12 +684,12 @@ bool AsyncAddressResolverImpl::GetResolvedAddress( } int AsyncAddressResolverImpl::GetError() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); return addresses_.empty() ? -1 : 0; } void AsyncAddressResolverImpl::Destroy(bool wait) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); resolver_->Cancel(); // Libjingle doesn't need this object any more and it's not going to delete // it explicitly. @@ -701,7 +698,7 @@ void AsyncAddressResolverImpl::Destroy(bool wait) { void AsyncAddressResolverImpl::OnAddressResolved( const Vector<net::IPAddress>& addresses) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); for (size_t i = 0; i < addresses.size(); ++i) { rtc::SocketAddress socket_address; if (!jingle_glue::IPEndPointToSocketAddress( diff --git a/chromium/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.cc b/chromium/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.cc index ffb14185658..3c725d7bd96 100644 --- a/chromium/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.cc +++ b/chromium/third_party/blink/renderer/platform/p2p/mdns_responder_adapter.cc @@ -6,7 +6,6 @@ #include <string> -#include "base/bind.h" #include "jingle/glue/utils.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "net/base/ip_address.h" @@ -14,6 +13,7 @@ #include "services/network/public/mojom/mdns_responder.mojom-blink.h" #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/webrtc/rtc_base/ip_address.h" @@ -56,14 +56,14 @@ void MdnsResponderAdapter::CreateNameForAddress(const rtc::IPAddress& addr, NameCreatedCallback callback) { shared_remote_client_->CreateNameForAddress( jingle_glue::RtcIPAddressToNetIPAddress(addr), - base::BindOnce(&OnNameCreatedForAddress, callback, addr)); + WTF::Bind(&OnNameCreatedForAddress, callback, addr)); } void MdnsResponderAdapter::RemoveNameForAddress(const rtc::IPAddress& addr, NameRemovedCallback callback) { shared_remote_client_->RemoveNameForAddress( jingle_glue::RtcIPAddressToNetIPAddress(addr), - base::BindOnce(&OnNameRemovedForAddress, callback)); + WTF::Bind(&OnNameRemovedForAddress, callback)); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/p2p/socket_client_impl.cc b/chromium/third_party/blink/renderer/platform/p2p/socket_client_impl.cc index cbf7777bd9d..eb405977fad 100644 --- a/chromium/third_party/blink/renderer/platform/p2p/socket_client_impl.cc +++ b/chromium/third_party/blink/renderer/platform/p2p/socket_client_impl.cc @@ -4,13 +4,13 @@ #include "third_party/blink/renderer/platform/p2p/socket_client_impl.h" -#include "base/bind.h" #include "base/location.h" #include "base/time/time.h" #include "crypto/random.h" #include "services/network/public/cpp/p2p_param_traits.h" #include "third_party/blink/renderer/platform/p2p/socket_client_delegate.h" #include "third_party/blink/renderer/platform/p2p/socket_dispatcher.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" namespace { @@ -60,8 +60,8 @@ void P2PSocketClientImpl::Init( type, local_address, network::P2PPortRange(min_port, max_port), remote_address, receiver_.BindNewPipeAndPassRemote(), socket_.BindNewPipeAndPassReceiver()); - receiver_.set_disconnect_handler(base::BindOnce( - &P2PSocketClientImpl::OnConnectionError, base::Unretained(this))); + receiver_.set_disconnect_handler(WTF::Bind( + &P2PSocketClientImpl::OnConnectionError, WTF::Unretained(this))); } uint64_t P2PSocketClientImpl::Send(const net::IPEndPoint& address, @@ -82,7 +82,7 @@ void P2PSocketClientImpl::SendWithPacketId(const net::IPEndPoint& address, const Vector<int8_t>& data, const rtc::PacketOptions& options, uint64_t packet_id) { - TRACE_EVENT_ASYNC_BEGIN0("p2p", "Send", packet_id); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("p2p", "Send", packet_id); socket_->Send(data, network::P2PPacketInfo(address, options, packet_id), net::MutableNetworkTrafficAnnotationTag(traffic_annotation_)); @@ -143,8 +143,8 @@ void P2PSocketClientImpl::IncomingTcpConnection( new_client->socket_.Bind(std::move(socket)); new_client->receiver_.Bind(std::move(client_receiver)); - new_client->receiver_.set_disconnect_handler(base::BindOnce( - &P2PSocketClientImpl::OnConnectionError, base::Unretained(this))); + new_client->receiver_.set_disconnect_handler(WTF::Bind( + &P2PSocketClientImpl::OnConnectionError, WTF::Unretained(this))); DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (delegate_) { diff --git a/chromium/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc b/chromium/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc index 370e34ed1ac..6c3adda46f7 100644 --- a/chromium/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc +++ b/chromium/third_party/blink/renderer/platform/p2p/socket_dispatcher.cc @@ -4,13 +4,14 @@ #include "third_party/blink/renderer/platform/p2p/socket_dispatcher.h" -#include "base/bind.h" #include "base/memory/scoped_refptr.h" #include "services/network/public/cpp/p2p_param_traits.h" #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/p2p/network_list_observer.h" #include "third_party/blink/renderer/platform/p2p/socket_client_impl.h" +#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" namespace blink { @@ -24,10 +25,10 @@ P2PSocketDispatcher::~P2PSocketDispatcher() {} void P2PSocketDispatcher::AddNetworkListObserver( blink::NetworkListObserver* network_list_observer) { network_list_observers_->AddObserver(network_list_observer); - main_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&P2PSocketDispatcher::RequestNetworkEventsIfNecessary, - this)); + PostCrossThreadTask( + *main_task_runner_.get(), FROM_HERE, + CrossThreadBindOnce(&P2PSocketDispatcher::RequestNetworkEventsIfNecessary, + scoped_refptr<P2PSocketDispatcher>(this))); } void P2PSocketDispatcher::RemoveNetworkListObserver( @@ -47,13 +48,14 @@ P2PSocketDispatcher::GetP2PSocketManager() { mojo::SharedRemote<network::mojom::blink::P2PSocketManager>( std::move(p2p_socket_manager)); p2p_socket_manager_.set_disconnect_handler( - base::BindOnce(&P2PSocketDispatcher::OnConnectionError, - base::Unretained(this)), + WTF::Bind(&P2PSocketDispatcher::OnConnectionError, + WTF::Unretained(this)), main_task_runner_); } - main_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&P2PSocketDispatcher::RequestInterfaceIfNecessary, this)); + PostCrossThreadTask( + *main_task_runner_.get(), FROM_HERE, + CrossThreadBindOnce(&P2PSocketDispatcher::RequestInterfaceIfNecessary, + scoped_refptr<P2PSocketDispatcher>(this))); return p2p_socket_manager_.get(); } diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/DEPS b/chromium/third_party/blink/renderer/platform/peerconnection/DEPS index 705fa3fe2c3..d6fd407fe98 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/DEPS +++ b/chromium/third_party/blink/renderer/platform/peerconnection/DEPS @@ -14,10 +14,12 @@ include_rules = [ "+base/strings/string_split.h", "+base/threading/thread_restrictions.h", "+media/base", + "+media/capture/capture_switches.h", "+media/media_buildflags.h", "+media/video/gpu_video_accelerator_factories.h", "+media/video/video_decode_accelerator.h", "+media/video/h264_parser.h", + "+media/video/supported_video_decoder_config.h", "+media/video/video_encode_accelerator.h", "+third_party/blink/renderer/platform/bindings/script_wrappable.h", "+third_party/blink/renderer/platform/heap", diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h index 6f841c8a270..d88a917ed73 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_answer_options_platform.h @@ -18,7 +18,7 @@ class RTCAnswerOptionsPlatform final bool VoiceActivityDetection() const { return voice_activity_detection_; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: bool voice_activity_detection_; diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h index 86aa4a620e2..dd0ff33cf06 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h @@ -39,7 +39,7 @@ class PLATFORM_EXPORT RtcDtmfSenderHandler final { virtual ~Client() = default; virtual void DidPlayTone(const String& tone) = 0; - void Trace(blink::Visitor* visitor) override {} + void Trace(Visitor* visitor) override {} }; RtcDtmfSenderHandler(scoped_refptr<base::SingleThreadTaskRunner> main_thread, diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.cc new file mode 100644 index 00000000000..18f46d1d094 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.cc @@ -0,0 +1,137 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.h" + +#include <utility> + +#include "base/memory/ptr_util.h" +#include "base/single_thread_task_runner.h" +#include "third_party/blink/renderer/platform/peerconnection/rtc_scoped_refptr_cross_thread_copier.h" +#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#include "third_party/webrtc/api/frame_transformer_interface.h" +#include "third_party/webrtc/rtc_base/ref_counted_object.h" + +namespace blink { + +namespace { + +// This delegate class exists to work around the fact that +// RTCEncodedAudioStreamTransformer cannot derive from rtc::RefCountedObject +// and post tasks referencing itself as an rtc::scoped_refptr. Instead, +// RTCEncodedAudioStreamTransformer creates a delegate using +// rtc::RefCountedObject and posts tasks referencing the delegate, which invokes +// the RTCEncodedAudioStreamTransformer via callbacks. +class RTCEncodedAudioStreamTransformerDelegate + : public webrtc::FrameTransformerInterface { + public: + RTCEncodedAudioStreamTransformerDelegate( + const base::WeakPtr<RTCEncodedAudioStreamTransformer>& transformer, + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) + : transformer_(transformer), + main_task_runner_(std::move(main_task_runner)) { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + } + + // webrtc::FrameTransformerInterface + void RegisterTransformedFrameCallback( + rtc::scoped_refptr<webrtc::TransformedFrameCallback> + send_frame_to_sink_callback) override { + PostCrossThreadTask( + *main_task_runner_, FROM_HERE, + CrossThreadBindOnce( + &RTCEncodedAudioStreamTransformer::RegisterTransformedFrameCallback, + transformer_, std::move(send_frame_to_sink_callback))); + } + + void UnregisterTransformedFrameCallback() override { + PostCrossThreadTask( + *main_task_runner_, FROM_HERE, + CrossThreadBindOnce(&RTCEncodedAudioStreamTransformer:: + UnregisterTransformedFrameCallback, + transformer_)); + } + + void Transform( + std::unique_ptr<webrtc::TransformableFrameInterface> frame) override { + auto audio_frame = base::WrapUnique( + static_cast<webrtc::TransformableFrameInterface*>(frame.release())); + PostCrossThreadTask( + *main_task_runner_, FROM_HERE, + CrossThreadBindOnce(&RTCEncodedAudioStreamTransformer::TransformFrame, + transformer_, std::move(audio_frame))); + } + + private: + base::WeakPtr<RTCEncodedAudioStreamTransformer> transformer_; + const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; +}; + +} // namespace + +RTCEncodedAudioStreamTransformer::RTCEncodedAudioStreamTransformer( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) { + DCHECK(main_task_runner->BelongsToCurrentThread()); + delegate_ = + new rtc::RefCountedObject<RTCEncodedAudioStreamTransformerDelegate>( + weak_factory_.GetWeakPtr(), std::move(main_task_runner)); +} + +void RTCEncodedAudioStreamTransformer::RegisterTransformedFrameCallback( + rtc::scoped_refptr<webrtc::TransformedFrameCallback> callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + send_frame_to_sink_cb_ = callback; +} + +void RTCEncodedAudioStreamTransformer::UnregisterTransformedFrameCallback() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + send_frame_to_sink_cb_ = nullptr; +} + +void RTCEncodedAudioStreamTransformer::TransformFrame( + std::unique_ptr<webrtc::TransformableFrameInterface> frame) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + // If no transformer callback has been set, drop the frame. + if (!transformer_callback_) + return; + + transformer_callback_.Run(std::move(frame)); +} + +void RTCEncodedAudioStreamTransformer::SendFrameToSink( + std::unique_ptr<webrtc::TransformableFrameInterface> frame) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (send_frame_to_sink_cb_) + send_frame_to_sink_cb_->OnTransformedFrame(std::move(frame)); +} + +void RTCEncodedAudioStreamTransformer::SetTransformerCallback( + TransformerCallback callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + transformer_callback_ = std::move(callback); +} + +void RTCEncodedAudioStreamTransformer::ResetTransformerCallback() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + transformer_callback_.Reset(); +} + +bool RTCEncodedAudioStreamTransformer::HasTransformerCallback() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return !transformer_callback_.is_null(); +} + +bool RTCEncodedAudioStreamTransformer::HasTransformedFrameCallback() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return !!send_frame_to_sink_cb_; +} + +rtc::scoped_refptr<webrtc::FrameTransformerInterface> +RTCEncodedAudioStreamTransformer::Delegate() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return delegate_; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.h new file mode 100644 index 00000000000..f1beece05fd --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.h @@ -0,0 +1,84 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ENCODED_AUDIO_STREAM_TRANSFORMER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ENCODED_AUDIO_STREAM_TRANSFORMER_H_ + +#include <stdint.h> + +#include <memory> +#include <vector> + +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/webrtc/api/scoped_refptr.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace webrtc { +class FrameTransformerInterface; +class TransformedFrameCallback; +class TransformableFrameInterface; +} // namespace webrtc + +namespace blink { + +class PLATFORM_EXPORT RTCEncodedAudioStreamTransformer { + public: + using TransformerCallback = base::RepeatingCallback<void( + std::unique_ptr<webrtc::TransformableFrameInterface>)>; + explicit RTCEncodedAudioStreamTransformer( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner); + + // Called by WebRTC to let us know about a callback object to send transformed + // frames to the WebRTC decoder. Runs on an internal WebRTC thread. + // The callback can run on any thread. + void RegisterTransformedFrameCallback( + rtc::scoped_refptr<webrtc::TransformedFrameCallback>); + + // Called by WebRTC to let us know that any reference to the callback object + // reported by RegisterTransformedFrameCallback() should be released since + // the callback is no longer useful and is intended for destruction. + void UnregisterTransformedFrameCallback(); + + // Called by WebRTC to notify of new untransformed frames from the WebRTC + // stack. Runs on an internal WebRTC thread. + void TransformFrame(std::unique_ptr<webrtc::TransformableFrameInterface>); + + // Send a transformed frame to the WebRTC sink. Must run on the main + // thread. + void SendFrameToSink( + std::unique_ptr<webrtc::TransformableFrameInterface> frame); + + // Set a callback to be invoked on every untransformed frame. Must run on the + // main thread. + void SetTransformerCallback(TransformerCallback); + + // Removes the callback + void ResetTransformerCallback(); + + // Returns true if a callback has been set with SetTransformerCallback(), + // false otherwise. Must run on the main thread. + bool HasTransformerCallback() const; + + // Returns true if a webrtc::TransformedFrameCallback is registered. + bool HasTransformedFrameCallback() const; + + rtc::scoped_refptr<webrtc::FrameTransformerInterface> Delegate(); + + private: + THREAD_CHECKER(thread_checker_); + rtc::scoped_refptr<webrtc::FrameTransformerInterface> delegate_; + rtc::scoped_refptr<webrtc::TransformedFrameCallback> send_frame_to_sink_cb_; + TransformerCallback transformer_callback_; + base::WeakPtrFactory<RTCEncodedAudioStreamTransformer> weak_factory_{this}; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ENCODED_AUDIO_STREAM_TRANSFORMER_H_ diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer_test.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer_test.cc new file mode 100644 index 00000000000..bd72fe57397 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer_test.cc @@ -0,0 +1,105 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_audio_stream_transformer.h" + +#include <stdint.h> + +#include <memory> +#include <vector> + +#include "base/memory/scoped_refptr.h" +#include "base/single_thread_task_runner.h" +#include "base/test/task_environment.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" +#include "third_party/blink/renderer/platform/peerconnection/rtc_scoped_refptr_cross_thread_copier.h" +#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" +#include "third_party/webrtc/api/frame_transformer_interface.h" +#include "third_party/webrtc/rtc_base/ref_counted_object.h" + +namespace blink { + +namespace { + +class MockWebRtcTransformedFrameCallback + : public webrtc::TransformedFrameCallback { + public: + MOCK_METHOD1(OnTransformedFrame, + void(std::unique_ptr<webrtc::TransformableFrameInterface>)); +}; + +class MockTransformerCallbackHolder { + public: + MOCK_METHOD1(OnEncodedFrame, + void(std::unique_ptr<webrtc::TransformableFrameInterface>)); +}; + +} // namespace + +class RTCEncodedAudioStreamTransformerTest : public ::testing::Test { + public: + RTCEncodedAudioStreamTransformerTest() + : main_task_runner_( + blink::scheduler::GetSingleThreadTaskRunnerForTesting()), + webrtc_task_runner_(base::ThreadPool::CreateSingleThreadTaskRunner({})), + webrtc_callback_( + new rtc::RefCountedObject<MockWebRtcTransformedFrameCallback>()), + encoded_audio_stream_transformer_(main_task_runner_) {} + + void SetUp() override { + EXPECT_FALSE( + encoded_audio_stream_transformer_.HasTransformedFrameCallback()); + encoded_audio_stream_transformer_.RegisterTransformedFrameCallback( + webrtc_callback_); + EXPECT_TRUE( + encoded_audio_stream_transformer_.HasTransformedFrameCallback()); + } + + void TearDown() override { + encoded_audio_stream_transformer_.UnregisterTransformedFrameCallback(); + EXPECT_FALSE( + encoded_audio_stream_transformer_.HasTransformedFrameCallback()); + } + + protected: + base::test::TaskEnvironment task_environment_; + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; + scoped_refptr<base::SingleThreadTaskRunner> webrtc_task_runner_; + rtc::scoped_refptr<MockWebRtcTransformedFrameCallback> webrtc_callback_; + MockTransformerCallbackHolder mock_transformer_callback_holder_; + RTCEncodedAudioStreamTransformer encoded_audio_stream_transformer_; +}; + +TEST_F(RTCEncodedAudioStreamTransformerTest, + TransformerForwardsFrameToTransformerCallback) { + EXPECT_FALSE(encoded_audio_stream_transformer_.HasTransformerCallback()); + encoded_audio_stream_transformer_.SetTransformerCallback( + WTF::BindRepeating(&MockTransformerCallbackHolder::OnEncodedFrame, + WTF::Unretained(&mock_transformer_callback_holder_))); + EXPECT_TRUE(encoded_audio_stream_transformer_.HasTransformerCallback()); + + EXPECT_CALL(mock_transformer_callback_holder_, OnEncodedFrame); + // Frames are pushed to the RTCEncodedAudioStreamTransformer via its delegate, + // which would normally be registered with a WebRTC sender or receiver. + // In this test, manually send the frame to the transformer on the simulated + // WebRTC thread. + PostCrossThreadTask( + *webrtc_task_runner_, FROM_HERE, + CrossThreadBindOnce(&webrtc::FrameTransformerInterface::Transform, + encoded_audio_stream_transformer_.Delegate(), + nullptr)); + task_environment_.RunUntilIdle(); +} + +TEST_F(RTCEncodedAudioStreamTransformerTest, TransformerForwardsFrameToWebRTC) { + EXPECT_CALL(*webrtc_callback_, OnTransformedFrame); + encoded_audio_stream_transformer_.SendFrameToSink(nullptr); + task_environment_.RunUntilIdle(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.cc new file mode 100644 index 00000000000..ba9c8507154 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.cc @@ -0,0 +1,191 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h" + +#include <utility> + +#include "base/memory/ptr_util.h" +#include "base/single_thread_task_runner.h" +#include "third_party/blink/renderer/platform/peerconnection/rtc_scoped_refptr_cross_thread_copier.h" +#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#include "third_party/webrtc/api/frame_transformer_interface.h" +#include "third_party/webrtc/rtc_base/ref_counted_object.h" + +namespace blink { + +namespace { + +// This delegate class exists to work around the fact that +// RTCEncodedVideoStreamTransformer cannot derive from rtc::RefCountedObject +// and post tasks referencing itself as an rtc::scoped_refptr. Instead, +// RTCEncodedVideoStreamTransformer creates a delegate using +// rtc::RefCountedObject and posts tasks referencing the delegate, which invokes +// the RTCEncodedVideoStreamTransformer via callbacks. +class RTCEncodedVideoStreamTransformerDelegate + : public webrtc::FrameTransformerInterface { + public: + RTCEncodedVideoStreamTransformerDelegate( + const base::WeakPtr<RTCEncodedVideoStreamTransformer>& transformer, + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) + : transformer_(transformer), + main_task_runner_(std::move(main_task_runner)) { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + } + + // webrtc::FrameTransformerInterface + // TODO(crbug.com/1065838): Remove the non-ssrc version of the registration + // and unregistration methods once WebRTC uses the ssrc version in all cases. + void RegisterTransformedFrameCallback( + rtc::scoped_refptr<webrtc::TransformedFrameCallback> + send_frame_to_sink_callback) override { + PostCrossThreadTask( + *main_task_runner_, FROM_HERE, + CrossThreadBindOnce(&RTCEncodedVideoStreamTransformer:: + RegisterTransformedFrameSinkCallback, + transformer_, + std::move(send_frame_to_sink_callback), 0)); + } + + void UnregisterTransformedFrameCallback() override { + PostCrossThreadTask( + *main_task_runner_, FROM_HERE, + CrossThreadBindOnce(&RTCEncodedVideoStreamTransformer:: + UnregisterTransformedFrameSinkCallback, + transformer_, 0)); + } + + void RegisterTransformedFrameSinkCallback( + rtc::scoped_refptr<webrtc::TransformedFrameCallback> + send_frame_to_sink_callback, + uint32_t ssrc) override { + PostCrossThreadTask( + *main_task_runner_, FROM_HERE, + CrossThreadBindOnce(&RTCEncodedVideoStreamTransformer:: + RegisterTransformedFrameSinkCallback, + transformer_, + std::move(send_frame_to_sink_callback), ssrc)); + } + + void UnregisterTransformedFrameSinkCallback(uint32_t ssrc) override { + PostCrossThreadTask( + *main_task_runner_, FROM_HERE, + CrossThreadBindOnce(&RTCEncodedVideoStreamTransformer:: + UnregisterTransformedFrameSinkCallback, + transformer_, ssrc)); + } + + void Transform( + std::unique_ptr<webrtc::TransformableFrameInterface> frame) override { + auto video_frame = + base::WrapUnique(static_cast<webrtc::TransformableVideoFrameInterface*>( + frame.release())); + PostCrossThreadTask( + *main_task_runner_, FROM_HERE, + CrossThreadBindOnce(&RTCEncodedVideoStreamTransformer::TransformFrame, + transformer_, std::move(video_frame))); + } + + private: + base::WeakPtr<RTCEncodedVideoStreamTransformer> transformer_; + const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; +}; + +} // namespace + +RTCEncodedVideoStreamTransformer::RTCEncodedVideoStreamTransformer( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) { + DCHECK(main_task_runner->BelongsToCurrentThread()); + delegate_ = + new rtc::RefCountedObject<RTCEncodedVideoStreamTransformerDelegate>( + weak_factory_.GetWeakPtr(), std::move(main_task_runner)); +} + +void RTCEncodedVideoStreamTransformer::RegisterTransformedFrameSinkCallback( + rtc::scoped_refptr<webrtc::TransformedFrameCallback> callback, + uint32_t ssrc) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + for (auto& sink_callback : send_frame_to_sink_callbacks_) { + if (sink_callback.first == ssrc) { + sink_callback.second = std::move(callback); + return; + } + } + send_frame_to_sink_callbacks_.push_back(std::make_pair(ssrc, callback)); +} + +void RTCEncodedVideoStreamTransformer::UnregisterTransformedFrameSinkCallback( + uint32_t ssrc) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + for (wtf_size_t i = 0; i < send_frame_to_sink_callbacks_.size(); ++i) { + if (send_frame_to_sink_callbacks_[i].first == ssrc) { + send_frame_to_sink_callbacks_.EraseAt(i); + return; + } + } +} + +void RTCEncodedVideoStreamTransformer::TransformFrame( + std::unique_ptr<webrtc::TransformableVideoFrameInterface> frame) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + // If no transformer callback has been set, drop the frame. + if (!transformer_callback_) + return; + + transformer_callback_.Run(std::move(frame)); +} + +void RTCEncodedVideoStreamTransformer::SendFrameToSink( + std::unique_ptr<webrtc::TransformableVideoFrameInterface> frame) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + // TODO(crbug.com/1069275): Remove this section once WebRTC reports ssrc in + // all sink callback registrations. + if (send_frame_to_sink_callbacks_.size() == 1 && + send_frame_to_sink_callbacks_[0].first == 0) { + send_frame_to_sink_callbacks_[0].second->OnTransformedFrame( + std::move(frame)); + return; + } + for (const auto& sink_callback : send_frame_to_sink_callbacks_) { + if (sink_callback.first == frame->GetSsrc()) { + sink_callback.second->OnTransformedFrame(std::move(frame)); + return; + } + } +} + +void RTCEncodedVideoStreamTransformer::SetTransformerCallback( + TransformerCallback callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + transformer_callback_ = std::move(callback); +} + +void RTCEncodedVideoStreamTransformer::ResetTransformerCallback() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + transformer_callback_.Reset(); +} + +bool RTCEncodedVideoStreamTransformer::HasTransformerCallback() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return !transformer_callback_.is_null(); +} + +bool RTCEncodedVideoStreamTransformer::HasTransformedFrameSinkCallback( + uint32_t ssrc) const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + for (const auto& sink_callbacks : send_frame_to_sink_callbacks_) { + if (sink_callbacks.first == ssrc) + return true; + } + return false; +} + +rtc::scoped_refptr<webrtc::FrameTransformerInterface> +RTCEncodedVideoStreamTransformer::Delegate() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return delegate_; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h new file mode 100644 index 00000000000..6da970263a4 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h @@ -0,0 +1,93 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ENCODED_VIDEO_STREAM_TRANSFORMER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ENCODED_VIDEO_STREAM_TRANSFORMER_H_ + +#include <stdint.h> + +#include <memory> +#include <utility> + +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" +#include "third_party/webrtc/api/scoped_refptr.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace webrtc { +class FrameTransformerInterface; +class TransformedFrameCallback; +class TransformableVideoFrameInterface; +} // namespace webrtc + +namespace blink { + +class PLATFORM_EXPORT RTCEncodedVideoStreamTransformer { + public: + using TransformerCallback = base::RepeatingCallback<void( + std::unique_ptr<webrtc::TransformableVideoFrameInterface>)>; + explicit RTCEncodedVideoStreamTransformer( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner); + + // Called by WebRTC to let us know about a callback object to send transformed + // frames to the WebRTC decoder. Runs on an internal WebRTC thread. + // The callback can run on any thread. + void RegisterTransformedFrameSinkCallback( + rtc::scoped_refptr<webrtc::TransformedFrameCallback>, + uint32_t ssrc); + + // Called by WebRTC to let us know that any reference to the callback object + // reported by RegisterTransformedFrameCallback() should be released since + // the callback is no longer useful and is intended for destruction. + // TODO(crbug.com/1065838): Remove the non-ssrc version once WebRTC uses the + // ssrc version in all cases. + // void UnregisterTransformedFrameCallback(); + void UnregisterTransformedFrameSinkCallback(uint32_t ssrc); + + // Called by WebRTC to notify of new untransformed frames from the WebRTC + // stack. Runs on an internal WebRTC thread. + void TransformFrame( + std::unique_ptr<webrtc::TransformableVideoFrameInterface>); + + // Send a transformed frame to the WebRTC sink. Must run on the main + // thread. + void SendFrameToSink( + std::unique_ptr<webrtc::TransformableVideoFrameInterface> frame); + + // Set a callback to be invoked on every untransformed frame. Must run on the + // main thread. + void SetTransformerCallback(TransformerCallback); + + // Removes the callback + void ResetTransformerCallback(); + + // Returns true if a callback has been set with SetTransformerCallback(), + // false otherwise. Must run on the main thread. + bool HasTransformerCallback() const; + + // Returns true if a webrtc::TransformedFrameCallback is registered for + // the given ssrc. + bool HasTransformedFrameSinkCallback(uint32_t ssrc) const; + + rtc::scoped_refptr<webrtc::FrameTransformerInterface> Delegate(); + + private: + THREAD_CHECKER(thread_checker_); + rtc::scoped_refptr<webrtc::FrameTransformerInterface> delegate_; + Vector< + std::pair<uint32_t, rtc::scoped_refptr<webrtc::TransformedFrameCallback>>> + send_frame_to_sink_callbacks_; + TransformerCallback transformer_callback_; + base::WeakPtrFactory<RTCEncodedVideoStreamTransformer> weak_factory_{this}; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ENCODED_VIDEO_STREAM_TRANSFORMER_H_ diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer_test.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer_test.cc new file mode 100644 index 00000000000..01f1af1a521 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer_test.cc @@ -0,0 +1,140 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/peerconnection/rtc_encoded_video_stream_transformer.h" + +#include <stdint.h> + +#include <memory> +#include <vector> + +#include "base/memory/scoped_refptr.h" +#include "base/single_thread_task_runner.h" +#include "base/test/task_environment.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" +#include "third_party/blink/renderer/platform/peerconnection/rtc_scoped_refptr_cross_thread_copier.h" +#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" +#include "third_party/webrtc/api/frame_transformer_interface.h" +#include "third_party/webrtc/rtc_base/ref_counted_object.h" + +namespace blink { + +namespace { + +const uint32_t kSSRC = 1; +const uint32_t kNonexistentSSRC = 0; + +class MockWebRtcTransformedFrameCallback + : public webrtc::TransformedFrameCallback { + public: + MOCK_METHOD1(OnTransformedFrame, + void(std::unique_ptr<webrtc::TransformableFrameInterface>)); +}; + +class MockTransformerCallbackHolder { + public: + MOCK_METHOD1(OnEncodedFrame, + void(std::unique_ptr<webrtc::TransformableVideoFrameInterface>)); +}; + +class FakeVideoFrame : public webrtc::TransformableVideoFrameInterface { + public: + explicit FakeVideoFrame(uint32_t ssrc) : ssrc_(ssrc) {} + + rtc::ArrayView<const uint8_t> GetData() const override { + return rtc::ArrayView<const uint8_t>(); + } + + // Copies |data| into the owned frame payload data. + void SetData(rtc::ArrayView<const uint8_t> data) override {} + uint32_t GetTimestamp() const override { return 0; } + uint32_t GetSsrc() const override { return ssrc_; } + bool IsKeyFrame() const override { return true; } + std::vector<uint8_t> GetAdditionalData() const override { + return std::vector<uint8_t>(); + } + + private: + uint32_t ssrc_; +}; + +std::unique_ptr<webrtc::TransformableVideoFrameInterface> CreateFakeFrame() { + return std::make_unique<FakeVideoFrame>(kSSRC); +} + +} // namespace + +class RTCEncodedVideoStreamTransformerTest : public ::testing::Test { + public: + RTCEncodedVideoStreamTransformerTest() + : main_task_runner_( + blink::scheduler::GetSingleThreadTaskRunnerForTesting()), + webrtc_task_runner_(base::ThreadPool::CreateSingleThreadTaskRunner({})), + webrtc_callback_( + new rtc::RefCountedObject<MockWebRtcTransformedFrameCallback>()), + encoded_video_stream_transformer_(main_task_runner_) {} + + void SetUp() override { + EXPECT_FALSE( + encoded_video_stream_transformer_.HasTransformedFrameSinkCallback( + kSSRC)); + encoded_video_stream_transformer_.RegisterTransformedFrameSinkCallback( + webrtc_callback_, kSSRC); + EXPECT_TRUE( + encoded_video_stream_transformer_.HasTransformedFrameSinkCallback( + kSSRC)); + EXPECT_FALSE( + encoded_video_stream_transformer_.HasTransformedFrameSinkCallback( + kNonexistentSSRC)); + } + + void TearDown() override { + encoded_video_stream_transformer_.UnregisterTransformedFrameSinkCallback( + kSSRC); + EXPECT_FALSE( + encoded_video_stream_transformer_.HasTransformedFrameSinkCallback( + kSSRC)); + } + + protected: + base::test::TaskEnvironment task_environment_; + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; + scoped_refptr<base::SingleThreadTaskRunner> webrtc_task_runner_; + rtc::scoped_refptr<MockWebRtcTransformedFrameCallback> webrtc_callback_; + MockTransformerCallbackHolder mock_transformer_callback_holder_; + RTCEncodedVideoStreamTransformer encoded_video_stream_transformer_; +}; + +TEST_F(RTCEncodedVideoStreamTransformerTest, + TransformerForwardsFrameToTransformerCallback) { + EXPECT_FALSE(encoded_video_stream_transformer_.HasTransformerCallback()); + encoded_video_stream_transformer_.SetTransformerCallback( + WTF::BindRepeating(&MockTransformerCallbackHolder::OnEncodedFrame, + WTF::Unretained(&mock_transformer_callback_holder_))); + EXPECT_TRUE(encoded_video_stream_transformer_.HasTransformerCallback()); + + EXPECT_CALL(mock_transformer_callback_holder_, OnEncodedFrame); + // Frames are pushed to the RTCEncodedVideoStreamTransformer via its delegate, + // which would normally be registered with a WebRTC sender or receiver. + // In this test, manually send the frame to the transformer on the simulated + // WebRTC thread. + PostCrossThreadTask( + *webrtc_task_runner_, FROM_HERE, + CrossThreadBindOnce(&webrtc::FrameTransformerInterface::Transform, + encoded_video_stream_transformer_.Delegate(), + CreateFakeFrame())); + task_environment_.RunUntilIdle(); +} + +TEST_F(RTCEncodedVideoStreamTransformerTest, TransformerForwardsFrameToWebRTC) { + EXPECT_CALL(*webrtc_callback_, OnTransformedFrame); + encoded_video_stream_transformer_.SendFrameToSink(CreateFakeFrame()); + task_environment_.RunUntilIdle(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink.h index be9e3f265a3..8e60db6aae9 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink.h @@ -5,15 +5,16 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_EVENT_LOG_OUTPUT_SINK_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_EVENT_LOG_OUTPUT_SINK_H_ +#include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/platform_export.h" namespace blink { -class PLATFORM_EXPORT RtcEventLogOutputSink { +class PLATFORM_EXPORT RtcEventLogOutputSink : public GarbageCollectedMixin { public: virtual ~RtcEventLogOutputSink() = default; - virtual void OnWebRtcEventLogWrite(const std::string& output) = 0; + virtual void OnWebRtcEventLogWrite(const WTF::Vector<uint8_t>& output) = 0; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink_proxy.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink_proxy.cc index 4a8649ad39c..f4c9b54ff52 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink_proxy.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink_proxy.cc @@ -22,7 +22,10 @@ bool RtcEventLogOutputSinkProxy::IsActive() const { } bool RtcEventLogOutputSinkProxy::Write(const std::string& output) { - sink_->OnWebRtcEventLogWrite(output); + WTF::Vector<uint8_t> converted_output; + converted_output.AppendRange(output.begin(), output.end()); + + sink_->OnWebRtcEventLogWrite(converted_output); return true; } diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink_proxy.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink_proxy.h index 8ecdf912ee2..40b38869bf7 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink_proxy.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_event_log_output_sink_proxy.h @@ -7,6 +7,7 @@ #include <memory> +#include "third_party/blink/renderer/platform/heap/persistent.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/webrtc/api/rtc_event_log_output.h" @@ -29,7 +30,7 @@ class PLATFORM_EXPORT RtcEventLogOutputSinkProxy final bool Write(const std::string& output) override; private: - RtcEventLogOutputSink* const sink_; + CrossThreadWeakPersistent<RtcEventLogOutputSink> sink_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.cc index 74d1d404a04..e9cbb3e94c5 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.cc @@ -4,6 +4,7 @@ #include "third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.h" +#include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/webrtc/api/candidate.h" #include "third_party/webrtc/p2p/base/p2p_constants.h" #include "third_party/webrtc/p2p/base/port.h" @@ -39,27 +40,6 @@ String CandidateTypeToString(const std::string& type) { } // namespace -// static -scoped_refptr<RTCIceCandidatePlatform> RTCIceCandidatePlatform::Create( - String candidate, - String sdp_mid, - base::Optional<uint16_t> sdp_m_line_index, - String username_fragment) { - return base::AdoptRef(new RTCIceCandidatePlatform( - std::move(candidate), std::move(sdp_mid), std::move(sdp_m_line_index), - std::move(username_fragment))); -} - -scoped_refptr<RTCIceCandidatePlatform> RTCIceCandidatePlatform::Create( - String candidate, - String sdp_mid, - int sdp_m_line_index) { - return base::AdoptRef(new RTCIceCandidatePlatform( - std::move(candidate), std::move(sdp_mid), - sdp_m_line_index < 0 ? base::Optional<uint16_t>() - : base::Optional<uint16_t>(sdp_m_line_index))); -} - RTCIceCandidatePlatform::RTCIceCandidatePlatform( String candidate, String sdp_mid, diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.h index 6baacd5ae97..2751f57f056 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_ice_candidate_platform.h @@ -32,29 +32,29 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_ICE_CANDIDATE_PLATFORM_H_ #include "base/optional.h" +#include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" -#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h" namespace blink { class PLATFORM_EXPORT RTCIceCandidatePlatform final - : public WTF::ThreadSafeRefCounted<RTCIceCandidatePlatform> { + : public GarbageCollected<RTCIceCandidatePlatform> { public: // Creates a new RTCIceCandidatePlatform using |candidate|, |sdp_mid| and // |sdp_m_line_index|. If |sdp_m_line_index| is negative, it is // considered as having no value. - static scoped_refptr<RTCIceCandidatePlatform> Create(String candidate, - String sdp_mid, - int sdp_m_line_index); + RTCIceCandidatePlatform(String candidate, + String sdp_mid, + base::Optional<uint16_t> sdp_m_line_index); // Creates a new RTCIceCandidatePlatform using |candidate|, |sdp_mid|, // |sdp_m_line_index|, and |username_fragment|. - static scoped_refptr<RTCIceCandidatePlatform> Create( - String candidate, - String sdp_mid, - base::Optional<uint16_t> sdp_m_line_index, - String username_fragment); + RTCIceCandidatePlatform(String candidate, + String sdp_mid, + base::Optional<uint16_t> sdp_m_line_index, + String username_fragment); + ~RTCIceCandidatePlatform() = default; const String& Candidate() const { return candidate_; } const String& SdpMid() const { return sdp_mid_; } @@ -73,22 +73,11 @@ class PLATFORM_EXPORT RTCIceCandidatePlatform final const base::Optional<uint16_t>& RelatedPort() const { return related_port_; } const String& UsernameFragment() const { return username_fragment_; } - private: - friend class WTF::ThreadSafeRefCounted<RTCIceCandidatePlatform>; - - RTCIceCandidatePlatform(String candidate, - String sdp_mid, - base::Optional<uint16_t> sdp_m_line_index); - - RTCIceCandidatePlatform(String candidate, - String sdp_mid, - base::Optional<uint16_t> sdp_m_line_index, - String username_fragment); + void Trace(Visitor*) {} + private: void PopulateFields(bool use_username_from_candidate); - ~RTCIceCandidatePlatform() = default; - String candidate_; String sdp_mid_; base::Optional<uint16_t> sdp_m_line_index_; diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h index a986b96c3c0..9e8e23a538a 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_offer_options_platform.h @@ -26,7 +26,7 @@ class RTCOfferOptionsPlatform final bool VoiceActivityDetection() const { return voice_activity_detection_; } bool IceRestart() const { return ice_restart_; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: int32_t offer_to_receive_video_; diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.cc new file mode 100644 index 00000000000..2b1e5693839 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.cc @@ -0,0 +1,13 @@ +// 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 "third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.h" + +namespace blink { + +RTCPeerConnectionHandlerClient::~RTCPeerConnectionHandlerClient() = default; + +void RTCPeerConnectionHandlerClient::ClosePeerConnection() {} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.h new file mode 100644 index 00000000000..f7f61d75818 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_client.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_PEER_CONNECTION_HANDLER_CLIENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_PEER_CONNECTION_HANDLER_CLIENT_H_ + +#include <memory> + +#include "base/memory/scoped_refptr.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" +#include "third_party/webrtc/api/peer_connection_interface.h" +#include "third_party/webrtc/api/sctp_transport_interface.h" + +namespace blink { + +class RTCIceCandidatePlatform; +class RTCRtpTransceiverPlatform; +class RTCRtpReceiverPlatform; + +struct PLATFORM_EXPORT WebRTCSctpTransportSnapshot { + rtc::scoped_refptr<webrtc::SctpTransportInterface> transport; + webrtc::SctpTransportInformation sctp_transport_state = + webrtc::SctpTransportInformation(webrtc::SctpTransportState::kNew); + webrtc::DtlsTransportInformation dtls_transport_state = + webrtc::DtlsTransportInformation(webrtc::DtlsTransportState::kNew); +}; + +class PLATFORM_EXPORT RTCPeerConnectionHandlerClient { + public: + virtual ~RTCPeerConnectionHandlerClient(); + + virtual void NegotiationNeeded() = 0; + virtual void DidGenerateICECandidate(RTCIceCandidatePlatform*) = 0; + virtual void DidFailICECandidate(const String& address, + base::Optional<uint16_t> port, + const String& host_candidate, + const String& url, + int error_code, + const String& error_text) = 0; + virtual void DidChangeSignalingState( + webrtc::PeerConnectionInterface::SignalingState) = 0; + virtual void DidChangeIceGatheringState( + webrtc::PeerConnectionInterface::IceGatheringState) = 0; + virtual void DidChangeIceConnectionState( + webrtc::PeerConnectionInterface::IceConnectionState) = 0; + virtual void DidChangePeerConnectionState( + webrtc::PeerConnectionInterface::PeerConnectionState) {} + virtual void DidAddReceiverPlanB(std::unique_ptr<RTCRtpReceiverPlatform>) = 0; + virtual void DidRemoveReceiverPlanB( + std::unique_ptr<RTCRtpReceiverPlatform>) = 0; + virtual void DidModifyTransceivers( + Vector<std::unique_ptr<RTCRtpTransceiverPlatform>>, + Vector<uintptr_t>, + bool is_remote_description) = 0; + virtual void DidModifySctpTransport(WebRTCSctpTransportSnapshot) = 0; + virtual void DidAddRemoteDataChannel( + scoped_refptr<webrtc::DataChannelInterface>) = 0; + virtual void DidNoteInterestingUsage(int usage_pattern) = 0; + virtual void UnregisterPeerConnectionHandler() = 0; + virtual void ClosePeerConnection(); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_PEER_CONNECTION_HANDLER_CLIENT_H_ diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_platform.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_platform.h new file mode 100644 index 00000000000..870ea35c526 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_peer_connection_handler_platform.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_PEER_CONNECTION_HANDLER_PLATFORM_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_PEER_CONNECTION_HANDLER_PLATFORM_H_ + +#include <memory> + +#include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" +#include "third_party/webrtc/api/peer_connection_interface.h" +#include "third_party/webrtc/api/rtc_error.h" +#include "third_party/webrtc/api/rtp_transceiver_interface.h" +#include "third_party/webrtc/api/stats/rtc_stats.h" + +namespace webrtc { +enum class RTCErrorType; +struct DataChannelInit; +} // namespace webrtc + +namespace blink { + +class MediaConstraints; +class RTCAnswerOptionsPlatform; +class RTCIceCandidatePlatform; +class RTCOfferOptionsPlatform; +class RTCRtpSenderPlatform; +class RTCRtpTransceiverPlatform; +class RTCSessionDescriptionPlatform; +class RTCSessionDescriptionRequest; +class RTCStatsRequest; +class RTCVoidRequest; +class WebLocalFrame; +class WebMediaStream; +class WebMediaStreamTrack; + +class PLATFORM_EXPORT RTCPeerConnectionHandlerPlatform { + public: + enum class IceConnectionStateVersion { + // Only applicable in Unified Plan when the JavaScript-exposed + // iceConnectionState is calculated in blink. In this case, kLegacy is used + // to report the webrtc::PeerConnectionInterface implementation which is not + // visible in JavaScript, but still useful to track for debugging purposes. + kLegacy, + // The JavaScript-visible iceConnectionState. In Plan B, this is the same as + // the webrtc::PeerConnectionInterface implementation. + kDefault, + }; + + virtual ~RTCPeerConnectionHandlerPlatform() = default; + + virtual bool Initialize( + const webrtc::PeerConnectionInterface::RTCConfiguration&, + const MediaConstraints&, + WebLocalFrame*) = 0; + + virtual void Stop() = 0; + // This function should be called when the object is taken out of service. + // There might be functions that need to return through the object, so it + // cannot be deleted yet, but no new operations should be allowed. + // All references to the object except the owning reference are deleted + // by this function. + virtual void StopAndUnregister() = 0; + + // Unified Plan: The list of transceivers after the createOffer() call. + // Because of offerToReceive[Audio/Video] it is possible for createOffer() to + // create new transceivers or update the direction of existing transceivers. + // https://w3c.github.io/webrtc-pc/#legacy-configuration-extensions + // Plan B: Returns an empty list. + virtual Vector<std::unique_ptr<RTCRtpTransceiverPlatform>> CreateOffer( + RTCSessionDescriptionRequest*, + const MediaConstraints&) = 0; + virtual Vector<std::unique_ptr<RTCRtpTransceiverPlatform>> CreateOffer( + RTCSessionDescriptionRequest*, + RTCOfferOptionsPlatform*) = 0; + virtual void CreateAnswer(RTCSessionDescriptionRequest*, + const MediaConstraints&) = 0; + virtual void CreateAnswer(RTCSessionDescriptionRequest*, + RTCAnswerOptionsPlatform*) = 0; + virtual void SetLocalDescription(RTCVoidRequest*) = 0; + virtual void SetLocalDescription(RTCVoidRequest*, + RTCSessionDescriptionPlatform*) = 0; + virtual void SetRemoteDescription(RTCVoidRequest*, + RTCSessionDescriptionPlatform*) = 0; + virtual RTCSessionDescriptionPlatform* LocalDescription() = 0; + virtual RTCSessionDescriptionPlatform* RemoteDescription() = 0; + virtual RTCSessionDescriptionPlatform* CurrentLocalDescription() = 0; + virtual RTCSessionDescriptionPlatform* CurrentRemoteDescription() = 0; + virtual RTCSessionDescriptionPlatform* PendingLocalDescription() = 0; + virtual RTCSessionDescriptionPlatform* PendingRemoteDescription() = 0; + virtual const webrtc::PeerConnectionInterface::RTCConfiguration& + GetConfiguration() const = 0; + virtual webrtc::RTCErrorType SetConfiguration( + const webrtc::PeerConnectionInterface::RTCConfiguration&) = 0; + + virtual void AddICECandidate(RTCVoidRequest*, RTCIceCandidatePlatform*) = 0; + virtual void RestartIce() = 0; + virtual void GetStats(RTCStatsRequest*) = 0; + // Gets stats using the new stats collection API, see + // third_party/webrtc/api/stats/. These will replace the old stats collection + // API when the new API has matured enough. + virtual void GetStats(RTCStatsReportCallback, + const Vector<webrtc::NonStandardGroupId>&) = 0; + virtual scoped_refptr<webrtc::DataChannelInterface> CreateDataChannel( + const String& label, + const webrtc::DataChannelInit&) = 0; + virtual webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>> + AddTransceiverWithTrack(const WebMediaStreamTrack&, + const webrtc::RtpTransceiverInit&) = 0; + virtual webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>> + AddTransceiverWithKind( + // webrtc::MediaStreamTrackInterface::kAudioKind or kVideoKind + const String& kind, + const webrtc::RtpTransceiverInit&) = 0; + // Adds the track to the peer connection, returning the resulting transceiver + // or error. + virtual webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>> + AddTrack(const WebMediaStreamTrack&, const Vector<WebMediaStream>&) = 0; + // Removes the sender. + // In Plan B: Returns OK() with value nullptr on success. The sender's track + // must be nulled by the caller. + // In Unified Plan: Returns OK() with the updated transceiver state. + virtual webrtc::RTCErrorOr<std::unique_ptr<RTCRtpTransceiverPlatform>> + RemoveTrack(RTCRtpSenderPlatform*) = 0; + + // Returns a pointer to the underlying native PeerConnection object. + virtual webrtc::PeerConnectionInterface* NativePeerConnection() = 0; + + virtual void RunSynchronousOnceClosureOnSignalingThread( + CrossThreadOnceClosure closure, + const char* trace_event_name) = 0; + virtual void RunSynchronousRepeatingClosureOnSignalingThread( + const base::RepeatingClosure& closure, + const char* trace_event_name) = 0; + + // Inform chrome://webrtc-internals/ that the iceConnectionState has changed. + virtual void TrackIceConnectionStateChange( + IceConnectionStateVersion version, + webrtc::PeerConnectionInterface::IceConnectionState state) = 0; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_PEER_CONNECTION_HANDLER_PLATFORM_H_ diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h index ad18bf33776..882a6893b69 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h @@ -8,16 +8,18 @@ #include <memory> #include "base/optional.h" -#include "third_party/blink/public/platform/web_common.h" -#include "third_party/blink/public/platform/web_rtc_stats.h" -#include "third_party/blink/public/platform/web_string.h" -#include "third_party/blink/public/platform/web_vector.h" +#include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/webrtc/api/dtls_transport_interface.h" #include "third_party/webrtc/api/rtp_parameters.h" #include "third_party/webrtc/api/stats/rtc_stats.h" namespace blink { +class RTCEncodedAudioStreamTransformer; +class RTCEncodedVideoStreamTransformer; class RTCRtpSource; class WebMediaStreamTrack; @@ -25,7 +27,7 @@ class WebMediaStreamTrack; // receiver alive through reference counting. Multiple |RTCRtpReceiverPlatform|s // could reference the same receiver, see |id|. // https://w3c.github.io/webrtc-pc/#rtcrtpreceiver-interface -class BLINK_PLATFORM_EXPORT RTCRtpReceiverPlatform { +class PLATFORM_EXPORT RTCRtpReceiverPlatform { public: virtual ~RTCRtpReceiverPlatform(); @@ -39,13 +41,21 @@ class BLINK_PLATFORM_EXPORT RTCRtpReceiverPlatform { // The information is only interesting if DtlsTransport() is non-null. virtual webrtc::DtlsTransportInformation DtlsTransportInformation() = 0; virtual const WebMediaStreamTrack& Track() const = 0; - virtual WebVector<WebString> StreamIds() const = 0; - virtual WebVector<std::unique_ptr<RTCRtpSource>> GetSources() = 0; - virtual void GetStats(blink::WebRTCStatsReportCallback, - const WebVector<webrtc::NonStandardGroupId>&) = 0; + virtual Vector<String> StreamIds() const = 0; + virtual Vector<std::unique_ptr<RTCRtpSource>> GetSources() = 0; + virtual void GetStats(RTCStatsReportCallback, + const Vector<webrtc::NonStandardGroupId>&) = 0; virtual std::unique_ptr<webrtc::RtpParameters> GetParameters() const = 0; virtual void SetJitterBufferMinimumDelay( base::Optional<double> delay_seconds) = 0; + virtual RTCEncodedAudioStreamTransformer* GetEncodedAudioStreamTransformer() + const { + return nullptr; + } + virtual RTCEncodedVideoStreamTransformer* GetEncodedVideoStreamTransformer() + const { + return nullptr; + } }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_sender_platform.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_sender_platform.h index e2db21cc70f..b39bca799ea 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_sender_platform.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_sender_platform.h @@ -7,18 +7,21 @@ #include <memory> -#include "third_party/blink/public/platform/web_rtc_stats.h" -#include "third_party/blink/public/platform/web_string.h" +#include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h" #include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/webrtc/api/dtls_transport_interface.h" #include "third_party/webrtc/api/rtp_parameters.h" #include "third_party/webrtc/api/stats/rtc_stats.h" namespace blink { +class RtcDtmfSenderHandler; +class RTCEncodedAudioStreamTransformer; +class RTCEncodedVideoStreamTransformer; class RTCVoidRequest; class WebMediaStreamTrack; -class RtcDtmfSenderHandler; // Implementations of this interface keep the corresponding WebRTC-layer sender // alive through reference counting. Multiple |RTCRtpSenderPlatform|s could @@ -39,19 +42,27 @@ class PLATFORM_EXPORT RTCRtpSenderPlatform { // The information is only interesting if DtlsTransport() is non-null. virtual webrtc::DtlsTransportInformation DtlsTransportInformation() = 0; virtual WebMediaStreamTrack Track() const = 0; - virtual WebVector<WebString> StreamIds() const = 0; + virtual Vector<String> StreamIds() const = 0; // TODO(hbos): Replace RTCVoidRequest by something resolving promises based // on RTCError, as to surface both exception type and error message. // https://crbug.com/790007 virtual void ReplaceTrack(WebMediaStreamTrack, RTCVoidRequest*) = 0; virtual std::unique_ptr<RtcDtmfSenderHandler> GetDtmfSender() const = 0; virtual std::unique_ptr<webrtc::RtpParameters> GetParameters() const = 0; - virtual void SetParameters(blink::WebVector<webrtc::RtpEncodingParameters>, - webrtc::DegradationPreference, + virtual void SetParameters(Vector<webrtc::RtpEncodingParameters>, + absl::optional<webrtc::DegradationPreference>, RTCVoidRequest*) = 0; - virtual void GetStats(blink::WebRTCStatsReportCallback, - const WebVector<webrtc::NonStandardGroupId>&) = 0; - virtual void SetStreams(const WebVector<WebString>& stream_ids) = 0; + virtual void GetStats(RTCStatsReportCallback, + const Vector<webrtc::NonStandardGroupId>&) = 0; + virtual void SetStreams(const Vector<String>& stream_ids) = 0; + virtual RTCEncodedAudioStreamTransformer* GetEncodedAudioStreamTransformer() + const { + return nullptr; + } + virtual RTCEncodedVideoStreamTransformer* GetEncodedVideoStreamTransformer() + const { + return nullptr; + } }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.cc index be6a3c88bf9..704a4e90670 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/time/time.h" #include "third_party/webrtc/api/scoped_refptr.h" +#include "third_party/webrtc/system_wrappers/include/ntp_time.h" namespace blink { @@ -54,4 +55,11 @@ uint32_t RTCRtpSource::RtpTimestamp() const { return source_.rtp_timestamp(); } +base::Optional<int64_t> RTCRtpSource::CaptureTimestamp() const { + if (!source_.absolute_capture_time()) + return base::nullopt; + return webrtc::UQ32x32ToInt64Ms( + source_.absolute_capture_time()->absolute_capture_timestamp); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.h index a36dd370c77..551ac0b3779 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_source.h @@ -35,6 +35,7 @@ class PLATFORM_EXPORT RTCRtpSource { uint32_t Source() const; base::Optional<double> AudioLevel() const; uint32_t RtpTimestamp() const; + base::Optional<int64_t> CaptureTimestamp() const; private: const webrtc::RtpSource source_; diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_transceiver_platform.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_transceiver_platform.h index 91e1c1a8b85..1ba93246f10 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_transceiver_platform.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_rtp_transceiver_platform.h @@ -8,9 +8,10 @@ #include <memory> #include "base/optional.h" -#include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/renderer/platform/peerconnection/rtc_rtp_receiver_platform.h" #include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/webrtc/api/rtp_transceiver_interface.h" namespace blink { @@ -51,8 +52,8 @@ class PLATFORM_EXPORT RTCRtpTransceiverPlatform { // Identifies the webrtc-layer transceiver. Multiple RTCRtpTransceiverPlatform // can exist for the same webrtc-layer transceiver. virtual uintptr_t Id() const = 0; - virtual WebString Mid() const = 0; - virtual void SetMid(base::Optional<WebString>) {} + virtual String Mid() const = 0; + virtual void SetMid(base::Optional<String>) {} virtual std::unique_ptr<RTCRtpSenderPlatform> Sender() const = 0; virtual std::unique_ptr<RTCRtpReceiverPlatform> Receiver() const = 0; virtual bool Stopped() const = 0; @@ -63,7 +64,7 @@ class PLATFORM_EXPORT RTCRtpTransceiverPlatform { virtual base::Optional<webrtc::RtpTransceiverDirection> FiredDirection() const = 0; virtual webrtc::RTCError SetCodecPreferences( - WebVector<webrtc::RtpCodecCapability>) { + Vector<webrtc::RtpCodecCapability>) { return {}; } }; diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_scoped_refptr_cross_thread_copier.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_scoped_refptr_cross_thread_copier.h new file mode 100644 index 00000000000..753bb216047 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_scoped_refptr_cross_thread_copier.h @@ -0,0 +1,22 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_SCOPED_REFPTR_CROSS_THREAD_COPIER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_SCOPED_REFPTR_CROSS_THREAD_COPIER_H_ + +#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h" +#include "third_party/webrtc/api/scoped_refptr.h" + +namespace WTF { + +template <typename T> +struct CrossThreadCopier<rtc::scoped_refptr<T>> { + STATIC_ONLY(CrossThreadCopier); + using Type = rtc::scoped_refptr<T>; + static Type Copy(Type pointer) { return pointer; } +}; + +} // namespace WTF + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_SCOPED_REFPTR_CROSS_THREAD_COPIER_H_ diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_platform.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_platform.h index 6c003142933..69ea1e6b320 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_platform.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_platform.h @@ -23,7 +23,7 @@ class PLATFORM_EXPORT RTCSessionDescriptionPlatform final String Sdp() { return sdp_; } void SetSdp(const String& sdp) { sdp_ = sdp; } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: String type_; diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_request.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_request.h index 9d88696043e..6bc18622c67 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_request.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_session_description_request.h @@ -49,7 +49,7 @@ class RTCSessionDescriptionRequest virtual void RequestSucceeded(RTCSessionDescriptionPlatform*) = 0; virtual void RequestFailed(const webrtc::RTCError&) = 0; - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} protected: RTCSessionDescriptionRequest() = default; diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.cc index 6093874c03c..a1f66c44901 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.cc @@ -9,24 +9,15 @@ #include <string> #include "base/logging.h" +#include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/time/time.h" +#include "third_party/blink/renderer/platform/peerconnection/rtc_scoped_refptr_cross_thread_copier.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/webrtc/api/stats/rtc_stats.h" #include "third_party/webrtc/api/stats/rtcstats_objects.h" -namespace WTF { - -template <typename T> -struct CrossThreadCopier<rtc::scoped_refptr<T>> { - STATIC_ONLY(CrossThreadCopier); - using Type = rtc::scoped_refptr<T>; - static Type Copy(Type pointer) { return pointer; } -}; - -} // namespace WTF - namespace blink { namespace { @@ -82,7 +73,7 @@ bool IsWhitelistedStats(const webrtc::RTCStats& stats) { // including one of its group IDs in |exposed_group_ids|. std::vector<const webrtc::RTCStatsMemberInterface*> FilterMembers( std::vector<const webrtc::RTCStatsMemberInterface*> stats_members, - const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) { + const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) { // Note that using "is_standarized" avoids having to maintain a whitelist of // every single standardized member, as we do at the "stats object" level // with "RTCStatsWhitelist". @@ -93,7 +84,7 @@ std::vector<const webrtc::RTCStatsMemberInterface*> FilterMembers( return false; } - const blink::WebVector<webrtc::NonStandardGroupId>& ids = + const std::vector<webrtc::NonStandardGroupId>& ids = member->group_ids(); for (const webrtc::NonStandardGroupId& id : exposed_group_ids) { if (std::find(ids.begin(), ids.end(), id) != ids.end()) { @@ -116,11 +107,18 @@ size_t CountWhitelistedStats( return size; } +template <typename T> +Vector<T> ToWTFVector(const std::vector<T>& vector) { + Vector<T> wtf_vector(SafeCast<WTF::wtf_size_t>(vector.size())); + std::move(vector.begin(), vector.end(), wtf_vector.begin()); + return wtf_vector; +} + } // namespace RTCStatsReportPlatform::RTCStatsReportPlatform( const scoped_refptr<const webrtc::RTCStatsReport>& stats_report, - const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) + const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) : stats_report_(stats_report), it_(stats_report_->begin()), end_(stats_report_->end()), @@ -164,7 +162,7 @@ size_t RTCStatsReportPlatform::Size() const { RTCStats::RTCStats( const scoped_refptr<const webrtc::RTCStatsReport>& stats_owner, const webrtc::RTCStats* stats, - const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) + const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) : stats_owner_(stats_owner), stats_(stats), stats_members_(FilterMembers(stats->Members(), exposed_group_ids)) { @@ -255,63 +253,63 @@ String RTCStatsMember::ValueString() const { *member_->cast_to<webrtc::RTCStatsMember<std::string>>()); } -blink::WebVector<int> RTCStatsMember::ValueSequenceBool() const { +Vector<bool> RTCStatsMember::ValueSequenceBool() const { DCHECK(IsDefined()); - const std::vector<bool>& vector = + const std::vector<bool> vector = *member_->cast_to<webrtc::RTCStatsMember<std::vector<bool>>>(); - std::vector<int> uint32_vector; - uint32_vector.reserve(vector.size()); - for (size_t i = 0; i < vector.size(); ++i) { - uint32_vector.push_back(vector[i] ? 1 : 0); - } - return blink::WebVector<int>(uint32_vector); + return ToWTFVector(vector); } -blink::WebVector<int32_t> RTCStatsMember::ValueSequenceInt32() const { +Vector<int32_t> RTCStatsMember::ValueSequenceInt32() const { DCHECK(IsDefined()); - return blink::WebVector<int32_t>( - *member_->cast_to<webrtc::RTCStatsMember<std::vector<int32_t>>>()); + const std::vector<int32_t> vector = + *member_->cast_to<webrtc::RTCStatsMember<std::vector<int32_t>>>(); + return ToWTFVector(vector); } -blink::WebVector<uint32_t> RTCStatsMember::ValueSequenceUint32() const { +Vector<uint32_t> RTCStatsMember::ValueSequenceUint32() const { DCHECK(IsDefined()); - return blink::WebVector<uint32_t>( - *member_->cast_to<webrtc::RTCStatsMember<std::vector<uint32_t>>>()); + const std::vector<uint32_t> vector = + *member_->cast_to<webrtc::RTCStatsMember<std::vector<uint32_t>>>(); + return ToWTFVector(vector); } -blink::WebVector<int64_t> RTCStatsMember::ValueSequenceInt64() const { +Vector<int64_t> RTCStatsMember::ValueSequenceInt64() const { DCHECK(IsDefined()); - return blink::WebVector<int64_t>( - *member_->cast_to<webrtc::RTCStatsMember<std::vector<int64_t>>>()); + const std::vector<int64_t> vector = + *member_->cast_to<webrtc::RTCStatsMember<std::vector<int64_t>>>(); + return ToWTFVector(vector); } -blink::WebVector<uint64_t> RTCStatsMember::ValueSequenceUint64() const { +Vector<uint64_t> RTCStatsMember::ValueSequenceUint64() const { DCHECK(IsDefined()); - return blink::WebVector<uint64_t>( - *member_->cast_to<webrtc::RTCStatsMember<std::vector<uint64_t>>>()); + const std::vector<uint64_t> vector = + *member_->cast_to<webrtc::RTCStatsMember<std::vector<uint64_t>>>(); + return ToWTFVector(vector); } -blink::WebVector<double> RTCStatsMember::ValueSequenceDouble() const { +Vector<double> RTCStatsMember::ValueSequenceDouble() const { DCHECK(IsDefined()); - return blink::WebVector<double>( - *member_->cast_to<webrtc::RTCStatsMember<std::vector<double>>>()); + const std::vector<double> vector = + *member_->cast_to<webrtc::RTCStatsMember<std::vector<double>>>(); + return ToWTFVector(vector); } -blink::WebVector<String> RTCStatsMember::ValueSequenceString() const { +Vector<String> RTCStatsMember::ValueSequenceString() const { DCHECK(IsDefined()); const std::vector<std::string>& sequence = *member_->cast_to<webrtc::RTCStatsMember<std::vector<std::string>>>(); - blink::WebVector<String> web_sequence(sequence.size()); + Vector<String> wtf_sequence(sequence.size()); for (size_t i = 0; i < sequence.size(); ++i) - web_sequence[i] = String::FromUTF8(sequence[i]); - return web_sequence; + wtf_sequence[i] = String::FromUTF8(sequence[i]); + return wtf_sequence; } rtc::scoped_refptr<webrtc::RTCStatsCollectorCallback> CreateRTCStatsCollectorCallback( scoped_refptr<base::SingleThreadTaskRunner> main_thread, - blink::WebRTCStatsReportCallback callback, - const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) { + RTCStatsReportCallback callback, + const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) { return rtc::scoped_refptr<RTCStatsCollectorCallbackImpl>( new rtc::RefCountedObject<RTCStatsCollectorCallbackImpl>( std::move(main_thread), std::move(callback), exposed_group_ids)); @@ -319,8 +317,8 @@ CreateRTCStatsCollectorCallback( RTCStatsCollectorCallbackImpl::RTCStatsCollectorCallbackImpl( scoped_refptr<base::SingleThreadTaskRunner> main_thread, - blink::WebRTCStatsReportCallback callback, - const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids) + RTCStatsReportCallback callback, + const Vector<webrtc::NonStandardGroupId>& exposed_group_ids) : main_thread_(std::move(main_thread)), callback_(std::move(callback)), exposed_group_ids_(exposed_group_ids) {} diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.h index e518f20d4d0..e6e85007988 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats.h @@ -5,15 +5,26 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_STATS_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_STATS_H_ -#include "base/memory/ref_counted.h" -#include "base/single_thread_task_runner.h" -#include "third_party/blink/public/platform/web_rtc_stats.h" +#include "base/callback.h" +#include "third_party/blink/public/platform/web_string.h" +#include "third_party/blink/public/platform/web_vector.h" +#include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/webrtc/api/scoped_refptr.h" #include "third_party/webrtc/api/stats/rtc_stats.h" #include "third_party/webrtc/api/stats/rtc_stats_collector_callback.h" #include "third_party/webrtc/api/stats/rtc_stats_report.h" +namespace base { +class SingleThreadTaskRunner; +} + +namespace webrtc { +class RTCStatsCollectorCallback; +enum class NonStandardGroupId; +} // namespace webrtc + namespace blink { class RTCStats; @@ -26,14 +37,11 @@ class RTCStatsMember; // // Note: This class is named |RTCStatsReportPlatform| not to collide with class // |RTCStatsReport|, from renderer/modules/peerconnection/rtc_stats_report.cc|h. -// -// TODO(crbug.com/787254): Switch over the classes below from using WebVector -// to WTF::Vector, when their respective parent classes are gone. class PLATFORM_EXPORT RTCStatsReportPlatform { public: RTCStatsReportPlatform( const scoped_refptr<const webrtc::RTCStatsReport>& stats_report, - const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids); + const Vector<webrtc::NonStandardGroupId>& exposed_group_ids); virtual ~RTCStatsReportPlatform(); // Creates a new report object that is a handle to the same underlying stats // report (the stats are not copied). The new report's iterator is reset, @@ -53,17 +61,16 @@ class PLATFORM_EXPORT RTCStatsReportPlatform { const scoped_refptr<const webrtc::RTCStatsReport> stats_report_; webrtc::RTCStatsReport::ConstIterator it_; const webrtc::RTCStatsReport::ConstIterator end_; - blink::WebVector<webrtc::NonStandardGroupId> exposed_group_ids_; + Vector<webrtc::NonStandardGroupId> exposed_group_ids_; // Number of whitelisted webrtc::RTCStats in |stats_report_|. const size_t size_; }; class PLATFORM_EXPORT RTCStats { public: - RTCStats( - const scoped_refptr<const webrtc::RTCStatsReport>& stats_owner, - const webrtc::RTCStats* stats, - const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids); + RTCStats(const scoped_refptr<const webrtc::RTCStatsReport>& stats_owner, + const webrtc::RTCStats* stats, + const Vector<webrtc::NonStandardGroupId>& exposed_group_ids); virtual ~RTCStats(); String Id() const; @@ -99,13 +106,13 @@ class PLATFORM_EXPORT RTCStatsMember { uint64_t ValueUint64() const; double ValueDouble() const; String ValueString() const; - blink::WebVector<int> ValueSequenceBool() const; - blink::WebVector<int32_t> ValueSequenceInt32() const; - blink::WebVector<uint32_t> ValueSequenceUint32() const; - blink::WebVector<int64_t> ValueSequenceInt64() const; - blink::WebVector<uint64_t> ValueSequenceUint64() const; - blink::WebVector<double> ValueSequenceDouble() const; - blink::WebVector<String> ValueSequenceString() const; + Vector<bool> ValueSequenceBool() const; + Vector<int32_t> ValueSequenceInt32() const; + Vector<uint32_t> ValueSequenceUint32() const; + Vector<int64_t> ValueSequenceInt64() const; + Vector<uint64_t> ValueSequenceUint64() const; + Vector<double> ValueSequenceDouble() const; + Vector<String> ValueSequenceString() const; private: // Reference to keep the report that owns |member_|'s stats object alive. @@ -114,6 +121,16 @@ class PLATFORM_EXPORT RTCStatsMember { const webrtc::RTCStatsMemberInterface* const member_; }; +using RTCStatsReportCallback = + base::OnceCallback<void(std::unique_ptr<RTCStatsReportPlatform>)>; + +PLATFORM_EXPORT +rtc::scoped_refptr<webrtc::RTCStatsCollectorCallback> +CreateRTCStatsCollectorCallback( + scoped_refptr<base::SingleThreadTaskRunner> main_thread, + RTCStatsReportCallback callback, + const Vector<webrtc::NonStandardGroupId>& exposed_group_ids); + // A stats collector callback. // It is invoked on the WebRTC signaling thread and will post a task to invoke // |callback| on the thread given in the |main_thread| argument. @@ -127,18 +144,20 @@ class PLATFORM_EXPORT RTCStatsCollectorCallbackImpl protected: RTCStatsCollectorCallbackImpl( scoped_refptr<base::SingleThreadTaskRunner> main_thread, - blink::WebRTCStatsReportCallback callback2, - const blink::WebVector<webrtc::NonStandardGroupId>& exposed_group_ids); + RTCStatsReportCallback callback, + const Vector<webrtc::NonStandardGroupId>& exposed_group_ids); ~RTCStatsCollectorCallbackImpl() override; void OnStatsDeliveredOnMainThread( rtc::scoped_refptr<const webrtc::RTCStatsReport> report); const scoped_refptr<base::SingleThreadTaskRunner> main_thread_; - blink::WebRTCStatsReportCallback callback_; - blink::WebVector<webrtc::NonStandardGroupId> exposed_group_ids_; + RTCStatsReportCallback callback_; + Vector<webrtc::NonStandardGroupId> exposed_group_ids_; }; +PLATFORM_EXPORT void WhitelistStatsForTesting(const char* type); + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_STATS_H_ diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h index 5c83bbd6d7f..e013c6b151d 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_request.h @@ -74,7 +74,7 @@ class RTCStatsRequest : public GarbageCollected<RTCStatsRequest> { virtual MediaStreamComponent* Component() = 0; virtual void RequestSucceeded(RTCStatsResponseBase*) = 0; - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} protected: RTCStatsRequest() = default; diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_test.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_test.cc index 12678fb053f..98ec0d03b41 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_test.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_stats_test.cc @@ -7,7 +7,7 @@ #include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/platform/web_rtc_stats.h" +#include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h" #include "third_party/webrtc/api/stats/rtc_stats_report.h" #include "third_party/webrtc/api/stats/rtcstats_objects.h" #include "third_party/webrtc/stats/test/rtc_test_stats.h" @@ -103,7 +103,7 @@ TEST(RTCStatsTest, IncludeAllMembers) { // Include both standard and non-standard member. RTCStatsReportPlatform report( - webrtc_report.get(), std::vector<webrtc::NonStandardGroupId>{ + webrtc_report.get(), Vector<webrtc::NonStandardGroupId>{ webrtc::NonStandardGroupId::kGroupIdForTesting}); std::unique_ptr<RTCStats> stats = report.GetStats("id"); ASSERT_NE(nullptr, stats); @@ -127,7 +127,7 @@ TEST(RTCStatsTest, CopyHandle) { ASSERT_EQ(1u, standard_members_copy->GetStats("id")->MembersCount()); RTCStatsReportPlatform all_members_report( - webrtc_report.get(), std::vector<webrtc::NonStandardGroupId>{ + webrtc_report.get(), Vector<webrtc::NonStandardGroupId>{ webrtc::NonStandardGroupId::kGroupIdForTesting}); std::unique_ptr<RTCStatsReportPlatform> all_members_copy = all_members_report.CopyHandle(); diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc index 3783f4c79f8..c61e2df1da5 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc @@ -8,6 +8,7 @@ #include <functional> #include <utility> +#include "base/bind_helpers.h" #include "base/feature_list.h" #include "base/location.h" #include "base/logging.h" @@ -18,6 +19,7 @@ #include "base/stl_util.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread_restrictions.h" +#include "base/time/time.h" #include "build/build_config.h" #include "media/base/media_log.h" #include "media/base/media_switches.h" @@ -68,12 +70,6 @@ const int32_t kMaxDecodeHistory = 32; // requesting fallback to software decode. const int32_t kMaxConsecutiveErrors = 5; -// Currently, RTCVideoDecoderAdapter only tries one VideoDecoderImplementation. -// Since we use it in multiple places, memorize it here to make it clear that -// they must be changed together. -constexpr media::VideoDecoderImplementation kImplementation = - media::VideoDecoderImplementation::kDefault; - // Map webrtc::VideoCodecType to media::VideoCodec. media::VideoCodec ToVideoCodec(webrtc::VideoCodecType video_codec_type) { switch (video_codec_type) { @@ -123,10 +119,10 @@ void FinishWait(base::WaitableEvent* waiter, bool* result_out, bool result) { } void OnRequestOverlayInfo(bool decoder_requires_restart_for_overlay, - const media::ProvideOverlayInfoCB& overlay_info_cb) { + media::ProvideOverlayInfoCB overlay_info_cb) { // Android overlays are not supported. if (overlay_info_cb) - overlay_info_cb.Run(media::OverlayInfo()); + std::move(overlay_info_cb).Run(media::OverlayInfo()); } } // namespace @@ -157,8 +153,9 @@ std::unique_ptr<RTCVideoDecoderAdapter> RTCVideoDecoderAdapter::Create( kDefaultSize, media::EmptyExtraData(), media::EncryptionScheme::kUnencrypted); if (gpu_factories->IsDecoderConfigSupported(kImplementation, config) == - media::GpuVideoAcceleratorFactories::Supported::kFalse) + media::GpuVideoAcceleratorFactories::Supported::kFalse) { return nullptr; + } // Synchronously verify that the decoder can be initialized. std::unique_ptr<RTCVideoDecoderAdapter> rtc_video_decoder_adapter = @@ -209,7 +206,9 @@ bool RTCVideoDecoderAdapter::InitializeSync( CrossThreadBindOnce(&RTCVideoDecoderAdapter::InitializeOnMediaThread, CrossThreadUnretained(this), config, std::move(init_cb)))) { - waiter.Wait(); + // TODO(crbug.com/1076817) Remove if a root cause is found. + if (!waiter.TimedWait(base::TimeDelta::FromSeconds(10))) + return false; } return result; } @@ -225,6 +224,10 @@ int32_t RTCVideoDecoderAdapter::InitDecode( base::AutoLock auto_lock(lock_); UMA_HISTOGRAM_BOOLEAN("Media.RTCVideoDecoderInitDecodeSuccess", !has_error_); + if (!has_error_) { + UMA_HISTOGRAM_BOOLEAN("Media.RTCVideoDecoderProfile", + GuessVideoCodecProfile(format_)); + } return has_error_ ? WEBRTC_VIDEO_CODEC_UNINITIALIZED : WEBRTC_VIDEO_CODEC_OK; } @@ -388,9 +391,17 @@ void RTCVideoDecoderAdapter::InitializeOnMediaThread( media::VideoDecoder::OutputCB output_cb = ConvertToBaseRepeatingCallback( CrossThreadBindRepeating(&RTCVideoDecoderAdapter::OnOutput, weak_this_)); - video_decoder_->Initialize(config, low_delay, cdm_context, - ConvertToBaseOnceCallback(std::move(init_cb)), - output_cb, base::DoNothing()); + video_decoder_->Initialize( + config, low_delay, cdm_context, + base::BindOnce(&RTCVideoDecoderAdapter::OnInitializeDone, + ConvertToBaseOnceCallback(std::move(init_cb))), + output_cb, base::DoNothing()); +} + +// static +void RTCVideoDecoderAdapter::OnInitializeDone(base::OnceCallback<void(bool)> cb, + media::Status status) { + std::move(cb).Run(status.is_ok()); } void RTCVideoDecoderAdapter::DecodeOnMediaThread() { @@ -454,7 +465,8 @@ void RTCVideoDecoderAdapter::OnOutput(scoped_refptr<media::VideoFrame> frame) { webrtc::VideoFrame::Builder() .set_video_frame_buffer( new rtc::RefCountedObject<blink::WebRtcVideoFrameAdapter>( - std::move(frame))) + std::move(frame), + WebRtcVideoFrameAdapter::LogStatus::kNoLogging)) .set_timestamp_rtp(static_cast<uint32_t>(timestamp.InMicroseconds())) .set_timestamp_us(0) .set_rotation(webrtc::kVideoRotation_0) diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h index 89bb76c62f2..9d67fb60e8b 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h @@ -14,9 +14,11 @@ #include "base/sequence_checker.h" #include "base/synchronization/lock.h" #include "media/base/decode_status.h" +#include "media/base/status.h" #include "media/base/video_codecs.h" #include "media/base/video_decoder.h" #include "media/base/video_decoder_config.h" +#include "media/video/supported_video_decoder_config.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/deque.h" #include "third_party/blink/renderer/platform/wtf/functional.h" @@ -51,6 +53,12 @@ namespace blink { // way to synchronize this correctly. class PLATFORM_EXPORT RTCVideoDecoderAdapter : public webrtc::VideoDecoder { public: + // Currently, RTCVideoDecoderAdapter only tries one + // VideoDecoderImplementation. + // Since we use it in multiple places, memorize it here to make it clear that + // they must be changed together. + static constexpr media::VideoDecoderImplementation kImplementation = + media::VideoDecoderImplementation::kDefault; // Creates and initializes an RTCVideoDecoderAdapter. Returns nullptr if // |format| cannot be supported. // Called on the worker thread. @@ -92,6 +100,8 @@ class PLATFORM_EXPORT RTCVideoDecoderAdapter : public webrtc::VideoDecoder { bool InitializeSync(const media::VideoDecoderConfig& config); void InitializeOnMediaThread(const media::VideoDecoderConfig& config, InitCB init_cb); + static void OnInitializeDone(base::OnceCallback<void(bool)> cb, + media::Status status); void DecodeOnMediaThread(); void OnDecodeDone(media::DecodeStatus status); void OnOutput(scoped_refptr<media::VideoFrame> frame); diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter_test.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter_test.cc index 213daa35f8a..37becc9aaf0 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter_test.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter_test.cc @@ -162,8 +162,12 @@ class RTCVideoDecoderAdapterTest : public ::testing::Test { bool CreateAndInitialize(bool init_cb_result = true) { EXPECT_CALL(*video_decoder_, Initialize_(_, _, _, _, _, _)) - .WillOnce(DoAll(SaveArg<0>(&vda_config_), SaveArg<4>(&output_cb_), - base::test::RunOnceCallback<3>(init_cb_result))); + .WillOnce(DoAll( + SaveArg<0>(&vda_config_), SaveArg<4>(&output_cb_), + base::test::RunOnceCallback<3>( + init_cb_result + ? media::OkStatus() + : media::Status(media::StatusCode::kCodeOnlyForTesting)))); rtc_video_decoder_adapter_ = RTCVideoDecoderAdapter::Create(&gpu_factories_, sdp_format_); return !!rtc_video_decoder_adapter_; @@ -363,7 +367,7 @@ TEST_F(RTCVideoDecoderAdapterTest, ReinitializesForHDRColorSpaceInitially) { // First Decode() should cause a reinitialize as new color space is given. EXPECT_CALL(*video_decoder_, Initialize_(_, _, _, _, _, _)) .WillOnce(DoAll(SaveArg<0>(&vda_config_), - base::test::RunOnceCallback<3>(true))); + base::test::RunOnceCallback<3>(media::OkStatus()))); webrtc::EncodedImage first_input_image = GetEncodedImageWithColorSpace(0); ASSERT_EQ(rtc_video_decoder_adapter_->Decode(first_input_image, false, 0), WEBRTC_VIDEO_CODEC_OK); @@ -395,7 +399,8 @@ TEST_F(RTCVideoDecoderAdapterTest, HandlesReinitializeFailure) { // Set Initialize() to fail. EXPECT_CALL(*video_decoder_, Initialize_(_, _, _, _, _, _)) - .WillOnce(base::test::RunOnceCallback<3>(false)); + .WillOnce(base::test::RunOnceCallback<3>( + media::Status(media::StatusCode::kCodeOnlyForTesting))); ASSERT_EQ(rtc_video_decoder_adapter_->Decode(input_image, false, 0), WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE); } diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc index 2ab30075132..cb12d36124c 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.cc @@ -4,17 +4,128 @@ #include "third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_factory.h" +#include <array> #include <memory> #include "base/logging.h" #include "base/memory/ptr_util.h" #include "build/build_config.h" +#include "media/base/media_util.h" +#include "media/base/video_codecs.h" #include "media/video/gpu_video_accelerator_factories.h" #include "third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.h" +#include "third_party/webrtc/media/base/h264_profile_level_id.h" +#include "third_party/webrtc/media/base/media_constants.h" +#include "third_party/webrtc/media/base/vp9_profile.h" +#include "ui/gfx/color_space.h" +#include "ui/gfx/geometry/size.h" namespace blink { namespace { +const int kDefaultFps = 30; +// Any reasonable size, will be overridden by the decoder anyway. +const gfx::Size kDefaultSize(640, 480); + +struct CodecConfig { + media::VideoCodec codec; + media::VideoCodecProfile profile; +}; + +constexpr std::array<CodecConfig, 6> kCodecConfigs = {{ + {media::kCodecVP8, media::VP8PROFILE_ANY}, + {media::kCodecVP9, media::VP9PROFILE_PROFILE0}, + {media::kCodecVP9, media::VP9PROFILE_PROFILE2}, + {media::kCodecH264, media::H264PROFILE_BASELINE}, + {media::kCodecH264, media::H264PROFILE_MAIN}, + {media::kCodecH264, media::H264PROFILE_HIGH}, +}}; + +// Translate from media::VideoDecoderConfig to webrtc::SdpVideoFormat, or return +// nothing if the profile isn't supported. +base::Optional<webrtc::SdpVideoFormat> VdcToWebRtcFormat( + const media::VideoDecoderConfig& config) { + switch (config.codec()) { + case media::VideoCodec::kCodecVP8: + return webrtc::SdpVideoFormat("VP8"); + case media::VideoCodec::kCodecVP9: { + webrtc::VP9Profile vp9_profile; + switch (config.profile()) { + case media::VP9PROFILE_PROFILE0: + vp9_profile = webrtc::VP9Profile::kProfile0; + break; + case media::VP9PROFILE_PROFILE2: + vp9_profile = webrtc::VP9Profile::kProfile2; + break; + default: + // Unsupported profile in WebRTC. + return base::nullopt; + } + return webrtc::SdpVideoFormat( + "VP9", {{webrtc::kVP9FmtpProfileId, + webrtc::VP9ProfileToString(vp9_profile)}}); + } + case media::VideoCodec::kCodecH264: { + webrtc::H264::Profile h264_profile; + switch (config.profile()) { + case media::H264PROFILE_BASELINE: + h264_profile = webrtc::H264::kProfileBaseline; + break; + case media::H264PROFILE_MAIN: + h264_profile = webrtc::H264::kProfileMain; + break; + case media::H264PROFILE_HIGH: + h264_profile = webrtc::H264::kProfileHigh; + break; + default: + // Unsupported H264 profile in WebRTC. + return base::nullopt; + } + + const int width = config.visible_rect().width(); + const int height = config.visible_rect().height(); + + const absl::optional<webrtc::H264::Level> h264_level = + webrtc::H264::SupportedLevel(width * height, kDefaultFps); + const webrtc::H264::ProfileLevelId profile_level_id( + h264_profile, h264_level.value_or(webrtc::H264::kLevel1)); + + webrtc::SdpVideoFormat format("H264"); + format.parameters = { + {cricket::kH264FmtpProfileLevelId, + *webrtc::H264::ProfileLevelIdToString(profile_level_id)}, + {cricket::kH264FmtpLevelAsymmetryAllowed, "1"}, + {cricket::kH264FmtpPacketizationMode, "1"}}; + return format; + } + default: + return base::nullopt; + } +} + +// Due to https://crbug.com/345569, HW decoders do not distinguish between +// Constrained Baseline(CBP) and Baseline(BP) profiles. Since CBP is a subset of +// BP, we can report support for both. It is safe to do so when SW fallback is +// available. +// TODO(emircan): Remove this when the bug referred above is fixed. +void MapBaselineProfile( + std::vector<webrtc::SdpVideoFormat>* supported_formats) { + for (const auto& format : *supported_formats) { + const absl::optional<webrtc::H264::ProfileLevelId> profile_level_id = + webrtc::H264::ParseSdpProfileLevelId(format.parameters); + if (profile_level_id && + profile_level_id->profile == webrtc::H264::kProfileBaseline) { + webrtc::SdpVideoFormat cbp_format = format; + webrtc::H264::ProfileLevelId cbp_profile = *profile_level_id; + cbp_profile.profile = webrtc::H264::kProfileConstrainedBaseline; + cbp_format.parameters[cricket::kH264FmtpProfileLevelId] = + *webrtc::H264::ProfileLevelIdToString(cbp_profile); + supported_formats->push_back(cbp_format); + return; + } + } +} + // This extra indirection is needed so that we can delete the decoder on the // correct thread. class ScopedVideoDecoder : public webrtc::VideoDecoder { @@ -66,8 +177,25 @@ RTCVideoDecoderFactory::RTCVideoDecoderFactory( std::vector<webrtc::SdpVideoFormat> RTCVideoDecoderFactory::GetSupportedFormats() const { - NOTREACHED(); - return std::vector<webrtc::SdpVideoFormat>(); + std::vector<webrtc::SdpVideoFormat> supported_formats; + for (auto& codec_config : kCodecConfigs) { + media::VideoDecoderConfig config( + codec_config.codec, codec_config.profile, + media::VideoDecoderConfig::AlphaMode::kIsOpaque, + media::VideoColorSpace(), media::kNoTransformation, kDefaultSize, + gfx::Rect(kDefaultSize), kDefaultSize, media::EmptyExtraData(), + media::EncryptionScheme::kUnencrypted); + if (gpu_factories_->IsDecoderConfigSupported( + RTCVideoDecoderAdapter::kImplementation, config) == + media::GpuVideoAcceleratorFactories::Supported::kTrue) { + base::Optional<webrtc::SdpVideoFormat> format = VdcToWebRtcFormat(config); + if (format) { + supported_formats.push_back(*format); + } + } + } + MapBaselineProfile(&supported_formats); + return supported_formats; } RTCVideoDecoderFactory::~RTCVideoDecoderFactory() { diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc index fdb679f48a6..9f7a84d505d 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc @@ -20,19 +20,19 @@ #include "base/strings/stringprintf.h" #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" +#include "base/thread_annotations.h" #include "base/threading/thread_checker.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "media/base/bind_to_current_loop.h" #include "media/base/bitstream_buffer.h" -#include "media/base/media_switches.h" #include "media/base/video_bitrate_allocation.h" #include "media/base/video_frame.h" #include "media/base/video_util.h" +#include "media/capture/capture_switches.h" #include "media/video/gpu_video_accelerator_factories.h" #include "media/video/h264_parser.h" #include "media/video/video_encode_accelerator.h" -#include "mojo/public/cpp/base/shared_memory_utils.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" #include "third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h" @@ -61,6 +61,38 @@ namespace blink { namespace { +webrtc::VideoEncoder::EncoderInfo CopyToWebrtcEncoderInfo( + const media::VideoEncoderInfo& enc_info) { + webrtc::VideoEncoder::EncoderInfo info; + info.implementation_name = enc_info.implementation_name; + info.supports_native_handle = enc_info.supports_native_handle; + info.has_trusted_rate_controller = enc_info.has_trusted_rate_controller; + info.is_hardware_accelerated = enc_info.is_hardware_accelerated; + info.supports_simulcast = enc_info.supports_simulcast; + // TODO(crbug.com/1034686): Copy ScalingSettings once getStats() hang issue + // is resolved. + // info.scaling_settings = webrtc::VideoEncoder::ScalingSettings( + // enc_info.scaling_settings.min_qp, enc_info.scaling_settings.max_qp); + static_assert( + webrtc::kMaxSpatialLayers >= media::VideoEncoderInfo::kMaxSpatialLayers, + "webrtc::kMaxSpatiallayers is less than " + "media::VideoEncoderInfo::kMaxSpatialLayers"); + for (size_t i = 0; i < base::size(enc_info.fps_allocation); ++i) { + if (enc_info.fps_allocation[i].empty()) + continue; + info.fps_allocation[i] = + absl::InlinedVector<uint8_t, webrtc::kMaxTemporalStreams>( + enc_info.fps_allocation[i].begin(), + enc_info.fps_allocation[i].end()); + } + for (const auto& limit : enc_info.resolution_bitrate_limits) { + info.resolution_bitrate_limits.emplace_back( + limit.frame_size.GetArea(), limit.min_start_bitrate_bps, + limit.min_bitrate_bps, limit.max_bitrate_bps); + } + return info; +} + struct RTCTimestamps { RTCTimestamps(const base::TimeDelta& media_timestamp, int32_t rtp_timestamp, @@ -156,6 +188,9 @@ class RTCVideoEncoder::Impl media::VideoCodecProfile profile, base::WaitableEvent* async_waiter, int32_t* async_retval); + + webrtc::VideoEncoder::EncoderInfo GetEncoderInfo() const; + // Enqueue a frame from WebRTC for encoding. // RTCVideoEncoder expects to be able to call this function synchronously from // its own thread, hence the |async_waiter| and |async_retval| arguments. @@ -196,6 +231,7 @@ class RTCVideoEncoder::Impl int32_t bitstream_buffer_id, const media::BitstreamBufferMetadata& metadata) override; void NotifyError(media::VideoEncodeAccelerator::Error error) override; + void NotifyEncoderInfoChange(const media::VideoEncoderInfo& info) override; private: friend class base::RefCountedThreadSafe<Impl>; @@ -319,15 +355,18 @@ class RTCVideoEncoder::Impl // The content type, as reported to WebRTC (screenshare vs realtime video). const webrtc::VideoContentType video_content_type_; - // Protect |status_|. |status_| is read or written on |gpu_task_runner_| in - // Impl. It can be read in RTCVideoEncoder on other threads. - mutable base::Lock status_lock_; + webrtc::VideoEncoder::EncoderInfo encoder_info_ GUARDED_BY(lock_); + + // Protect |status_| and |encoder_info_|. |status_| is read or written on + // |gpu_task_runner_| in Impl. It can be read in RTCVideoEncoder on other + // threads. + mutable base::Lock lock_; // We cannot immediately return error conditions to the WebRTC user of this // class, as there is no error callback in the webrtc::VideoEncoder interface. // Instead, we cache an error status here and return it the next time an - // interface entry point is called. This is protected by |status_lock_|. - int32_t status_; + // interface entry point is called. This is protected by |lock_|. + int32_t status_ GUARDED_BY(lock_); DISALLOW_COPY_AND_ASSIGN(Impl); }; @@ -348,6 +387,13 @@ RTCVideoEncoder::Impl::Impl(media::GpuVideoAcceleratorFactories* gpu_factories, video_content_type_(video_content_type), status_(WEBRTC_VIDEO_CODEC_UNINITIALIZED) { DETACH_FROM_THREAD(thread_checker_); + + // The default values of EncoderInfo. + encoder_info_.implementation_name = + RTCVideoEncoder::Impl::ImplementationName(); + encoder_info_.supports_native_handle = true; + encoder_info_.is_hardware_accelerated = true; + encoder_info_.has_internal_source = false; } void RTCVideoEncoder::Impl::CreateAndInitializeVEA( @@ -369,8 +415,10 @@ void RTCVideoEncoder::Impl::CreateAndInitializeVEA( // Check that |profile| supports |input_visible_size|. if (base::FeatureList::IsEnabled(features::kWebRtcUseMinMaxVEADimensions)) { const auto vea_supported_profiles = - gpu_factories_->GetVideoEncodeAcceleratorSupportedProfiles(); - for (const auto vea_profile : vea_supported_profiles) { + gpu_factories_->GetVideoEncodeAcceleratorSupportedProfiles().value_or( + media::VideoEncodeAccelerator::SupportedProfiles()); + + for (const auto& vea_profile : vea_supported_profiles) { if (vea_profile.profile == profile && (input_visible_size.width() > vea_profile.max_resolution.width() || input_visible_size.height() > vea_profile.max_resolution.height() || @@ -424,6 +472,18 @@ void RTCVideoEncoder::Impl::CreateAndInitializeVEA( // be signaled. } +webrtc::VideoEncoder::EncoderInfo RTCVideoEncoder::Impl::GetEncoderInfo() + const { + base::AutoLock lock(lock_); + return encoder_info_; +} + +void RTCVideoEncoder::Impl::NotifyEncoderInfoChange( + const media::VideoEncoderInfo& info) { + base::AutoLock lock(lock_); + encoder_info_ = CopyToWebrtcEncoderInfo(info); +} + void RTCVideoEncoder::Impl::Enqueue(const webrtc::VideoFrame* input_frame, bool force_keyframe, base::WaitableEvent* async_waiter, @@ -542,12 +602,12 @@ void RTCVideoEncoder::Impl::Destroy(base::WaitableEvent* async_waiter) { } int32_t RTCVideoEncoder::Impl::GetStatus() const { - base::AutoLock lock(status_lock_); + base::AutoLock lock(lock_); return status_; } void RTCVideoEncoder::Impl::SetStatus(int32_t status) { - base::AutoLock lock(status_lock_); + base::AutoLock lock(lock_); status_ = status; } @@ -571,9 +631,9 @@ void RTCVideoEncoder::Impl::RequireBitstreamBuffers( input_frame_coded_size_ = input_coded_size; for (unsigned int i = 0; i < input_count + kInputBufferExtraCount; ++i) { - base::UnsafeSharedMemoryRegion shm = - mojo::CreateUnsafeSharedMemoryRegion(media::VideoFrame::AllocationSize( - media::PIXEL_FORMAT_I420, input_coded_size)); + base::UnsafeSharedMemoryRegion shm = base::UnsafeSharedMemoryRegion::Create( + media::VideoFrame::AllocationSize(media::PIXEL_FORMAT_I420, + input_coded_size)); if (!shm.IsValid()) { LogAndNotifyError(FROM_HERE, "failed to create input buffer ", media::VideoEncodeAccelerator::kPlatformFailureError); @@ -1223,11 +1283,10 @@ void RTCVideoEncoder::SetRates( } webrtc::VideoEncoder::EncoderInfo RTCVideoEncoder::GetEncoderInfo() const { - EncoderInfo info; - info.implementation_name = RTCVideoEncoder::Impl::ImplementationName(); - info.supports_native_handle = true; - info.is_hardware_accelerated = true; - info.has_internal_source = false; + webrtc::VideoEncoder::EncoderInfo info; + if (impl_) + info = impl_->GetEncoderInfo(); + return info; } diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc index 5ba62ba4f96..2ad849e639a 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc @@ -21,6 +21,31 @@ namespace blink { namespace { +base::Optional<media::VideoCodecProfile> WebRTCFormatToCodecProfile( + const webrtc::SdpVideoFormat& sdp) { + if (sdp.name == "H264") { +#if !defined(OS_ANDROID) + // Enable H264 HW encode for WebRTC when SW fallback is available, which is + // checked by kWebRtcH264WithOpenH264FFmpeg flag. This check should be + // removed when SW implementation is fully enabled. + bool webrtc_h264_sw_enabled = false; +#if BUILDFLAG(RTC_USE_H264) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS) + webrtc_h264_sw_enabled = base::FeatureList::IsEnabled( + blink::features::kWebRtcH264WithOpenH264FFmpeg); +#endif // BUILDFLAG(RTC_USE_H264) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS) + if (!webrtc_h264_sw_enabled) + return base::nullopt; +#endif + + return media::VideoCodecProfile::H264PROFILE_MIN; + } else if (sdp.name == "VP8") { + return media::VideoCodecProfile::VP8PROFILE_MIN; + } else if (sdp.name == "VP9") { + return media::VideoCodecProfile::VP9PROFILE_MIN; + } + return base::nullopt; +} + // Translate from media::VideoEncodeAccelerator::SupportedProfile to // webrtc::SdpVideoFormat, or return nothing if the profile isn't supported. base::Optional<webrtc::SdpVideoFormat> VEAToWebRTCFormat( @@ -101,38 +126,63 @@ bool IsSameFormat(const webrtc::SdpVideoFormat& format1, format2.parameters); } -} // anonymous namespace - -RTCVideoEncoderFactory::RTCVideoEncoderFactory( - media::GpuVideoAcceleratorFactories* gpu_factories) - : gpu_factories_(gpu_factories) { - const media::VideoEncodeAccelerator::SupportedProfiles& profiles = - gpu_factories_->GetVideoEncodeAcceleratorSupportedProfiles(); - for (const auto& profile : profiles) { +struct SupportedFormats { + bool unknown = true; + std::vector<media::VideoCodecProfile> profiles; + std::vector<webrtc::SdpVideoFormat> sdp_formats; +}; + +SupportedFormats GetSupportedFormatsInternal( + media::GpuVideoAcceleratorFactories* gpu_factories) { + SupportedFormats supported_formats; + auto profiles = gpu_factories->GetVideoEncodeAcceleratorSupportedProfiles(); + if (!profiles) + return supported_formats; + + // |profiles| are either the info at GpuInfo instance or the info got by + // querying GPU process. + supported_formats.unknown = false; + for (const auto& profile : *profiles) { base::Optional<webrtc::SdpVideoFormat> format = VEAToWebRTCFormat(profile); if (format) { - supported_formats_.push_back(std::move(*format)); - profiles_.push_back(profile.profile); + supported_formats.profiles.push_back(profile.profile); + supported_formats.sdp_formats.push_back(std::move(*format)); } } + return supported_formats; } +} // anonymous namespace + +RTCVideoEncoderFactory::RTCVideoEncoderFactory( + media::GpuVideoAcceleratorFactories* gpu_factories) + : gpu_factories_(gpu_factories) {} + RTCVideoEncoderFactory::~RTCVideoEncoderFactory() {} std::unique_ptr<webrtc::VideoEncoder> RTCVideoEncoderFactory::CreateVideoEncoder( const webrtc::SdpVideoFormat& format) { - for (size_t i = 0; i < supported_formats_.size(); ++i) { - if (IsSameFormat(format, supported_formats_[i])) { - return std::make_unique<RTCVideoEncoder>(profiles_[i], gpu_factories_); + std::unique_ptr<webrtc::VideoEncoder> encoder; + auto supported_formats = GetSupportedFormatsInternal(gpu_factories_); + if (!supported_formats.unknown) { + for (size_t i = 0; i < supported_formats.sdp_formats.size(); ++i) { + if (IsSameFormat(format, supported_formats.sdp_formats[i])) { + encoder = std::make_unique<RTCVideoEncoder>( + supported_formats.profiles[i], gpu_factories_); + } } + } else { + auto profile = WebRTCFormatToCodecProfile(format); + if (profile) + encoder = std::make_unique<RTCVideoEncoder>(*profile, gpu_factories_); } - return nullptr; + return encoder; } std::vector<webrtc::SdpVideoFormat> RTCVideoEncoderFactory::GetSupportedFormats() const { - return supported_formats_; + return GetSupportedFormatsInternal(gpu_factories_).sdp_formats; } webrtc::VideoEncoderFactory::CodecInfo diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.h index 06236092987..72f0768a1db 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/optional.h" #include "media/base/video_codecs.h" #include "third_party/webrtc/api/video_codecs/video_encoder_factory.h" @@ -36,12 +37,6 @@ class RTCVideoEncoderFactory : public webrtc::VideoEncoderFactory { private: media::GpuVideoAcceleratorFactories* gpu_factories_; - // List of supported webrtc::SdpVideoFormat. |profiles_| and - // |supported_formats_| have the same length and the profile for - // |supported_formats_[i]| is |profiles_[i]|. - std::vector<media::VideoCodecProfile> profiles_; - std::vector<webrtc::SdpVideoFormat> supported_formats_; - DISALLOW_COPY_AND_ASSIGN(RTCVideoEncoderFactory); }; diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h index 173a1bddab5..9376feb9ced 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/rtc_void_request.h @@ -44,7 +44,7 @@ class RTCVoidRequest : public GarbageCollected<RTCVoidRequest> { virtual void RequestSucceeded() = 0; virtual void RequestFailed(const webrtc::RTCError&) = 0; - virtual void Trace(blink::Visitor* visitor) {} + virtual void Trace(Visitor* visitor) {} protected: RTCVoidRequest() = default; diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/two_keys_adapter_map.h b/chromium/third_party/blink/renderer/platform/peerconnection/two_keys_adapter_map.h index 792fc6562b7..75e3561bd2b 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/two_keys_adapter_map.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/two_keys_adapter_map.h @@ -5,11 +5,12 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_TWO_KEYS_ADAPTER_MAP_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_TWO_KEYS_ADAPTER_MAP_H_ -#include <map> #include <memory> #include <utility> #include "base/logging.h" +#include "base/optional.h" +#include "third_party/blink/renderer/platform/wtf/hash_map.h" namespace blink { @@ -27,10 +28,6 @@ namespace blink { // webrtc/blink object that was used to create the adapter and the secondary key // is based on the resulting blink/webrtc object after the adapter has been // initialized. -// -// TODO(crbug.com/787254): Move this class out of the Blink exposed API when -// its clients get Onion souped, and change the use of std::map below to -// WTF::HashMap. template <typename PrimaryKey, typename SecondaryKey, typename Value> class TwoKeysAdapterMap { public: @@ -41,13 +38,13 @@ class TwoKeysAdapterMap { // words |!FindByPrimary(primary)| must hold. Value* Insert(PrimaryKey primary, Value value) { DCHECK(entries_by_primary_.find(primary) == entries_by_primary_.end()); - auto it = entries_by_primary_ - .insert(std::make_pair( - std::move(primary), - std::unique_ptr<Entry>(new Entry(std::move(value))))) - .first; - it->second->primary_it = it; - return &it->second->value; + auto* add_result = + entries_by_primary_ + .insert(std::move(primary), + std::unique_ptr<Entry>(new Entry(std::move(value)))) + .stored_value; + add_result->value->primary_key = add_result->key; + return &add_result->value->value; } // Maps the secondary key to the value mapped by the primary key, increasing @@ -61,11 +58,10 @@ class TwoKeysAdapterMap { DCHECK(it != entries_by_primary_.end()); DCHECK(entries_by_secondary_.find(secondary) == entries_by_secondary_.end()); - Entry* entry = it->second.get(); - entry->secondary_it = - entries_by_secondary_ - .insert(std::make_pair(std::move(secondary), entry)) - .first; + Entry* entry = it->value.get(); + auto* add_result = + entries_by_secondary_.insert(std::move(secondary), entry).stored_value; + entry->secondary_key = add_result->key; } // Returns a pointer to the value mapped by the primary key, or null if the @@ -75,7 +71,7 @@ class TwoKeysAdapterMap { auto it = entries_by_primary_.find(primary); if (it == entries_by_primary_.end()) return nullptr; - return &it->second->value; + return &it->value->value; } // Returns a pointer to the value mapped by the secondary key, or null if the @@ -85,7 +81,7 @@ class TwoKeysAdapterMap { auto it = entries_by_secondary_.find(secondary); if (it == entries_by_secondary_.end()) return nullptr; - return &it->second->value; + return &it->value->value; } // Erases the value associated with the primary key, removing the mapping of @@ -95,8 +91,13 @@ class TwoKeysAdapterMap { auto primary_it = entries_by_primary_.find(primary); if (primary_it == entries_by_primary_.end()) return false; - if (primary_it->second->secondary_it != entries_by_secondary_.end()) - entries_by_secondary_.erase(primary_it->second->secondary_it); + + if (primary_it->value->secondary_key.has_value()) { + auto secondary_it = + entries_by_secondary_.find(*primary_it->value->secondary_key); + if (secondary_it != entries_by_secondary_.end()) + entries_by_secondary_.erase(secondary_it); + } entries_by_primary_.erase(primary_it); return true; } @@ -108,7 +109,10 @@ class TwoKeysAdapterMap { auto secondary_it = entries_by_secondary_.find(secondary); if (secondary_it == entries_by_secondary_.end()) return false; - entries_by_primary_.erase(secondary_it->second->primary_it); + + auto primary_it = + entries_by_primary_.find(secondary_it->value->primary_key); + entries_by_primary_.erase(primary_it); entries_by_secondary_.erase(secondary_it); return true; } @@ -117,21 +121,34 @@ class TwoKeysAdapterMap { size_t PrimarySize() const { return entries_by_primary_.size(); } // The number of elements in the map which have secondary keys. size_t SecondarySize() const { return entries_by_secondary_.size(); } - bool empty() const { return entries_by_primary_.empty(); } + bool empty() const { return entries_by_primary_.IsEmpty(); } private: - // TODO(crbug.com/787254): Move this class out of the Blink exposed API when - // its clients get Onion souped. struct Entry { Entry(Value value) : value(std::move(value)) {} Value value; - typename std::map<PrimaryKey, std::unique_ptr<Entry>>::iterator primary_it; - typename std::map<SecondaryKey, Entry*>::iterator secondary_it; + + // The primary and secondary keys are cached here, instead of the + // respective iterators, because WTF::HashMap invalidates iterators + // upon changes on the set (eg insertion, deletions). + // + // Entries are only created in TwoKeysAdapterMap::Insert, which initializes + // |primary_key| right afterward (so it can never be read while + // uninitialized). + PrimaryKey primary_key; + + // However, for |secondary_key|, calling EraseByPrimaryKey() can + // read an uninitialized secondary_key in case it is left uninitialized. + // Hence, it is guarded with base::Optional. + base::Optional<SecondaryKey> secondary_key; }; - typename std::map<PrimaryKey, std::unique_ptr<Entry>> entries_by_primary_; - typename std::map<SecondaryKey, Entry*> entries_by_secondary_; + using PrimaryMap = WTF::HashMap<PrimaryKey, std::unique_ptr<Entry>>; + using SecondaryMap = WTF::HashMap<SecondaryKey, Entry*>; + + PrimaryMap entries_by_primary_; + SecondaryMap entries_by_secondary_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/two_keys_adapter_map_unittest.cc b/chromium/third_party/blink/renderer/platform/peerconnection/two_keys_adapter_map_unittest.cc index 0d27ce7e9ae..511f5322f13 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/two_keys_adapter_map_unittest.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/two_keys_adapter_map_unittest.cc @@ -6,25 +6,27 @@ #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace blink { class TwoKeysAdapterMapTest : public ::testing::Test { public: struct MoveOnlyValue { - explicit MoveOnlyValue(std::string str) : str(std::move(str)) {} + explicit MoveOnlyValue(String str) : str(std::move(str)) {} MoveOnlyValue(MoveOnlyValue&& other) : str(std::move(other.str)) {} MoveOnlyValue& operator=(MoveOnlyValue&& other) { str = std::move(other.str); return *this; } - std::string str; + String str; DISALLOW_COPY_AND_ASSIGN(MoveOnlyValue); }; - TwoKeysAdapterMap<std::string, std::string, MoveOnlyValue> map_; + TwoKeysAdapterMap<String, String, MoveOnlyValue> map_; }; TEST_F(TwoKeysAdapterMapTest, ShouldInitiallyBeEmpty) { diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink.cc b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink.cc index 0f6c85f8258..2de94cef8fa 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink.cc @@ -11,10 +11,21 @@ #include "base/location.h" #include "base/logging.h" #include "base/stl_util.h" +#include "base/strings/stringprintf.h" +#include "media/base/audio_timestamp_helper.h" +#include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/webrtc/rtc_base/ref_counted_object.h" +namespace { + +void SendLogMessage(const std::string& message) { + blink::WebRtcLogMessage("WRAS::" + message); +} + +} // namespace + namespace WTF { template <> @@ -48,12 +59,14 @@ WebRtcAudioSink::WebRtcAudioSink( fifo_(ConvertToBaseRepeatingCallback( CrossThreadBindRepeating(&WebRtcAudioSink::DeliverRebufferedAudio, CrossThreadUnretained(this)))) { - DVLOG(1) << "WebRtcAudioSink::WebRtcAudioSink()"; + SendLogMessage(base::StringPrintf("WebRtcAudioSink({label=%s})", + adapter_->label().c_str())); } WebRtcAudioSink::~WebRtcAudioSink() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DVLOG(1) << "WebRtcAudioSink::~WebRtcAudioSink()"; + SendLogMessage(base::StringPrintf("~WebRtcAudioSink([label=%s])", + adapter_->label().c_str())); } void WebRtcAudioSink::SetAudioProcessor( @@ -72,6 +85,9 @@ void WebRtcAudioSink::SetLevel( void WebRtcAudioSink::OnEnabledChanged(bool enabled) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + SendLogMessage(base::StringPrintf("OnEnabledChanged([label=%s] {enabled=%s})", + adapter_->label().c_str(), + (enabled ? "true" : "false"))); PostCrossThreadTask( *adapter_->signaling_task_runner(), FROM_HERE, CrossThreadBindOnce( @@ -83,6 +99,16 @@ void WebRtcAudioSink::OnData(const media::AudioBus& audio_bus, base::TimeTicks estimated_capture_time) { // No thread check: OnData might be called on different threads (but not // concurrently). + + // TODO(crbug.com/1054769): Better to let |fifo_| handle the estimated capture + // time and let it return a corrected interpolated capture time to + // DeliverRebufferedAudio(). Current, similar treatment is used at different + // places where |AudioPushFifo| is applied. So a update to |AudioPushFifo| + // will be a joint effort, and should be carefully carried out. + last_estimated_capture_time_ = estimated_capture_time; + + adapter_->UpdateTimestampAligner(estimated_capture_time); + // The following will result in zero, one, or multiple synchronous calls to // DeliverRebufferedAudio(). fifo_.Push(audio_bus); @@ -90,6 +116,9 @@ void WebRtcAudioSink::OnData(const media::AudioBus& audio_bus, void WebRtcAudioSink::OnSetFormat(const media::AudioParameters& params) { DCHECK(params.IsValid()); + SendLogMessage(base::StringPrintf("OnSetFormat([label=%s] {params=[%s]})", + adapter_->label().c_str(), + params.AsHumanReadableString().c_str())); params_ = params; // Make sure that our params always reflect a buffer size of 10ms. params_.set_frames_per_buffer(params_.sample_rate() / 100); @@ -112,9 +141,14 @@ void WebRtcAudioSink::DeliverRebufferedAudio(const media::AudioBus& audio_bus, "ToInterleaved expects 2 bytes."); audio_bus.ToInterleaved<media::SignedInt16SampleTypeTraits>( audio_bus.frames(), interleaved_data_.get()); + + const base::TimeTicks estimated_capture_time = + last_estimated_capture_time_ + media::AudioTimestampHelper::FramesToTime( + frame_delay, params_.sample_rate()); + adapter_->DeliverPCMToWebRtcSinks(interleaved_data_.get(), params_.sample_rate(), audio_bus.channels(), - audio_bus.frames()); + audio_bus.frames(), estimated_capture_time); } namespace { @@ -129,14 +163,19 @@ WebRtcAudioSink::Adapter::Adapter( scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner) : webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>(label), + label_(label), source_(std::move(source)), signaling_task_runner_(std::move(signaling_task_runner)), main_task_runner_(std::move(main_task_runner)) { DCHECK(signaling_task_runner_); DCHECK(main_task_runner_); + SendLogMessage( + base::StringPrintf("Adapter::Adapter({label=%s})", label_.c_str())); } WebRtcAudioSink::Adapter::~Adapter() { + SendLogMessage( + base::StringPrintf("Adapter::~Adapter([label=%s])", label_.c_str())); if (audio_processor_) { PostCrossThreadTask(*main_task_runner_.get(), FROM_HERE, CrossThreadBindOnce(&DereferenceOnMainThread, @@ -148,11 +187,20 @@ void WebRtcAudioSink::Adapter::DeliverPCMToWebRtcSinks( const int16_t* audio_data, int sample_rate, size_t number_of_channels, - size_t number_of_frames) { + size_t number_of_frames, + base::TimeTicks estimated_capture_time) { base::AutoLock auto_lock(lock_); + + // This use |timestamp_aligner_| to transform |estimated_capture_timestamp| to + // rtc::TimeMicros(). See the comment at UpdateTimestampAligner() for more + // details. + const int64_t capture_timestamp_us = timestamp_aligner_.TranslateTimestamp( + estimated_capture_time.since_origin().InMicroseconds()); + for (webrtc::AudioTrackSinkInterface* sink : sinks_) { sink->OnData(audio_data, sizeof(int16_t) * 8, sample_rate, - number_of_channels, number_of_frames); + number_of_channels, number_of_frames, + capture_timestamp_us / rtc::kNumMicrosecsPerMillisec); } } @@ -163,6 +211,9 @@ std::string WebRtcAudioSink::Adapter::kind() const { bool WebRtcAudioSink::Adapter::set_enabled(bool enable) { DCHECK(!signaling_task_runner_ || signaling_task_runner_->RunsTasksInCurrentSequence()); + SendLogMessage( + base::StringPrintf("Adapter::set_enabled([label=%s] {enable=%s})", + label_.c_str(), (enable ? "true" : "false"))); return webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>::set_enabled( enable); } @@ -171,6 +222,8 @@ void WebRtcAudioSink::Adapter::AddSink(webrtc::AudioTrackSinkInterface* sink) { DCHECK(!signaling_task_runner_ || signaling_task_runner_->RunsTasksInCurrentSequence()); DCHECK(sink); + SendLogMessage( + base::StringPrintf("Adapter::AddSink({label=%s})", label_.c_str())); base::AutoLock auto_lock(lock_); DCHECK(!base::Contains(sinks_, sink)); sinks_.push_back(sink); @@ -180,6 +233,8 @@ void WebRtcAudioSink::Adapter::RemoveSink( webrtc::AudioTrackSinkInterface* sink) { DCHECK(!signaling_task_runner_ || signaling_task_runner_->RunsTasksInCurrentSequence()); + SendLogMessage( + base::StringPrintf("Adapter::RemoveSink([label=%s])", label_.c_str())); base::AutoLock auto_lock(lock_); const auto it = std::find(sinks_.begin(), sinks_.end(), sink); if (it != sinks_.end()) @@ -200,6 +255,8 @@ bool WebRtcAudioSink::Adapter::GetSignalLevel(int* level) { // Convert from float in range [0.0,1.0] to an int in range [0,32767]. *level = static_cast<int>(signal_level * std::numeric_limits<int16_t>::max() + 0.5f /* rounding to nearest int */); + // TODO(crbug/1073391): possibly log the signal level but first check the + // calling frequency of this method to avoid creating too much data. return true; } @@ -216,4 +273,16 @@ webrtc::AudioSourceInterface* WebRtcAudioSink::Adapter::GetSource() const { return source_.get(); } +void WebRtcAudioSink::Adapter::UpdateTimestampAligner( + base::TimeTicks capture_time) { + // The |timestamp_aligner_| stamps an audio frame as if it is captured 'now', + // taking rtc::TimeMicros as the reference clock. It does not provide the time + // that the frame was originally captured, Using |timestamp_aligner_| rather + // than calling rtc::TimeMicros is to take the advantage that it aligns its + // output timestamps such that the time spacing in the |capture_time| is + // maintained. + timestamp_aligner_.TranslateTimestamp( + capture_time.since_origin().InMicroseconds(), rtc::TimeMicros()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink.h b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink.h index b405636dffe..20782171732 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink.h @@ -24,6 +24,8 @@ #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/webrtc/api/media_stream_interface.h" #include "third_party/webrtc/pc/media_stream_track.h" +#include "third_party/webrtc/rtc_base/time_utils.h" +#include "third_party/webrtc/rtc_base/timestamp_aligner.h" namespace blink { @@ -100,7 +102,10 @@ class PLATFORM_EXPORT WebRtcAudioSink : public WebMediaStreamAudioSink { void DeliverPCMToWebRtcSinks(const int16_t* audio_data, int sample_rate, size_t number_of_channels, - size_t number_of_frames); + size_t number_of_frames, + base::TimeTicks estimated_capture_time); + + std::string label() const { return label_; } // webrtc::MediaStreamTrack implementation. std::string kind() const override; @@ -114,10 +119,14 @@ class PLATFORM_EXPORT WebRtcAudioSink : public WebMediaStreamAudioSink { override; webrtc::AudioSourceInterface* GetSource() const override; + void UpdateTimestampAligner(base::TimeTicks capture_time); + protected: ~Adapter() override; private: + const std::string label_; + const scoped_refptr<webrtc::AudioSourceInterface> source_; // Task runner for operations that must be done on libjingle's signaling @@ -147,6 +156,11 @@ class PLATFORM_EXPORT WebRtcAudioSink : public WebMediaStreamAudioSink { // receive the audio data. std::vector<webrtc::AudioTrackSinkInterface*> sinks_; + // Used for getting capture timestamps referenced on the rtc::TimeMicros() + // clock. See the comment at the implementation of UpdateTimestampAligner() + // for more details. + rtc::TimestampAligner timestamp_aligner_; + DISALLOW_COPY_AND_ASSIGN(Adapter); }; @@ -178,6 +192,8 @@ class PLATFORM_EXPORT WebRtcAudioSink : public WebMediaStreamAudioSink { // interleaved samples. std::unique_ptr<int16_t[]> interleaved_data_; + base::TimeTicks last_estimated_capture_time_; + // In debug builds, check that WebRtcAudioSink's public methods are all being // called on the main render thread. THREAD_CHECKER(thread_checker_); diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink_test.cc b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink_test.cc new file mode 100644 index 00000000000..0088ef19091 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink_test.cc @@ -0,0 +1,126 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/peerconnection/webrtc_audio_sink.h" + +#include "media/base/fake_single_thread_task_runner.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; + +namespace blink { + +namespace { + +class MockAudioSink : public webrtc::AudioTrackSinkInterface { + public: + MockAudioSink() = default; + ~MockAudioSink() override = default; + MOCK_METHOD6(OnData, + void(const void* audio_data, + int bits_per_sample, + int sample_rate, + size_t number_of_channels, + size_t number_of_samples, + absl::optional<int64_t> absolute_capture_timestamp_ms)); +}; + +class ScopedFakeClock : public rtc::ClockInterface { + public: + explicit ScopedFakeClock(int64_t init_time_ms) + : prev_clock_(rtc::SetClockForTesting(this)), + time_ns_(init_time_ms * rtc::kNumNanosecsPerMillisec) {} + + ~ScopedFakeClock() override { rtc::SetClockForTesting(prev_clock_); } + + int64_t TimeNanos() const override { return time_ns_; } + + void AdvanceTimeMilliseconds(int64_t time_ms) { + time_ns_ += time_ms * rtc::kNumNanosecsPerMillisec; + } + + private: + ClockInterface* const prev_clock_; + int64_t time_ns_; +}; + +} // namespace + +TEST(WebRtcAudioSinkTest, CaptureTimestamp) { + MockAudioSink sink_1; + MockAudioSink sink_2; + base::SimpleTestTickClock dummy_clock; + std::unique_ptr<WebRtcAudioSink> webrtc_audio_sink( + new WebRtcAudioSink("test_sink", nullptr, + /*signaling_task_runner=*/ + new media::FakeSingleThreadTaskRunner(&dummy_clock), + /*main_task_runner=*/ + new media::FakeSingleThreadTaskRunner(&dummy_clock))); + + // |web_media_stream_audio_sink| is to access methods that are privately + // inherited by WebRtcAudioSink. + WebMediaStreamAudioSink* const web_media_stream_audio_sink = + static_cast<WebMediaStreamAudioSink*>(webrtc_audio_sink.get()); + + webrtc_audio_sink->webrtc_audio_track()->AddSink(&sink_1); + webrtc_audio_sink->webrtc_audio_track()->AddSink(&sink_2); + + constexpr int kInputChannels = 2; + constexpr int kInputFramesPerBuffer = 96; + constexpr int kSampleRateHz = 8000; + constexpr int kOutputFramesPerBuffer = kSampleRateHz / 100; + constexpr int kEnqueueFrames = kInputFramesPerBuffer - kOutputFramesPerBuffer; + + constexpr int64_t kStartRtcTimestampMs = 87654321; + constexpr int64_t kStartCaptureTimestampMs = 12345678; + constexpr int64_t kCaptureIntervalMs = 567; + + web_media_stream_audio_sink->OnSetFormat(media::AudioParameters( + media::AudioParameters::AUDIO_PCM_LINEAR, media::CHANNEL_LAYOUT_STEREO, + kSampleRateHz, kOutputFramesPerBuffer)); + std::unique_ptr<media::AudioBus> bus = + media::AudioBus::Create(kInputChannels, kInputFramesPerBuffer); + bus->Zero(); + + { + ScopedFakeClock clock(kStartRtcTimestampMs); + + base::TimeTicks capture_time = + base::TimeTicks() + + base::TimeDelta::FromMilliseconds(kStartCaptureTimestampMs); + + // The first time to the call OnData(), the TimestampAligner should have no + // effect work. So expected capture timestamp is from fake_clock. + EXPECT_CALL( + sink_1, + OnData(_, _, kSampleRateHz, kInputChannels, kOutputFramesPerBuffer, + absl::make_optional<int64_t>(kStartRtcTimestampMs))); + EXPECT_CALL( + sink_2, + OnData(_, _, kSampleRateHz, kInputChannels, kOutputFramesPerBuffer, + absl::make_optional<int64_t>(kStartRtcTimestampMs))); + + web_media_stream_audio_sink->OnData(*bus, capture_time); + + capture_time += base::TimeDelta::FromMilliseconds(kCaptureIntervalMs); + clock.AdvanceTimeMilliseconds(kCaptureIntervalMs); + + constexpr int64_t kExpectedTimestampMs = + kStartRtcTimestampMs + kCaptureIntervalMs - + kEnqueueFrames * 1000 / kSampleRateHz; + EXPECT_CALL( + sink_1, + OnData(_, _, kSampleRateHz, kInputChannels, kOutputFramesPerBuffer, + absl::make_optional<int64_t>(kExpectedTimestampMs))); + EXPECT_CALL( + sink_2, + OnData(_, _, kSampleRateHz, kInputChannels, kOutputFramesPerBuffer, + absl::make_optional<int64_t>(kExpectedTimestampMs))); + + web_media_stream_audio_sink->OnData(*bus, capture_time); + } +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.cc b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.cc index 0fcc39afecb..3ed4b1a517a 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.cc +++ b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.cc @@ -67,12 +67,18 @@ gfx::Rect ScaleRectangle(const gfx::Rect& input_rect, namespace blink { +const base::Feature kWebRtcLogWebRtcVideoFrameAdapter{ + "WebRtcLogWebRtcVideoFrameAdapter", base::FEATURE_DISABLED_BY_DEFAULT}; + WebRtcVideoTrackSource::WebRtcVideoTrackSource( bool is_screencast, absl::optional<bool> needs_denoising) : AdaptedVideoTrackSource(/*required_alignment=*/1), is_screencast_(is_screencast), - needs_denoising_(needs_denoising) { + needs_denoising_(needs_denoising), + log_to_webrtc_(is_screencast && + base::FeatureList::IsEnabled( + blink::kWebRtcLogWebRtcVideoFrameAdapter)) { DETACH_FROM_THREAD(thread_checker_); } @@ -325,7 +331,11 @@ void WebRtcVideoTrackSource::DeliverFrame( webrtc::VideoFrame::Builder frame_builder = webrtc::VideoFrame::Builder() .set_video_frame_buffer( - new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(frame)) + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( + frame, + (log_to_webrtc_ + ? WebRtcVideoFrameAdapter::LogStatus::kLogToWebRtc + : WebRtcVideoFrameAdapter::LogStatus::kNoLogging))) .set_rotation(webrtc::kVideoRotation_0) .set_timestamp_us(timestamp_us); if (update_rect) { diff --git a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.h b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.h index 9a6c26f42cb..5fada0dbf5b 100644 --- a/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.h +++ b/chromium/third_party/blink/renderer/platform/peerconnection/webrtc_video_track_source.h @@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_WEBRTC_VIDEO_TRACK_SOURCE_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_WEBRTC_VIDEO_TRACK_SOURCE_H_ +#include "base/feature_list.h" #include "base/memory/scoped_refptr.h" #include "base/threading/thread_checker.h" #include "media/base/video_frame_pool.h" @@ -14,6 +15,8 @@ namespace blink { +PLATFORM_EXPORT extern const base::Feature kWebRtcLogWebRtcVideoFrameAdapter; + // This class implements webrtc's VideoTrackSourceInterface. To pass frames down // the webrtc video pipeline, each received a media::VideoFrame is converted to // a webrtc::VideoFrame, taking any adaptation requested by downstream classes @@ -80,6 +83,8 @@ class PLATFORM_EXPORT WebRtcVideoTrackSource absl::optional<FrameAdaptationParams> custom_frame_adaptation_params_for_testing_; + const bool log_to_webrtc_; + DISALLOW_COPY_AND_ASSIGN(WebRtcVideoTrackSource); }; diff --git a/chromium/third_party/blink/renderer/platform/platform.gni b/chromium/third_party/blink/renderer/platform/platform.gni index 1afa35e16a2..aa108489f23 100644 --- a/chromium/third_party/blink/renderer/platform/platform.gni +++ b/chromium/third_party/blink/renderer/platform/platform.gni @@ -25,9 +25,8 @@ template("blink_platform_sources") { assert( !defined(invoker.public_deps), "$target_name's public_deps should be moved to //third_party/blink/renderer/platform:blink_platform_public_deps") - deps = [ - "//third_party/blink/renderer/platform:blink_platform_public_deps", - ] + deps = + [ "//third_party/blink/renderer/platform:blink_platform_public_deps" ] if (defined(invoker.deps)) { deps += invoker.deps } diff --git a/chromium/third_party/blink/renderer/platform/prerender.cc b/chromium/third_party/blink/renderer/platform/prerender.cc deleted file mode 100644 index 5f2e2ce8b63..00000000000 --- a/chromium/third_party/blink/renderer/platform/prerender.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "third_party/blink/renderer/platform/prerender.h" - -#include "third_party/blink/public/platform/web_prerender.h" -#include "third_party/blink/public/platform/web_prerendering_support.h" -#include "third_party/blink/renderer/platform/prerender_client.h" - -namespace blink { - -Prerender::Prerender(PrerenderClient* client, - const KURL& url, - const unsigned rel_types, - const Referrer& referrer, - const SecurityOrigin* security_origin) - : client_(client), - url_(url), - rel_types_(rel_types), - referrer_(referrer), - security_origin_(security_origin) {} - -Prerender::~Prerender() = default; - -void Prerender::Trace(blink::Visitor* visitor) { - visitor->Trace(client_); -} - -void Prerender::Dispose() { - client_ = nullptr; - extra_data_ = nullptr; -} - -void Prerender::Add() { - if (WebPrerenderingSupport* platform = WebPrerenderingSupport::Current()) - platform->Add(WebPrerender(this)); -} - -void Prerender::Cancel() { - if (WebPrerenderingSupport* platform = WebPrerenderingSupport::Current()) - platform->Cancel(WebPrerender(this)); -} - -void Prerender::Abandon() { - if (WebPrerenderingSupport* platform = WebPrerenderingSupport::Current()) - platform->Abandon(WebPrerender(this)); -} - -void Prerender::DidStartPrerender() { - if (client_) - client_->DidStartPrerender(); -} - -void Prerender::DidStopPrerender() { - if (client_) - client_->DidStopPrerender(); -} - -void Prerender::DidSendLoadForPrerender() { - if (client_) - client_->DidSendLoadForPrerender(); -} - -void Prerender::DidSendDOMContentLoadedForPrerender() { - if (client_) - client_->DidSendDOMContentLoadedForPrerender(); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/prerender.h b/chromium/third_party/blink/renderer/platform/prerender.h deleted file mode 100644 index 06b32a45f55..00000000000 --- a/chromium/third_party/blink/renderer/platform/prerender.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PRERENDER_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PRERENDER_H_ - -#include "base/macros.h" -#include "base/memory/scoped_refptr.h" -#include "third_party/blink/renderer/platform/heap/handle.h" -#include "third_party/blink/renderer/platform/heap/heap.h" -#include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/weborigin/kurl.h" -#include "third_party/blink/renderer/platform/weborigin/referrer.h" -#include "third_party/blink/renderer/platform/weborigin/security_origin.h" -#include "third_party/blink/renderer/platform/wtf/ref_counted.h" -#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" - -namespace blink { - -class PrerenderClient; - -class PLATFORM_EXPORT Prerender final : public GarbageCollected<Prerender> { - DISALLOW_COPY_AND_ASSIGN(Prerender); - - public: - class ExtraData : public RefCounted<ExtraData> { - public: - virtual ~ExtraData() = default; - }; - - Prerender(PrerenderClient*, - const KURL&, - unsigned rel_types, - const Referrer&, - const SecurityOrigin* security_origin); - ~Prerender(); - void Trace(blink::Visitor*); - - void Dispose(); - - void Add(); - void Cancel(); - void Abandon(); - - const KURL& Url() const { return url_; } - unsigned RelTypes() const { return rel_types_; } - const String& GetReferrer() const { return referrer_.referrer; } - network::mojom::ReferrerPolicy GetReferrerPolicy() const { - return referrer_.referrer_policy; - } - const SecurityOrigin* GetSecurityOrigin() const { return security_origin_; } - - void SetExtraData(scoped_refptr<ExtraData> extra_data) { - extra_data_ = std::move(extra_data); - } - ExtraData* GetExtraData() { return extra_data_.get(); } - - void DidStartPrerender(); - void DidStopPrerender(); - void DidSendLoadForPrerender(); - void DidSendDOMContentLoadedForPrerender(); - - private: - // The embedder's prerendering support holds on to pending Prerender objects; - // those references should not keep the PrerenderClient alive -- if the client - // becomes otherwise unreachable it should be GCed (at which point it will - // abandon this Prerender object.) - WeakMember<PrerenderClient> client_; - - const KURL url_; - const unsigned rel_types_; - const Referrer referrer_; - const SecurityOrigin* const security_origin_; - - scoped_refptr<ExtraData> extra_data_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_PRERENDER_H_ diff --git a/chromium/third_party/blink/renderer/platform/prerender_client.h b/chromium/third_party/blink/renderer/platform/prerender_client.h deleted file mode 100644 index 0d56cf628c9..00000000000 --- a/chromium/third_party/blink/renderer/platform/prerender_client.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PRERENDER_CLIENT_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PRERENDER_CLIENT_H_ - -#include "third_party/blink/renderer/platform/heap/handle.h" -#include "third_party/blink/renderer/platform/platform_export.h" - -namespace blink { - -class PLATFORM_EXPORT PrerenderClient : public GarbageCollectedMixin { - public: - virtual ~PrerenderClient() = default; - - virtual void DidStartPrerender() = 0; - virtual void DidStopPrerender() = 0; - virtual void DidSendLoadForPrerender() = 0; - virtual void DidSendDOMContentLoadedForPrerender() = 0; - - void Trace(blink::Visitor* visitor) override {} -}; - -} // namespace blink - -#endif diff --git a/chromium/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/chromium/third_party/blink/renderer/platform/runtime_enabled_features.json5 index fb78dd6dfb9..502ead1d51b 100644 --- a/chromium/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/chromium/third_party/blink/renderer/platform/runtime_enabled_features.json5 @@ -94,11 +94,6 @@ valid_type: "bool", }, - // Set to true to have customised {feature}Enabled() method implementation. - custom: { - valid_type: "bool", - }, - // Feature policy IDL extended attribute (see crrev.com/2247923004). feature_policy: { }, @@ -116,14 +111,17 @@ }, { name: "AccessibilityExposeARIAAnnotations", - origin_trial_feature_name: "AccessibilityExposeARIAAnnotations", - status: "experimental", + status: "stable", }, { name: "AccessibilityExposeDisplayNone", status: "test", }, { + name: "AccessibilityExposeHTMLElement", + status: "experimental", + }, + { name: "AccessibilityObjectModel", status: "experimental", }, @@ -147,22 +145,25 @@ { name: "AllowSyncXHRInPageDismissal", origin_trial_feature_name: "AllowSyncXHRInPageDismissal", - status: "experimental", }, { name: "AnimationWorklet", - origin_trial_feature_name: "AnimationWorklet", status: "experimental", }, { name: "AOMAriaProperties", - status: "experimental", + status: "stable", }, { name: "AOMAriaRelationshipProperties", status: "experimental", }, { + // Enabled when blink::features::kAppCache is enabled. + name: "AppCache", + status: "stable", + }, + { // Use an aspect ratio from the HTML attributes even when we use sizing // from CSS. // https://github.com/WICG/intrinsicsize-attribute/issues/16 @@ -170,10 +171,6 @@ status: "stable", }, { - name: "AsyncClipboard", - status: "stable", - }, - { name: "AudioOutputDevices", // Android does not yet support switching of audio output devices status: {"Android": "", "default": "stable"}, @@ -228,56 +225,41 @@ }, { name: "Badging", - origin_trial_feature_name: "BadgingV2", - status: "experimental", + status: "stable", }, { - name: "BidiCaretAffinity", + name: "BarcodeDetector", + status: "stable", }, { - name: "BlinkRuntimeCallStats", + name: "BidiCaretAffinity", }, { - // Adding simpler reading methods - stream(), text(), and arrayBuffer() - - // to the Blob interface. See: https://github.com/w3c/FileAPI/pull/117 - name: "BlobReadMethods", - status: "stable" + name: "BlinkRuntimeCallStats", }, { name: "BlockCredentialedSubresources", status: "stable", }, { + name: "BlockFlowHandlesWebkitLineClamp", + }, + { name: "BlockHTMLParserOnStyleSheets", }, { name: "BlockingDownloadsInSandbox", - status: "test", + status: "stable", }, { name: "BlockingFocusWithoutUserActivation", status: "experimental", }, { - name: "BuiltInModuleAll", - implied_by: ["ExperimentalProductivityFeatures"], - }, - { - name: "BuiltInModuleInfra", - origin_trial_feature_name: "BuiltInModuleInfra", - implied_by: ["ExperimentalProductivityFeatures", - "BuiltInModuleAll", - "BuiltInModuleKvStorage", - "BuiltInModuleSwitchElement"], - }, - { - name: "BuiltInModuleKvStorage", - origin_trial_feature_name: "BuiltInModuleKvStorage", - implied_by: ["ExperimentalProductivityFeatures"], + name: "BrowserVerifiedUserActivationKeyboard", }, { - name: "BuiltInModuleSwitchElement", - implied_by: ["BuiltInModuleAll"], + name: "BrowserVerifiedUserActivationMouse", }, { name: "CacheInlineScriptCode" @@ -311,6 +293,10 @@ status: "experimental", }, { + name: "CaptureTimeInCsrc", + status: "stable", + }, + { name: "ClickPointerEvent", status: "experimental", }, @@ -324,6 +310,8 @@ // NativeFileSystem is in Origin Trial, which doesn't support having // non-origin-trial-enabled features depend on it. https://crbug.com/1000486 // depends_on: ["NativeFileSystem"] + origin_trial_feature_name: "NativeFileSystem2", + origin_trial_os: ["win", "macosx", "linux", "chromeos"], }, { name: "CompositeAfterPaint", @@ -364,12 +352,19 @@ status: "experimental", }, { + name: "ConversionMeasurement", + status: "test", + }, + { name: "CookieDeprecationMessages", status: "experimental", }, { - name: "CookieStore", - origin_trial_feature_name: "CookieStore", + name: "CookieStoreDocument", + status: "experimental", + }, + { + name: "CookieStoreWorker", status: "experimental", }, { @@ -381,7 +376,6 @@ }, { name: "CorsRFC1918", - depends_on: ["AddressSpace"], }, { name: "CSS3Text", @@ -392,9 +386,8 @@ status: "stable", }, { - name: "CSSAdditiveAnimations", - depends_on: ["StackedCSSPropertyAnimations"], - status: "experimental", + name: "CSSAspectRatioProperty", + status: "test", }, { name: "CSSCalcAsInt", @@ -402,17 +395,21 @@ }, { name: "CSSCascade", + status: "stable", }, - // Support for the CSS color-scheme property from the css-color-adjust spec. - // https://drafts.csswg.org/css-color-adjust/#color-scheme-prop { + // Support for the CSS color-scheme property from the css-color-adjust spec. + // https://drafts.csswg.org/css-color-adjust/#color-scheme-prop name: "CSSColorScheme", + status: "stable", }, { - // CSS min(), max() and clamp() - // https://drafts.csswg.org/css-values-4/#comp-func - name: "CSSComparisonFunctions", - status: "stable", + // When the color-scheme is supported via the CSS color-scheme property + // (CSSColorScheme) or the meta tag (MetaColorScheme), the only UA + // rendering change is for the canvas background and the :root element + // color property. Enabling this runtime flag will enable dark UA + // rendering for form controls, scrollbars, etc. + name: "CSSColorSchemeUARendering", }, { name: "CSSFocusVisible", @@ -434,11 +431,11 @@ status: "experimental", }, { - // Support for CSS intrinsic-* sizing properties. - // https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override + // Support for CSS contain-intrinsic-size property. + // https://wicg.github.io/display-locking/contain-intrinsic-size.html name: "CSSIntrinsicSize", - implied_by: ["DisplayLocking"], - status: "experimental", + implied_by: ["CSSSubtreeVisibility"], + status: "stable", }, { name: "CSSLayoutAPI", @@ -462,6 +459,15 @@ status: "experimental", }, { + name: "CSSMathStyle", + status: "experimental", + implied_by: ["MathMLCore"], + }, + { + name: "CSSModules", + status: "experimental", + }, + { name: "CSSOffsetPathRay", status: "experimental", }, @@ -496,7 +502,9 @@ status: "experimental", }, { - name: "CSSRenderSubtree", + // Perform partial style and layout invalidation on web font loading. + // See https://crbug.com/441925 and https://bit.ly/35JjPmq for details. + name: "CSSReducedFontLoadingInvalidations", status: "test", }, { @@ -504,6 +512,32 @@ status: "experimental", }, { + // The main subtree-visibility feature. + // https://wicg.github.io/display-locking/ + name: "CSSSubtreeVisibility", + status: "experimental", + implied_by: ["CSSSubtreeVisibilityHiddenMatchable"] + }, + { + // The subtree-visibility activation event which will be replaced by + // the beforematch event. When beforematch is available, this feaure + // will be removed. + name: "CSSSubtreeVisibilityActivationEvent", + implied_by: ["CSSSubtreeVisibility"] + }, + { + // The subtree-visibility: hidden-matchable feature. This is a planned + // follow-up to the main CSSSubtreeVisibility feature, thus it is gated + // by a different flag. + // https://wicg.github.io/display-locking/ + name: "CSSSubtreeVisibilityHiddenMatchable", + status: "experimental" + }, + { + name: "CSSSupportsSelector", + status: "stable", + }, + { name: "CSSVariables2", status: "stable", }, @@ -551,7 +585,12 @@ status: "stable", }, { + name: "DeclarativeShadowDOM", + status: "experimental", + }, + { name: "DecodeJpeg420ImagesToYUV", + status: "test", }, { name: "DecodeLossyWebPImagesToYUV", @@ -571,19 +610,10 @@ status: "experimental", }, { - name: "DiscardInputToMovingIframes", - status: "stable", - }, - { name: "DisplayCutoutAPI", settable_from_internals: true, }, { - name: "DisplayLocking", - origin_trial_feature_name: "DisplayLocking", - status: "experimental", - }, - { name: "DocumentCookie", }, { @@ -591,6 +621,8 @@ }, { name: "DocumentPolicy", + origin_trial_feature_name: "DocumentPolicy", + status: "experimental", }, { name: "DocumentWrite", @@ -605,12 +637,8 @@ name: "EditingNG", }, { - name: "EmbeddedVTTStylesheets", - status: "experimental", - }, - { name: "EncryptedMediaEncryptionSchemeQuery", - status: "test", + status: "stable", }, { name: "EncryptedMediaHdcpPolicyCheck", @@ -679,11 +707,11 @@ name: "ExtraWebGLVideoTextureMetadata", }, { - name: "FallbackCursorMode", + name: "FaceDetector", + status: "experimental", }, { - name: "FastBorderRadius", - status: "experimental", + name: "FallbackCursorMode", }, { name: "FeaturePolicyForClientHints", @@ -707,18 +735,13 @@ name: "FeaturePolicyVibrateFeature" }, { - name: "FetchMetadata", - status: "stable" - }, - { - name: "FetchMetadataDestination", - status: "experimental" - }, - { + // Also enabled when blink::features::kFileHandlingAPI is overridden + // on the command line (or via chrome://flags). name: "FileHandling", - // NativeFileSystem is in Origin Trial, which doesn't support having - // non-origin-trial-enabled features depend on it. https://crbug.com/1000486 - // depends_on: ["NativeFileSystem"] + depends_on: ["NativeFileSystem"], + status: {"Android": "test", "default": "experimental"}, + origin_trial_feature_name: "FileHandling", + origin_trial_os: ["win", "macosx", "linux", "chromeos"], }, { name: "FileSystem", @@ -735,6 +758,10 @@ settable_from_internals: true, }, { + name: "FontAccess", + status: "experimental", + }, + { name: "FontSrcLocalMatching", // No status, as the web platform runtime enabled feature is controlled by // a Chromium level feature. @@ -748,22 +775,23 @@ name: "ForceDeferScriptIntervention", }, { - name: "ForceOverlayFullscreenVideo", + // This is used in tests to perform memory measurement without + // waiting for GC. + name:"ForceEagerMeasureMemory", }, { - name: "ForceSynchronousHTMLParsing", + name: "ForceLoadAtTop", + origin_trial_feature_name: "ForceLoadAtTop", }, { - name: "ForceTallerSelectPopup", - status: {"ChromeOS": "stable"}, + name: "ForceOverlayFullscreenVideo", }, - // This is to communicate features::FormControlsRefresh from ui (and can be - // removed when the feature launches). { - name: "FormControlsRefresh", + name: "ForceSynchronousHTMLParsing", }, { - name: "FractionalMouseEvent", + name: "ForceTallerSelectPopup", + status: {"ChromeOS": "stable"}, }, { name: "FractionalScrollOffsets", @@ -779,7 +807,7 @@ }, { name: "GetDisplayMedia", - status: "stable", + status: {"Android": "", "default": "stable"}, }, { name: "GroupEffect", @@ -789,7 +817,7 @@ name: "HrefTranslate", depends_on: ["TranslateService"], origin_trial_feature_name: "HrefTranslate", - status: "experimental", + status: "stable", }, // TODO(937746): Web Components v0 is disabled by default, and will be // removed after M87. @@ -806,7 +834,7 @@ }, { name: "IDBRelaxedDurability", - status: "experimental", + status: "stable", }, { name: "IdleDetection", @@ -818,7 +846,7 @@ }, { name: "ImageOrientation", - status: "test", + status: "stable", }, { name: "ImplicitRootScroller", @@ -827,13 +855,17 @@ }, { name: "ImportMaps", - implied_by: ["ExperimentalProductivityFeatures", "BuiltInModuleInfra"], + implied_by: ["ExperimentalProductivityFeatures"], }, { name: "InertAttribute", status: "experimental", }, { + name: "InputElementRawValue", + status: "experimental", + }, + { name: "InputMultipleFieldsUI", // No plan to support complex UI for date/time INPUT types on Android. status: {"Android": "test", "default": "stable"}, @@ -844,7 +876,7 @@ }, { name: "IntersectionObserverDocumentScrollingElementRoot", - status: "experimental", + status: "stable", }, { name: "IntersectionObserverV2", @@ -866,9 +898,6 @@ name: "IsolatedWorldCSP" }, { - name: "JSONModules", - }, - { name: "KeyboardFocusableScrollers", status: "experimental", }, @@ -880,8 +909,14 @@ status: "experimental", }, { + // LayoutNG has been enabled in M76, but we still keep this flag for + // testing. See web_tests/FlagExpectations/disable-layout-ng for more + // details about running web tests with LayoutNG disabled. This flag also + // provides a convenient way for testing legacy layout code path in blink + // unit tests. name: "LayoutNG", - implied_by: ["LayoutNGBlockFragmentation", "LayoutNGFieldset", "LayoutNGFlexBox", "LayoutNGFragmentItem", "LayoutNGLineCache", "EditingNG", "BidiCaretAffinity", "LayoutNGTable"], + // Keep this list in sync with the one in LayoutNGFlexBox below. + implied_by: ["LayoutNGBlockFragmentation", "LayoutNGFieldset", "LayoutNGFragmentItem", "LayoutNGLineCache", "EditingNG", "BidiCaretAffinity", "LayoutNGTable", "LayoutNGFragmentTraversal"], status: "stable", }, { @@ -892,17 +927,22 @@ }, { name: "LayoutNGFlexBox", + implied_by: ["LayoutNGBlockFragmentation", "LayoutNGFieldset", "LayoutNGFragmentItem", "LayoutNGLineCache", "EditingNG", "BidiCaretAffinity", "LayoutNGTable", "LayoutNGFragmentTraversal"], + status: "experimental", }, { - name: "LayoutNGFragmentCaching", - implied_by: ["LayoutNG"], + name: "LayoutNGForControls", + depends_on: ["LayoutNG"], + status: "experimental", }, { name: "LayoutNGFragmentItem", + implied_by: ["LayoutNGBlockFragmentation", "LayoutNGFragmentTraversal"], }, { - name: "LayoutNGFragmentPaint", - implied_by: ["LayoutNG"], + // Traverse the fragment tree when painting and hit-testing, instead of + // the layout object tree. + name: "LayoutNGFragmentTraversal", }, { name: "LayoutNGLineCache", @@ -937,20 +977,17 @@ // Enabled by features::kLegacyWindowsDWriteFontFallback; }, { - name: "LinkSystemColors", - status: "stable", - }, - { name:"ManualSlotting", status:"experimental", }, { name: "MathMLCore", - status:"test", + status:"experimental", depends_on: ["LayoutNG"], }, { name:"MeasureMemory", + origin_trial_feature_name: "MeasureMemory", status:"experimental", }, { @@ -1007,10 +1044,6 @@ name: "MediaQueryNavigationControls", }, { - name: "MediaQueryPrefersColorScheme", - status: "stable", - }, - { name: "MediaQueryShape", status: "experimental", }, @@ -1020,7 +1053,7 @@ }, { name: "MediaSessionPosition", - status: "experimental", + status: "stable", }, { name: "MediaSessionSeeking", @@ -1051,6 +1084,7 @@ // https://drafts.csswg.org/css-color-adjust/#color-scheme-meta { name: "MetaColorScheme", + status: "stable", }, // This is enabled by default on Windows only. The only part that's // "experimental" is the support on other platforms. @@ -1063,14 +1097,14 @@ name: "MobileLayoutTheme", }, { - name: "ModuleDedicatedWorker", - status: "stable", - }, - { name: "ModuleServiceWorker", status: "test", }, { + name: "ModuleSharedWorker", + status: "stable", + }, + { name: "MojoJS", status: "test", }, @@ -1085,14 +1119,23 @@ name: "MouseSubframeNoImplicitCapture", }, { + // Named pages for pagination (the "page" CSS property). + name: "NamedPages", + status: "experimental", + }, + { // Also enabled when blink::features::kNativeFileSystemAPI is overridden // on the command line (or via chrome://flags). name: "NativeFileSystem", status: {"Android": "test", "default": "experimental"}, - origin_trial_feature_name: "NativeFileSystem", + origin_trial_feature_name: "NativeFileSystem2", origin_trial_os: ["win", "macosx", "linux", "chromeos"], }, { + name: "NativeIO", + status: "experimental", + }, + { name: "NavigatorContentUtils", // Android does not yet support NavigatorContentUtils. status: {"Android": "", "default": "stable"}, @@ -1118,10 +1161,6 @@ name: "NewRemotePlaybackPipeline", }, { - name: "NewSystemColors", - status: "stable", - }, - { name: "NoIdleEncodingForWebTests", status: "test", }, @@ -1148,6 +1187,7 @@ }, { name: "OffMainThreadCSSPaint", + status: "stable", }, { name: "OffscreenCanvasCommit", @@ -1162,6 +1202,10 @@ name: "OrientationEvent", status: {"Android": "stable"}, }, + { + name: "OriginPolicy", + status: "experimental", + }, // Define a sample API for testing integration with the Origin Trials // Framework. The sample API is used in both unit and web tests for the @@ -1251,7 +1295,6 @@ // This is to add an option to enable the Reveal button on password inputs while waiting ::reveal gets standardized. { name: "PasswordReveal", - depends_on: ["FormControlsRefresh"], }, { name: "PaymentApp", @@ -1266,6 +1309,10 @@ status: "stable", }, { + name: "PaymentHandlerMinimalUI", + status: "experimental", + }, + { name: "PaymentMethodChangeEvent", status: "stable", }, @@ -1283,14 +1330,11 @@ status: "stable", }, { - name: "PerformanceManagerInstrumentation", + name: "PercentBasedScrolling", + settable_from_internals: true, }, { - // Flag enabling the performance observers buffered flag. The buffered - // flag indicates whether or not the buffered entries should be loaded - // into the observer's buffer upon registration. See crbug.com/725567. - name: "PerformanceObserverBufferedFlag", - status: "stable", + name: "PerformanceManagerInstrumentation", }, { name: "PeriodicBackgroundSync", @@ -1344,6 +1388,7 @@ { name: "Portals", status: "test", + origin_trial_feature_name: "Portals", }, { name: "PostAnimationFrame", @@ -1367,9 +1412,6 @@ status: "stable", }, { - name: "PrintBrowser", - }, - { name: "PriorityHints", origin_trial_feature_name: "PriorityHints", status: "experimental", @@ -1387,7 +1429,12 @@ status: "experimental", }, { + // Enabled when blink::features::kRawClipboard is enabled. + name: "RawClipboard", + }, + { name: "ReducedReferrerGranularity", + status: "experimental", }, { name: "RemotePlayback", @@ -1400,6 +1447,15 @@ status: {"Android": "stable", "default": ""}, }, { + // See https://crbug.com/1012063 + name: "RequestVideoFrameCallback", + status: "stable", + }, + { + name: "ResizeObserverUpdates", + status: "test", + }, + { name: "RestrictAppCacheToSecureContexts", status: "stable", }, @@ -1414,10 +1470,6 @@ status: "stable", }, { - name: "RestrictedWebkitAppearance", - status: "stable", - }, - { name: "RtcAudioJitterBufferMaxPackets", origin_trial_feature_name: "RtcAudioJitterBufferMaxPackets", status: "experimental", @@ -1427,28 +1479,24 @@ origin_trial_feature_name: "RtcAudioJitterBufferRtxHandling", status: "experimental", }, - // Enables the use of the RTCDtlsTransport object. - { - name: "RTCDtlsTransport", - status: "stable", - }, // Enables the use of the RTCIceTransport with extensions. { name: "RTCIceTransportExtension", origin_trial_feature_name: "RTCQuicTransport", status: "experimental", }, + // Enables the use of Insertable Streams. + { + name: "RTCInsertableStreams", + origin_trial_feature_name: "RTCInsertableStreams", + status: "experimental", + }, // Enables the use of the RTCQuicTransport object. { name: "RTCQuicTransport", origin_trial_feature_name: "RTCQuicTransport", status: "experimental", }, - // Enables the use of the RTCSctpTransport object. - { - name: "RTCSctpTransport", - status: "stable", - }, { name: "RTCStatsRelativePacketArrivalDelay", origin_trial_feature_name: "RTCStatsRelativePacketArrivalDelay", @@ -1481,6 +1529,11 @@ name: "ScreenEnumeration", status: "experimental", }, + { + name: "ScreenWakeLock", + origin_trial_feature_name: "WakeLock", + status: "experimental", + }, // WebSpeech API with both speech recognition and synthesis functionality // is not fully enabled on all platforms. { @@ -1499,12 +1552,11 @@ }, { name: "ScrollSnapAfterLayout", - status: "experimental", + status: "stable", }, { name: "ScrollTimeline", status: "experimental", - origin_trial_feature_name: "AnimationWorklet", implied_by: ['AnimationWorklet'] }, // Implements documentElement.scrollTop/Left and bodyElement.scrollTop/Left @@ -1556,10 +1608,6 @@ status: "experimental", }, { - name: "ShapeDetection", - status: "experimental", - }, - { name: "SharedArrayBuffer", status: "stable", }, @@ -1594,9 +1642,10 @@ }, { name: "SmsReceiver", - origin_trial_feature_name: "SmsReceiver", - origin_trial_os: ["android"], - status: {"default": "experimental"}, + status: {"default": "experimental", "Android": "experimental"}, + }, + { + name: "SquashAfterPaint", }, // Used as argument in attribute of stable-release functions/interfaces // where a runtime-enabled feature name is required for correct IDL syntax. @@ -1606,19 +1655,11 @@ status: "stable", }, { - name: "StackedCSSPropertyAnimations", - status: "experimental", - }, - { // Enabled when blink::features::kStorageAccessAPI is enabled. name: "StorageAccessAPI", status: "test", }, { - name: "StorageQuotaDetails", - status: "stable" - }, - { name: "StrictMimeTypesForWorkers", status: "experimental" }, @@ -1627,9 +1668,31 @@ status: "stable", }, { + name: "SystemWakeLock", + status: "experimental", + }, + // For unit tests. + { + name: "TestFeature", + }, + // For unit tests. + { + name: "TestFeatureDependent", + depends_on: ["TestFeatureImplied"], + }, + // For unit tests. + { + name: "TestFeatureImplied", + implied_by: ["TestFeature"], + }, + { + name: "TextDetector", + status: "experimental", + }, + { name: "TextFragmentIdentifiers", origin_trial_feature_name: "TextFragmentIdentifiers", - status: "experimental", + status: "stable", }, { name: "TimerThrottlingForBackgroundTabs", @@ -1639,6 +1702,10 @@ name: "TimerThrottlingForHiddenFrames", status: "stable", }, + { + name: "TimeZoneChangeEvent", + status: "experimental", + }, // Many websites disable mouse support when touch APIs are available. We'd // like to enable this always but can't until more websites fix this bug. // Chromium sets this conditionally (eg. based on the presence of a @@ -1657,6 +1724,8 @@ { name: "TransferableStreams", status: "experimental", + origin_trial_feature_name: "RTCInsertableStreams", + implied_by: ["RTCInsertableStreams"], }, // This is conditionally set if the platform supports translation. { @@ -1664,13 +1733,21 @@ }, { name: "TrustedDOMTypes", - status: "experimental", + status: "stable", + }, + { + name: "TrustTokens", + status: "test", }, { name: "UnclosedFormControlIsInvalid", status: "experimental", }, { + name: "UnderlineOffsetThickness", + status: "test", + }, + { name: "UnifiedPointerCaptureInBlink", status: "stable", }, @@ -1691,10 +1768,6 @@ implied_by: ["ExperimentalProductivityFeatures"] }, { - name: "UpdateHoverAtBeginFrame", - settable_from_internals: true, - }, - { name: "UserActivationAPI", status: "stable", }, @@ -1722,9 +1795,6 @@ settable_from_internals: true, }, { - name: "VideoFullscreenDetection", - }, - { name: "VideoFullscreenOrientationLock", }, { @@ -1732,11 +1802,6 @@ status: "stable", }, { - // See https://crbug.com/1012063 - name: "VideoRequestAnimationFrame", - status: "experimental", - }, - { name: "VideoRotateToFullscreen", }, { @@ -1745,11 +1810,7 @@ { name: "WakeLock", origin_trial_feature_name: "WakeLock", - status: "experimental", - }, - { - name: "WasmCodeCache", - status: "experimental", + implied_by: ['ScreenWakeLock', 'SystemWakeLock'], }, { name: "WebAnimationsAPI", @@ -1787,10 +1848,22 @@ }, }, { + name: "WebBluetoothGetDevices", + status: "experimental", + }, + { name: "WebBluetoothScanning", status: "experimental", }, { + name: "WebCodecs", + status: "test", + }, + { + name: "WebCryptoCurve25519", + status: "experimental", + }, + { name: "WebGL2ComputeContext", status: "experimental", }, @@ -1814,15 +1887,14 @@ status: "experimental", }, { - name: "WebkitBoxLayoutUsesFlexLayout", - status: "stable", - }, - { name: "WebNFC", + origin_trial_feature_name: "WebNFC", + origin_trial_os: ["android"], status: "experimental", }, { name: "WebScheduler", + origin_trial_feature_name: "WebScheduler", status: "experimental", }, // WebShare is enabled by default on Android. @@ -1858,32 +1930,24 @@ status: "stable", }, { - name: "WebXRAnchors", - // depends_on: ["WebXRARModule"], // TODO(https://crbug.com/954679): uncomment once bug is fixed - }, - { - name: "WebXRARDOMOverlay", - // depends_on: ["WebXRARModule"], // TODO(https://crbug.com/954679): uncomment once bug is fixed - status: "experimental", - }, - { name: "WebXRARModule", depends_on: ["WebXR"], - status: "experimental", + status: "stable", }, { - name: "WebXrGamepadModule", - // depends_on: ["WebXR"], // TODO(https://crbug.com/954679): uncomment once bug is fixed + name: "WebXRHitTest", + depends_on: ["WebXRARModule"], status: "stable", }, { - name: "WebXRHitTest", - // depends_on: ["WebXRARModule"], // TODO(https://crbug.com/954679): uncomment once bug is fixed + name: "WebXRHitTestEntityTypes", + depends_on: ["WebXRHitTest"], status: "experimental" }, { - name: "WebXRPlaneDetection", - // depends_on: ["WebXRARModule"], // TODO(https://crbug.com/954679): uncomment once bug is fixed + name: "WebXRIncubations", + depends_on: ["WebXRARModule"], + status: "experimental", }, // Extends window placement functionality for multi-screen devices. { @@ -1891,6 +1955,15 @@ status: "experimental", }, { + name: "WritableFileStream", + status: "experimental", + // NativeFileSystem is in Origin Trial, which doesn't support having + // non-origin-trial-enabled features depend on it. https://crbug.com/1000486 + // depends_on: ["NativeFileSystem"] + origin_trial_feature_name: "NativeFileSystem2", + origin_trial_os: ["win", "macosx", "linux", "chromeos"], + }, + { name: "XSLT", status: "stable", }, diff --git a/chromium/third_party/blink/renderer/platform/runtime_enabled_features_test.cc b/chromium/third_party/blink/renderer/platform/runtime_enabled_features_test.cc new file mode 100644 index 00000000000..099680c6127 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/runtime_enabled_features_test.cc @@ -0,0 +1,275 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" + +namespace blink { +namespace { + +class RuntimeEnabledFeaturesTest : public testing::Test { + void CheckAllDisabled() { + CHECK(!RuntimeEnabledFeatures::TestFeatureEnabled()); + CHECK(!RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + CHECK(!RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + CHECK(!RuntimeEnabledFeatures::OriginTrialsSampleAPIEnabledByRuntimeFlag()); + CHECK(!RuntimeEnabledFeatures:: + OriginTrialsSampleAPIImpliedEnabledByRuntimeFlag()); + CHECK(!RuntimeEnabledFeatures:: + OriginTrialsSampleAPIDependentEnabledByRuntimeFlag()); + } + void SetUp() override { CheckAllDisabled(); } + void TearDown() override { + backup_.Restore(); + CheckAllDisabled(); + } + RuntimeEnabledFeatures::Backup backup_; +}; + +// Test setup: +// TestFeatureDependent +// depends_on +// TestFeatureImplied +// implied_by +// TestFeature + +TEST_F(RuntimeEnabledFeaturesTest, Relationship) { + // Internal status: false, false, false. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + RuntimeEnabledFeatures::SetTestFeatureEnabled(true); + // Internal status: true, false, false. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + // Implied by TestFeature. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + RuntimeEnabledFeatures::SetTestFeatureImpliedEnabled(true); + // Internal status: true, true, false. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + RuntimeEnabledFeatures::SetTestFeatureDependentEnabled(true); + // Internal status: true, true, true. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + RuntimeEnabledFeatures::SetTestFeatureImpliedEnabled(false); + // Internal status: true, false, true. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + // Implied by TestFeature. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + RuntimeEnabledFeatures::SetTestFeatureEnabled(false); + // Internal status: false, false, true. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + // Depends on TestFeatureImplied. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + RuntimeEnabledFeatures::SetTestFeatureImpliedEnabled(true); + // Internal status: false, true, true. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + RuntimeEnabledFeatures::SetTestFeatureDependentEnabled(false); + // Internal status: false, true, false. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); +} + +TEST_F(RuntimeEnabledFeaturesTest, ScopedForTest) { + // Internal status: false, false, false. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + { + ScopedTestFeatureForTest f1(true); + // Internal status: true, false, false. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + // Implied by TestFeature. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + { + ScopedTestFeatureImpliedForTest f2(true); + // Internal status: true, true, false. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + { + ScopedTestFeatureDependentForTest f3(true); + // Internal status: true, true, true. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + { + ScopedTestFeatureDependentForTest f3a(false); + // Internal status: true, true, true. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + } + // Internal status: true, true, true. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + } + } + // Internal status: true, false, false. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + // Implied by TestFeature. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + { + ScopedTestFeatureImpliedForTest f2a(false); + // Internal status: true, false, false. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + // Implied by TestFeature. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + } + } + // Internal status: false, false, false. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + { + ScopedTestFeatureDependentForTest f3(true); + // Internal status: false, false, true. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + // Depends on TestFeatureImplied. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + { + ScopedTestFeatureImpliedForTest f2(true); + // Internal status: false, true, true. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + { + ScopedTestFeatureForTest f1(true); + // Internal status: true, true, true. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + } + // Internal status: false, true, true. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + } + // Internal status: false, false, true. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + // Depends on TestFeatureImplied. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + { + ScopedTestFeatureImpliedForTest f2(true); + // Internal status: false, true, true. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + } + } + // Internal status: false, false, false. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); +} + +TEST_F(RuntimeEnabledFeaturesTest, BackupRestore) { + // Internal status: false, false, false. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + RuntimeEnabledFeatures::SetTestFeatureEnabled(true); + RuntimeEnabledFeatures::SetTestFeatureDependentEnabled(true); + // Internal status: true, false, true. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + // Implied by TestFeature. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + RuntimeEnabledFeatures::Backup backup; + + RuntimeEnabledFeatures::SetTestFeatureEnabled(false); + RuntimeEnabledFeatures::SetTestFeatureImpliedEnabled(true); + RuntimeEnabledFeatures::SetTestFeatureDependentEnabled(false); + // Internal status: false, true, false. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + backup.Restore(); + // Should restore the internal status to: true, false, true. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureEnabled()); + // Implied by TestFeature. + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + EXPECT_TRUE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); + + RuntimeEnabledFeatures::SetTestFeatureEnabled(false); + // Internal status: false, false, true. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureEnabled()); + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureImpliedEnabled()); + // Depends on TestFeatureImplied. + EXPECT_FALSE(RuntimeEnabledFeatures::TestFeatureDependentEnabled()); +} + +// Test setup: +// OriginTrialsSampleAPIImplied impled_by \ +// OriginTrialsSampleAPI +// OriginTrialsSampleAPIDependent depends_on / +TEST_F(RuntimeEnabledFeaturesTest, OriginTrialsByRuntimeEnabled) { + // Internal status: false, false, false. + EXPECT_FALSE( + RuntimeEnabledFeatures::OriginTrialsSampleAPIEnabledByRuntimeFlag()); + EXPECT_FALSE(RuntimeEnabledFeatures:: + OriginTrialsSampleAPIImpliedEnabledByRuntimeFlag()); + EXPECT_FALSE(RuntimeEnabledFeatures:: + OriginTrialsSampleAPIDependentEnabledByRuntimeFlag()); + + RuntimeEnabledFeatures::SetOriginTrialsSampleAPIEnabled(true); + // Internal status: true, false, false. + EXPECT_TRUE( + RuntimeEnabledFeatures::OriginTrialsSampleAPIEnabledByRuntimeFlag()); + // Implied by OriginTrialsSampleAPI. + EXPECT_TRUE(RuntimeEnabledFeatures:: + OriginTrialsSampleAPIImpliedEnabledByRuntimeFlag()); + EXPECT_FALSE(RuntimeEnabledFeatures:: + OriginTrialsSampleAPIDependentEnabledByRuntimeFlag()); + + RuntimeEnabledFeatures::SetOriginTrialsSampleAPIImpliedEnabled(true); + RuntimeEnabledFeatures::SetOriginTrialsSampleAPIDependentEnabled(true); + // Internal status: true, true, true. + EXPECT_TRUE( + RuntimeEnabledFeatures::OriginTrialsSampleAPIEnabledByRuntimeFlag()); + EXPECT_TRUE(RuntimeEnabledFeatures:: + OriginTrialsSampleAPIImpliedEnabledByRuntimeFlag()); + EXPECT_TRUE(RuntimeEnabledFeatures:: + OriginTrialsSampleAPIDependentEnabledByRuntimeFlag()); + + RuntimeEnabledFeatures::SetOriginTrialsSampleAPIEnabled(false); + // Internal status: false, true, true. + EXPECT_FALSE( + RuntimeEnabledFeatures::OriginTrialsSampleAPIEnabledByRuntimeFlag()); + EXPECT_TRUE(RuntimeEnabledFeatures:: + OriginTrialsSampleAPIImpliedEnabledByRuntimeFlag()); + // Depends on OriginTrialsSampleAPI. + EXPECT_FALSE(RuntimeEnabledFeatures:: + OriginTrialsSampleAPIDependentEnabledByRuntimeFlag()); +} + +} // namespace +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn b/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn index 751e13bde93..0d5114005ab 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn +++ b/chromium/third_party/blink/renderer/platform/scheduler/BUILD.gn @@ -14,7 +14,6 @@ blink_platform_sources("scheduler") { "common/cooperative_scheduling_manager.cc", "common/dummy_schedulers.cc", "common/event_loop.cc", - "common/features.cc", "common/features.h", "common/frame_or_worker_scheduler.cc", "common/idle_helper.cc", @@ -69,6 +68,8 @@ blink_platform_sources("scheduler") { "main_thread/compositor_priority_experiments.h", "main_thread/deadline_task_runner.cc", "main_thread/deadline_task_runner.h", + "main_thread/find_in_page_budget_pool_controller.cc", + "main_thread/find_in_page_budget_pool_controller.h", "main_thread/frame_origin_type.cc", "main_thread/frame_origin_type.h", "main_thread/frame_scheduler_impl.cc", @@ -90,6 +91,8 @@ blink_platform_sources("scheduler") { "main_thread/main_thread_task_queue.h", "main_thread/memory_purge_manager.cc", "main_thread/memory_purge_manager.h", + "main_thread/non_waking_time_domain.cc", + "main_thread/non_waking_time_domain.h", "main_thread/page_scheduler_impl.cc", "main_thread/page_scheduler_impl.h", "main_thread/page_visibility_state.cc", @@ -114,6 +117,8 @@ blink_platform_sources("scheduler") { "main_thread/web_scheduling_task_queue_impl.cc", "main_thread/web_scheduling_task_queue_impl.h", "main_thread/web_scoped_virtual_time_pauser.cc", + "main_thread/widget_scheduler.cc", + "main_thread/widget_scheduler.h", "public/aggregated_metric_reporter.h", "public/cooperative_scheduling_manager.h", "public/dummy_schedulers.h", @@ -188,6 +193,8 @@ jumbo_source_set("test_support") { "test/test_queueing_time_estimator_client.cc", "test/test_queueing_time_estimator_client.h", "test/web_fake_thread_scheduler.cc", + "test/web_fake_widget_scheduler.cc", + "test/web_fake_widget_scheduler.h", ] deps = [ @@ -232,7 +239,6 @@ jumbo_source_set("unit_tests") { "main_thread/queueing_time_estimator_unittest.cc", "main_thread/render_widget_signals_unittest.cc", "main_thread/user_model_unittest.cc", - "worker/compositor_thread_scheduler_unittest.cc", "worker/worker_scheduler_proxy_unittest.cc", "worker/worker_scheduler_unittest.cc", "worker/worker_thread_scheduler_unittest.cc", @@ -253,9 +259,7 @@ jumbo_source_set("unit_tests") { source_set("perf_tests") { testonly = true - sources = [ - "test/queueing_time_estimator_perf_test.cc", - ] + sources = [ "test/queueing_time_estimator_perf_test.cc" ] deps = [ "//base", @@ -327,7 +331,5 @@ fuzzer_test("sequence_manager_fuzzer") { } proto_library("sequence_manager_test_description_proto") { - sources = [ - "test/fuzzer/proto/sequence_manager_test_description.proto", - ] + sources = [ "test/fuzzer/proto/sequence_manager_test_description.proto" ] } diff --git a/chromium/third_party/blink/renderer/platform/scheduler/DEPS b/chromium/third_party/blink/renderer/platform/scheduler/DEPS index a42789826bd..09f3deab389 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/DEPS +++ b/chromium/third_party/blink/renderer/platform/scheduler/DEPS @@ -52,7 +52,6 @@ include_rules = [ "+services/metrics", "+third_party/blink/renderer/platform/bindings/parkable_string_manager.h", - "+third_party/blink/renderer/platform/instrumentation/histogram.h", "+third_party/blink/renderer/platform/instrumentation", "+third_party/blink/renderer/platform/platform_export.h", "+third_party/blink/renderer/platform/runtime_enabled_features.h", diff --git a/chromium/third_party/blink/renderer/platform/scheduler/OWNERS b/chromium/third_party/blink/renderer/platform/scheduler/OWNERS index 3df140deab0..a1de77fc540 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/OWNERS +++ b/chromium/third_party/blink/renderer/platform/scheduler/OWNERS @@ -1,6 +1,5 @@ # LON scheduling team altimin@chromium.org -alexclarke@chromium.org carlscab@google.com rmcilroy@chromium.org skyostil@chromium.org diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/cooperative_scheduling_manager.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/cooperative_scheduling_manager.cc index 0413fe7b99a..f6a029de1f6 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/cooperative_scheduling_manager.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/cooperative_scheduling_manager.cc @@ -47,15 +47,17 @@ CooperativeSchedulingManager::CooperativeSchedulingManager() } void CooperativeSchedulingManager::EnterAllowedStackScope() { - TRACE_EVENT_ASYNC_BEGIN0("renderer.scheduler", "PreemptionAllowedStackScope", - this); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("renderer.scheduler", + "PreemptionAllowedStackScope", + TRACE_ID_LOCAL(this)); allowed_stack_scope_depth_++; } void CooperativeSchedulingManager::LeaveAllowedStackScope() { - TRACE_EVENT_ASYNC_END0("renderer.scheduler", "PreemptionAllowedStackScope", - this); + TRACE_EVENT_NESTABLE_ASYNC_END0("renderer.scheduler", + "PreemptionAllowedStackScope", + TRACE_ID_LOCAL(this)); allowed_stack_scope_depth_--; DCHECK_GE(allowed_stack_scope_depth_, 0); } diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc index 4633243ee58..c456a695bac 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc @@ -37,8 +37,8 @@ class DummyFrameScheduler : public FrameScheduler { bool IsPageVisible() const override { return true; } void SetPaused(bool) override {} void SetShouldReportPostedTasksWhenDisabled(bool) override {} - void SetCrossOrigin(bool) override {} - bool IsCrossOrigin() const override { return false; } + void SetCrossOriginToMainFrame(bool) override {} + bool IsCrossOriginToMainFrame() const override { return false; } void SetIsAdFrame() override {} bool IsAdFrame() const override { return false; } void TraceUrlChange(const String&) override {} @@ -175,6 +175,10 @@ class DummyThreadScheduler : public ThreadScheduler { return base::ThreadTaskRunnerHandle::Get(); } + scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override { + return base::ThreadTaskRunnerHandle::Get(); + } + std::unique_ptr<PageScheduler> CreatePageScheduler( PageScheduler::Delegate*) override { return std::make_unique<DummyPageScheduler>(); @@ -231,11 +235,6 @@ class DummyWebThreadScheduler : public WebThreadScheduler, return base::ThreadTaskRunnerHandle::Get(); } - scoped_refptr<base::SingleThreadTaskRunner> InputTaskRunner() override { - DCHECK(WTF::IsMainThread()); - return base::ThreadTaskRunnerHandle::Get(); - } - scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override { DCHECK(WTF::IsMainThread()); return base::ThreadTaskRunnerHandle::Get(); diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/features.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/features.cc deleted file mode 100644 index b49ffd20c5b..00000000000 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/features.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 "third_party/blink/renderer/platform/scheduler/common/features.h" - -namespace blink { -namespace scheduler { - -// Field trial parameters associated with |kThrottleAndFreezeTaskTypes| feature. -// These override the throttleable and freezable QueueTraits bits for the tasks -// specified in the parameters. The parameters are comma separated lists, of the -// the form: "throttleable": "name1,name2", "freezable": "name1,name3". The -// names should be those returned by TaskTypeNames::TaskTypeToString(). -const char kThrottleableTaskTypesListParam[] = "ThrottleableTasks"; -const char kFreezableTaskTypesListParam[] = "FreezableTasks"; - -} // namespace scheduler -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/features.h b/chromium/third_party/blink/renderer/platform/scheduler/common/features.h index b65ab55a0a4..6f12fc5f39a 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/features.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/features.h @@ -12,9 +12,6 @@ namespace blink { namespace scheduler { -const base::Feature kHighPriorityInputOnMainThread{ - "BlinkSchedulerHighPriorityInput", base::FEATURE_ENABLED_BY_DEFAULT}; - const base::Feature kHighPriorityInputOnCompositorThread{ "BlinkSchedulerHighPriorityInputOnCompositorThread", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -22,6 +19,10 @@ const base::Feature kHighPriorityInputOnCompositorThread{ const base::Feature kDedicatedWorkerThrottling{ "BlinkSchedulerWorkerThrottling", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kBestEffortPriorityForFindInPage{ + "BlinkSchedulerBestEffortPriorityForFindInPage", + base::FEATURE_DISABLED_BY_DEFAULT}; + // COMPOSITING PRIORITY EXPERIMENT CONTROLS // Enables experiment to increase priority of the compositing tasks during @@ -76,7 +77,7 @@ const base::Feature kVeryHighPriorityForCompositingAlternating{ // to kNormalPriority. const base::Feature kVeryHighPriorityForCompositingAfterDelay{ "BlinkSchedulerVeryHighPriorityForCompositingAfterDelay", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; // Param for kVeryHighPriorityForCompositingAfterDelay experiment. How long // in ms the compositor will wait to be prioritized if no compositor tasks run. @@ -197,32 +198,11 @@ const base::Feature kLowPriorityForCrossOriginOnlyWhenLoading{ "BlinkSchedulerLowPriorityForCrossOriginOnlyWhenLoading", base::FEATURE_DISABLED_BY_DEFAULT}; -// Enable setting throttleable and freezable task types from field trial -// parameters. -const base::Feature kThrottleAndFreezeTaskTypes{ - "ThrottleAndFreezeTaskTypes", base::FEATURE_DISABLED_BY_DEFAULT}; - // Prioritizes loading and compositing tasks while loading. const base::Feature kPrioritizeCompositingAndLoadingDuringEarlyLoading{ "PrioritizeCompositingAndLoadingDuringEarlyLoading", base::FEATURE_DISABLED_BY_DEFAULT}; -// Parameters for |kThrottleAndFreezeTaskTypes|. -extern const char PLATFORM_EXPORT kThrottleableTaskTypesListParam[]; -extern const char PLATFORM_EXPORT kFreezableTaskTypesListParam[]; - -// If enabled, the scheduler will bypass the priority-based anti-starvation -// logic that prevents indefinite starvation of lower priority tasks in the -// presence of higher priority tasks by occasionally selecting lower -// priority task queues over higher priority task queues. -// -// Note: this does not affect the anti-starvation logic that is in place for -// preventing delayed tasks from starving immediate tasks, which is always -// enabled. -const base::Feature kBlinkSchedulerDisableAntiStarvationForPriorities{ - "BlinkSchedulerDisableAntiStarvationForPriorities", - base::FEATURE_ENABLED_BY_DEFAULT}; - // Enable setting high priority database task type from field trial parameters. const base::Feature kHighPriorityDatabaseTaskType{ "HighPriorityDatabaseTaskType", base::FEATURE_DISABLED_BY_DEFAULT}; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper.cc index 23246956a95..4f769b7a650 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper.cc @@ -439,9 +439,15 @@ void IdleHelper::State::TraceEventIdlePeriodStateChange( !new_running_idle_task) { running_idle_task_for_tracing_ = false; if (!idle_period_deadline_.is_null() && now > idle_period_deadline_) { - TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0( - "renderer.scheduler", idle_period_tracing_name_, this, - "DeadlineOverrun", + if (last_sub_trace_event_name_) { + TRACE_EVENT_NESTABLE_ASYNC_END0("renderer.scheduler", + last_sub_trace_event_name_, + TRACE_ID_LOCAL(this)); + } + last_sub_trace_event_name_ = "DeadlineOverrun"; + TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( + "renderer.scheduler", last_sub_trace_event_name_, + TRACE_ID_LOCAL(this), std::max(idle_period_deadline_, last_idle_task_trace_time_)); } } @@ -449,35 +455,46 @@ void IdleHelper::State::TraceEventIdlePeriodStateChange( if (IsInIdlePeriod(new_state)) { if (!idle_period_trace_event_started_) { idle_period_trace_event_started_ = true; - TRACE_EVENT_ASYNC_BEGIN1("renderer.scheduler", idle_period_tracing_name_, - this, "idle_period_length_ms", - (new_deadline - now).InMillisecondsF()); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( + "renderer.scheduler", idle_period_tracing_name_, TRACE_ID_LOCAL(this), + "idle_period_length_ms", (new_deadline - now).InMillisecondsF()); } + const char* new_sub_trace_event_name = nullptr; + if (new_running_idle_task) { last_idle_task_trace_time_ = now; running_idle_task_for_tracing_ = true; - TRACE_EVENT_ASYNC_STEP_INTO0("renderer.scheduler", - idle_period_tracing_name_, this, - "RunningIdleTask"); + new_sub_trace_event_name = "RunningIdleTask"; } else if (new_state == IdlePeriodState::kInShortIdlePeriod) { - TRACE_EVENT_ASYNC_STEP_INTO0("renderer.scheduler", - idle_period_tracing_name_, this, - "ShortIdlePeriod"); + new_sub_trace_event_name = "ShortIdlePeriod"; } else if (IsInLongIdlePeriod(new_state) && new_state != IdlePeriodState::kInLongIdlePeriodPaused) { - TRACE_EVENT_ASYNC_STEP_INTO0("renderer.scheduler", - idle_period_tracing_name_, this, - "LongIdlePeriod"); + new_sub_trace_event_name = "LongIdlePeriod"; } else if (new_state == IdlePeriodState::kInLongIdlePeriodPaused) { - TRACE_EVENT_ASYNC_STEP_INTO0("renderer.scheduler", - idle_period_tracing_name_, this, - "LongIdlePeriodPaused"); + new_sub_trace_event_name = "LongIdlePeriodPaused"; + } + + if (new_sub_trace_event_name) { + if (last_sub_trace_event_name_) { + TRACE_EVENT_NESTABLE_ASYNC_END0("renderer.scheduler", + last_sub_trace_event_name_, + TRACE_ID_LOCAL(this)); + } + TRACE_EVENT_NESTABLE_ASYNC_BEGIN0( + "renderer.scheduler", new_sub_trace_event_name, TRACE_ID_LOCAL(this)); + last_sub_trace_event_name_ = new_sub_trace_event_name; } } else if (idle_period_trace_event_started_) { + if (last_sub_trace_event_name_) { + TRACE_EVENT_NESTABLE_ASYNC_END0("renderer.scheduler", + last_sub_trace_event_name_, + TRACE_ID_LOCAL(this)); + last_sub_trace_event_name_ = nullptr; + } + TRACE_EVENT_NESTABLE_ASYNC_END0( + "renderer.scheduler", idle_period_tracing_name_, TRACE_ID_LOCAL(this)); idle_period_trace_event_started_ = false; - TRACE_EVENT_ASYNC_END0("renderer.scheduler", idle_period_tracing_name_, - this); } } diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper.h index aa80c6a2e39..51c7593d95e 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper.h @@ -195,6 +195,7 @@ class PLATFORM_EXPORT IdleHelper : public base::TaskObserver, bool idle_period_trace_event_started_; bool running_idle_task_for_tracing_; const char* idle_period_tracing_name_; + const char* last_sub_trace_event_name_ = nullptr; DISALLOW_COPY_AND_ASSIGN(State); }; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc index 8d6f03efff5..5110c90ee31 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/idle_helper_unittest.cc @@ -172,26 +172,12 @@ class IdleHelperForTest : public IdleHelper, public IdleHelper::Delegate { class BaseIdleHelperTest : public testing::Test { public: - BaseIdleHelperTest( - std::unique_ptr<base::MessageLoop> message_loop, + explicit BaseIdleHelperTest( base::TimeDelta required_quiescence_duration_before_long_idle_period) - : message_loop_(std::move(message_loop)), - test_task_runner_(base::MakeRefCounted<base::TestMockTimeTaskRunner>( + : test_task_runner_(base::MakeRefCounted<base::TestMockTimeTaskRunner>( base::TestMockTimeTaskRunner::Type::kStandalone)) { - if (!message_loop_) { - sequence_manager_ = - base::sequence_manager::SequenceManagerForTest::Create( - nullptr, test_task_runner_, - test_task_runner_->GetMockTickClock()); - } else { - // It's okay to use |test_task_runner_| just as a mock clock because - // it isn't bound to thread and all tasks will go through a MessageLoop. - sequence_manager_ = - base::sequence_manager::SequenceManagerForTest::CreateOnCurrentThread( - base::sequence_manager::SequenceManager::Settings::Builder() - .SetTickClock(test_task_runner_->GetMockTickClock()) - .Build()); - } + sequence_manager_ = base::sequence_manager::SequenceManagerForTest::Create( + nullptr, test_task_runner_, test_task_runner_->GetMockTickClock()); scheduler_helper_ = std::make_unique<NonMainThreadSchedulerHelper>( sequence_manager_.get(), nullptr, TaskType::kInternalTest); idle_helper_ = std::make_unique<IdleHelperForTest>( @@ -275,7 +261,6 @@ class BaseIdleHelperTest : public testing::Test { return idle_helper_->idle_queue_; } - std::unique_ptr<base::MessageLoop> message_loop_; scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_; std::unique_ptr<SequenceManager> sequence_manager_; std::unique_ptr<NonMainThreadSchedulerHelper> scheduler_helper_; @@ -289,7 +274,7 @@ class BaseIdleHelperTest : public testing::Test { class IdleHelperTest : public BaseIdleHelperTest { public: - IdleHelperTest() : BaseIdleHelperTest(nullptr, base::TimeDelta()) {} + IdleHelperTest() : BaseIdleHelperTest(base::TimeDelta()) {} ~IdleHelperTest() override = default; @@ -400,7 +385,7 @@ TEST_F(IdleHelperTest, TestIdleTaskExceedsDeadline) { class IdleHelperTestWithIdlePeriodObserver : public BaseIdleHelperTest { public: IdleHelperTestWithIdlePeriodObserver() - : BaseIdleHelperTest(nullptr, base::TimeDelta()) {} + : BaseIdleHelperTest(base::TimeDelta()) {} ~IdleHelperTestWithIdlePeriodObserver() override = default; @@ -785,7 +770,6 @@ class IdleHelperWithQuiescencePeriodTest : public BaseIdleHelperTest { IdleHelperWithQuiescencePeriodTest() : BaseIdleHelperTest( - nullptr, base::TimeDelta::FromMilliseconds(kQuiescenceDelayMs)) {} ~IdleHelperWithQuiescencePeriodTest() override = default; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.cc index d26f6d7ac75..aab5cf3e5cf 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.cc @@ -7,8 +7,6 @@ PollableThreadSafeFlag::PollableThreadSafeFlag(base::Lock* write_lock_) : flag_(false), write_lock_(write_lock_) {} -PollableThreadSafeFlag::~PollableThreadSafeFlag() = default; - void PollableThreadSafeFlag::SetWhileLocked(bool value) { write_lock_->AssertAcquired(); base::subtle::Release_Store(&flag_, value); diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.h b/chromium/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.h index a78dc2bb7ff..1fdd4a2eefb 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/pollable_thread_safe_flag.h @@ -21,7 +21,6 @@ class PollableThreadSafeFlag { public: explicit PollableThreadSafeFlag(base::Lock* write_lock); - ~PollableThreadSafeFlag(); // Set the flag. May only be called if |write_lock| is held. void SetWhileLocked(bool value); diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc index ef55b44c5ee..c751d9ccc24 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc @@ -41,7 +41,7 @@ void SchedulerHelper::InitDefaultQueues( DCHECK(sequence_manager_); sequence_manager_->SetDefaultTaskRunner(default_task_runner_); - blink_task_executor_.emplace(default_task_runner_, sequence_manager_); + simple_task_executor_.emplace(default_task_runner_); } SchedulerHelper::~SchedulerHelper() { @@ -182,18 +182,5 @@ bool SchedulerHelper::HasCPUTimingForEachTask() const { return false; } -SchedulerHelper::BlinkTaskExecutor::BlinkTaskExecutor( - scoped_refptr<base::SingleThreadTaskRunner> default_task_queue, - base::sequence_manager::SequenceManager* sequence_manager) - : base::SimpleTaskExecutor(sequence_manager, std::move(default_task_queue)), - sequence_manager_(sequence_manager) {} - -SchedulerHelper::BlinkTaskExecutor::~BlinkTaskExecutor() = default; - -const scoped_refptr<base::SequencedTaskRunner>& -SchedulerHelper::BlinkTaskExecutor::GetContinuationTaskRunner() { - return sequence_manager_->GetTaskRunnerForCurrentTask(); -} - } // namespace scheduler } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h index 52b84746c4b..3f2267ef8ec 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h @@ -130,29 +130,12 @@ class PLATFORM_EXPORT SchedulerHelper private: friend class SchedulerHelperTest; - // Like SimpleTaskExecutor except it knows how to get the current task runner - // from the SequenceManager to implement GetContinuationTaskRunner. - class BlinkTaskExecutor : public base::SimpleTaskExecutor { - public: - BlinkTaskExecutor( - scoped_refptr<base::SingleThreadTaskRunner> default_task_queue, - base::sequence_manager::SequenceManager* sequence_manager); - - ~BlinkTaskExecutor() override; - - // base::TaskExecutor implementation. - const scoped_refptr<base::SequencedTaskRunner>& GetContinuationTaskRunner() - override; - - private: - base::sequence_manager::SequenceManager* sequence_manager_; // NOT OWNED - }; scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_; Observer* observer_; // NOT OWNED UkmTaskSampler ukm_task_sampler_; - base::Optional<BlinkTaskExecutor> blink_task_executor_; + base::Optional<base::SimpleTaskExecutor> simple_task_executor_; DISALLOW_COPY_AND_ASSIGN(SchedulerHelper); }; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc index 24d577ddcd3..ca7327cff8f 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc @@ -13,15 +13,18 @@ bool SchedulingPolicy::IsFeatureSticky(SchedulingPolicy::Feature feature) { case Feature::kWebSocket: case Feature::kWebRTC: case Feature::kDedicatedWorkerOrWorklet: - case Feature::kOutstandingNetworkRequest: + case Feature::kOutstandingNetworkRequestFetch: + case Feature::kOutstandingNetworkRequestXHR: + case Feature::kOutstandingNetworkRequestOthers: case Feature::kOutstandingIndexedDBTransaction: - case Feature::kHasScriptableFramesInMultipleTabs: case Feature::kBroadcastChannel: case Feature::kIndexedDBConnection: case Feature::kWebGL: case Feature::kWebVR: case Feature::kWebXR: case Feature::kSharedWorker: + case Feature::kWebHID: + case Feature::kWebShare: return false; case Feature::kMainResourceHasCacheControlNoStore: case Feature::kMainResourceHasCacheControlNoCache: @@ -44,6 +47,10 @@ bool SchedulingPolicy::IsFeatureSticky(SchedulingPolicy::Feature feature) { case Feature::kRequestedBackForwardCacheBlockedSensors: case Feature::kRequestedBackgroundWorkPermission: case Feature::kWebLocks: + case Feature::kWakeLock: + case Feature::kRequestedStorageAccessGrant: + case Feature::kWebNfc: + case Feature::kWebFileSystem: return true; } } diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.cc index 920153dc8b1..97dbeaa9d3b 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.cc @@ -61,6 +61,11 @@ SimpleThreadScheduler::DeprecatedDefaultTaskRunner() { return base::ThreadTaskRunnerHandle::Get(); } +scoped_refptr<base::SingleThreadTaskRunner> +SimpleThreadScheduler::NonWakingTaskRunner() { + return base::ThreadTaskRunnerHandle::Get(); +} + std::unique_ptr<PageScheduler> SimpleThreadScheduler::CreatePageScheduler( PageScheduler::Delegate* delegate) { return nullptr; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.h index 35c83b2c291..1111310914d 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.h @@ -55,6 +55,7 @@ class SimpleThreadScheduler : public ThreadScheduler { scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override; + scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner() override; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/thread_cpu_throttler.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/thread_cpu_throttler.cc index c184e225ab1..655b96a1802 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/thread_cpu_throttler.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/thread_cpu_throttler.cc @@ -106,6 +106,9 @@ void ThreadCPUThrottler::ThrottlingThread::InstallSignalHandler() { struct sigaction sa; sa.sa_handler = &HandleSignal; sigemptyset(&sa.sa_mask); + // Block SIGPROF while our handler is running so that the V8 CPU profiler + // doesn't try to sample the stack while our signal handler is active. + sigaddset(&sa.sa_mask, SIGPROF); sa.sa_flags = SA_RESTART; signal_handler_installed_ = (sigaction(SIGUSR2, &sa, &old_signal_handler_) == 0); @@ -164,15 +167,16 @@ void ThreadCPUThrottler::ThrottlingThread::Throttle() { } void ThreadCPUThrottler::ThrottlingThread::Start() { -#ifdef USE_SIGNALS +#if defined(USE_SIGNALS) || defined(OS_WIN) +#if defined(USE_SIGNALS) InstallSignalHandler(); -#elif !defined(OS_WIN) - LOG(ERROR) << "CPU throttling is not supported."; - return; #endif if (!base::PlatformThread::Create(0, this, &throttling_thread_handle_)) { LOG(ERROR) << "Failed to create throttling thread."; } +#else + LOG(ERROR) << "CPU throttling is not supported."; +#endif } void ThreadCPUThrottler::ThrottlingThread::Sleep(base::TimeDelta duration) { diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc index 0dc678298e4..9570477f5f0 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.cc @@ -602,9 +602,6 @@ bool TaskQueueThrottler::Metadata::DecrementRefCount() { return false; } -void TaskQueueThrottler::Metadata::OnPostTask(base::Location from_here, - base::TimeDelta delay) {} - void TaskQueueThrottler::Metadata::OnQueueNextWakeUpChanged( base::TimeTicks wake_up) { throttler_->OnQueueNextWakeUpChanged(queue_, wake_up); diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h index 449e843172b..ad6775949d2 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h @@ -143,7 +143,6 @@ class PLATFORM_EXPORT TaskQueueThrottler : public BudgetPoolController { bool DecrementRefCount(); // TaskQueue::Observer implementation: - void OnPostTask(base::Location from_here, base::TimeDelta delay) override; void OnQueueNextWakeUpChanged(base::TimeTicks wake_up) override; size_t throttling_ref_count() const { return throttling_ref_count_; } @@ -157,6 +156,8 @@ class PLATFORM_EXPORT TaskQueueThrottler : public BudgetPoolController { TaskQueueThrottler* const throttler_; size_t throttling_ref_count_ = 0; HashSet<BudgetPool*> budget_pools_; + + DISALLOW_COPY_AND_ASSIGN(Metadata); }; using TaskQueueMap = diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h b/chromium/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h index e957afa1292..d82b092c265 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h @@ -100,7 +100,7 @@ class StateTracer { ~StateTracer() { if (slice_is_open_) - TRACE_EVENT_ASYNC_END0(category, name_, object_); + TRACE_EVENT_NESTABLE_ASYNC_END0(category, name_, TRACE_ID_LOCAL(object_)); } // String will be copied before leaving this function. @@ -122,22 +122,19 @@ class StateTracer { private: void TraceImpl(const char* state, bool need_copy) { if (slice_is_open_) { - TRACE_EVENT_ASYNC_END0(category, name_, object_); + TRACE_EVENT_NESTABLE_ASYNC_END0(category, name_, TRACE_ID_LOCAL(object_)); slice_is_open_ = false; } if (!state || !is_enabled()) return; - // Trace viewer logic relies on subslice starting at the exact same time - // as the async event. - base::TimeTicks now = TRACE_TIME_TICKS_NOW(); - TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(category, name_, object_, now); if (need_copy) { - TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(category, name_, object_, - TRACE_STR_COPY(state), now); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(category, name_, + TRACE_ID_LOCAL(object_), "state", + TRACE_STR_COPY(state)); } else { - TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(category, name_, object_, - state, now); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( + category, name_, TRACE_ID_LOCAL(object_), "state", state); } slice_is_open_ = true; } diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc index ac27cae4d42..641f84cc4b8 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc @@ -10,6 +10,8 @@ #include "base/message_loop/message_pump_type.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" +#include "third_party/blink/public/common/input/web_input_event_attribution.h" +#include "third_party/blink/public/platform/scheduler/web_widget_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/common/features.h" #include "third_party/blink/renderer/platform/scheduler/common/tracing_helper.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h" @@ -29,9 +31,6 @@ WebThreadScheduler::CreateMainThreadScheduler( .SetMessagePumpType(base::MessagePumpType::DEFAULT) .SetRandomisedSamplingEnabled(true) .SetAddQueueTimeToTasks(true) - .SetAntiStarvationLogicForPrioritiesDisabled( - base::FeatureList::IsEnabled( - kBlinkSchedulerDisableAntiStarvationForPriorities)) .Build(); auto sequence_manager = message_pump @@ -74,12 +73,6 @@ WebThreadScheduler::CompositorTaskRunner() { } scoped_refptr<base::SingleThreadTaskRunner> -WebThreadScheduler::InputTaskRunner() { - NOTREACHED(); - return nullptr; -} - -scoped_refptr<base::SingleThreadTaskRunner> WebThreadScheduler::IPCTaskRunner() { NOTREACHED(); return nullptr; @@ -102,6 +95,12 @@ std::unique_ptr<Thread> WebThreadScheduler::CreateMainThread() { return nullptr; } +std::unique_ptr<WebWidgetScheduler> +WebThreadScheduler::CreateWidgetScheduler() { + NOTREACHED(); + return nullptr; +} + std::unique_ptr<WebRenderWidgetSchedulingState> WebThreadScheduler::NewRenderWidgetSchedulingState() { NOTREACHED(); @@ -131,12 +130,14 @@ void WebThreadScheduler::DidHandleInputEventOnCompositorThread( } void WebThreadScheduler::WillPostInputEventToMainThread( - WebInputEvent::Type web_input_event_type) { + WebInputEvent::Type web_input_event_type, + const WebInputEventAttribution& attribution) { NOTREACHED(); } void WebThreadScheduler::WillHandleInputEventOnMainThread( - WebInputEvent::Type web_input_event_type) { + WebInputEvent::Type web_input_event_type, + const WebInputEventAttribution& attribution) { NOTREACHED(); } diff --git a/chromium/third_party/blink/renderer/platform/scheduler/common/worker_pool.cc b/chromium/third_party/blink/renderer/platform/scheduler/common/worker_pool.cc index 50634f1db7f..c5492c3f3cf 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/common/worker_pool.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/common/worker_pool.cc @@ -5,24 +5,22 @@ #include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h" #include "base/location.h" -#include "base/task/post_task.h" +#include "base/task/thread_pool.h" namespace blink { namespace worker_pool { void PostTask(const base::Location& location, CrossThreadOnceClosure closure) { - PostTask( - location, - {base::ThreadPool(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, - std::move(closure)); + PostTask(location, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, + std::move(closure)); } void PostTask(const base::Location& location, const base::TaskTraits& traits, CrossThreadOnceClosure closure) { - base::PostTask(location, traits, - ConvertToBaseOnceCallback(std::move(closure))); + base::ThreadPool::PostTask(location, traits, + ConvertToBaseOnceCallback(std::move(closure))); } } // namespace worker_pool diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.cc index 8564cbca5b1..f755622d8dc 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.cc @@ -15,17 +15,14 @@ CompositorPriorityExperiments::CompositorPriorityExperiments( MainThreadSchedulerImpl* scheduler) : scheduler_(scheduler), experiment_(GetExperimentFromFeatureList()), + last_compositor_task_time_(scheduler_->GetTickClock()->NowTicks()), prioritize_compositing_after_delay_length_( base::TimeDelta::FromMilliseconds(kCompositingDelayLength.Get())), stop_signal_(base::FeatureList::IsEnabled( kPrioritizeCompositingUntilBeginMainFrame) ? StopSignalType::kBeginMainFrameTask - : StopSignalType::kAnyCompositorTask) { - do_prioritize_compositing_after_delay_callback_.Reset(base::BindRepeating( - &CompositorPriorityExperiments::DoPrioritizeCompositingAfterDelay, - base::Unretained(this))); -} -CompositorPriorityExperiments::~CompositorPriorityExperiments() {} + : StopSignalType::kAnyCompositorTask) {} +CompositorPriorityExperiments::~CompositorPriorityExperiments() = default; CompositorPriorityExperiments::Experiment CompositorPriorityExperiments::GetExperimentFromFeatureList() { @@ -38,11 +35,11 @@ CompositorPriorityExperiments::GetExperimentFromFeatureList() { kVeryHighPriorityForCompositingAlternating)) { return Experiment::kVeryHighPriorityForCompositingAlternating; } else if (base::FeatureList::IsEnabled( - kVeryHighPriorityForCompositingAfterDelay)) { - return Experiment::kVeryHighPriorityForCompositingAfterDelay; - } else if (base::FeatureList::IsEnabled( kVeryHighPriorityForCompositingBudget)) { return Experiment::kVeryHighPriorityForCompositingBudget; + } else if (base::FeatureList::IsEnabled( + kVeryHighPriorityForCompositingAfterDelay)) { + return Experiment::kVeryHighPriorityForCompositingAfterDelay; } else { return Experiment::kNone; } @@ -75,15 +72,7 @@ QueuePriority CompositorPriorityExperiments::GetCompositorPriority() const { } } -void CompositorPriorityExperiments::DoPrioritizeCompositingAfterDelay() { - delay_compositor_priority_ = QueuePriority::kVeryHighPriority; - scheduler_->OnCompositorPriorityExperimentUpdateCompositorPriority(); -} - void CompositorPriorityExperiments::OnMainThreadSchedulerInitialized() { - if (experiment_ == Experiment::kVeryHighPriorityForCompositingAfterDelay) { - PostPrioritizeCompositingAfterDelayTask(); - } if (experiment_ == Experiment::kVeryHighPriorityForCompositingBudget) { budget_pool_controller_.reset(new CompositorBudgetPoolController( this, scheduler_, scheduler_->CompositorTaskQueue().get(), @@ -102,14 +91,6 @@ void CompositorPriorityExperiments::OnWillBeginMainFrame() { will_begin_main_frame_ = true; } -void CompositorPriorityExperiments::PostPrioritizeCompositingAfterDelayTask() { - DCHECK_EQ(experiment_, Experiment::kVeryHighPriorityForCompositingAfterDelay); - - scheduler_->ControlTaskRunner()->PostDelayedTask( - FROM_HERE, do_prioritize_compositing_after_delay_callback_.GetCallback(), - prioritize_compositing_after_delay_length_); -} - void CompositorPriorityExperiments::OnTaskCompleted( MainThreadTaskQueue* queue, QueuePriority current_compositor_priority, @@ -150,12 +131,15 @@ void CompositorPriorityExperiments::OnTaskCompleted( case Experiment::kVeryHighPriorityForCompositingAfterDelay: if (have_seen_stop_signal) { delay_compositor_priority_ = QueuePriority::kNormalPriority; - do_prioritize_compositing_after_delay_callback_.Cancel(); - PostPrioritizeCompositingAfterDelayTask(); - - if (current_compositor_priority != delay_compositor_priority_) - scheduler_->OnCompositorPriorityExperimentUpdateCompositorPriority(); + last_compositor_task_time_ = task_timing->end_time(); + } else { + if (task_timing->end_time() - last_compositor_task_time_ >= + prioritize_compositing_after_delay_length_) { + delay_compositor_priority_ = QueuePriority::kVeryHighPriority; + } } + if (current_compositor_priority != delay_compositor_priority_) + scheduler_->OnCompositorPriorityExperimentUpdateCompositorPriority(); return; case Experiment::kVeryHighPriorityForCompositingBudget: budget_pool_controller_->OnTaskCompleted(queue, task_timing, @@ -188,7 +172,7 @@ CompositorPriorityExperiments::CompositorBudgetPoolController:: TraceableVariableController* tracing_controller, base::TimeDelta min_budget, double budget_recovery_rate) - : experiment_(experiment), tick_clock_(scheduler->GetTickClock()) { + : experiment_(experiment) { DCHECK_EQ(compositor_queue->queue_type(), MainThreadTaskQueue::QueueType::kCompositor); base::TimeTicks now = scheduler->GetTickClock()->NowTicks(); diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.h index bb034bf4713..725d31d8f3f 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.h @@ -93,16 +93,10 @@ class PLATFORM_EXPORT CompositorPriorityExperiments { CompositorPriorityExperiments* experiment_; std::unique_ptr<CPUTimeBudgetPool> compositor_budget_pool_; bool is_exhausted_ = false; - - const base::TickClock* tick_clock_; // Not owned. }; static Experiment GetExperimentFromFeatureList(); - void DoPrioritizeCompositingAfterDelay(); - - void PostPrioritizeCompositingAfterDelayTask(); - enum class StopSignalType { kAnyCompositorTask, kBeginMainFrameTask }; MainThreadSchedulerImpl* scheduler_; // Not owned. @@ -113,7 +107,7 @@ class PLATFORM_EXPORT CompositorPriorityExperiments { QueuePriority::kVeryHighPriority; QueuePriority delay_compositor_priority_ = QueuePriority::kNormalPriority; - CancelableClosureHolder do_prioritize_compositing_after_delay_callback_; + base::TimeTicks last_compositor_task_time_; base::TimeDelta prioritize_compositing_after_delay_length_; QueuePriority budget_compositor_priority_ = QueuePriority::kVeryHighPriority; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.cc new file mode 100644 index 00000000000..3fc1cfd2f2b --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.cc @@ -0,0 +1,87 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.h" + +#include "third_party/blink/renderer/platform/scheduler/common/features.h" +#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h" +#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h" + +namespace blink { +namespace scheduler { + +namespace { +// We will accumulate at most 1000ms for find-in-page budget. +constexpr base::TimeDelta kFindInPageMaxBudget = + base::TimeDelta::FromSeconds(1); +// At least 25% of the total CPU time will go to find-in-page tasks. +// TODO(rakina): Experiment with this number to figure out the right percentage +// for find-in-page. Currently this is following CompositorPriorityExperiments. +const double kFindInPageBudgetRecoveryRate = 0.25; +} // namespace + +const QueuePriority + FindInPageBudgetPoolController::kFindInPageBudgetNotExhaustedPriority; +const QueuePriority + FindInPageBudgetPoolController::kFindInPageBudgetExhaustedPriority; + +FindInPageBudgetPoolController::FindInPageBudgetPoolController( + MainThreadSchedulerImpl* scheduler) + : scheduler_(scheduler), + best_effort_budget_experiment_enabled_( + base::FeatureList::IsEnabled(kBestEffortPriorityForFindInPage)) { + if (best_effort_budget_experiment_enabled_) { + task_priority_ = QueuePriority::kBestEffortPriority; + } else { + task_priority_ = kFindInPageBudgetNotExhaustedPriority; + } +} + +FindInPageBudgetPoolController::~FindInPageBudgetPoolController() = default; + +void FindInPageBudgetPoolController::EnsureBudgetPoolInitialized() { + DCHECK(!best_effort_budget_experiment_enabled_); + if (find_in_page_budget_pool_) + return; + base::TimeTicks now = scheduler_->GetTickClock()->NowTicks(); + find_in_page_budget_pool_.reset(new CPUTimeBudgetPool( + "FindInPageBudgetPool", this, &scheduler_->tracing_controller_, now)); + // Set no minimum budget for find-in-page, so that we won't delay running + // find-in-page tasks when budget is available. + find_in_page_budget_pool_->SetMinBudgetLevelToRun(now, base::TimeDelta()); + find_in_page_budget_pool_->SetMaxBudgetLevel(now, kFindInPageMaxBudget); + find_in_page_budget_pool_->SetTimeBudgetRecoveryRate( + now, kFindInPageBudgetRecoveryRate); +} + +void FindInPageBudgetPoolController::OnTaskCompleted( + MainThreadTaskQueue* queue, + TaskQueue::TaskTiming* task_timing) { + if (!queue || best_effort_budget_experiment_enabled_) + return; + EnsureBudgetPoolInitialized(); + if (queue->GetPrioritisationType() == + MainThreadTaskQueue::QueueTraits::PrioritisationType::kFindInPage) { + find_in_page_budget_pool_->RecordTaskRunTime( + queue, task_timing->start_time(), task_timing->end_time()); + } + + bool is_exhausted = !find_in_page_budget_pool_->CanRunTasksAt( + task_timing->end_time(), false /* is_wake_up */); + QueuePriority task_priority = is_exhausted + ? kFindInPageBudgetExhaustedPriority + : kFindInPageBudgetNotExhaustedPriority; + + if (task_priority != task_priority_) { + task_priority_ = task_priority; + // If the priority changed, we need to make sure all find-in-page task + // queues across all frames get updated. Note that UpdatePolicy will + // update all task queues for all frames, which is a bit overkill - this + // should probably be optimized in the future. + scheduler_->UpdatePolicy(); + } +} + +} // namespace scheduler +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.h new file mode 100644 index 00000000000..5fd8f296d48 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.h @@ -0,0 +1,67 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_FIND_IN_PAGE_BUDGET_POOL_CONTROLLER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_FIND_IN_PAGE_BUDGET_POOL_CONTROLLER_H_ + +#include "base/task/sequence_manager/task_queue.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool_controller.h" +#include "third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h" +#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h" + +namespace blink { +namespace scheduler { + +using TaskQueue = base::sequence_manager::TaskQueue; +using QueuePriority = base::sequence_manager::TaskQueue::QueuePriority; + +class CPUTimeBudgetPool; +class MainThreadSchedulerImpl; + +class PLATFORM_EXPORT FindInPageBudgetPoolController + : public BudgetPoolController { + public: + static constexpr auto kFindInPageBudgetNotExhaustedPriority = + QueuePriority::kVeryHighPriority; + static constexpr auto kFindInPageBudgetExhaustedPriority = + QueuePriority::kNormalPriority; + + explicit FindInPageBudgetPoolController(MainThreadSchedulerImpl* scheduler); + ~FindInPageBudgetPoolController() override; + + void OnTaskCompleted(MainThreadTaskQueue* queue, + MainThreadTaskQueue::TaskTiming* task_timing); + + QueuePriority CurrentTaskPriority() { return task_priority_; } + + // Unimplemented methods. + // TODO(crbug.com/1056512): Remove these functions once we factor out the + // budget calculating logic from BudgetPoolController. + void UpdateQueueSchedulingLifecycleState(base::TimeTicks now, + TaskQueue* queue) override {} + void AddQueueToBudgetPool(TaskQueue* queue, + BudgetPool* budget_pool) override {} + void RemoveQueueFromBudgetPool(TaskQueue* queue, + BudgetPool* budget_pool) override {} + void UnregisterBudgetPool(BudgetPool* budget_pool) override {} + bool IsThrottled(TaskQueue* queue) const override { return false; } + + private: + // We need to call this from OnTaskCompleted, because PartitionAlloc might not + // be initialized yet when we got constructed. + // TODO(crbug.com/1058645): Initialize |find_in_page_budget_pool_| from the + // constructor and remove this function. + void EnsureBudgetPoolInitialized(); + + MainThreadSchedulerImpl* scheduler_; // Not owned. + std::unique_ptr<CPUTimeBudgetPool> find_in_page_budget_pool_; + QueuePriority task_priority_; + const bool best_effort_budget_experiment_enabled_; +}; + +} // namespace scheduler +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_FIND_IN_PAGE_BUDGET_POOL_CONTROLLER_H_ diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.cc index fa4f2fc7761..631724eda29 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.cc @@ -16,10 +16,10 @@ FrameOriginType GetFrameOriginType(FrameScheduler* scheduler) { if (scheduler->GetFrameType() == FrameScheduler::FrameType::kMainFrame) return FrameOriginType::kMainFrame; - if (scheduler->IsCrossOrigin()) { - return FrameOriginType::kCrossOriginFrame; + if (scheduler->IsCrossOriginToMainFrame()) { + return FrameOriginType::kCrossOriginToMainFrame; } else { - return FrameOriginType::kSameOriginFrame; + return FrameOriginType::kSameOriginToMainFrame; } } @@ -27,10 +27,10 @@ const char* FrameOriginTypeToString(FrameOriginType origin) { switch (origin) { case FrameOriginType::kMainFrame: return "main-frame"; - case FrameOriginType::kSameOriginFrame: - return "same-origin"; - case FrameOriginType::kCrossOriginFrame: - return "cross-origin"; + case FrameOriginType::kSameOriginToMainFrame: + return "same-origin-to-main-frame"; + case FrameOriginType::kCrossOriginToMainFrame: + return "cross-origin-to-main-frame"; case FrameOriginType::kCount: NOTREACHED(); } diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h index 4739e0e0fac..573ba2f4afd 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_origin_type.h @@ -14,9 +14,10 @@ namespace scheduler { // and should not be renumbered. enum class FrameOriginType { kMainFrame = 0, - kSameOriginFrame = 1, - kCrossOriginFrame = 2, - kCount = 3 + kSameOriginToMainFrame = 1, + kCrossOriginToMainFrame = 2, + // TODO(dcheng): Get rid of this and use the kMaxValue idiom. + kCount = 3, }; FrameOriginType GetFrameOriginType(FrameScheduler* frame_scheduler); diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc index 0e52953485d..2f15127c778 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc @@ -10,6 +10,7 @@ #include "base/metrics/histogram_macros.h" #include "base/trace_event/blame_context.h" #include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h" #include "third_party/blink/public/platform/blame_context.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -17,6 +18,7 @@ #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h" #include "third_party/blink/renderer/platform/scheduler/common/tracing_helper.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h" +#include "third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/page_visibility_state.h" @@ -77,45 +79,6 @@ void UpdatePriority(MainThreadTaskQueue* task_queue) { task_queue->SetQueuePriority(frame_scheduler->ComputePriority(task_queue)); } -// Extract a substring from |source| from [start to end), trimming leading -// whitespace. -String ExtractAndTrimString(String source, size_t start, size_t end) { - DCHECK(start < source.length()); - DCHECK(end <= source.length()); - DCHECK(start <= end); - // Trim whitespace - while (start < end && source[start] == ' ') - ++start; - if (start < end) - return source.Substring(start, end - start); - return ""; -} - -HashSet<String> TaskTypesFromFieldTrialParam(const char* param) { - HashSet<String> result; - String task_type_list = - String::FromUTF8(base::GetFieldTrialParamValueByFeature( - kThrottleAndFreezeTaskTypes, param)); - if (!task_type_list.length()) - return result; - // Extract the individual names, separated by ",". - size_t pos = 0, start = 0; - while ((pos = task_type_list.find(',', start)) != kNotFound) { - String task_type = ExtractAndTrimString(task_type_list, start, pos); - // Not valid to start with "," or have ",," in the list. - DCHECK(task_type.length()); - result.insert(task_type); - start = pos + 1; - } - // Handle the last or only task type name. - String task_type = - ExtractAndTrimString(task_type_list, start, task_type_list.length()); - DCHECK(task_type.length()); - result.insert(task_type); - - return result; -} - } // namespace FrameSchedulerImpl::PauseSubresourceLoadingHandleImpl:: @@ -169,7 +132,7 @@ FrameSchedulerImpl::FrameSchedulerImpl( PausedStateToString), frame_origin_type_(frame_type == FrameType::kMainFrame ? FrameOriginType::kMainFrame - : FrameOriginType::kSameOriginFrame, + : FrameOriginType::kSameOriginToMainFrame, "FrameScheduler.Origin", this, &tracing_controller_, @@ -321,16 +284,16 @@ bool FrameSchedulerImpl::IsFrameVisible() const { return frame_visible_; } -void FrameSchedulerImpl::SetCrossOrigin(bool cross_origin) { +void FrameSchedulerImpl::SetCrossOriginToMainFrame(bool cross_origin) { DCHECK(parent_page_scheduler_); if (frame_origin_type_ == FrameOriginType::kMainFrame) { DCHECK(!cross_origin); return; } if (cross_origin) { - frame_origin_type_ = FrameOriginType::kCrossOriginFrame; + frame_origin_type_ = FrameOriginType::kCrossOriginToMainFrame; } else { - frame_origin_type_ = FrameOriginType::kSameOriginFrame; + frame_origin_type_ = FrameOriginType::kSameOriginToMainFrame; } UpdatePolicy(); } @@ -344,8 +307,8 @@ bool FrameSchedulerImpl::IsAdFrame() const { return is_ad_frame_; } -bool FrameSchedulerImpl::IsCrossOrigin() const { - return frame_origin_type_ == FrameOriginType::kCrossOriginFrame; +bool FrameSchedulerImpl::IsCrossOriginToMainFrame() const { + return frame_origin_type_ == FrameOriginType::kCrossOriginToMainFrame; } void FrameSchedulerImpl::TraceUrlChange(const String& url) { @@ -370,42 +333,8 @@ FrameScheduler::FrameType FrameSchedulerImpl::GetFrameType() const { return frame_type_; } -void FrameSchedulerImpl::InitializeTaskTypeQueueTraitsMap( - FrameTaskTypeToQueueTraitsArray& frame_task_types_to_queue_traits) { - DCHECK_EQ(frame_task_types_to_queue_traits.size(), - static_cast<size_t>(TaskType::kCount)); - // Using std set and strings here because field trial parameters are std - // strings, and we cannot use WTF strings as Blink is not yet initialized. - HashSet<String> throttleable_task_type_names; - HashSet<String> freezable_task_type_names; - if (base::FeatureList::IsEnabled(kThrottleAndFreezeTaskTypes)) { - throttleable_task_type_names = - TaskTypesFromFieldTrialParam(kThrottleableTaskTypesListParam); - freezable_task_type_names = - TaskTypesFromFieldTrialParam(kFreezableTaskTypesListParam); - } - for (size_t i = 0; i < static_cast<size_t>(TaskType::kCount); i++) { - TaskType type = static_cast<TaskType>(i); - base::Optional<QueueTraits> queue_traits = - CreateQueueTraitsForTaskType(type); - if (queue_traits && (!throttleable_task_type_names.IsEmpty() || - !freezable_task_type_names.IsEmpty())) { - const char* task_type_name = TaskTypeNames::TaskTypeToString(type); - if (!throttleable_task_type_names.Take(task_type_name).IsEmpty()) - queue_traits->SetCanBeThrottled(true); - if (freezable_task_type_names.Take(task_type_name).IsEmpty()) - queue_traits->SetCanBeFrozen(true); - } - frame_task_types_to_queue_traits[i] = queue_traits; - } - // Protect against configuration errors. - DCHECK(throttleable_task_type_names.IsEmpty()); - DCHECK(freezable_task_type_names.IsEmpty()); -} - // static -base::Optional<QueueTraits> FrameSchedulerImpl::CreateQueueTraitsForTaskType( - TaskType type) { +QueueTraits FrameSchedulerImpl::CreateQueueTraitsForTaskType(TaskType type) { // TODO(haraken): Optimize the mapping from TaskTypes to task runners. // TODO(sreejakshetty): Clean up the PrioritisationType QueueTrait and // QueueType for kInternalContinueScriptLoading and kInternalContentCapture. @@ -464,13 +393,15 @@ base::Optional<QueueTraits> FrameSchedulerImpl::CreateQueueTraitsForTaskType( case TaskType::kInternalUserInteraction: case TaskType::kInternalIntersectionObserver: return PausableTaskQueueTraits(); + case TaskType::kInternalFindInPage: + return FindInPageTaskQueueTraits(); case TaskType::kInternalContinueScriptLoading: return PausableTaskQueueTraits().SetPrioritisationType( QueueTraits::PrioritisationType::kVeryHigh); case TaskType::kDatabaseAccess: if (base::FeatureList::IsEnabled(kHighPriorityDatabaseTaskType)) { return PausableTaskQueueTraits().SetPrioritisationType( - QueueTraits::PrioritisationType::kHigh); + QueueTraits::PrioritisationType::kExperimentalDatabase); } else { return PausableTaskQueueTraits(); } @@ -508,19 +439,22 @@ base::Optional<QueueTraits> FrameSchedulerImpl::CreateQueueTraitsForTaskType( case TaskType::kWorkerThreadTaskQueueDefault: case TaskType::kWorkerThreadTaskQueueV8: case TaskType::kWorkerThreadTaskQueueCompositor: + case TaskType::kMainThreadTaskQueueNonWaking: // The web scheduling API task types are used by WebSchedulingTaskQueues. // The associated TaskRunner should be obtained by creating a // WebSchedulingTaskQueue with CreateWebSchedulingTaskQueue(). case TaskType::kExperimentalWebScheduling: case TaskType::kCount: // Not a valid frame-level TaskType. - return base::nullopt; + NOTREACHED(); + return QueueTraits(); } // This method is called for all values between 0 and kCount. TaskType, // however, has numbering gaps, so even though all enumerated TaskTypes are // handled in the switch and return a value, we fall through for some values // of |type|. - return base::nullopt; + NOTREACHED(); + return QueueTraits(); } scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner( @@ -532,17 +466,8 @@ scoped_refptr<base::SingleThreadTaskRunner> FrameSchedulerImpl::GetTaskRunner( scoped_refptr<MainThreadTaskQueue> FrameSchedulerImpl::GetTaskQueue( TaskType type) { - DCHECK_LT(static_cast<size_t>(type), - main_thread_scheduler_->scheduling_settings() - .frame_task_types_to_queue_traits.size()); - base::Optional<QueueTraits> queue_traits = - main_thread_scheduler_->scheduling_settings() - .frame_task_types_to_queue_traits[static_cast<size_t>(type)]; - // We don't have a QueueTraits mapping for |task_type| if it is not a - // frame-level task type. - DCHECK(queue_traits); - return frame_task_queue_controller_->GetTaskQueue( - queue_traits.value()); + QueueTraits queue_traits = CreateQueueTraitsForTaskType(type); + return frame_task_queue_controller_->GetTaskQueue(queue_traits); } std::unique_ptr<WebResourceLoadingTaskRunnerHandle> @@ -638,6 +563,13 @@ WebScopedVirtualTimePauser FrameSchedulerImpl::CreateWebScopedVirtualTimePauser( void FrameSchedulerImpl::ResetForNavigation() { document_bound_weak_factory_.InvalidateWeakPtrs(); + for (const auto& it : back_forward_cache_opt_out_counts_) { + TRACE_EVENT_NESTABLE_ASYNC_END0( + "renderer.scheduler", "ActiveSchedulerTrackedFeature", + TRACE_ID_LOCAL(reinterpret_cast<intptr_t>(this) ^ + static_cast<int>(it.first))); + } + back_forward_cache_opt_out_counts_.clear(); back_forward_cache_opt_outs_.reset(); last_uploaded_active_features_ = 0; @@ -650,13 +582,20 @@ void FrameSchedulerImpl::OnStartedUsingFeature( if (policy.disable_aggressive_throttling) OnAddedAggressiveThrottlingOptOut(); - if (policy.disable_back_forward_cache) + if (policy.disable_back_forward_cache) { OnAddedBackForwardCacheOptOut(feature); + } uint64_t new_mask = GetActiveFeaturesTrackedForBackForwardCacheMetricsMask(); - if (old_mask != new_mask) + if (old_mask != new_mask) { NotifyDelegateAboutFeaturesAfterCurrentTask(); + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( + "renderer.scheduler", "ActiveSchedulerTrackedFeature", + TRACE_ID_LOCAL(reinterpret_cast<intptr_t>(this) ^ + static_cast<int>(feature)), + "feature", FeatureToString(feature)); + } } void FrameSchedulerImpl::OnStoppedUsingFeature( @@ -671,8 +610,13 @@ void FrameSchedulerImpl::OnStoppedUsingFeature( uint64_t new_mask = GetActiveFeaturesTrackedForBackForwardCacheMetricsMask(); - if (old_mask != new_mask) + if (old_mask != new_mask) { NotifyDelegateAboutFeaturesAfterCurrentTask(); + TRACE_EVENT_NESTABLE_ASYNC_END0( + "renderer.scheduler", "ActiveSchedulerTrackedFeature", + TRACE_ID_LOCAL(reinterpret_cast<intptr_t>(this) ^ + static_cast<int>(feature))); + } } void FrameSchedulerImpl::NotifyDelegateAboutFeaturesAfterCurrentTask() { @@ -743,7 +687,7 @@ void FrameSchedulerImpl::AsValueInto( base::trace_event::TracedValue* state) const { state->SetBoolean("frame_visible", frame_visible_); state->SetBoolean("page_visible", parent_page_scheduler_->IsPageVisible()); - state->SetBoolean("cross_origin", IsCrossOrigin()); + state->SetBoolean("cross_origin_to_main_frame", IsCrossOriginToMainFrame()); state->SetString("frame_type", frame_type_ == FrameScheduler::FrameType::kMainFrame ? "MainFrame" @@ -892,7 +836,7 @@ bool FrameSchedulerImpl::ShouldThrottleTaskQueues() const { if (!parent_page_scheduler_->IsPageVisible()) return true; return RuntimeEnabledFeatures::TimerThrottlingForHiddenFramesEnabled() && - !frame_visible_ && IsCrossOrigin(); + !frame_visible_ && IsCrossOriginToMainFrame(); } void FrameSchedulerImpl::UpdateTaskQueueThrottling( @@ -938,16 +882,12 @@ TaskQueue::QueuePriority FrameSchedulerImpl::ComputePriority( // and add a range of new priorities less than low. if (task_queue->web_scheduling_priority()) { switch (task_queue->web_scheduling_priority().value()) { - case WebSchedulingPriority::kImmediatePriority: - return TaskQueue::QueuePriority::kHighestPriority; - case WebSchedulingPriority::kHighPriority: + case WebSchedulingPriority::kUserBlockingPriority: return TaskQueue::QueuePriority::kHighPriority; - case WebSchedulingPriority::kDefaultPriority: + case WebSchedulingPriority::kUserVisiblePriority: return TaskQueue::QueuePriority::kNormalPriority; - case WebSchedulingPriority::kLowPriority: + case WebSchedulingPriority::kBackgroundPriority: return TaskQueue::QueuePriority::kLowPriority; - case WebSchedulingPriority::kIdlePriority: - return TaskQueue::QueuePriority::kBestEffortPriority; } } @@ -1022,7 +962,7 @@ TaskQueue::QueuePriority FrameSchedulerImpl::ComputePriority( } // Frame origin type experiment. - if (IsCrossOrigin()) { + if (IsCrossOriginToMainFrame()) { if (main_thread_scheduler_->scheduling_settings() .low_priority_cross_origin || (main_thread_scheduler_->scheduling_settings() @@ -1046,6 +986,21 @@ TaskQueue::QueuePriority FrameSchedulerImpl::ComputePriority( return main_thread_scheduler_->compositor_priority(); } + if (task_queue->GetPrioritisationType() == + MainThreadTaskQueue::QueueTraits::PrioritisationType::kFindInPage) { + return main_thread_scheduler_->find_in_page_priority(); + } + + if (task_queue->GetPrioritisationType() == + MainThreadTaskQueue::QueueTraits::PrioritisationType:: + kExperimentalDatabase) { + // TODO(shaseley): This decision should probably be based on Agent + // visibility. Consider changing this before shipping anything. + return parent_page_scheduler_->IsPageVisible() + ? TaskQueue::QueuePriority::kHighPriority + : TaskQueue::QueuePriority::kNormalPriority; + } + return TaskQueue::QueuePriority::kNormalPriority; } @@ -1242,5 +1197,10 @@ FrameSchedulerImpl::LoadingControlTaskQueueTraits() { QueueTraits::PrioritisationType::kLoadingControl); } +MainThreadTaskQueue::QueueTraits +FrameSchedulerImpl::FindInPageTaskQueueTraits() { + return PausableTaskQueueTraits().SetPrioritisationType( + QueueTraits::PrioritisationType::kFindInPage); +} } // namespace scheduler } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h index 860cd684172..cb6d72cf306 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h @@ -93,8 +93,8 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler, void SetPaused(bool frame_paused) override; void SetShouldReportPostedTasksWhenDisabled(bool should_report) override; - void SetCrossOrigin(bool cross_origin) override; - bool IsCrossOrigin() const override; + void SetCrossOriginToMainFrame(bool cross_origin) override; + bool IsCrossOriginToMainFrame() const override; void SetIsAdFrame() override; bool IsAdFrame() const override; @@ -160,16 +160,6 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler, MainThreadTaskQueue*, base::sequence_manager::TaskQueue::QueueEnabledVoter*) override; - using FrameTaskTypeToQueueTraitsArray = - std::array<base::Optional<MainThreadTaskQueue::QueueTraits>, - static_cast<size_t>(TaskType::kCount)>; - - // Initializes the mapping from TaskType to QueueTraits for frame-level tasks. - // We control the policy and initialize this, but the map is stored with main - // thread scheduling settings to avoid redundancy. - static void InitializeTaskTypeQueueTraitsMap( - FrameTaskTypeToQueueTraitsArray&); - // Returns the list of active features which currently tracked by the // scheduler for back-forward cache metrics. WTF::HashSet<SchedulingPolicy::Feature> @@ -269,8 +259,8 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler, // Create the QueueTraits for a specific TaskType. This returns base::nullopt // for loading tasks and non-frame-level tasks. - static base::Optional<MainThreadTaskQueue::QueueTraits> - CreateQueueTraitsForTaskType(TaskType); + static MainThreadTaskQueue::QueueTraits CreateQueueTraitsForTaskType( + TaskType); // Reset the state which should not persist across navigations. void ResetForNavigation(); @@ -294,6 +284,7 @@ class PLATFORM_EXPORT FrameSchedulerImpl : public FrameScheduler, DoesNotUseVirtualTimeTaskQueueTraits(); static MainThreadTaskQueue::QueueTraits LoadingTaskQueueTraits(); static MainThreadTaskQueue::QueueTraits LoadingControlTaskQueueTraits(); + static MainThreadTaskQueue::QueueTraits FindInPageTaskQueueTraits(); const FrameScheduler::FrameType frame_type_; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc index 43efd83327e..6411d32f558 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc @@ -40,6 +40,11 @@ namespace scheduler { namespace frame_scheduler_impl_unittest { using FeatureHandle = FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle; +using PrioritisationType = MainThreadTaskQueue::QueueTraits::PrioritisationType; + +void AppendToVectorTestTask(Vector<String>* vector, String value) { + vector->push_back(std::move(value)); +} class FrameSchedulerDelegateForTesting : public FrameScheduler::Delegate { public: @@ -126,6 +131,49 @@ class FrameSchedulerImplTest : public testing::Test { UseCase::kLoading); } + // Helper for posting several tasks of specific prioritisation types for + // testing the relative order of tasks. |task_descriptor| is a string with + // space delimited task identifiers. The first letter of each task identifier + // specifies the prioritisation type: + // - 'R': Regular (normal priority) + // - 'V': Very high + // - 'B': Best-effort + // - 'D': Database + void PostTestTasksForPrioritisationType(Vector<String>* run_order, + const String& task_descriptor) { + std::istringstream stream(task_descriptor.Utf8()); + PrioritisationType prioritisation_type; + while (!stream.eof()) { + std::string task; + stream >> task; + switch (task[0]) { + case 'R': + prioritisation_type = PrioritisationType::kRegular; + break; + case 'V': + prioritisation_type = PrioritisationType::kVeryHigh; + break; + case 'B': + prioritisation_type = PrioritisationType::kBestEffort; + break; + case 'D': + prioritisation_type = PrioritisationType::kExperimentalDatabase; + break; + default: + EXPECT_FALSE(true); + return; + } + auto queue_traits = + FrameSchedulerImpl::PausableTaskQueueTraits().SetPrioritisationType( + prioritisation_type); + GetTaskQueue(queue_traits) + ->task_runner() + ->PostTask(FROM_HERE, + base::BindOnce(&AppendToVectorTestTask, run_order, + String::FromUTF8(task))); + } + } + static void ResetForNavigation(FrameSchedulerImpl* frame_scheduler) { frame_scheduler->ResetForNavigation(); } @@ -306,19 +354,10 @@ void IncrementCounter(int* counter) { ++*counter; } -void ExpectAndIncrementCounter(int expected, int* actual) { - EXPECT_EQ(expected, *actual); - IncrementCounter(actual); -} - void RecordQueueName(String name, Vector<String>* tasks) { tasks->push_back(std::move(name)); } -void AppendToVectorTestTask(Vector<String>* vector, String value) { - vector->push_back(std::move(value)); -} - // Simulate running a task of a particular length by fast forwarding the task // environment clock, which is used to determine the wall time of a task. void RunTaskOfLength(base::test::TaskEnvironment* task_environment, @@ -374,10 +413,10 @@ TEST_F(FrameSchedulerImplTest, LazyInitThrottleableTaskQueue(); EXPECT_FALSE(IsThrottled()); frame_scheduler_->SetFrameVisible(false); - frame_scheduler_->SetCrossOrigin(true); - frame_scheduler_->SetCrossOrigin(false); + frame_scheduler_->SetCrossOriginToMainFrame(true); + frame_scheduler_->SetCrossOriginToMainFrame(false); EXPECT_FALSE(IsThrottled()); - frame_scheduler_->SetCrossOrigin(true); + frame_scheduler_->SetCrossOriginToMainFrame(true); EXPECT_TRUE(IsThrottled()); frame_scheduler_->SetFrameVisible(true); EXPECT_FALSE(IsThrottled()); @@ -388,7 +427,7 @@ TEST_F(FrameSchedulerImplTest, TEST_F(FrameSchedulerImplTest, FrameHidden_CrossOrigin_LazyInit) { ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(true); frame_scheduler_->SetFrameVisible(false); - frame_scheduler_->SetCrossOrigin(true); + frame_scheduler_->SetCrossOriginToMainFrame(true); LazyInitThrottleableTaskQueue(); EXPECT_TRUE(IsThrottled()); } @@ -399,14 +438,14 @@ TEST_F(FrameSchedulerImplTest, LazyInitThrottleableTaskQueue(); EXPECT_FALSE(IsThrottled()); frame_scheduler_->SetFrameVisible(false); - frame_scheduler_->SetCrossOrigin(true); + frame_scheduler_->SetCrossOriginToMainFrame(true); EXPECT_FALSE(IsThrottled()); } TEST_F(FrameSchedulerImplTest, FrameHidden_CrossOrigin_NoThrottling_LazyInit) { ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(false); frame_scheduler_->SetFrameVisible(false); - frame_scheduler_->SetCrossOrigin(true); + frame_scheduler_->SetCrossOriginToMainFrame(true); LazyInitThrottleableTaskQueue(); EXPECT_FALSE(IsThrottled()); } @@ -433,14 +472,14 @@ TEST_F(FrameSchedulerImplTest, FrameVisible_CrossOrigin_ExplicitInit) { EXPECT_TRUE(throttleable_task_queue()); frame_scheduler_->SetFrameVisible(true); EXPECT_FALSE(IsThrottled()); - frame_scheduler_->SetCrossOrigin(true); + frame_scheduler_->SetCrossOriginToMainFrame(true); EXPECT_FALSE(IsThrottled()); } TEST_F(FrameSchedulerImplTest, FrameVisible_CrossOrigin_LazyInit) { ScopedTimerThrottlingForHiddenFramesForTest throttle_hidden_frames(true); frame_scheduler_->SetFrameVisible(true); - frame_scheduler_->SetCrossOrigin(true); + frame_scheduler_->SetCrossOriginToMainFrame(true); LazyInitThrottleableTaskQueue(); EXPECT_FALSE(IsThrottled()); } @@ -1678,7 +1717,7 @@ class LowPriorityCrossOriginTaskExperimentTest : public FrameSchedulerImplTest { }; TEST_F(LowPriorityCrossOriginTaskExperimentTest, FrameQueuesPriorities) { - EXPECT_FALSE(frame_scheduler_->IsCrossOrigin()); + EXPECT_FALSE(frame_scheduler_->IsCrossOriginToMainFrame()); // Same Origin Task Queues. EXPECT_EQ(LoadingTaskQueue()->GetQueuePriority(), @@ -1694,8 +1733,8 @@ TEST_F(LowPriorityCrossOriginTaskExperimentTest, FrameQueuesPriorities) { EXPECT_EQ(UnpausableTaskQueue()->GetQueuePriority(), TaskQueue::QueuePriority::kNormalPriority); - frame_scheduler_->SetCrossOrigin(true); - EXPECT_TRUE(frame_scheduler_->IsCrossOrigin()); + frame_scheduler_->SetCrossOriginToMainFrame(true); + EXPECT_TRUE(frame_scheduler_->IsCrossOriginToMainFrame()); EXPECT_EQ(LoadingTaskQueue()->GetQueuePriority(), TaskQueue::QueuePriority::kLowPriority); @@ -1737,8 +1776,8 @@ TEST_F(LowPriorityCrossOriginTaskDuringLoadingExperimentTest, EXPECT_EQ(UnpausableTaskQueue()->GetQueuePriority(), TaskQueue::QueuePriority::kNormalPriority); - frame_scheduler_->SetCrossOrigin(true); - EXPECT_TRUE(frame_scheduler_->IsCrossOrigin()); + frame_scheduler_->SetCrossOriginToMainFrame(true); + EXPECT_TRUE(frame_scheduler_->IsCrossOriginToMainFrame()); EXPECT_EQ(LoadingTaskQueue()->GetQueuePriority(), TaskQueue::QueuePriority::kLowPriority); @@ -1787,191 +1826,6 @@ TEST_F(FrameSchedulerImplTest, TaskTypeToTaskQueueMapping) { ForegroundOnlyTaskQueue()); } -class ThrottleAndFreezeTaskTypesExperimentTest : public FrameSchedulerImplTest { - public: - ThrottleAndFreezeTaskTypesExperimentTest(const base::FieldTrialParams& params, - const char* group_name) { - scoped_feature_list().InitAndEnableFeatureWithParameters( - kThrottleAndFreezeTaskTypes, params); - } -}; - -class ThrottleableAndFreezableTaskTypesTest - : public ThrottleAndFreezeTaskTypesExperimentTest { - public: - ThrottleableAndFreezableTaskTypesTest() - : ThrottleAndFreezeTaskTypesExperimentTest( - base::FieldTrialParams{ - // Leading spaces are allowed. - {kThrottleableTaskTypesListParam, "PostedMessage"}, - {kFreezableTaskTypesListParam, - "PostedMessage, MediaElementEvent,DOMManipulation"}}, - "Group1") {} -}; - -TEST_F(ThrottleableAndFreezableTaskTypesTest, QueueTraitsFromFieldTrialParams) { - if (base::FeatureList::IsEnabled(blink::features::kStopNonTimersInBackground)) - return; - // These tests will start to fail if the default task queues or queue traits - // change for these task types. - - // Check that the overrides work. - auto task_queue = GetTaskQueue(TaskType::kPostedMessage); - EXPECT_EQ(task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits() - .SetCanBeThrottled(true) - .SetCanBeFrozen(true) - .SetCanBePaused(true) - .SetCanRunWhenVirtualTimePaused(false)); - - task_queue = GetTaskQueue(TaskType::kMediaElementEvent); - EXPECT_EQ(task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits() - .SetCanBeFrozen(true) - .SetCanBePaused(true) - .SetCanRunWhenVirtualTimePaused(false)); - - task_queue = GetTaskQueue(TaskType::kDatabaseAccess); - EXPECT_EQ(task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits() - .SetCanBePaused(true) - .SetCanRunWhenVirtualTimePaused(false)); - - task_queue = GetTaskQueue(TaskType::kDOMManipulation); - EXPECT_EQ(task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits() - .SetCanBeFrozen(true) - .SetCanBeDeferred(true) - .SetCanBePaused(true) - .SetCanRunWhenVirtualTimePaused(false)); - - // Test some task types that were not configured through field trial - // parameters. - task_queue = GetTaskQueue(TaskType::kWebLocks); - EXPECT_EQ( - task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits().SetCanRunWhenVirtualTimePaused(false)); - - task_queue = GetTaskQueue(TaskType::kMiscPlatformAPI); - EXPECT_EQ(task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits() - .SetCanBeDeferred(true) - .SetCanBePaused(true) - .SetCanRunWhenVirtualTimePaused(false)); -} - - -class FreezableOnlyTaskTypesTest - : public ThrottleAndFreezeTaskTypesExperimentTest { - public: - FreezableOnlyTaskTypesTest() - : ThrottleAndFreezeTaskTypesExperimentTest( - base::FieldTrialParams{ - {kThrottleableTaskTypesListParam, ""}, - {kFreezableTaskTypesListParam, - "PostedMessage,MediaElementEvent,DOMManipulation"}}, - "Group2") {} -}; - -TEST_F(FreezableOnlyTaskTypesTest, QueueTraitsFromFieldTrialParams) { - if (base::FeatureList::IsEnabled(blink::features::kStopNonTimersInBackground)) - return; - - // These tests will start to fail if the default task queues or queue traits - // change for these task types. - - // Check that the overrides work. - auto task_queue = GetTaskQueue(TaskType::kPostedMessage); - EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits() - .SetCanBeFrozen(true) - .SetCanBePaused(true)); - - task_queue = GetTaskQueue(TaskType::kMediaElementEvent); - EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits() - .SetCanBeFrozen(true) - .SetCanBePaused(true)); - - task_queue = GetTaskQueue(TaskType::kDatabaseAccess); - EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits() - .SetCanBePaused(true)); - - task_queue = GetTaskQueue(TaskType::kDOMManipulation); - EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits() - .SetCanBeFrozen(true) - .SetCanBeDeferred(true) - .SetCanBePaused(true)); - - // Test some task types that were not configured through field trial - // parameters. - task_queue = GetTaskQueue(TaskType::kWebLocks); - EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits()); - - task_queue = GetTaskQueue(TaskType::kMiscPlatformAPI); - EXPECT_EQ(task_queue->GetQueueTraits(), MainThreadTaskQueue::QueueTraits() - .SetCanBeDeferred(true) - .SetCanBePaused(true)); -} - -class ThrottleableOnlyTaskTypesTest - : public ThrottleAndFreezeTaskTypesExperimentTest { - public: - ThrottleableOnlyTaskTypesTest() - : ThrottleAndFreezeTaskTypesExperimentTest( - base::FieldTrialParams{ - {kFreezableTaskTypesListParam, ""}, - {kThrottleableTaskTypesListParam, "PostedMessage"}}, - "Group3") {} -}; - -TEST_F(ThrottleableOnlyTaskTypesTest, QueueTraitsFromFieldTrialParams) { - if (base::FeatureList::IsEnabled(blink::features::kStopNonTimersInBackground)) - return; - - // These tests will start to fail if the default task queues or queue traits - // change for these task types. - - // Check that the overrides work. - auto task_queue = GetTaskQueue(TaskType::kPostedMessage); - EXPECT_EQ(task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits() - .SetCanBeThrottled(true) - .SetCanBePaused(true) - .SetCanRunWhenVirtualTimePaused(false)); - - task_queue = GetTaskQueue(TaskType::kMediaElementEvent); - EXPECT_EQ(task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits() - .SetCanBePaused(true) - .SetCanRunWhenVirtualTimePaused(false)); - - task_queue = GetTaskQueue(TaskType::kDatabaseAccess); - EXPECT_EQ(task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits() - .SetCanBePaused(true) - .SetCanRunWhenVirtualTimePaused(false)); - - task_queue = GetTaskQueue(TaskType::kDOMManipulation); - EXPECT_EQ(task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits() - .SetCanBeDeferred(true) - .SetCanBePaused(true) - .SetCanRunWhenVirtualTimePaused(false)); - - // Test some task types that were not configured through field trial - // parameters. - task_queue = GetTaskQueue(TaskType::kWebLocks); - EXPECT_EQ( - task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits().SetCanRunWhenVirtualTimePaused(false)); - - task_queue = GetTaskQueue(TaskType::kMiscPlatformAPI); - EXPECT_EQ(task_queue->GetQueueTraits(), - MainThreadTaskQueue::QueueTraits() - .SetCanBeDeferred(true) - .SetCanBePaused(true) - .SetCanRunWhenVirtualTimePaused(false)); -} - class FrameSchedulerImplDatabaseAccessWithoutHighPriority : public FrameSchedulerImplTest { public: @@ -1980,37 +1834,11 @@ class FrameSchedulerImplDatabaseAccessWithoutHighPriority }; TEST_F(FrameSchedulerImplDatabaseAccessWithoutHighPriority, QueueTraits) { - // These tests will start to fail if the default task queues or queue traits - // change for these task types. - - int counter = 0; - - auto loading_queue = GetTaskQueue(TaskType::kInternalContinueScriptLoading); - EXPECT_EQ(loading_queue->GetQueuePriority(), - TaskQueue::QueuePriority::kVeryHighPriority); - loading_queue->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ExpectAndIncrementCounter, 0, - base::Unretained(&counter))); - auto da_queue = GetTaskQueue(TaskType::kDatabaseAccess); EXPECT_EQ(da_queue->GetQueueTraits().prioritisation_type, MainThreadTaskQueue::QueueTraits::PrioritisationType::kRegular); EXPECT_EQ(da_queue->GetQueuePriority(), TaskQueue::QueuePriority::kNormalPriority); - da_queue->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ExpectAndIncrementCounter, 1, - base::Unretained(&counter))); - - auto content_queue = GetTaskQueue(TaskType::kInternalContentCapture); - EXPECT_EQ(content_queue->GetQueuePriority(), - TaskQueue::QueuePriority::kBestEffortPriority); - content_queue->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ExpectAndIncrementCounter, 2, - base::Unretained(&counter))); - - EXPECT_EQ(0, counter); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(3, counter); } class FrameSchedulerImplDatabaseAccessWithHighPriority @@ -2021,37 +1849,31 @@ class FrameSchedulerImplDatabaseAccessWithHighPriority }; TEST_F(FrameSchedulerImplDatabaseAccessWithHighPriority, QueueTraits) { - // These tests will start to fail if the default task queues or queue traits - // change for these task types. - - int counter = 0; - - auto loading_queue = GetTaskQueue(TaskType::kInternalContinueScriptLoading); - EXPECT_EQ(loading_queue->GetQueuePriority(), - TaskQueue::QueuePriority::kVeryHighPriority); - loading_queue->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ExpectAndIncrementCounter, 0, - base::Unretained(&counter))); - auto da_queue = GetTaskQueue(TaskType::kDatabaseAccess); EXPECT_EQ(da_queue->GetQueueTraits().prioritisation_type, - MainThreadTaskQueue::QueueTraits::PrioritisationType::kHigh); + MainThreadTaskQueue::QueueTraits::PrioritisationType:: + kExperimentalDatabase); EXPECT_EQ(da_queue->GetQueuePriority(), TaskQueue::QueuePriority::kHighPriority); - da_queue->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ExpectAndIncrementCounter, 1, - base::Unretained(&counter))); +} - auto pausable_queue = PausableTaskQueue(); - EXPECT_EQ(pausable_queue->GetQueuePriority(), - TaskQueue::QueuePriority::kNormalPriority); - pausable_queue->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ExpectAndIncrementCounter, 2, - base::Unretained(&counter))); +TEST_F(FrameSchedulerImplDatabaseAccessWithHighPriority, RunOrder) { + Vector<String> run_order; + PostTestTasksForPrioritisationType(&run_order, "D1 R1 D2 V1 B1"); - EXPECT_EQ(0, counter); base::RunLoop().RunUntilIdle(); - EXPECT_EQ(3, counter); + EXPECT_THAT(run_order, testing::ElementsAre("V1", "D1", "D2", "R1", "B1")); +} + +TEST_F(FrameSchedulerImplDatabaseAccessWithHighPriority, + NormalPriorityInBackground) { + page_scheduler_->SetPageVisible(false); + + Vector<String> run_order; + PostTestTasksForPrioritisationType(&run_order, "D1 R1 D2 V1 B1"); + + base::RunLoop().RunUntilIdle(); + EXPECT_THAT(run_order, testing::ElementsAre("V1", "D1", "R1", "D2", "B1")); } TEST_F(FrameSchedulerImplTest, ContentCaptureHasIdleTaskQueue) { @@ -2314,11 +2136,9 @@ class WebSchedulingTaskQueueTest : public FrameSchedulerImplTest { // Helper for posting tasks to a WebSchedulingTaskQueue. |task_descriptor| is // a string with space delimited task identifiers. The first letter of each // task identifier specifies the task queue priority: - // - 'I': Immediate - // - 'H': High - // - 'D': Default - // - 'L': Low - // - 'E': Idle + // - 'U': UserBlocking + // - 'V': UserVisible + // - 'B': Background void PostWebSchedulingTestTasks(Vector<String>* run_order, const String& task_descriptor) { std::istringstream stream(task_descriptor.Utf8()); @@ -2327,20 +2147,14 @@ class WebSchedulingTaskQueueTest : public FrameSchedulerImplTest { stream >> task; WebSchedulingPriority priority; switch (task[0]) { - case 'I': - priority = WebSchedulingPriority::kImmediatePriority; - break; - case 'H': - priority = WebSchedulingPriority::kHighPriority; + case 'U': + priority = WebSchedulingPriority::kUserBlockingPriority; break; - case 'D': - priority = WebSchedulingPriority::kDefaultPriority; + case 'V': + priority = WebSchedulingPriority::kUserVisiblePriority; break; - case 'L': - priority = WebSchedulingPriority::kLowPriority; - break; - case 'E': - priority = WebSchedulingPriority::kIdlePriority; + case 'B': + priority = WebSchedulingPriority::kBackgroundPriority; break; default: EXPECT_FALSE(true); @@ -2361,23 +2175,23 @@ class WebSchedulingTaskQueueTest : public FrameSchedulerImplTest { TEST_F(WebSchedulingTaskQueueTest, TasksRunInPriorityOrder) { Vector<String> run_order; - PostWebSchedulingTestTasks(&run_order, "E1 E2 L1 L2 D1 D2 H1 H2 I1 I2"); + PostWebSchedulingTestTasks(&run_order, "B1 B2 V1 V2 U1 U2"); base::RunLoop().RunUntilIdle(); - EXPECT_THAT(run_order, testing::ElementsAre("I1", "I2", "H1", "H2", "D1", - "D2", "L1", "L2", "E1", "E2")); + EXPECT_THAT(run_order, + testing::ElementsAre("U1", "U2", "V1", "V2", "B1", "B2")); } TEST_F(WebSchedulingTaskQueueTest, DynamicTaskPriorityOrder) { Vector<String> run_order; - PostWebSchedulingTestTasks(&run_order, "E1 E2 D1 D2 I1 I2"); - task_queues_[static_cast<int>(WebSchedulingPriority::kImmediatePriority)] - ->SetPriority(WebSchedulingPriority::kLowPriority); + PostWebSchedulingTestTasks(&run_order, "B1 B2 V1 V2 U1 U2"); + task_queues_[static_cast<int>(WebSchedulingPriority::kUserBlockingPriority)] + ->SetPriority(WebSchedulingPriority::kBackgroundPriority); base::RunLoop().RunUntilIdle(); EXPECT_THAT(run_order, - testing::ElementsAre("D1", "D2", "I1", "I2", "E1", "E2")); + testing::ElementsAre("V1", "V2", "B1", "B2", "U1", "U2")); } } // namespace frame_scheduler_impl_unittest diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_status.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_status.cc index cfda104e005..d8a1c929c2b 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_status.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_status.cc @@ -26,9 +26,10 @@ enum class FrameThrottlingState { enum class FrameOriginState { kMainFrame = 0, - kSameOrigin = 1, - kCrossOrigin = 2, + kSameOriginToMainFrame = 1, + kCrossOriginToMainFrame = 2, + // TODO(dcheng): get rid of kCount here. kCount = 3 }; @@ -60,9 +61,9 @@ FrameOriginState GetFrameOriginState(const FrameScheduler& frame_scheduler) { if (frame_scheduler.GetFrameType() == FrameScheduler::FrameType::kMainFrame) { return FrameOriginState::kMainFrame; } - if (frame_scheduler.IsCrossOrigin()) - return FrameOriginState::kCrossOrigin; - return FrameOriginState::kSameOrigin; + if (frame_scheduler.IsCrossOriginToMainFrame()) + return FrameOriginState::kCrossOriginToMainFrame; + return FrameOriginState::kSameOriginToMainFrame; } } // namespace diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc index 710f3d595b9..362b0e1b5f1 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc @@ -107,10 +107,6 @@ void FrameTaskQueueController::CreateTaskQueue( queue_creation_params = queue_creation_params.SetFixedPriority( TaskQueue::QueuePriority::kVeryHighPriority); break; - case QueueTraits::PrioritisationType::kHigh: - queue_creation_params = queue_creation_params.SetFixedPriority( - TaskQueue::QueuePriority::kHighPriority); - break; case QueueTraits::PrioritisationType::kBestEffort: queue_creation_params = queue_creation_params.SetFixedPriority( TaskQueue::QueuePriority::kBestEffortPriority); @@ -179,7 +175,7 @@ bool FrameTaskQueueController::RemoveResourceLoadingTaskQueue( void FrameTaskQueueController::AsValueInto( base::trace_event::TracedValue* state) const { state->BeginArray("task_queues"); - for (const auto it : task_queues_) { + for (const auto& it : task_queues_) { state->AppendString(PointerToString(it.value.get())); } state->EndArray(); diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc index 18f4199534c..28f6af4d89a 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc @@ -260,38 +260,24 @@ TEST_F(FrameTaskQueueControllerTest, TEST_F(FrameTaskQueueControllerTest, AddWebSchedulingTaskQueues) { scoped_refptr<MainThreadTaskQueue> task_queue = frame_task_queue_controller_->NewWebSchedulingTaskQueue( - QueueTraits(), WebSchedulingPriority::kImmediatePriority); + QueueTraits(), WebSchedulingPriority::kUserBlockingPriority); EXPECT_EQ(1u, frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); - EXPECT_EQ(WebSchedulingPriority::kImmediatePriority, + EXPECT_EQ(WebSchedulingPriority::kUserBlockingPriority, task_queue->web_scheduling_priority().value()); task_queue = frame_task_queue_controller_->NewWebSchedulingTaskQueue( - QueueTraits(), WebSchedulingPriority::kHighPriority); + QueueTraits(), WebSchedulingPriority::kUserVisiblePriority); EXPECT_EQ(2u, frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); - EXPECT_EQ(WebSchedulingPriority::kHighPriority, + EXPECT_EQ(WebSchedulingPriority::kUserVisiblePriority, task_queue->web_scheduling_priority().value()); task_queue = frame_task_queue_controller_->NewWebSchedulingTaskQueue( - QueueTraits(), WebSchedulingPriority::kDefaultPriority); + QueueTraits(), WebSchedulingPriority::kBackgroundPriority); EXPECT_EQ(3u, frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); - EXPECT_EQ(WebSchedulingPriority::kDefaultPriority, - task_queue->web_scheduling_priority().value()); - - task_queue = frame_task_queue_controller_->NewWebSchedulingTaskQueue( - QueueTraits(), WebSchedulingPriority::kLowPriority); - EXPECT_EQ(4u, - frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); - EXPECT_EQ(WebSchedulingPriority::kLowPriority, - task_queue->web_scheduling_priority().value()); - - task_queue = frame_task_queue_controller_->NewWebSchedulingTaskQueue( - QueueTraits(), WebSchedulingPriority::kIdlePriority); - EXPECT_EQ(5u, - frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); - EXPECT_EQ(WebSchedulingPriority::kIdlePriority, + EXPECT_EQ(WebSchedulingPriority::kBackgroundPriority, task_queue->web_scheduling_priority().value()); } @@ -299,18 +285,18 @@ TEST_F(FrameTaskQueueControllerTest, AddMultipleSamePriorityWebSchedulingTaskQueues) { scoped_refptr<MainThreadTaskQueue> task_queue1 = frame_task_queue_controller_->NewWebSchedulingTaskQueue( - QueueTraits(), WebSchedulingPriority::kImmediatePriority); + QueueTraits(), WebSchedulingPriority::kUserBlockingPriority); EXPECT_EQ(1u, frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); - EXPECT_EQ(WebSchedulingPriority::kImmediatePriority, + EXPECT_EQ(WebSchedulingPriority::kUserBlockingPriority, task_queue1->web_scheduling_priority().value()); scoped_refptr<MainThreadTaskQueue> task_queue2 = frame_task_queue_controller_->NewWebSchedulingTaskQueue( - QueueTraits(), WebSchedulingPriority::kImmediatePriority); + QueueTraits(), WebSchedulingPriority::kUserBlockingPriority); EXPECT_EQ(2u, frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size()); - EXPECT_EQ(WebSchedulingPriority::kImmediatePriority, + EXPECT_EQ(WebSchedulingPriority::kUserBlockingPriority, task_queue2->web_scheduling_priority().value()); EXPECT_NE(task_queue1.get(), task_queue2.get()); @@ -338,11 +324,12 @@ INSTANTIATE_TEST_SUITE_P( All, TaskQueueCreationFromQueueTraitsTest, ::testing::Values(QueueTraits::PrioritisationType::kVeryHigh, - QueueTraits::PrioritisationType::kHigh, QueueTraits::PrioritisationType::kBestEffort, QueueTraits::PrioritisationType::kRegular, QueueTraits::PrioritisationType::kLoading, - QueueTraits::PrioritisationType::kLoadingControl)); + QueueTraits::PrioritisationType::kLoadingControl, + QueueTraits::PrioritisationType::kFindInPage, + QueueTraits::PrioritisationType::kExperimentalDatabase)); TEST_P(TaskQueueCreationFromQueueTraitsTest, AddAndRetrieveAllTaskQueues) { diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc index 2adac49f386..9b7e85a92b6 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc @@ -191,38 +191,38 @@ class MainThreadMetricsHelperTest : public testing::Test { break; case FrameStatus::kCrossOriginVisible: builder.SetFrameType(FrameScheduler::FrameType::kSubframe) - .SetIsCrossOrigin(true) + .SetIsCrossOriginToMainFrame(true) .SetIsPageVisible(true) .SetIsFrameVisible(true); break; case FrameStatus::kCrossOriginVisibleService: builder.SetFrameType(FrameScheduler::FrameType::kSubframe) - .SetIsCrossOrigin(true) + .SetIsCrossOriginToMainFrame(true) .SetPageScheduler(playing_view_.get()) .SetIsFrameVisible(true); break; case FrameStatus::kCrossOriginHidden: builder.SetFrameType(FrameScheduler::FrameType::kSubframe) - .SetIsCrossOrigin(true) + .SetIsCrossOriginToMainFrame(true) .SetIsPageVisible(true); break; case FrameStatus::kCrossOriginHiddenService: builder.SetFrameType(FrameScheduler::FrameType::kSubframe) - .SetIsCrossOrigin(true) + .SetIsCrossOriginToMainFrame(true) .SetPageScheduler(playing_view_.get()); break; case FrameStatus::kCrossOriginBackground: builder.SetFrameType(FrameScheduler::FrameType::kSubframe) - .SetIsCrossOrigin(true); + .SetIsCrossOriginToMainFrame(true); break; case FrameStatus::kCrossOriginBackgroundExemptSelf: builder.SetFrameType(FrameScheduler::FrameType::kSubframe) - .SetIsCrossOrigin(true) + .SetIsCrossOriginToMainFrame(true) .SetIsExemptFromThrottling(true); break; case FrameStatus::kCrossOriginBackgroundExemptOther: builder.SetFrameType(FrameScheduler::FrameType::kSubframe) - .SetIsCrossOrigin(true) + .SetIsCrossOriginToMainFrame(true) .SetPageScheduler(throtting_exempt_view_.get()); break; case FrameStatus::kCount: diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc index 1c1a13067ab..9bf6ae8538a 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc @@ -22,10 +22,11 @@ #include "build/build_config.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "services/metrics/public/cpp/ukm_builders.h" +#include "third_party/blink/public/common/input/web_input_event_attribution.h" +#include "third_party/blink/public/common/input/web_mouse_wheel_event.h" +#include "third_party/blink/public/common/input/web_touch_event.h" #include "third_party/blink/public/common/page/launching_process_state.h" #include "third_party/blink/public/platform/scheduler/web_renderer_process_type.h" -#include "third_party/blink/public/platform/web_mouse_wheel_event.h" -#include "third_party/blink/public/platform/web_touch_event.h" #include "third_party/blink/renderer/platform/bindings/parkable_string_manager.h" #include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -37,6 +38,7 @@ #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.h" +#include "third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/event_loop.h" #include "v8/include/v8.h" @@ -201,24 +203,15 @@ MainThreadSchedulerImpl::MainThreadSchedulerImpl( .SetFixedPriority( TaskQueue::QueuePriority::kBestEffortPriority))), render_widget_scheduler_signals_(this), + find_in_page_budget_pool_controller_( + new FindInPageBudgetPoolController(this)), control_task_queue_(helper_.ControlMainThreadTaskQueue()), compositor_task_queue_( helper_.NewTaskQueue(MainThreadTaskQueue::QueueCreationParams( MainThreadTaskQueue::QueueType::kCompositor) .SetShouldMonitorQuiescence(true))), - input_task_queue_(helper_.NewTaskQueue( - MainThreadTaskQueue::QueueCreationParams( - MainThreadTaskQueue::QueueType::kInput) - .SetShouldMonitorQuiescence(true) - .SetFixedPriority( - scheduling_settings_.high_priority_input - ? base::make_optional( - TaskQueue::QueuePriority::kHighestPriority) - : base::nullopt))), compositor_task_queue_enabled_voter_( compositor_task_queue_->CreateQueueEnabledVoter()), - input_task_queue_enabled_voter_( - input_task_queue_->CreateQueueEnabledVoter()), memory_purge_task_queue_(helper_.NewTaskQueue( MainThreadTaskQueue::QueueCreationParams( MainThreadTaskQueue::QueueType::kIdle) @@ -226,6 +219,7 @@ MainThreadSchedulerImpl::MainThreadSchedulerImpl( TaskQueue::QueuePriority::kBestEffortPriority))), memory_purge_manager_(memory_purge_task_queue_->CreateTaskRunner( TaskType::kMainThreadTaskQueueMemoryPurge)), + non_waking_time_domain_(tick_clock()), delayed_update_policy_runner_( base::BindRepeating(&MainThreadSchedulerImpl::UpdatePolicy, base::Unretained(this)), @@ -246,8 +240,8 @@ MainThreadSchedulerImpl::MainThreadSchedulerImpl( task_runners_.emplace(helper_.DefaultMainThreadTaskQueue(), nullptr); task_runners_.emplace(compositor_task_queue_, compositor_task_queue_->CreateQueueEnabledVoter()); - task_runners_.emplace(input_task_queue_, - input_task_queue_->CreateQueueEnabledVoter()); + + RegisterTimeDomain(&non_waking_time_domain_); v8_task_queue_ = NewTaskQueue(MainThreadTaskQueue::QueueCreationParams( MainThreadTaskQueue::QueueType::kV8)); @@ -255,6 +249,10 @@ MainThreadSchedulerImpl::MainThreadSchedulerImpl( MainThreadTaskQueue::QueueType::kIPC)); cleanup_task_queue_ = NewTaskQueue(MainThreadTaskQueue::QueueCreationParams( MainThreadTaskQueue::QueueType::kCleanup)); + non_waking_task_queue_ = + NewTaskQueue(MainThreadTaskQueue::QueueCreationParams( + MainThreadTaskQueue::QueueType::kNonWaking) + .SetTimeDomain(&non_waking_time_domain_)); v8_task_runner_ = v8_task_queue_->CreateTaskRunner(TaskType::kMainThreadTaskQueueV8); @@ -262,12 +260,12 @@ MainThreadSchedulerImpl::MainThreadSchedulerImpl( TaskType::kMainThreadTaskQueueCompositor); control_task_runner_ = helper_.ControlMainThreadTaskQueue()->CreateTaskRunner( TaskType::kMainThreadTaskQueueControl); - input_task_runner_ = - input_task_queue_->CreateTaskRunner(TaskType::kMainThreadTaskQueueInput); ipc_task_runner_ = ipc_task_queue_->CreateTaskRunner(TaskType::kMainThreadTaskQueueIPC); cleanup_task_runner_ = cleanup_task_queue_->CreateTaskRunner( TaskType::kMainThreadTaskQueueCleanup); + non_waking_task_runner_ = non_waking_task_queue_->CreateTaskRunner( + TaskType::kMainThreadTaskQueueNonWaking); // TaskQueueThrottler requires some task runners, then initialize // TaskQueueThrottler after task queues/runners are initialized. @@ -307,6 +305,9 @@ MainThreadSchedulerImpl::MainThreadSchedulerImpl( main_thread_only() .compositor_priority_experiments.OnMainThreadSchedulerInitialized(); + main_thread_only().current_policy.find_in_page_priority() = + find_in_page_budget_pool_controller_->CurrentTaskPriority(); + g_main_thread_scheduler = this; } @@ -319,6 +320,8 @@ MainThreadSchedulerImpl::~MainThreadSchedulerImpl() { pair.first->ShutdownTaskQueue(); } + UnregisterTimeDomain(&non_waking_time_domain_); + if (virtual_time_domain_) UnregisterTimeDomain(virtual_time_domain_.get()); @@ -552,9 +555,6 @@ MainThreadSchedulerImpl::AnyThread::AnyThread( &main_thread_scheduler_impl->tracing_controller_) {} MainThreadSchedulerImpl::SchedulingSettings::SchedulingSettings() { - high_priority_input = - base::FeatureList::IsEnabled(kHighPriorityInputOnMainThread); - low_priority_background_page = base::FeatureList::IsEnabled(kLowPriorityForBackgroundPages); best_effort_background_page = @@ -607,9 +607,6 @@ MainThreadSchedulerImpl::SchedulingSettings::SchedulingSettings() { } } } - - FrameSchedulerImpl::InitializeTaskTypeQueueTraitsMap( - frame_task_types_to_queue_traits); } MainThreadSchedulerImpl::AnyThread::~AnyThread() = default; @@ -662,6 +659,11 @@ std::unique_ptr<Thread> MainThreadSchedulerImpl::CreateMainThread() { return std::make_unique<MainThread>(this); } +std::unique_ptr<WebWidgetScheduler> +MainThreadSchedulerImpl::CreateWidgetScheduler() { + return std::make_unique<WidgetScheduler>(this); +} + scoped_refptr<base::SingleThreadTaskRunner> MainThreadSchedulerImpl::ControlTaskRunner() { return control_task_runner_; @@ -672,12 +674,6 @@ MainThreadSchedulerImpl::DefaultTaskRunner() { return helper_.DefaultTaskRunner(); } -scoped_refptr<base::SingleThreadTaskRunner> -MainThreadSchedulerImpl::InputTaskRunner() { - helper_.CheckOnValidThread(); - return input_task_runner_; -} - scoped_refptr<SingleThreadIdleTaskRunner> MainThreadSchedulerImpl::IdleTaskRunner() { return idle_helper_.IdleTaskRunner(); @@ -709,11 +705,6 @@ MainThreadSchedulerImpl::CompositorTaskQueue() { return compositor_task_queue_; } -scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerImpl::InputTaskQueue() { - helper_.CheckOnValidThread(); - return input_task_queue_; -} - scoped_refptr<MainThreadTaskQueue> MainThreadSchedulerImpl::V8TaskQueue() { helper_.CheckOnValidThread(); return v8_task_queue_; @@ -1269,13 +1260,15 @@ void MainThreadSchedulerImpl::UpdateForInputEventOnCompositorThread( } void MainThreadSchedulerImpl::WillPostInputEventToMainThread( - WebInputEvent::Type web_input_event_type) { + WebInputEvent::Type web_input_event_type, + const WebInputEventAttribution& web_input_event_attribution) { base::AutoLock lock(any_thread_lock_); any_thread().pending_input_monitor.OnEnqueue(web_input_event_type); } void MainThreadSchedulerImpl::WillHandleInputEventOnMainThread( - WebInputEvent::Type web_input_event_type) { + WebInputEvent::Type web_input_event_type, + const WebInputEventAttribution& web_input_event_attribution) { helper_.CheckOnValidThread(); base::AutoLock lock(any_thread_lock_); @@ -1484,7 +1477,7 @@ void MainThreadSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) { break; case UseCase::kNone: - // It's only safe to block tasks that if we are expecting a compositor + // It's only safe to block tasks if we are expecting a compositor // driven gesture. if (main_thread_only().blocking_input_expected_soon && any_thread().last_gesture_was_compositor_driven) { @@ -1549,6 +1542,9 @@ void MainThreadSchedulerImpl::UpdatePolicyLocked(UpdateType update_type) { new_policy.should_disable_throttling() = main_thread_only().use_virtual_time; + new_policy.find_in_page_priority() = + find_in_page_budget_pool_controller_->CurrentTaskPriority(); + // Tracing is done before the early out check, because it's quite possible we // will otherwise miss this information in traces. CreateTraceEventObjectSnapshotLocked(); @@ -1810,6 +1806,9 @@ void MainThreadSchedulerImpl::DisableVirtualTimeForTesting() { virtual_time_control_task_queue_ = nullptr; ApplyVirtualTimePolicy(); + main_thread_only().initial_virtual_time = base::Time(); + main_thread_only().initial_virtual_time_ticks = base::TimeTicks(); + // Reset the MetricsHelper because it gets confused by time going backwards. base::TimeTicks now = tick_clock()->NowTicks(); main_thread_only().metrics_helper.ResetForTest(now); @@ -1954,13 +1953,26 @@ void MainThreadSchedulerImpl::CreateTraceEventObjectSnapshotLocked() const { std::unique_ptr<base::trace_event::ConvertableToTraceFormat> MainThreadSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const { + auto state = std::make_unique<base::trace_event::TracedValue>(); + AsValueIntoLocked(state.get(), optional_now); + return std::move(state); +} + +std::string MainThreadSchedulerImpl::ToString() const { + base::AutoLock lock(any_thread_lock_); + base::trace_event::TracedValueJSON value; + AsValueIntoLocked(&value, base::TimeTicks()); + return value.ToJSON(); +} + +void MainThreadSchedulerImpl::AsValueIntoLocked( + base::trace_event::TracedValue* state, + base::TimeTicks optional_now) const { helper_.CheckOnValidThread(); any_thread_lock_.AssertAcquired(); if (optional_now.is_null()) optional_now = helper_.NowTicks(); - std::unique_ptr<base::trace_event::TracedValue> state( - new base::trace_event::TracedValue()); state->SetBoolean( "has_visible_render_widget_with_touch_handler", main_thread_only().has_visible_render_widget_with_touch_handler); @@ -2024,13 +2036,13 @@ MainThreadSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const { state->BeginDictionary("page_schedulers"); for (PageSchedulerImpl* page_scheduler : main_thread_only().page_schedulers) { state->BeginDictionaryWithCopiedName(PointerToString(page_scheduler)); - page_scheduler->AsValueInto(state.get()); + page_scheduler->AsValueInto(state); state->EndDictionary(); } state->EndDictionary(); state->BeginDictionary("policy"); - main_thread_only().current_policy.AsValueInto(state.get()); + main_thread_only().current_policy.AsValueInto(state); state->EndDictionary(); // TODO(skyostil): Can we somehow trace how accurate these estimates were? @@ -2046,14 +2058,12 @@ MainThreadSchedulerImpl::AsValueLocked(base::TimeTicks optional_now) const { .InMillisecondsF()); state->SetBoolean("in_idle_period", any_thread().in_idle_period); - any_thread().user_model.AsValueInto(state.get()); - render_widget_scheduler_signals_.AsValueInto(state.get()); + any_thread().user_model.AsValueInto(state); + render_widget_scheduler_signals_.AsValueInto(state); state->BeginDictionary("task_queue_throttler"); - task_queue_throttler_->AsValueInto(state.get(), optional_now); + task_queue_throttler_->AsValueInto(state, optional_now); state->EndDictionary(); - - return std::move(state); } bool MainThreadSchedulerImpl::TaskQueuePolicy::IsQueueEnabled( @@ -2090,6 +2100,8 @@ MainThreadSchedulerImpl::Policy::Policy() should_prioritize_loading_with_compositing_(false), compositor_priority_( base::sequence_manager::TaskQueue::QueuePriority::kNormalPriority), + find_in_page_priority_(FindInPageBudgetPoolController:: + kFindInPageBudgetNotExhaustedPriority), use_case_(UseCase::kNone) {} void MainThreadSchedulerImpl::Policy::AsValueInto( @@ -2336,6 +2348,11 @@ MainThreadSchedulerImpl::CompositorTaskRunner() { return compositor_task_runner_; } +scoped_refptr<base::SingleThreadTaskRunner> +MainThreadSchedulerImpl::NonWakingTaskRunner() { + return non_waking_task_runner_; +} + std::unique_ptr<PageScheduler> MainThreadSchedulerImpl::CreatePageScheduler( PageScheduler::Delegate* delegate) { return std::make_unique<PageSchedulerImpl>(delegate, this); @@ -2482,6 +2499,9 @@ void MainThreadSchedulerImpl::OnTaskCompleted( main_thread_only().compositor_priority_experiments.OnTaskCompleted( queue.get(), main_thread_only().current_policy.compositor_priority(), task_timing); + + find_in_page_budget_pool_controller_->OnTaskCompleted(queue.get(), + task_timing); } void MainThreadSchedulerImpl::RecordTaskUkm( @@ -2577,7 +2597,7 @@ TaskQueue::QueuePriority MainThreadSchedulerImpl::ComputePriority( MainThreadTaskQueue* task_queue) const { DCHECK(task_queue); - // If |task_queue| is associated to a frame, the the frame scheduler computes + // If |task_queue| is associated to a frame, then the frame scheduler computes // the priority. FrameSchedulerImpl* frame_scheduler = task_queue->GetFrameScheduler(); @@ -2735,7 +2755,9 @@ bool MainThreadSchedulerImpl::ShouldUpdateTaskQueuePriorities( return old_policy.use_case() != main_thread_only().current_policy.use_case() || old_policy.compositor_priority() != - main_thread_only().current_policy.compositor_priority(); + main_thread_only().current_policy.compositor_priority() || + old_policy.find_in_page_priority() != + main_thread_only().current_policy.find_in_page_priority(); } UseCase MainThreadSchedulerImpl::current_use_case() const { diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h index 612cd8ea852..5914b7f7175 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h @@ -32,11 +32,13 @@ #include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h" +#include "third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager.h" +#include "third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h" @@ -96,9 +98,6 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl struct SchedulingSettings { SchedulingSettings(); - // High priority input experiment. - bool high_priority_input; - // Background page priority experiment (crbug.com/848835). bool low_priority_background_page; bool best_effort_background_page; @@ -137,14 +136,6 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl std::array<base::sequence_manager::TaskQueue::QueuePriority, net::RequestPrioritySize::NUM_PRIORITIES> net_to_blink_priority; - - using FrameTaskTypeToQueueTraitsArray = - std::array<base::Optional<MainThreadTaskQueue::QueueTraits>, - static_cast<size_t>(TaskType::kCount)>; - // Array of QueueTraits indexed by TaskType, containing TaskType::kCount - // entries. This is initialized early with all valid entries. Entries that - // aren't valid task types, i.e. non-frame level, are base::nullopt. - FrameTaskTypeToQueueTraitsArray frame_task_types_to_queue_traits; }; static const char* UseCaseToString(UseCase use_case); @@ -170,9 +161,11 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl // WebThreadScheduler implementation: std::unique_ptr<Thread> CreateMainThread() override; + std::unique_ptr<WebWidgetScheduler> CreateWidgetScheduler() override; // Note: this is also shared by the ThreadScheduler interface. scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner() override; + scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner() override; std::unique_ptr<WebRenderWidgetSchedulingState> @@ -185,9 +178,11 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl const WebInputEvent& web_input_event, InputEventState event_state) override; void WillPostInputEventToMainThread( - WebInputEvent::Type web_input_event_type) override; + WebInputEvent::Type web_input_event_type, + const WebInputEventAttribution& web_input_event_attribution) override; void WillHandleInputEventOnMainThread( - WebInputEvent::Type web_input_event_type) override; + WebInputEvent::Type web_input_event_type, + const WebInputEventAttribution& web_input_event_attribution) override; void DidHandleInputEventOnMainThread(const WebInputEvent& web_input_event, WebInputEventResult result) override; void DidAnimateForInputOnCompositorThread() override; @@ -240,7 +235,6 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl // WebThreadScheduler implementation: scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override; - scoped_refptr<base::SingleThreadTaskRunner> InputTaskRunner() override; // The following functions are defined in both WebThreadScheduler and // ThreadScheduler, and have the same function signatures -- see above. @@ -428,11 +422,14 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl return main_thread_only().main_thread_compositing_is_fast; } + QueuePriority find_in_page_priority() const { + return main_thread_only().current_policy.find_in_page_priority(); + } + protected: scoped_refptr<MainThreadTaskQueue> ControlTaskQueue(); scoped_refptr<MainThreadTaskQueue> DefaultTaskQueue(); scoped_refptr<MainThreadTaskQueue> CompositorTaskQueue(); - scoped_refptr<MainThreadTaskQueue> InputTaskQueue(); scoped_refptr<MainThreadTaskQueue> V8TaskQueue(); // A control task queue which also respects virtual time. Only available if // virtual time has been enabled. @@ -459,6 +456,7 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl friend class main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest; friend class CompositorPriorityExperiments; + friend class FindInPageBudgetPoolController; FRIEND_TEST_ALL_PREFIXES( main_thread_scheduler_impl_unittest::MainThreadSchedulerImplTest, @@ -577,6 +575,14 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl return compositor_priority_; } + base::sequence_manager::TaskQueue::QueuePriority& find_in_page_priority() { + return find_in_page_priority_; + } + base::sequence_manager::TaskQueue::QueuePriority find_in_page_priority() + const { + return find_in_page_priority_; + } + UseCase& use_case() { return use_case_; } UseCase use_case() const { return use_case_; } @@ -587,6 +593,7 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl should_prioritize_loading_with_compositing_ == other.should_prioritize_loading_with_compositing_ && compositor_priority_ == other.compositor_priority_ && + find_in_page_priority_ == other.find_in_page_priority_ && use_case_ == other.use_case_; } @@ -602,6 +609,8 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl // MainThread::QueueClass). base::sequence_manager::TaskQueue::QueuePriority compositor_priority_; + base::sequence_manager::TaskQueue::QueuePriority find_in_page_priority_; + UseCase use_case_; std::array<TaskQueuePolicy, @@ -647,6 +656,8 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl base::TimeTicks optional_now) const; void CreateTraceEventObjectSnapshotLocked() const; + std::string ToString() const; + static bool ShouldPrioritizeInputEvent(const WebInputEvent& web_input_event); // The amount of time which idle periods can continue being scheduled when the @@ -775,6 +786,9 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl // task. void DispatchOnTaskCompletionCallbacks(); + void AsValueIntoLocked(base::trace_event::TracedValue*, + base::TimeTicks optional_now) const; + // Indicates that scheduler has been shutdown. // It should be accessed only on the main thread, but couldn't be a member // of MainThreadOnly struct because last might be destructed before we @@ -799,14 +813,14 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl std::unique_ptr<TaskQueueThrottler> task_queue_throttler_; RenderWidgetSignals render_widget_scheduler_signals_; + std::unique_ptr<FindInPageBudgetPoolController> + find_in_page_budget_pool_controller_; + const scoped_refptr<MainThreadTaskQueue> control_task_queue_; const scoped_refptr<MainThreadTaskQueue> compositor_task_queue_; - const scoped_refptr<MainThreadTaskQueue> input_task_queue_; scoped_refptr<MainThreadTaskQueue> virtual_time_control_task_queue_; std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> compositor_task_queue_enabled_voter_; - std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> - input_task_queue_enabled_voter_; using TaskQueueVoterMap = std::map< scoped_refptr<MainThreadTaskQueue>, @@ -818,20 +832,22 @@ class PLATFORM_EXPORT MainThreadSchedulerImpl scoped_refptr<MainThreadTaskQueue> ipc_task_queue_; scoped_refptr<MainThreadTaskQueue> cleanup_task_queue_; scoped_refptr<MainThreadTaskQueue> memory_purge_task_queue_; + scoped_refptr<MainThreadTaskQueue> non_waking_task_queue_; scoped_refptr<base::SingleThreadTaskRunner> v8_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> control_task_runner_; - scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> cleanup_task_runner_; + scoped_refptr<base::SingleThreadTaskRunner> non_waking_task_runner_; MemoryPurgeManager memory_purge_manager_; // Note |virtual_time_domain_| is lazily created. std::unique_ptr<AutoAdvancingVirtualTimeDomain> virtual_time_domain_; + NonWakingTimeDomain non_waking_time_domain_; - base::Closure update_policy_closure_; + base::RepeatingClosure update_policy_closure_; DeadlineTaskRunner delayed_update_policy_runner_; CancelableClosureHolder end_renderer_hidden_idle_period_closure_; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc index 807506d92e1..1f4d240aa79 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/callback.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/task/post_task.h" @@ -18,7 +19,6 @@ #include "base/task/sequence_manager/test/sequence_manager_for_test.h" #include "base/task/task_executor.h" #include "base/test/bind_test_util.h" -#include "base/test/gtest_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/simple_test_tick_clock.h" @@ -27,12 +27,14 @@ #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/input/web_mouse_wheel_event.h" +#include "third_party/blink/public/common/input/web_touch_event.h" #include "third_party/blink/public/common/page/launching_process_state.h" -#include "third_party/blink/public/platform/web_mouse_wheel_event.h" -#include "third_party/blink/public/platform/web_touch_event.h" +#include "third_party/blink/public/platform/scheduler/web_widget_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/common/features.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h" +#include "third_party/blink/renderer/platform/scheduler/main_thread/find_in_page_budget_pool_controller.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h" #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h" #include "third_party/blink/renderer/platform/scheduler/test/recording_task_time_observer.h" @@ -46,40 +48,23 @@ namespace scheduler { // To avoid symbol collisions in jumbo builds. namespace main_thread_scheduler_impl_unittest { -using testing::IsNull; using testing::Mock; using testing::NotNull; using InputEventState = WebThreadScheduler::InputEventState; using base::sequence_manager::FakeTask; using base::sequence_manager::FakeTaskTiming; -enum class AntiStarvationLogic { - kEnabled, - kDisabled, -}; - -std::string ToString(AntiStarvationLogic type) { - switch (type) { - case AntiStarvationLogic::kEnabled: - return "AntiStarvationLogicEnabled"; - case AntiStarvationLogic::kDisabled: - return "AntiStarvationLogicDisabled"; - } -} - -std::string GetTestNameSuffix( - const testing::TestParamInfo<AntiStarvationLogic>& info) { - return "With" + ToString(info.param); -} - class FakeInputEvent : public blink::WebInputEvent { public: explicit FakeInputEvent(blink::WebInputEvent::Type event_type, int modifiers = WebInputEvent::kNoModifiers) - : WebInputEvent(sizeof(FakeInputEvent), - event_type, + : WebInputEvent(event_type, modifiers, WebInputEvent::GetStaticTimeStampForTests()) {} + + std::unique_ptr<WebInputEvent> Clone() const override { + return std::make_unique<FakeInputEvent>(*this); + } }; class FakeTouchEvent : public blink::WebTouchEvent { @@ -254,7 +239,6 @@ class MainThreadSchedulerImplForTest : public MainThreadSchedulerImpl { using MainThreadSchedulerImpl::ControlTaskQueue; using MainThreadSchedulerImpl::DefaultTaskQueue; using MainThreadSchedulerImpl::EstimateLongestJankFreeTaskDuration; - using MainThreadSchedulerImpl::InputTaskQueue; using MainThreadSchedulerImpl::OnIdlePeriodEnded; using MainThreadSchedulerImpl::OnIdlePeriodStarted; using MainThreadSchedulerImpl::OnPendingTasksChanged; @@ -332,8 +316,7 @@ class MainThreadSchedulerImplForTest : public MainThreadSchedulerImpl { return os << MainThreadSchedulerImpl::UseCaseToString(use_case); } -class MainThreadSchedulerImplTest - : public testing::TestWithParam<AntiStarvationLogic> { +class MainThreadSchedulerImplTest : public testing::Test { public: MainThreadSchedulerImplTest(std::vector<base::Feature> features_to_enable, std::vector<base::Feature> features_to_disable) { @@ -341,8 +324,7 @@ class MainThreadSchedulerImplTest } MainThreadSchedulerImplTest() - : MainThreadSchedulerImplTest({kHighPriorityInputOnMainThread}, - {kPrioritizeCompositingAfterInput}) {} + : MainThreadSchedulerImplTest({}, {kPrioritizeCompositingAfterInput}) {} ~MainThreadSchedulerImplTest() override = default; @@ -353,8 +335,6 @@ class MainThreadSchedulerImplTest nullptr, test_task_runner_, test_task_runner_->GetMockTickClock(), base::sequence_manager::SequenceManager::Settings::Builder() .SetRandomisedSamplingEnabled(true) - .SetAntiStarvationLogicForPrioritiesDisabled( - GetParam() == AntiStarvationLogic::kDisabled) .Build()), base::nullopt)); if (initially_ensure_usecase_none_) @@ -381,7 +361,6 @@ class MainThreadSchedulerImplTest default_task_runner_ = scheduler_->DefaultTaskQueue()->task_runner(); compositor_task_runner_ = scheduler_->CompositorTaskQueue()->task_runner(); - input_task_runner_ = scheduler_->InputTaskQueue()->task_runner(); idle_task_runner_ = scheduler_->IdleTaskRunner(); v8_task_runner_ = scheduler_->V8TaskQueue()->task_runner(); @@ -391,12 +370,17 @@ class MainThreadSchedulerImplTest FrameSchedulerImpl::Create(page_scheduler_.get(), nullptr, nullptr, FrameScheduler::FrameType::kMainFrame); + widget_scheduler_ = scheduler_->CreateWidgetScheduler(); + input_task_runner_ = widget_scheduler_->InputTaskRunner(); + loading_control_task_runner_ = main_frame_scheduler_->FrameTaskQueueControllerForTest() ->GetTaskQueue( main_frame_scheduler_->LoadingControlTaskQueueTraits()) ->task_runner(); timer_task_runner_ = timer_task_queue()->task_runner(); + find_in_page_task_runner_ = main_frame_scheduler_->GetTaskRunner( + blink::TaskType::kInternalFindInPage); } TaskQueue* loading_task_queue() { @@ -414,7 +398,17 @@ class MainThreadSchedulerImplTest .get(); } + MainThreadTaskQueue* find_in_page_task_queue() { + auto* frame_task_queue_controller = + main_frame_scheduler_->FrameTaskQueueControllerForTest(); + + return frame_task_queue_controller + ->GetTaskQueue(main_frame_scheduler_->FindInPageTaskQueueTraits()) + .get(); + } + void TearDown() override { + widget_scheduler_.reset(); main_frame_scheduler_.reset(); page_scheduler_.reset(); scheduler_->Shutdown(); @@ -768,6 +762,11 @@ class MainThreadSchedulerImplTest FROM_HERE, base::BindOnce(&AppendToVectorTestTask, run_order, String::FromUTF8(task))); break; + case 'F': + find_in_page_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&AppendToVectorTestTask, run_order, + String::FromUTF8(task))); + break; default: NOTREACHED(); } @@ -846,6 +845,7 @@ class MainThreadSchedulerImplTest std::unique_ptr<MainThreadSchedulerImplForTest> scheduler_; std::unique_ptr<PageSchedulerImpl> page_scheduler_; std::unique_ptr<FrameSchedulerImpl> main_frame_scheduler_; + std::unique_ptr<WebWidgetScheduler> widget_scheduler_; scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_; @@ -854,6 +854,7 @@ class MainThreadSchedulerImplTest scoped_refptr<SingleThreadIdleTaskRunner> idle_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> v8_task_runner_; + scoped_refptr<base::SingleThreadTaskRunner> find_in_page_task_runner_; bool simulate_timer_task_ran_; bool initially_ensure_usecase_none_ = true; uint64_t next_begin_frame_number_ = viz::BeginFrameArgs::kStartingFrameNumber; @@ -861,13 +862,7 @@ class MainThreadSchedulerImplTest DISALLOW_COPY_AND_ASSIGN(MainThreadSchedulerImplTest); }; -INSTANTIATE_TEST_SUITE_P(All, - MainThreadSchedulerImplTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(MainThreadSchedulerImplTest, TestPostDefaultTask) { +TEST_F(MainThreadSchedulerImplTest, TestPostDefaultTask) { Vector<String> run_order; PostTestTasks(&run_order, "D1 D2 D3 D4"); @@ -875,7 +870,7 @@ TEST_P(MainThreadSchedulerImplTest, TestPostDefaultTask) { EXPECT_THAT(run_order, testing::ElementsAre("D1", "D2", "D3", "D4")); } -TEST_P(MainThreadSchedulerImplTest, TestPostDefaultAndCompositor) { +TEST_F(MainThreadSchedulerImplTest, TestPostDefaultAndCompositor) { Vector<String> run_order; PostTestTasks(&run_order, "D1 C1 P1"); base::RunLoop().RunUntilIdle(); @@ -884,7 +879,7 @@ TEST_P(MainThreadSchedulerImplTest, TestPostDefaultAndCompositor) { EXPECT_THAT(run_order, testing::Contains("P1")); } -TEST_P(MainThreadSchedulerImplTest, TestRentrantTask) { +TEST_F(MainThreadSchedulerImplTest, TestRentrantTask) { int count = 0; Vector<int> run_order; default_task_runner_->PostTask( @@ -896,7 +891,7 @@ TEST_P(MainThreadSchedulerImplTest, TestRentrantTask) { EXPECT_THAT(run_order, testing::ElementsAre(0, 1, 2, 3, 4)); } -TEST_P(MainThreadSchedulerImplTest, TestPostIdleTask) { +TEST_F(MainThreadSchedulerImplTest, TestPostIdleTask) { int run_count = 0; base::TimeTicks expected_deadline = Now() + base::TimeDelta::FromMilliseconds(2300); @@ -935,7 +930,7 @@ TEST_P(MainThreadSchedulerImplTest, TestPostIdleTask) { EXPECT_EQ(expected_deadline, deadline_in_task); } -TEST_P(MainThreadSchedulerImplTest, TestRepostingIdleTask) { +TEST_F(MainThreadSchedulerImplTest, TestRepostingIdleTask) { int run_count = 0; g_max_idle_task_reposts = 2; @@ -956,7 +951,7 @@ TEST_P(MainThreadSchedulerImplTest, TestRepostingIdleTask) { EXPECT_EQ(2, run_count); } -TEST_P(MainThreadSchedulerImplTest, TestIdleTaskExceedsDeadline) { +TEST_F(MainThreadSchedulerImplTest, TestIdleTaskExceedsDeadline) { int run_count = 0; // Post two UpdateClockToDeadlineIdleTestTask tasks. @@ -978,7 +973,7 @@ TEST_P(MainThreadSchedulerImplTest, TestIdleTaskExceedsDeadline) { EXPECT_EQ(2, run_count); } -TEST_P(MainThreadSchedulerImplTest, TestDelayedEndIdlePeriodCanceled) { +TEST_F(MainThreadSchedulerImplTest, TestDelayedEndIdlePeriodCanceled) { int run_count = 0; base::TimeTicks deadline_in_task; @@ -1018,7 +1013,7 @@ TEST_P(MainThreadSchedulerImplTest, TestDelayedEndIdlePeriodCanceled) { EXPECT_EQ(1, run_count); // We should still be in the new idle period. } -TEST_P(MainThreadSchedulerImplTest, TestDefaultPolicy) { +TEST_F(MainThreadSchedulerImplTest, TestDefaultPolicy) { EnsureUseCaseNone(); Vector<String> run_order; @@ -1034,7 +1029,7 @@ TEST_P(MainThreadSchedulerImplTest, TestDefaultPolicy) { EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, TestDefaultPolicyWithSlowCompositor) { +TEST_F(MainThreadSchedulerImplTest, TestDefaultPolicyWithSlowCompositor) { RunSlowCompositorTask(); Vector<String> run_order; @@ -1048,7 +1043,7 @@ TEST_P(MainThreadSchedulerImplTest, TestDefaultPolicyWithSlowCompositor) { EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_CompositorHandlesInput_WithTouchHandler) { Vector<String> run_order; PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); @@ -1062,7 +1057,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_MainThreadHandlesInput_WithoutScrollUpdates) { Vector<String> run_order; PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); @@ -1076,7 +1071,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_MainThreadHandlesInput_WithoutPreventDefault) { Vector<String> run_order; PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); @@ -1090,7 +1085,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_CompositorHandlesInput_LongGestureDuration) { EnableIdleTasks(); SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart); @@ -1120,7 +1115,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_CompositorHandlesInput_WithoutTouchHandler) { Vector<String> run_order; PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); @@ -1133,7 +1128,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_MainThreadHandlesInput_WithTouchHandler) { Vector<String> run_order; PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); @@ -1151,7 +1146,7 @@ TEST_P(MainThreadSchedulerImplTest, WebInputEventResult::kHandledSystem); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_MainThreadHandlesInput_WithoutTouchHandler) { Vector<String> run_order; PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); @@ -1168,7 +1163,7 @@ TEST_P(MainThreadSchedulerImplTest, WebInputEventResult::kHandledSystem); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_MainThreadHandlesInput_SingleEvent_PreventDefault) { Vector<String> run_order; PostTestTasks(&run_order, "L1 I1 D1 C1 D2 C2"); @@ -1189,7 +1184,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase()); } -TEST_P( +TEST_F( MainThreadSchedulerImplTest, TestCompositorPolicy_MainThreadHandlesInput_SingleEvent_NoPreventDefault) { Vector<String> run_order; @@ -1210,7 +1205,7 @@ TEST_P( EXPECT_EQ(UseCase::kTouchstart, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, TestCompositorPolicy_DidAnimateForInput) { +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_DidAnimateForInput) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); @@ -1226,7 +1221,7 @@ TEST_P(MainThreadSchedulerImplTest, TestCompositorPolicy_DidAnimateForInput) { EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, Navigation_ResetsTaskCostEstimations) { +TEST_F(MainThreadSchedulerImplTest, Navigation_ResetsTaskCostEstimations) { Vector<String> run_order; scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); @@ -1245,7 +1240,7 @@ TEST_P(MainThreadSchedulerImplTest, Navigation_ResetsTaskCostEstimations) { EXPECT_THAT(run_order, testing::ElementsAre("C1", "T1")); } -TEST_P(MainThreadSchedulerImplTest, TestTouchstartPolicy_Compositor) { +TEST_F(MainThreadSchedulerImplTest, TestTouchstartPolicy_Compositor) { Vector<String> run_order; PostTestTasks(&run_order, "L1 D1 C1 D2 C2 T1 T2"); @@ -1282,7 +1277,7 @@ TEST_P(MainThreadSchedulerImplTest, TestTouchstartPolicy_Compositor) { EXPECT_THAT(run_order, testing::ElementsAre("L1", "T1", "T2")); } -TEST_P(MainThreadSchedulerImplTest, TestTouchstartPolicy_MainThread) { +TEST_F(MainThreadSchedulerImplTest, TestTouchstartPolicy_MainThread) { Vector<String> run_order; PostTestTasks(&run_order, "L1 D1 C1 D2 C2 T1 T2"); @@ -1336,13 +1331,7 @@ class DefaultUseCaseTest : public MainThreadSchedulerImplTest { } }; -INSTANTIATE_TEST_SUITE_P(All, - DefaultUseCaseTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(DefaultUseCaseTest, InitiallyInEarlyLoadingUseCase) { +TEST_F(DefaultUseCaseTest, InitiallyInEarlyLoadingUseCase) { // Should be early loading by default. EXPECT_EQ(UseCase::kEarlyLoading, ForceUpdatePolicyAndGetCurrentUseCase()); @@ -1362,7 +1351,7 @@ class PrioritizeCompositingAndLoadingInUseCaseLoadingTest {}) {} }; -TEST_P(PrioritizeCompositingAndLoadingInUseCaseLoadingTest, LoadingUseCase) { +TEST_F(PrioritizeCompositingAndLoadingInUseCaseLoadingTest, LoadingUseCase) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 T1 L1 D2 C2 T2 L2"); @@ -1386,15 +1375,8 @@ TEST_P(PrioritizeCompositingAndLoadingInUseCaseLoadingTest, LoadingUseCase) { EnableIdleTasks(); base::RunLoop().RunUntilIdle(); - String default_order_expected[] = {"D1", - "C1" - "T1", - "L1", - "D2", - "C2", - "T2", - "L2", - "I1"}; + String default_order_expected[] = {"D1", "C1", "T1", "L1", "D2", + "C2", "T2", "L2", "I1"}; EXPECT_THAT(run_order, testing::ElementsAreArray(default_order_expected)); EXPECT_EQ(UseCase::kLoading, CurrentUseCase()); @@ -1413,7 +1395,7 @@ TEST_P(PrioritizeCompositingAndLoadingInUseCaseLoadingTest, LoadingUseCase) { EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EventConsumedOnCompositorThread_IgnoresMouseMove_WhenMouseUp) { RunSlowCompositorTask(); @@ -1430,7 +1412,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_THAT(run_order, testing::ElementsAre("D1", "C1", "D2", "C2", "I1")); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EventForwardedToMainThread_IgnoresMouseMove_WhenMouseUp) { RunSlowCompositorTask(); @@ -1447,7 +1429,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_THAT(run_order, testing::ElementsAre("D1", "C1", "D2", "C2", "I1")); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EventConsumedOnCompositorThread_MouseMove_WhenMouseDown) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); @@ -1465,7 +1447,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_THAT(run_order, testing::ElementsAre("D1", "D2", "C1", "C2", "I1")); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EventForwardedToMainThread_MouseMove_WhenMouseDown) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); @@ -1484,7 +1466,7 @@ TEST_P(MainThreadSchedulerImplTest, WebInputEventResult::kHandledSystem); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EventForwardedToMainThread_MouseMove_WhenMouseDown_AfterMouseWheel) { // Simulate a main thread driven mouse wheel scroll gesture. SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart, @@ -1515,7 +1497,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_THAT(run_order, testing::ElementsAre("C1", "C2", "D1", "D2", "I1")); } -TEST_P(MainThreadSchedulerImplTest, EventForwardedToMainThread_MouseClick) { +TEST_F(MainThreadSchedulerImplTest, EventForwardedToMainThread_MouseClick) { // A mouse click should be detected as main thread input handling, which means // we won't try to defer expensive tasks because of one. We can, however, // prioritize compositing/input handling. @@ -1539,7 +1521,7 @@ TEST_P(MainThreadSchedulerImplTest, EventForwardedToMainThread_MouseClick) { EXPECT_THAT(run_order, testing::ElementsAre("C1", "C2", "D1", "D2", "I1")); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EventConsumedOnCompositorThread_MouseWheel) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); @@ -1554,7 +1536,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EventForwardedToMainThread_MouseWheel_PreventDefault) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); @@ -1569,7 +1551,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kMainThreadCustomInputHandling, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EventForwardedToMainThread_NoPreventDefault) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 D2 C2"); @@ -1593,7 +1575,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kMainThreadGesture, CurrentUseCase()); } -TEST_P( +TEST_F( MainThreadSchedulerImplTest, EventForwardedToMainThreadAndBackToCompositor_MouseWheel_NoPreventDefault) { Vector<String> run_order; @@ -1618,7 +1600,7 @@ TEST_P( EXPECT_EQ(UseCase::kCompositorGesture, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EventConsumedOnCompositorThread_IgnoresKeyboardEvents) { RunSlowCompositorTask(); @@ -1635,7 +1617,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EventForwardedToMainThread_IgnoresKeyboardEvents) { RunSlowCompositorTask(); @@ -1656,7 +1638,7 @@ TEST_P(MainThreadSchedulerImplTest, WebInputEventResult::kHandledSystem); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestMainthreadScrollingUseCaseDoesNotStarveDefaultTasks) { SimulateMainThreadGestureStart(TouchEventPolicy::kDontSendTouchStart, blink::WebInputEvent::kGestureScrollBegin); @@ -1675,21 +1657,10 @@ TEST_P(MainThreadSchedulerImplTest, InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); base::RunLoop().RunUntilIdle(); - switch (GetParam()) { - case AntiStarvationLogic::kEnabled: - // Ensure that the default D1 task gets to run at some point before the - // final C2 compositor task. - EXPECT_THAT(run_order, testing::ElementsAre("C1", "D1", "C2")); - break; - case AntiStarvationLogic::kDisabled: - // Without anti-starvation logic, the default D1 task should get stuck at - // the end. - EXPECT_THAT(run_order, testing::ElementsAre("C1", "C2", "D1")); - break; - } + EXPECT_THAT(run_order, testing::ElementsAre("C1", "C2", "D1")); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicyEnds_CompositorHandlesInput) { SimulateCompositorGestureStart(TouchEventPolicy::kDontSendTouchStart); EXPECT_EQ(UseCase::kCompositorGesture, @@ -1699,7 +1670,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicyEnds_MainThreadHandlesInput) { SimulateMainThreadGestureStart(TouchEventPolicy::kDontSendTouchStart, blink::WebInputEvent::kGestureScrollBegin); @@ -1710,7 +1681,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, TestTouchstartPolicyEndsAfterTimeout) { +TEST_F(MainThreadSchedulerImplTest, TestTouchstartPolicyEndsAfterTimeout) { Vector<String> run_order; PostTestTasks(&run_order, "L1 D1 C1 D2 C2"); @@ -1732,7 +1703,7 @@ TEST_P(MainThreadSchedulerImplTest, TestTouchstartPolicyEndsAfterTimeout) { EXPECT_THAT(run_order, testing::ElementsAre("L1", "D1", "D2")); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestTouchstartPolicyEndsAfterConsecutiveTouchmoves) { Vector<String> run_order; PostTestTasks(&run_order, "L1 D1 C1 D2 C2"); @@ -1761,7 +1732,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_THAT(run_order, testing::ElementsAre("L1")); } -TEST_P(MainThreadSchedulerImplTest, TestIsHighPriorityWorkAnticipated) { +TEST_F(MainThreadSchedulerImplTest, TestIsHighPriorityWorkAnticipated) { bool is_anticipated_before = false; bool is_anticipated_after = false; @@ -1829,7 +1800,7 @@ TEST_P(MainThreadSchedulerImplTest, TestIsHighPriorityWorkAnticipated) { EXPECT_FALSE(is_anticipated_after); } -TEST_P(MainThreadSchedulerImplTest, TestShouldYield) { +TEST_F(MainThreadSchedulerImplTest, TestShouldYield) { bool should_yield_before = false; bool should_yield_after = false; @@ -1863,7 +1834,7 @@ TEST_P(MainThreadSchedulerImplTest, TestShouldYield) { EXPECT_TRUE(should_yield_after); } -TEST_P(MainThreadSchedulerImplTest, TestShouldYield_TouchStart) { +TEST_F(MainThreadSchedulerImplTest, TestShouldYield_TouchStart) { // Receiving a touchstart should immediately trigger yielding, even if // there's no immediately pending work in the compositor queue. EXPECT_FALSE(scheduler_->ShouldYieldForHighPriorityWork()); @@ -1874,7 +1845,7 @@ TEST_P(MainThreadSchedulerImplTest, TestShouldYield_TouchStart) { base::RunLoop().RunUntilIdle(); } -TEST_P(MainThreadSchedulerImplTest, SlowMainThreadInputEvent) { +TEST_F(MainThreadSchedulerImplTest, SlowMainThreadInputEvent) { EXPECT_EQ(UseCase::kNone, CurrentUseCase()); // An input event should bump us into input priority. @@ -1903,7 +1874,7 @@ TEST_P(MainThreadSchedulerImplTest, SlowMainThreadInputEvent) { EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, OnlyOnePendingUrgentPolicyUpdate) { +TEST_F(MainThreadSchedulerImplTest, OnlyOnePendingUrgentPolicyUpdate) { for (int i = 0; i < 4; i++) { scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread(); } @@ -1911,7 +1882,7 @@ TEST_P(MainThreadSchedulerImplTest, OnlyOnePendingUrgentPolicyUpdate) { EXPECT_EQ(1, scheduler_->update_policy_count_); } -TEST_P(MainThreadSchedulerImplTest, OnePendingDelayedAndOneUrgentUpdatePolicy) { +TEST_F(MainThreadSchedulerImplTest, OnePendingDelayedAndOneUrgentUpdatePolicy) { scheduler_->ScheduleDelayedPolicyUpdate(Now(), base::TimeDelta::FromMilliseconds(1)); scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread(); @@ -1921,7 +1892,7 @@ TEST_P(MainThreadSchedulerImplTest, OnePendingDelayedAndOneUrgentUpdatePolicy) { EXPECT_EQ(2, scheduler_->update_policy_count_); } -TEST_P(MainThreadSchedulerImplTest, OneUrgentAndOnePendingDelayedUpdatePolicy) { +TEST_F(MainThreadSchedulerImplTest, OneUrgentAndOnePendingDelayedUpdatePolicy) { scheduler_->EnsureUrgentPolicyUpdatePostedOnMainThread(); scheduler_->ScheduleDelayedPolicyUpdate(Now(), base::TimeDelta::FromMilliseconds(1)); @@ -1931,7 +1902,7 @@ TEST_P(MainThreadSchedulerImplTest, OneUrgentAndOnePendingDelayedUpdatePolicy) { EXPECT_EQ(2, scheduler_->update_policy_count_); } -TEST_P(MainThreadSchedulerImplTest, UpdatePolicyCountTriggeredByOneInputEvent) { +TEST_F(MainThreadSchedulerImplTest, UpdatePolicyCountTriggeredByOneInputEvent) { // We expect DidHandleInputEventOnCompositorThread to post an urgent policy // update. scheduler_->DidHandleInputEventOnCompositorThread( @@ -1952,7 +1923,7 @@ TEST_P(MainThreadSchedulerImplTest, UpdatePolicyCountTriggeredByOneInputEvent) { EXPECT_EQ(2, scheduler_->update_policy_count_); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, UpdatePolicyCountTriggeredByThreeInputEvents) { // We expect DidHandleInputEventOnCompositorThread to post // an urgent policy update. @@ -2002,7 +1973,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(3, scheduler_->update_policy_count_); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, UpdatePolicyCountTriggeredByTwoInputEventsWithALongSeparatingDelay) { // We expect DidHandleInputEventOnCompositorThread to post an urgent policy // update. @@ -2040,7 +2011,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(4, scheduler_->update_policy_count_); } -TEST_P(MainThreadSchedulerImplTest, EnsureUpdatePolicyNotTriggeredTooOften) { +TEST_F(MainThreadSchedulerImplTest, EnsureUpdatePolicyNotTriggeredTooOften) { EXPECT_EQ(0, scheduler_->update_policy_count_); scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); EXPECT_EQ(1, scheduler_->update_policy_count_); @@ -2094,7 +2065,7 @@ TEST_P(MainThreadSchedulerImplTest, EnsureUpdatePolicyNotTriggeredTooOften) { "none blocking input expected", "none")); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, BlockingInputExpectedSoonWhenBlockInputEventSeen) { SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart); EXPECT_TRUE(HaveSeenABlockingGesture()); @@ -2102,7 +2073,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_TRUE(BlockingInputExpectedSoon()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, BlockingInputNotExpectedSoonWhenNoBlockInputEventSeen) { SimulateCompositorGestureStart(TouchEventPolicy::kDontSendTouchStart); EXPECT_FALSE(HaveSeenABlockingGesture()); @@ -2110,7 +2081,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_FALSE(BlockingInputExpectedSoon()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, GetTaskExecutorForCurrentThreadInPostedTask) { base::TaskExecutor* task_executor = base::GetTaskExecutorForCurrentThread(); EXPECT_THAT(task_executor, NotNull()); @@ -2126,36 +2097,7 @@ TEST_P(MainThreadSchedulerImplTest, run_loop.Run(); } -TEST_P(MainThreadSchedulerImplTest, CurrentThread) { - EXPECT_EQ(scheduler_->DeprecatedDefaultTaskRunner(), - base::CreateSingleThreadTaskRunner({base::CurrentThread()})); - - // base::TaskPriority is currently ignored in blink. - EXPECT_EQ(scheduler_->DeprecatedDefaultTaskRunner(), - base::CreateSingleThreadTaskRunner( - {base::CurrentThread(), base::TaskPriority::BEST_EFFORT})); -} - -TEST_P(MainThreadSchedulerImplTest, GetContinuationTaskRunner) { - scoped_refptr<MainThreadTaskQueue> timer_tq = scheduler_->NewTimerTaskQueue( - MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr); - auto task_runner = timer_tq->CreateTaskRunner(TaskType::kJavascriptTimer); - - base::RunLoop run_loop; - task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() { - EXPECT_EQ(task_runner, - base::GetContinuationTaskRunner()); - run_loop.Quit(); - })); - run_loop.Run(); -} - -TEST_P(MainThreadSchedulerImplTest, - GetContinuationTaskRunnerWithNoTaskRunning) { - EXPECT_DCHECK_DEATH(base::GetContinuationTaskRunner()); -} - -TEST_P(MainThreadSchedulerImplTest, TestBeginMainFrameNotExpectedUntil) { +TEST_F(MainThreadSchedulerImplTest, TestBeginMainFrameNotExpectedUntil) { base::TimeDelta ten_millis(base::TimeDelta::FromMilliseconds(10)); base::TimeTicks expected_deadline = Now() + ten_millis; base::TimeTicks deadline_in_task; @@ -2177,7 +2119,7 @@ TEST_P(MainThreadSchedulerImplTest, TestBeginMainFrameNotExpectedUntil) { EXPECT_EQ(expected_deadline, deadline_in_task); } -TEST_P(MainThreadSchedulerImplTest, TestLongIdlePeriod) { +TEST_F(MainThreadSchedulerImplTest, TestLongIdlePeriod) { base::TimeTicks expected_deadline = Now() + maximum_idle_period_duration(); base::TimeTicks deadline_in_task; int run_count = 0; @@ -2194,7 +2136,7 @@ TEST_P(MainThreadSchedulerImplTest, TestLongIdlePeriod) { EXPECT_EQ(expected_deadline, deadline_in_task); } -TEST_P(MainThreadSchedulerImplTest, TestLongIdlePeriodWithPendingDelayedTask) { +TEST_F(MainThreadSchedulerImplTest, TestLongIdlePeriodWithPendingDelayedTask) { base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(30); base::TimeTicks expected_deadline = Now() + pending_task_delay; base::TimeTicks deadline_in_task; @@ -2211,7 +2153,7 @@ TEST_P(MainThreadSchedulerImplTest, TestLongIdlePeriodWithPendingDelayedTask) { EXPECT_EQ(expected_deadline, deadline_in_task); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestLongIdlePeriodWithLatePendingDelayedTask) { base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(10); base::TimeTicks deadline_in_task; @@ -2238,7 +2180,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(1, run_count); } -TEST_P(MainThreadSchedulerImplTest, TestLongIdlePeriodRepeating) { +TEST_F(MainThreadSchedulerImplTest, TestLongIdlePeriodRepeating) { Vector<base::TimeTicks> actual_deadlines; int run_count = 0; @@ -2277,7 +2219,7 @@ TEST_P(MainThreadSchedulerImplTest, TestLongIdlePeriodRepeating) { EXPECT_EQ(4, run_count); } -TEST_P(MainThreadSchedulerImplTest, TestLongIdlePeriodInTouchStartPolicy) { +TEST_F(MainThreadSchedulerImplTest, TestLongIdlePeriodInTouchStartPolicy) { base::TimeTicks deadline_in_task; int run_count = 0; @@ -2305,7 +2247,7 @@ void TestCanExceedIdleDeadlineIfRequiredTask(ThreadScheduler* scheduler, (*run_count)++; } -TEST_P(MainThreadSchedulerImplTest, CanExceedIdleDeadlineIfRequired) { +TEST_F(MainThreadSchedulerImplTest, CanExceedIdleDeadlineIfRequired) { int run_count = 0; bool can_exceed_idle_deadline = false; @@ -2355,7 +2297,7 @@ TEST_P(MainThreadSchedulerImplTest, CanExceedIdleDeadlineIfRequired) { EXPECT_FALSE(scheduler_->CanExceedIdleDeadlineIfRequired()); } -TEST_P(MainThreadSchedulerImplTest, TestRendererHiddenIdlePeriod) { +TEST_F(MainThreadSchedulerImplTest, TestRendererHiddenIdlePeriod) { int run_count = 0; g_max_idle_task_reposts = 2; @@ -2387,14 +2329,14 @@ TEST_P(MainThreadSchedulerImplTest, TestRendererHiddenIdlePeriod) { EXPECT_EQ(2, run_count); } -TEST_P(MainThreadSchedulerImplTest, TimerQueueEnabledByDefault) { +TEST_F(MainThreadSchedulerImplTest, TimerQueueEnabledByDefault) { Vector<String> run_order; PostTestTasks(&run_order, "T1 T2"); base::RunLoop().RunUntilIdle(); EXPECT_THAT(run_order, testing::ElementsAre("T1", "T2")); } -TEST_P(MainThreadSchedulerImplTest, StopAndResumeRenderer) { +TEST_F(MainThreadSchedulerImplTest, StopAndResumeRenderer) { Vector<String> run_order; PostTestTasks(&run_order, "T1 T2"); @@ -2407,7 +2349,7 @@ TEST_P(MainThreadSchedulerImplTest, StopAndResumeRenderer) { EXPECT_THAT(run_order, testing::ElementsAre("T1", "T2")); } -TEST_P(MainThreadSchedulerImplTest, StopAndThrottleTimerQueue) { +TEST_F(MainThreadSchedulerImplTest, StopAndThrottleTimerQueue) { Vector<String> run_order; PostTestTasks(&run_order, "T1 T2"); @@ -2419,7 +2361,7 @@ TEST_P(MainThreadSchedulerImplTest, StopAndThrottleTimerQueue) { EXPECT_THAT(run_order, testing::ElementsAre()); } -TEST_P(MainThreadSchedulerImplTest, ThrottleAndPauseRenderer) { +TEST_F(MainThreadSchedulerImplTest, ThrottleAndPauseRenderer) { Vector<String> run_order; PostTestTasks(&run_order, "T1 T2"); @@ -2431,7 +2373,7 @@ TEST_P(MainThreadSchedulerImplTest, ThrottleAndPauseRenderer) { EXPECT_THAT(run_order, testing::ElementsAre()); } -TEST_P(MainThreadSchedulerImplTest, MultipleStopsNeedMultipleResumes) { +TEST_F(MainThreadSchedulerImplTest, MultipleStopsNeedMultipleResumes) { Vector<String> run_order; PostTestTasks(&run_order, "T1 T2"); @@ -2454,7 +2396,7 @@ TEST_P(MainThreadSchedulerImplTest, MultipleStopsNeedMultipleResumes) { EXPECT_THAT(run_order, testing::ElementsAre("T1", "T2")); } -TEST_P(MainThreadSchedulerImplTest, PauseRenderer) { +TEST_F(MainThreadSchedulerImplTest, PauseRenderer) { // Tasks in some queues don't fire when the renderer is paused. Vector<String> run_order; PostTestTasks(&run_order, "D1 C1 L1 I1 T1"); @@ -2470,11 +2412,11 @@ TEST_P(MainThreadSchedulerImplTest, PauseRenderer) { EXPECT_THAT(run_order, testing::ElementsAre("L1", "T1")); } -TEST_P(MainThreadSchedulerImplTest, UseCaseToString) { +TEST_F(MainThreadSchedulerImplTest, UseCaseToString) { CheckAllUseCaseToString(); } -TEST_P(MainThreadSchedulerImplTest, MismatchedDidHandleInputEventOnMainThread) { +TEST_F(MainThreadSchedulerImplTest, MismatchedDidHandleInputEventOnMainThread) { // This should not DCHECK because there was no corresponding compositor side // call to DidHandleInputEventOnCompositorThread with // INPUT_EVENT_ACK_STATE_NOT_CONSUMED. There are legitimate reasons for the @@ -2484,7 +2426,7 @@ TEST_P(MainThreadSchedulerImplTest, MismatchedDidHandleInputEventOnMainThread) { WebInputEventResult::kHandledSystem); } -TEST_P(MainThreadSchedulerImplTest, BeginMainFrameOnCriticalPath) { +TEST_F(MainThreadSchedulerImplTest, BeginMainFrameOnCriticalPath) { ASSERT_FALSE(scheduler_->BeginMainFrameOnCriticalPath()); viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create( @@ -2499,7 +2441,7 @@ TEST_P(MainThreadSchedulerImplTest, BeginMainFrameOnCriticalPath) { ASSERT_FALSE(scheduler_->BeginMainFrameOnCriticalPath()); } -TEST_P(MainThreadSchedulerImplTest, ShutdownPreventsPostingOfNewTasks) { +TEST_F(MainThreadSchedulerImplTest, ShutdownPreventsPostingOfNewTasks) { main_frame_scheduler_.reset(); page_scheduler_.reset(); scheduler_->Shutdown(); @@ -2509,7 +2451,7 @@ TEST_P(MainThreadSchedulerImplTest, ShutdownPreventsPostingOfNewTasks) { EXPECT_THAT(run_order, testing::ElementsAre()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EstimateLongestJankFreeTaskDuration_UseCase_NONE) { EnsureUseCaseNone(); EXPECT_EQ(UseCase::kNone, CurrentUseCase()); @@ -2517,7 +2459,7 @@ TEST_P(MainThreadSchedulerImplTest, scheduler_->EstimateLongestJankFreeTaskDuration()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EstimateLongestJankFreeTaskDuration_UseCase_kCompositorGesture) { SimulateCompositorGestureStart(TouchEventPolicy::kDontSendTouchStart); EXPECT_EQ(UseCase::kCompositorGesture, @@ -2526,7 +2468,7 @@ TEST_P(MainThreadSchedulerImplTest, scheduler_->EstimateLongestJankFreeTaskDuration()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EstimateLongestJankFreeTaskDuration_UseCase_EarlyLoading) { scheduler_->DidStartProvisionalLoad(true); EXPECT_EQ(UseCase::kEarlyLoading, ForceUpdatePolicyAndGetCurrentUseCase()); @@ -2534,7 +2476,7 @@ TEST_P(MainThreadSchedulerImplTest, scheduler_->EstimateLongestJankFreeTaskDuration()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EstimateLongestJankFreeTaskDuration_UseCase_Loading) { scheduler_->DidStartProvisionalLoad(true); scheduler_->OnFirstContentfulPaint(); @@ -2543,7 +2485,7 @@ TEST_P(MainThreadSchedulerImplTest, scheduler_->EstimateLongestJankFreeTaskDuration()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EstimateLongestJankFreeTaskDuration_UseCase_MAIN_THREAD_GESTURE) { SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart, blink::WebInputEvent::kGestureScrollUpdate); @@ -2569,7 +2511,7 @@ TEST_P(MainThreadSchedulerImplTest, scheduler_->EstimateLongestJankFreeTaskDuration()); } -TEST_P( +TEST_F( MainThreadSchedulerImplTest, EstimateLongestJankFreeTaskDuration_UseCase_MAIN_THREAD_CUSTOM_INPUT_HANDLING) { viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create( @@ -2594,7 +2536,7 @@ TEST_P( scheduler_->EstimateLongestJankFreeTaskDuration()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, EstimateLongestJankFreeTaskDuration_UseCase_SYNCHRONIZED_GESTURE) { SimulateCompositorGestureStart(TouchEventPolicy::kDontSendTouchStart); @@ -2654,7 +2596,7 @@ void SlowCountingTask(size_t* count, } } // namespace -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, SYNCHRONIZED_GESTURE_TimerTaskThrottling_TimersStopped) { SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart); @@ -2711,7 +2653,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(2u, count); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, SYNCHRONIZED_GESTURE_TimerTaskThrottling_task_not_expensive) { SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart); @@ -2749,7 +2691,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(500u, count); } -TEST_P(MainThreadSchedulerImplTest, DenyLongIdleDuringTouchStart) { +TEST_F(MainThreadSchedulerImplTest, DenyLongIdleDuringTouchStart) { scheduler_->DidHandleInputEventOnCompositorThread( FakeTouchEvent(blink::WebInputEvent::kTouchStart), InputEventState::EVENT_CONSUMED_BY_COMPOSITOR); @@ -2769,7 +2711,7 @@ TEST_P(MainThreadSchedulerImplTest, DenyLongIdleDuringTouchStart) { EXPECT_GE(next_time_to_check, base::TimeDelta()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, TestCompositorPolicy_TouchStartDuringFling) { scheduler_->SetHasVisibleRenderWidgetWithTouchHandler(true); scheduler_->DidAnimateForInputOnCompositorThread(); @@ -2785,7 +2727,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_EQ(UseCase::kTouchstart, ForceUpdatePolicyAndGetCurrentUseCase()); } -TEST_P(MainThreadSchedulerImplTest, SYNCHRONIZED_GESTURE_CompositingExpensive) { +TEST_F(MainThreadSchedulerImplTest, SYNCHRONIZED_GESTURE_CompositingExpensive) { SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart); // With the compositor task taking 20ms, there is not enough time to run @@ -2824,7 +2766,7 @@ TEST_P(MainThreadSchedulerImplTest, SYNCHRONIZED_GESTURE_CompositingExpensive) { EXPECT_EQ(1000u, run_order.size()); } -TEST_P(MainThreadSchedulerImplTest, MAIN_THREAD_CUSTOM_INPUT_HANDLING) { +TEST_F(MainThreadSchedulerImplTest, MAIN_THREAD_CUSTOM_INPUT_HANDLING) { SimulateMainThreadGestureStart(TouchEventPolicy::kSendTouchStart, blink::WebInputEvent::kGestureScrollBegin); @@ -2865,7 +2807,7 @@ TEST_P(MainThreadSchedulerImplTest, MAIN_THREAD_CUSTOM_INPUT_HANDLING) { EXPECT_EQ(1000u, run_order.size()); } -TEST_P(MainThreadSchedulerImplTest, MAIN_THREAD_GESTURE) { +TEST_F(MainThreadSchedulerImplTest, MAIN_THREAD_GESTURE) { SimulateMainThreadGestureStart(TouchEventPolicy::kDontSendTouchStart, blink::WebInputEvent::kGestureScrollBegin); @@ -2909,7 +2851,7 @@ class MockRAILModeObserver : public RAILModeObserver { MOCK_METHOD1(OnRAILModeChanged, void(RAILMode rail_mode)); }; -TEST_P(MainThreadSchedulerImplTest, TestResponseRAILMode) { +TEST_F(MainThreadSchedulerImplTest, TestResponseRAILMode) { MockRAILModeObserver observer; scheduler_->AddRAILModeObserver(&observer); EXPECT_CALL(observer, OnRAILModeChanged(RAILMode::kResponse)); @@ -2921,7 +2863,7 @@ TEST_P(MainThreadSchedulerImplTest, TestResponseRAILMode) { scheduler_->RemoveRAILModeObserver(&observer); } -TEST_P(MainThreadSchedulerImplTest, TestAnimateRAILMode) { +TEST_F(MainThreadSchedulerImplTest, TestAnimateRAILMode) { MockRAILModeObserver observer; scheduler_->AddRAILModeObserver(&observer); EXPECT_CALL(observer, OnRAILModeChanged(RAILMode::kAnimation)).Times(0); @@ -2932,7 +2874,7 @@ TEST_P(MainThreadSchedulerImplTest, TestAnimateRAILMode) { scheduler_->RemoveRAILModeObserver(&observer); } -TEST_P(MainThreadSchedulerImplTest, TestIdleRAILMode) { +TEST_F(MainThreadSchedulerImplTest, TestIdleRAILMode) { MockRAILModeObserver observer; scheduler_->AddRAILModeObserver(&observer); EXPECT_CALL(observer, OnRAILModeChanged(RAILMode::kAnimation)); @@ -2947,7 +2889,7 @@ TEST_P(MainThreadSchedulerImplTest, TestIdleRAILMode) { scheduler_->RemoveRAILModeObserver(&observer); } -TEST_P(MainThreadSchedulerImplTest, TestLoadRAILMode) { +TEST_F(MainThreadSchedulerImplTest, TestLoadRAILMode) { MockRAILModeObserver observer; scheduler_->AddRAILModeObserver(&observer); EXPECT_CALL(observer, OnRAILModeChanged(RAILMode::kAnimation)); @@ -2964,7 +2906,7 @@ TEST_P(MainThreadSchedulerImplTest, TestLoadRAILMode) { scheduler_->RemoveRAILModeObserver(&observer); } -TEST_P(MainThreadSchedulerImplTest, InputTerminatesLoadRAILMode) { +TEST_F(MainThreadSchedulerImplTest, InputTerminatesLoadRAILMode) { MockRAILModeObserver observer; scheduler_->AddRAILModeObserver(&observer); EXPECT_CALL(observer, OnRAILModeChanged(RAILMode::kAnimation)); @@ -2985,7 +2927,7 @@ TEST_P(MainThreadSchedulerImplTest, InputTerminatesLoadRAILMode) { scheduler_->RemoveRAILModeObserver(&observer); } -TEST_P(MainThreadSchedulerImplTest, UnthrottledTaskRunner) { +TEST_F(MainThreadSchedulerImplTest, UnthrottledTaskRunner) { // Ensure neither suspension nor timer task throttling affects an unthrottled // task runner. SimulateCompositorGestureStart(TouchEventPolicy::kSendTouchStart); @@ -3030,7 +2972,7 @@ TEST_P(MainThreadSchedulerImplTest, UnthrottledTaskRunner) { EXPECT_EQ(500u, unthrottled_count); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, VirtualTimePolicyDoesNotAffectNewTimerTaskQueueIfVirtualTimeNotEnabled) { scheduler_->SetVirtualTimePolicy( PageSchedulerImpl::VirtualTimePolicy::kPause); @@ -3039,7 +2981,7 @@ TEST_P(MainThreadSchedulerImplTest, EXPECT_FALSE(timer_tq->HasActiveFence()); } -TEST_P(MainThreadSchedulerImplTest, EnableVirtualTime) { +TEST_F(MainThreadSchedulerImplTest, EnableVirtualTime) { EXPECT_FALSE(scheduler_->IsVirtualTimeEnabled()); scheduler_->EnableVirtualTime( MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE); @@ -3101,7 +3043,7 @@ TEST_P(MainThreadSchedulerImplTest, EnableVirtualTime) { scheduler_->GetVirtualTimeDomain()); } -TEST_P(MainThreadSchedulerImplTest, EnableVirtualTimeAfterThrottling) { +TEST_F(MainThreadSchedulerImplTest, EnableVirtualTimeAfterThrottling) { std::unique_ptr<PageSchedulerImpl> page_scheduler = base::WrapUnique(new PageSchedulerImpl(nullptr, scheduler_.get())); scheduler_->AddPageScheduler(page_scheduler.get()); @@ -3112,7 +3054,7 @@ TEST_P(MainThreadSchedulerImplTest, EnableVirtualTimeAfterThrottling) { TaskQueue* timer_tq = ThrottleableTaskQueue(frame_scheduler.get()).get(); - frame_scheduler->SetCrossOrigin(true); + frame_scheduler->SetCrossOriginToMainFrame(true); frame_scheduler->SetFrameVisible(false); EXPECT_TRUE(scheduler_->task_queue_throttler()->IsThrottled(timer_tq)); @@ -3122,7 +3064,7 @@ TEST_P(MainThreadSchedulerImplTest, EnableVirtualTimeAfterThrottling) { EXPECT_FALSE(scheduler_->task_queue_throttler()->IsThrottled(timer_tq)); } -TEST_P(MainThreadSchedulerImplTest, DisableVirtualTimeForTesting) { +TEST_F(MainThreadSchedulerImplTest, DisableVirtualTimeForTesting) { scheduler_->EnableVirtualTime( MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE); @@ -3148,7 +3090,7 @@ TEST_P(MainThreadSchedulerImplTest, DisableVirtualTimeForTesting) { EXPECT_FALSE(scheduler_->VirtualTimeControlTaskQueue()); } -TEST_P(MainThreadSchedulerImplTest, VirtualTimePauser) { +TEST_F(MainThreadSchedulerImplTest, VirtualTimePauser) { scheduler_->EnableVirtualTime( MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE); scheduler_->SetVirtualTimePolicy( @@ -3169,7 +3111,7 @@ TEST_P(MainThreadSchedulerImplTest, VirtualTimePauser) { EXPECT_EQ(after, before); } -TEST_P(MainThreadSchedulerImplTest, VirtualTimePauserNonInstantTask) { +TEST_F(MainThreadSchedulerImplTest, VirtualTimePauserNonInstantTask) { scheduler_->EnableVirtualTime( MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE); scheduler_->SetVirtualTimePolicy( @@ -3186,7 +3128,7 @@ TEST_P(MainThreadSchedulerImplTest, VirtualTimePauserNonInstantTask) { EXPECT_GT(after, before); } -TEST_P(MainThreadSchedulerImplTest, VirtualTimeWithOneQueueWithoutVirtualTime) { +TEST_F(MainThreadSchedulerImplTest, VirtualTimeWithOneQueueWithoutVirtualTime) { // This test ensures that we do not do anything strange like stopping // processing task queues after we encountered one task queue with // DoNotUseVirtualTime trait. @@ -3232,7 +3174,7 @@ TEST_P(MainThreadSchedulerImplTest, VirtualTimeWithOneQueueWithoutVirtualTime) { EXPECT_EQ(counter, kTaskQueueCount); } -TEST_P(MainThreadSchedulerImplTest, Tracing) { +TEST_F(MainThreadSchedulerImplTest, Tracing) { // This test sets renderer scheduler to some non-trivial state // (by posting tasks, creating child schedulers, etc) and converts it into a // traced value. This test checks that no internal checks fire during this. @@ -3260,10 +3202,7 @@ TEST_P(MainThreadSchedulerImplTest, Tracing) { FROM_HERE, base::BindOnce(NullTask), base::TimeDelta::FromMilliseconds(10)); - std::unique_ptr<base::trace_event::ConvertableToTraceFormat> value = - scheduler_->AsValue(base::TimeTicks()); - EXPECT_TRUE(value); - EXPECT_FALSE(value->ToString().empty()); + EXPECT_FALSE(scheduler_->ToString().empty()); } void RecordingTimeTestTask( @@ -3285,7 +3224,7 @@ void RecordingTimeTestTask( // // MaxEQT1 = 500ms is recorded and observed in histogram. // MaxEQT2 is recorded but not yet in histogram for not being flushed. -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, MaxQueueingTimeMetricRecordedOnlyDuringNavigation) { base::HistogramTester tester; // Start with a long task whose queueing time will be ignored. @@ -3305,7 +3244,7 @@ TEST_P(MainThreadSchedulerImplTest, } // Only the max of all the queueing times is recorded. -TEST_P(MainThreadSchedulerImplTest, MaxQueueingTimeMetricRecordTheMax) { +TEST_F(MainThreadSchedulerImplTest, MaxQueueingTimeMetricRecordTheMax) { base::HistogramTester tester; scheduler_->DidCommitProvisionalLoad(false, false, false); // The smaller queuing time will be ignored. @@ -3318,7 +3257,7 @@ TEST_P(MainThreadSchedulerImplTest, MaxQueueingTimeMetricRecordTheMax) { tester.ExpectUniqueSample("RendererScheduler.MaxQueueingTime", 500, 1); } -TEST_P(MainThreadSchedulerImplTest, DidCommitProvisionalLoad) { +TEST_F(MainThreadSchedulerImplTest, DidCommitProvisionalLoad) { scheduler_->OnFirstMeaningfulPaint(); EXPECT_FALSE(scheduler_->waiting_for_meaningful_paint()); @@ -3372,7 +3311,7 @@ TEST_P(MainThreadSchedulerImplTest, DidCommitProvisionalLoad) { EXPECT_TRUE(scheduler_->waiting_for_meaningful_paint()); // State cleared. } -TEST_P(MainThreadSchedulerImplTest, LoadingControlTasks) { +TEST_F(MainThreadSchedulerImplTest, LoadingControlTasks) { // Expect control loading tasks (M) to jump ahead of any regular loading // tasks (L). Vector<String> run_order; @@ -3382,7 +3321,7 @@ TEST_P(MainThreadSchedulerImplTest, LoadingControlTasks) { "L4", "L5", "L6")); } -TEST_P(MainThreadSchedulerImplTest, RequestBeginMainFrameNotExpected) { +TEST_F(MainThreadSchedulerImplTest, RequestBeginMainFrameNotExpected) { std::unique_ptr<PageSchedulerImplForTest> page_scheduler = std::make_unique<PageSchedulerImplForTest>(scheduler_.get()); scheduler_->AddPageScheduler(page_scheduler.get()); @@ -3404,7 +3343,7 @@ TEST_P(MainThreadSchedulerImplTest, RequestBeginMainFrameNotExpected) { Mock::VerifyAndClearExpectations(page_scheduler.get()); } -TEST_P(MainThreadSchedulerImplTest, +TEST_F(MainThreadSchedulerImplTest, RequestBeginMainFrameNotExpected_MultipleCalls) { std::unique_ptr<PageSchedulerImplForTest> page_scheduler = std::make_unique<PageSchedulerImplForTest>(scheduler_.get()); @@ -3422,7 +3361,7 @@ TEST_P(MainThreadSchedulerImplTest, } #if defined(OS_ANDROID) -TEST_P(MainThreadSchedulerImplTest, PauseTimersForAndroidWebView) { +TEST_F(MainThreadSchedulerImplTest, PauseTimersForAndroidWebView) { // Tasks in some queues don't fire when the timers are paused. Vector<String> run_order; PostTestTasks(&run_order, "D1 C1 L1 I1 T1"); @@ -3448,20 +3387,12 @@ class MainThreadSchedulerImplWithInitalVirtualTimeTest nullptr, test_task_runner_, test_task_runner_->GetMockTickClock(), base::sequence_manager::SequenceManager::Settings::Builder() .SetRandomisedSamplingEnabled(true) - .SetAntiStarvationLogicForPrioritiesDisabled( - GetParam() == AntiStarvationLogic::kDisabled) .Build()), base::Time::FromJsTime(1000000.0))); } }; -INSTANTIATE_TEST_SUITE_P(All, - MainThreadSchedulerImplWithInitalVirtualTimeTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(MainThreadSchedulerImplWithInitalVirtualTimeTest, VirtualTimeOverride) { +TEST_F(MainThreadSchedulerImplWithInitalVirtualTimeTest, VirtualTimeOverride) { EXPECT_TRUE(scheduler_->IsVirtualTimeEnabled()); EXPECT_EQ(PageSchedulerImpl::VirtualTimePolicy::kPause, scheduler_->virtual_time_policy()); @@ -3473,19 +3404,13 @@ class CompositingExperimentWithExplicitSignalsTest public: CompositingExperimentWithExplicitSignalsTest() : MainThreadSchedulerImplTest( - {kHighPriorityInputOnMainThread, kPrioritizeCompositingAfterInput, + {kPrioritizeCompositingAfterInput, kUseExplicitSignalForTriggeringCompositingPrioritization, kUseWillBeginMainFrameForCompositingPrioritization}, {}) {} }; -INSTANTIATE_TEST_SUITE_P(All, - CompositingExperimentWithExplicitSignalsTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(CompositingExperimentWithExplicitSignalsTest, CompositingAfterInput) { +TEST_F(CompositingExperimentWithExplicitSignalsTest, CompositingAfterInput) { Vector<String> run_order; PostTestTasks(&run_order, "P1 T1 C1"); base::RunLoop().RunUntilIdle(); @@ -3515,19 +3440,13 @@ class CompositingExperimentWithImplicitSignalsTest public: CompositingExperimentWithImplicitSignalsTest() : MainThreadSchedulerImplTest( - {kHighPriorityInputOnMainThread, kPrioritizeCompositingAfterInput}, + {kPrioritizeCompositingAfterInput}, {kHighestPriorityForCompositingAfterInput, kUseExplicitSignalForTriggeringCompositingPrioritization, kUseWillBeginMainFrameForCompositingPrioritization}) {} }; -INSTANTIATE_TEST_SUITE_P(All, - CompositingExperimentWithImplicitSignalsTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(CompositingExperimentWithImplicitSignalsTest, CompositingAfterInput) { +TEST_F(CompositingExperimentWithImplicitSignalsTest, CompositingAfterInput) { Vector<String> run_order; PostTestTasks(&run_order, "T1 C1 C2 P1 P2"); base::RunLoop().RunUntilIdle(); @@ -3535,7 +3454,7 @@ TEST_P(CompositingExperimentWithImplicitSignalsTest, CompositingAfterInput) { EXPECT_THAT(run_order, testing::ElementsAre("P1", "P2", "C1", "T1", "C2")); } -TEST_P(MainThreadSchedulerImplTest, EQTWithNestedLoop) { +TEST_F(MainThreadSchedulerImplTest, EQTWithNestedLoop) { AdvanceMockTickClockBy(base::TimeDelta::FromMilliseconds(100)); RunTask(base::BindLambdaForTesting([&] { @@ -3574,7 +3493,7 @@ TEST_P(MainThreadSchedulerImplTest, EQTWithNestedLoop) { base::TimeDelta::FromMicroseconds(400 + 50 + 50 + 1250))); } -TEST_P(MainThreadSchedulerImplTest, TaskQueueReferenceClearedOnShutdown) { +TEST_F(MainThreadSchedulerImplTest, TaskQueueReferenceClearedOnShutdown) { // Ensure that the scheduler clears its references to a task queue after // |shutdown| and doesn't try to update its policies. scoped_refptr<MainThreadTaskQueue> queue1 = scheduler_->NewTimerTaskQueue( @@ -3596,7 +3515,7 @@ TEST_P(MainThreadSchedulerImplTest, TaskQueueReferenceClearedOnShutdown) { EXPECT_EQ(queue2->GetTimeDomain(), scheduler_->GetVirtualTimeDomain()); } -TEST_P(MainThreadSchedulerImplTest, MicrotaskCheckpointTiming) { +TEST_F(MainThreadSchedulerImplTest, MicrotaskCheckpointTiming) { base::RunLoop().RunUntilIdle(); base::TimeTicks start_time = Now(); @@ -3623,7 +3542,7 @@ TEST_P(MainThreadSchedulerImplTest, MicrotaskCheckpointTiming) { observer.result().front().second); } -TEST_P(MainThreadSchedulerImplTest, IsBeginMainFrameScheduled) { +TEST_F(MainThreadSchedulerImplTest, IsBeginMainFrameScheduled) { EXPECT_FALSE(scheduler_->IsBeginMainFrameScheduled()); scheduler_->DidScheduleBeginMainFrame(); EXPECT_TRUE(scheduler_->IsBeginMainFrameScheduled()); @@ -3638,6 +3557,119 @@ TEST_P(MainThreadSchedulerImplTest, IsBeginMainFrameScheduled) { EXPECT_FALSE(scheduler_->IsBeginMainFrameScheduled()); } +TEST_F(MainThreadSchedulerImplTest, NonWakingTaskQueue) { + std::vector<std::pair<std::string, base::TimeTicks>> log; + base::TimeTicks start = scheduler_->GetTickClock()->NowTicks(); + + scheduler_->DefaultTaskQueue()->task_runner()->PostTask( + FROM_HERE, + base::BindOnce( + [](std::vector<std::pair<std::string, base::TimeTicks>>* log, + const base::TickClock* clock) { + log->emplace_back("regular (immediate)", clock->NowTicks()); + }, + &log, scheduler_->GetTickClock())); + scheduler_->NonWakingTaskRunner()->PostDelayedTask( + FROM_HERE, + base::BindOnce( + [](std::vector<std::pair<std::string, base::TimeTicks>>* log, + const base::TickClock* clock) { + log->emplace_back("non-waking", clock->NowTicks()); + }, + &log, scheduler_->GetTickClock()), + base::TimeDelta::FromSeconds(3)); + scheduler_->DefaultTaskQueue()->task_runner()->PostDelayedTask( + FROM_HERE, + base::BindOnce( + [](std::vector<std::pair<std::string, base::TimeTicks>>* log, + const base::TickClock* clock) { + log->emplace_back("regular (delayed)", clock->NowTicks()); + }, + &log, scheduler_->GetTickClock()), + base::TimeDelta::FromSeconds(5)); + + test_task_runner_->FastForwardUntilNoTasksRemain(); + + // Check that the non-waking task runner didn't generate an unnecessary + // wake-up. + // Note: the exact order of these tasks is not fixed and depends on the time + // domain iteration order. + EXPECT_THAT( + log, + testing::UnorderedElementsAre( + std::make_pair("regular (immediate)", start), + std::make_pair("non-waking", start + base::TimeDelta::FromSeconds(5)), + std::make_pair("regular (delayed)", + start + base::TimeDelta::FromSeconds(5)))); +} + +class BestEffortPriorityForFindInPageExperimentTest + : public MainThreadSchedulerImplTest { + public: + BestEffortPriorityForFindInPageExperimentTest() + : MainThreadSchedulerImplTest({kBestEffortPriorityForFindInPage}, {}) {} +}; + +TEST_F(BestEffortPriorityForFindInPageExperimentTest, + FindInPageTasksAreBestEffortPriorityUnderExperiment) { + Vector<String> run_order; + PostTestTasks(&run_order, "F1 D1 F2 D2 F3 D3"); + EnableIdleTasks(); + EXPECT_EQ(scheduler_->find_in_page_priority(), + QueuePriority::kBestEffortPriority); + base::RunLoop().RunUntilIdle(); + // Find-in-page tasks have "best-effort" priority, so they will be done after + // the default tasks (which have normal priority). + EXPECT_THAT(run_order, + testing::ElementsAre("D1", "D2", "D3", "F1", "F2", "F3")); +} + +TEST_F(MainThreadSchedulerImplTest, FindInPageTasksAreVeryHighPriority) { + Vector<String> run_order; + PostTestTasks(&run_order, "D1 D2 D3 F1 F2 F3"); + EnableIdleTasks(); + EXPECT_EQ( + scheduler_->find_in_page_priority(), + FindInPageBudgetPoolController::kFindInPageBudgetNotExhaustedPriority); + base::RunLoop().RunUntilIdle(); + // Find-in-page tasks have very high task priority, so we will do them before + // the default tasks. + EXPECT_THAT(run_order, + testing::ElementsAre("F1", "F2", "F3", "D1", "D2", "D3")); +} + +TEST_F(MainThreadSchedulerImplTest, FindInPageTasksChangeToNormalPriority) { + EXPECT_EQ( + scheduler_->find_in_page_priority(), + FindInPageBudgetPoolController::kFindInPageBudgetNotExhaustedPriority); + EnableIdleTasks(); + // Simulate a really long find-in-page task that takes 30% of CPU time + // (300ms out of 1000 ms). + base::TimeTicks task_start_time = Now(); + base::TimeTicks task_end_time = + task_start_time + base::TimeDelta::FromMilliseconds(300); + FakeTask fake_task; + fake_task.set_enqueue_order( + base::sequence_manager::EnqueueOrder::FromIntForTesting(42)); + FakeTaskTiming task_timing(task_start_time, task_end_time); + scheduler_->OnTaskStarted(find_in_page_task_queue(), fake_task, task_timing); + AdvanceMockTickClockTo(task_start_time + + base::TimeDelta::FromMilliseconds(1000)); + scheduler_->OnTaskCompleted(find_in_page_task_queue()->AsWeakPtr(), fake_task, + &task_timing, nullptr); + + // Now the find-in-page tasks have normal priority (same priority as default + // tasks, so we will do them in order). + EXPECT_EQ(scheduler_->find_in_page_priority(), + FindInPageBudgetPoolController::kFindInPageBudgetExhaustedPriority); + Vector<String> run_order; + PostTestTasks(&run_order, "D1 D2 F1 F2 D3 F3"); + + base::RunLoop().RunUntilIdle(); + EXPECT_THAT(run_order, + testing::ElementsAre("D1", "D2", "F1", "F2", "D3", "F3")); +} + class VeryHighPriorityForCompositingAlwaysExperimentTest : public MainThreadSchedulerImplTest { public: @@ -3646,13 +3678,7 @@ class VeryHighPriorityForCompositingAlwaysExperimentTest {}) {} }; -INSTANTIATE_TEST_SUITE_P(All, - VeryHighPriorityForCompositingAlwaysExperimentTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(VeryHighPriorityForCompositingAlwaysExperimentTest, +TEST_F(VeryHighPriorityForCompositingAlwaysExperimentTest, TestCompositorPolicy) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 D2 C2 P1"); @@ -3672,13 +3698,7 @@ class VeryHighPriorityForCompositingWhenFastExperimentTest {}) {} }; -INSTANTIATE_TEST_SUITE_P(All, - VeryHighPriorityForCompositingWhenFastExperimentTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(VeryHighPriorityForCompositingWhenFastExperimentTest, +TEST_F(VeryHighPriorityForCompositingWhenFastExperimentTest, TestCompositorPolicy_FastCompositing) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 D2 C2 P1"); @@ -3690,7 +3710,7 @@ TEST_P(VeryHighPriorityForCompositingWhenFastExperimentTest, EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(VeryHighPriorityForCompositingWhenFastExperimentTest, +TEST_F(VeryHighPriorityForCompositingWhenFastExperimentTest, TestCompositorPolicy_SlowCompositing) { RunSlowCompositorTask(); Vector<String> run_order; @@ -3703,7 +3723,7 @@ TEST_P(VeryHighPriorityForCompositingWhenFastExperimentTest, EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(VeryHighPriorityForCompositingWhenFastExperimentTest, +TEST_F(VeryHighPriorityForCompositingWhenFastExperimentTest, TestCompositorPolicy_CompositingStaysAtHighest) { Vector<String> run_order; PostTestTasks(&run_order, "L1 I1 D1 C1 D2 P1 C2"); @@ -3727,14 +3747,7 @@ class VeryHighPriorityForCompositingAlternatingExperimentTest {}) {} }; -INSTANTIATE_TEST_SUITE_P( - All, - VeryHighPriorityForCompositingAlternatingExperimentTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(VeryHighPriorityForCompositingAlternatingExperimentTest, +TEST_F(VeryHighPriorityForCompositingAlternatingExperimentTest, TestCompositorPolicy_AlternatingCompositorTasks) { Vector<String> run_order; PostTestTasks(&run_order, "D1 D2 D3 C1 C2 C3"); @@ -3746,7 +3759,7 @@ TEST_P(VeryHighPriorityForCompositingAlternatingExperimentTest, EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(VeryHighPriorityForCompositingAlternatingExperimentTest, +TEST_F(VeryHighPriorityForCompositingAlternatingExperimentTest, TestCompositorPolicy_AlternatingCompositorStaysAtHighest) { Vector<String> run_order; PostTestTasks(&run_order, "D1 D2 D3 C1 C2 C3"); @@ -3769,13 +3782,7 @@ class VeryHighPriorityForCompositingAfterDelayExperimentTest {}) {} }; -INSTANTIATE_TEST_SUITE_P(All, - VeryHighPriorityForCompositingAfterDelayExperimentTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(VeryHighPriorityForCompositingAfterDelayExperimentTest, +TEST_F(VeryHighPriorityForCompositingAfterDelayExperimentTest, TestCompositorPolicy_CompositorStaysAtNormalPriority) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 D2 C2 P1"); @@ -3787,7 +3794,7 @@ TEST_P(VeryHighPriorityForCompositingAfterDelayExperimentTest, EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(VeryHighPriorityForCompositingAfterDelayExperimentTest, +TEST_F(VeryHighPriorityForCompositingAfterDelayExperimentTest, TestCompositorPolicy_FirstCompositorTaskSetToVeryHighPriority) { // 150ms task to complete the countdown and prioritze compositing. AdvanceTimeWithTask(0.15); @@ -3802,7 +3809,7 @@ TEST_P(VeryHighPriorityForCompositingAfterDelayExperimentTest, EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(VeryHighPriorityForCompositingAfterDelayExperimentTest, +TEST_F(VeryHighPriorityForCompositingAfterDelayExperimentTest, TestCompositorPolicy_FirstCompositorTaskStaysAtNormalPriority) { // 0.5ms task should not prioritize compositing. AdvanceTimeWithTask(0.05); @@ -3825,13 +3832,7 @@ class VeryHighPriorityForCompositingBudgetExperimentTest {}) {} }; -INSTANTIATE_TEST_SUITE_P(All, - VeryHighPriorityForCompositingBudgetExperimentTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(VeryHighPriorityForCompositingBudgetExperimentTest, +TEST_F(VeryHighPriorityForCompositingBudgetExperimentTest, TestCompositorPolicy_CompositorPriorityVeryHighToNormal) { Vector<String> run_order; PostTestTasks(&run_order, "I1 D1 C1 D2 C2 P1"); @@ -3858,7 +3859,7 @@ TEST_P(VeryHighPriorityForCompositingBudgetExperimentTest, EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(VeryHighPriorityForCompositingBudgetExperimentTest, +TEST_F(VeryHighPriorityForCompositingBudgetExperimentTest, TestCompositorPolicy_CompositorPriorityNormalToVeryHigh) { // 1000ms compositor task will exhaust the budget. RunSlowCompositorTask(); @@ -3897,14 +3898,7 @@ class VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest {}) {} }; -INSTANTIATE_TEST_SUITE_P( - , - VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P(VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest, +TEST_F(VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest, TestCompositorPolicy_AlternatingCompositorTasks) { Vector<String> run_order; PostTestTasks(&run_order, "C1 D1 C2 D2"); @@ -3934,14 +3928,7 @@ class VeryHighPriorityForCompositingAfterDelayUntilBeginMainFrameExperimentTest {}) {} }; -INSTANTIATE_TEST_SUITE_P( - , - VeryHighPriorityForCompositingAfterDelayUntilBeginMainFrameExperimentTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P( +TEST_F( VeryHighPriorityForCompositingAfterDelayUntilBeginMainFrameExperimentTest, TestCompositorPolicy_FirstCompositorTaskSetToVeryHighPriority) { // 150ms task to complete the countdown and prioritze compositing. @@ -3973,14 +3960,7 @@ class VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest {}) {} }; -INSTANTIATE_TEST_SUITE_P( - , - VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest, - testing::Values(AntiStarvationLogic::kEnabled, - AntiStarvationLogic::kDisabled), - GetTestNameSuffix); - -TEST_P( +TEST_F( VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest, TestCompositorPolicy_CompositorPriorityNonBeginMainFrameDoesntExhaustBudget) { // 1000ms compositor task will not exhaust the budget. @@ -3994,7 +3974,7 @@ TEST_P( EXPECT_EQ(UseCase::kNone, CurrentUseCase()); } -TEST_P(VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest, +TEST_F(VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest, TestCompositorPolicy_CompositorPriorityBeginMainFrameExhaustsBudget) { // 1000ms BeginMainFrame will exhaust the budget. DoMainFrame(); diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc index d008643dc43..0d54d317672 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc @@ -63,6 +63,8 @@ const char* MainThreadTaskQueue::NameForQueueType( return "other_tq"; case MainThreadTaskQueue::QueueType::kWebScheduling: return "web_scheduling_tq"; + case MainThreadTaskQueue::QueueType::kNonWaking: + return "non_waking_tq"; case MainThreadTaskQueue::QueueType::kCount: NOTREACHED(); return nullptr; @@ -95,6 +97,7 @@ bool MainThreadTaskQueue::IsPerFrameTaskQueue( case MainThreadTaskQueue::QueueType::kInput: case MainThreadTaskQueue::QueueType::kDetached: case MainThreadTaskQueue::QueueType::kCleanup: + case MainThreadTaskQueue::QueueType::kNonWaking: case MainThreadTaskQueue::QueueType::kOther: return false; case MainThreadTaskQueue::QueueType::kCount: @@ -114,6 +117,7 @@ MainThreadTaskQueue::QueueClass MainThreadTaskQueue::QueueClassForQueueType( case QueueType::kTest: case QueueType::kV8: case QueueType::kIPC: + case QueueType::kNonWaking: case QueueType::kCleanup: return QueueClass::kNone; case QueueType::kFrameLoading: diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h index c1ea93cd184..b9c51d21ea8 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h @@ -76,10 +76,11 @@ class PLATFORM_EXPORT MainThreadTaskQueue // 22 : kWebSchedulingBestEffort, obsolete. kWebScheduling = 24, + kNonWaking = 25, // Used to group multiple types when calculating Expected Queueing Time. kOther = 23, - kCount = 25 + kCount = 26 }; // Returns name of the given queue type. Returned string has application @@ -123,13 +124,14 @@ class PLATFORM_EXPORT MainThreadTaskQueue // Separate enum class for handling prioritisation decisions in task queues. enum class PrioritisationType { kVeryHigh = 0, - kHigh = 1, - kBestEffort = 2, - kRegular = 3, - kLoading = 4, - kLoadingControl = 5, - - kCount = 6 + kBestEffort = 1, + kRegular = 2, + kLoading = 3, + kLoadingControl = 4, + kFindInPage = 5, + kExperimentalDatabase = 6, + + kCount = 7 }; // kPrioritisationTypeWidthBits is the number of bits required diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager.cc index 811cc0cb55f..07afb6e13a7 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager.cc @@ -76,6 +76,10 @@ void MemoryPurgeManager::OnPageDestroyed(PageLifecycleState state) { total_page_count_--; if (state == PageLifecycleState::kFrozen) frozen_page_count_--; + + if (!CanPurge()) + purge_timer_.Stop(); + DCHECK_LE(frozen_page_count_, total_page_count_); } @@ -110,6 +114,10 @@ void MemoryPurgeManager::OnRendererBackgrounded() { if (!base::FeatureList::IsEnabled( features::kPurgeRendererMemoryWhenBackgrounded)) return; + // A spare renderer has no pages. We would like to avoid purging memory + // on a spare renderer. + if (total_page_count_ == 0) + return; backgrounded_purge_pending_ = true; RequestMemoryPurgeWithDelay(GetTimeToPurgeAfterBackgrounded()); @@ -145,6 +153,9 @@ void MemoryPurgeManager::PerformMemoryPurge() { } bool MemoryPurgeManager::CanPurge() const { + if (total_page_count_ == 0) + return false; + if (backgrounded_purge_pending_) return true; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager_unittest.cc index 3293c3151d3..3dea63c3bbb 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager_unittest.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/memory_purge_manager_unittest.cc @@ -25,8 +25,7 @@ class MemoryPurgeManagerTest : public testing::Test { MemoryPurgeManagerTest() : task_environment_(base::test::TaskEnvironment::MainThreadType::UI, base::test::TaskEnvironment::TimeSource::MOCK_TIME), - memory_purge_manager_(task_environment_.GetMainThreadTaskRunner()), - observed_memory_pressure_(false) {} + memory_purge_manager_(task_environment_.GetMainThreadTaskRunner()) {} void SetUp() override { memory_pressure_listener_ = @@ -49,34 +48,23 @@ class MemoryPurgeManagerTest : public testing::Test { {features::kPurgeRendererMemoryWhenBackgrounded}); } - void ExpectMemoryPressure( - base::TimeDelta delay = base::TimeDelta::FromMinutes(0)) { - FastForwardBy(delay); - EXPECT_TRUE(observed_memory_pressure_); - observed_memory_pressure_ = false; - } - - void ExpectNoMemoryPressure( - base::TimeDelta delay = base::TimeDelta::FromMinutes(0)) { - FastForwardBy(delay); - EXPECT_FALSE(observed_memory_pressure_); - } - void FastForwardBy(base::TimeDelta delta) { task_environment_.FastForwardBy(delta); } + unsigned MemoryPressureCount() const { return memory_pressure_count_; } + base::test::ScopedFeatureList scoped_feature_list_; base::test::TaskEnvironment task_environment_; std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_; MemoryPurgeManager memory_purge_manager_; - bool observed_memory_pressure_; + unsigned memory_pressure_count_ = 0; private: void OnMemoryPressure(base::MemoryPressureListener::MemoryPressureLevel) { - observed_memory_pressure_ = true; + memory_pressure_count_++; } DISALLOW_COPY_AND_ASSIGN(MemoryPurgeManagerTest); @@ -92,7 +80,8 @@ TEST_F(MemoryPurgeManagerTest, PageFrozenInBackgroundedRenderer) { memory_purge_manager_.OnPageCreated(PageLifecycleState::kActive); memory_purge_manager_.SetRendererBackgrounded(true); memory_purge_manager_.OnPageFrozen(); - ExpectMemoryPressure(); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(1U, MemoryPressureCount()); } // Verify that OnPageFrozen() does not trigger a memory pressure notification in @@ -105,7 +94,8 @@ TEST_F(MemoryPurgeManagerTest, PageFrozenInForegroundedRenderer) { memory_purge_manager_.OnPageCreated(PageLifecycleState::kActive); memory_purge_manager_.SetRendererBackgrounded(false); memory_purge_manager_.OnPageFrozen(); - ExpectNoMemoryPressure(); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(0U, MemoryPressureCount()); } TEST_F(MemoryPurgeManagerTest, PageResumedUndoMemoryPressureSuppression) { @@ -117,7 +107,9 @@ TEST_F(MemoryPurgeManagerTest, PageResumedUndoMemoryPressureSuppression) { memory_purge_manager_.SetRendererBackgrounded(true); memory_purge_manager_.OnPageFrozen(); - ExpectMemoryPressure(); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(1U, MemoryPressureCount()); + EXPECT_TRUE(base::MemoryPressureListener::AreNotificationsSuppressed()); memory_purge_manager_.OnPageResumed(); EXPECT_FALSE(base::MemoryPressureListener::AreNotificationsSuppressed()); @@ -139,15 +131,18 @@ TEST_F(MemoryPurgeManagerTest, PageFrozenPurgeMemoryAllPagesFrozenDisabled) { memory_purge_manager_.OnPageCreated(PageLifecycleState::kActive); memory_purge_manager_.OnPageFrozen(); - ExpectMemoryPressure(); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(1U, MemoryPressureCount()); EXPECT_FALSE(base::MemoryPressureListener::AreNotificationsSuppressed()); memory_purge_manager_.OnPageFrozen(); - ExpectMemoryPressure(); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(2U, MemoryPressureCount()); EXPECT_FALSE(base::MemoryPressureListener::AreNotificationsSuppressed()); memory_purge_manager_.OnPageFrozen(); - ExpectMemoryPressure(); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(3U, MemoryPressureCount()); EXPECT_TRUE(base::MemoryPressureListener::AreNotificationsSuppressed()); memory_purge_manager_.OnPageResumed(); @@ -177,15 +172,18 @@ TEST_F(MemoryPurgeManagerTest, PageFrozenPurgeMemoryAllPagesFrozenEnabled) { memory_purge_manager_.OnPageCreated(PageLifecycleState::kActive); memory_purge_manager_.OnPageFrozen(); - ExpectNoMemoryPressure(); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(0U, MemoryPressureCount()); EXPECT_FALSE(base::MemoryPressureListener::AreNotificationsSuppressed()); memory_purge_manager_.OnPageFrozen(); - ExpectNoMemoryPressure(); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(0U, MemoryPressureCount()); EXPECT_FALSE(base::MemoryPressureListener::AreNotificationsSuppressed()); memory_purge_manager_.OnPageFrozen(); - ExpectMemoryPressure(); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(1U, MemoryPressureCount()); EXPECT_TRUE(base::MemoryPressureListener::AreNotificationsSuppressed()); memory_purge_manager_.OnPageResumed(); @@ -211,10 +209,12 @@ TEST_F(MemoryPurgeManagerTest, MemoryPurgeWithDelay) { memory_purge_manager_.OnPageFrozen(); // The memory pressure notification should not occur immediately - ExpectNoMemoryPressure(); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(0U, MemoryPressureCount()); // The memory pressure notification should occur after 1 minute - ExpectMemoryPressure(kDelayForPurgeAfterFreeze); + FastForwardBy(kDelayForPurgeAfterFreeze); + EXPECT_EQ(1U, MemoryPressureCount()); memory_purge_manager_.OnPageDestroyed(PageLifecycleState::kFrozen); } @@ -227,12 +227,13 @@ TEST_F(MemoryPurgeManagerTest, CancelMemoryPurgeWithDelay) { memory_purge_manager_.SetRendererBackgrounded(true); memory_purge_manager_.OnPageFrozen(); FastForwardBy(base::TimeDelta::FromSeconds(40)); - ExpectNoMemoryPressure(); + EXPECT_EQ(0U, MemoryPressureCount()); // If the page is resumed before the memory purge timer expires, the purge // should be cancelled. memory_purge_manager_.OnPageResumed(); - ExpectNoMemoryPressure(kDelayForPurgeAfterFreeze); + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(0U, MemoryPressureCount()); memory_purge_manager_.OnPageDestroyed(PageLifecycleState::kActive); } @@ -245,11 +246,12 @@ TEST_F(MemoryPurgeManagerTest, MemoryPurgeWithDelayNewActivePageCreated) { memory_purge_manager_.SetRendererBackgrounded(true); memory_purge_manager_.OnPageFrozen(); FastForwardBy(base::TimeDelta::FromSeconds(40)); - ExpectNoMemoryPressure(); + EXPECT_EQ(0U, MemoryPressureCount()); // All pages are no longer frozen, the memory purge should be cancelled. memory_purge_manager_.OnPageCreated(PageLifecycleState::kActive); - ExpectNoMemoryPressure(kDelayForPurgeAfterFreeze); + FastForwardBy(kDelayForPurgeAfterFreeze); + EXPECT_EQ(0U, MemoryPressureCount()); memory_purge_manager_.OnPageDestroyed(PageLifecycleState::kFrozen); memory_purge_manager_.OnPageDestroyed(PageLifecycleState::kActive); @@ -263,11 +265,12 @@ TEST_F(MemoryPurgeManagerTest, MemoryPurgeWithDelayNewFrozenPageCreated) { memory_purge_manager_.SetRendererBackgrounded(true); memory_purge_manager_.OnPageFrozen(); FastForwardBy(base::TimeDelta::FromSeconds(40)); - ExpectNoMemoryPressure(); + EXPECT_EQ(0U, MemoryPressureCount()); // All pages are still frozen and the memory purge should occur. memory_purge_manager_.OnPageCreated(PageLifecycleState::kFrozen); - ExpectMemoryPressure(kDelayForPurgeAfterFreeze); + FastForwardBy(kDelayForPurgeAfterFreeze); + EXPECT_EQ(1U, MemoryPressureCount()); memory_purge_manager_.OnPageDestroyed(PageLifecycleState::kFrozen); memory_purge_manager_.OnPageDestroyed(PageLifecycleState::kFrozen); @@ -281,7 +284,8 @@ TEST_F(MemoryPurgeManagerTest, PurgeRendererMemoryWhenBackgroundedEnabled) { memory_purge_manager_.SetRendererBackgrounded(true); FastForwardBy(base::TimeDelta::FromMinutes( MemoryPurgeManager::kDefaultMaxTimeToPurgeAfterBackgrounded)); - ExpectMemoryPressure(); + // No page, no memory pressure. + EXPECT_EQ(0U, MemoryPressureCount()); } TEST_F(MemoryPurgeManagerTest, PurgeRendererMemoryWhenBackgroundedDisabled) { @@ -291,7 +295,7 @@ TEST_F(MemoryPurgeManagerTest, PurgeRendererMemoryWhenBackgroundedDisabled) { memory_purge_manager_.SetRendererBackgrounded(true); FastForwardBy(base::TimeDelta::Max()); - ExpectNoMemoryPressure(); + EXPECT_EQ(0U, MemoryPressureCount()); } TEST_F(MemoryPurgeManagerTest, @@ -302,11 +306,11 @@ TEST_F(MemoryPurgeManagerTest, memory_purge_manager_.SetRendererBackgrounded(true); FastForwardBy(base::TimeDelta::FromSeconds(30)); - ExpectNoMemoryPressure(); + EXPECT_EQ(0U, MemoryPressureCount()); memory_purge_manager_.SetRendererBackgrounded(false); FastForwardBy(base::TimeDelta::Max()); - ExpectNoMemoryPressure(); + EXPECT_EQ(0U, MemoryPressureCount()); } TEST_F(MemoryPurgeManagerTest, PageFrozenAndResumedWhileBackgrounded) { @@ -329,7 +333,8 @@ TEST_F(MemoryPurgeManagerTest, PageFrozenAndResumedWhileBackgrounded) { memory_purge_manager_.SetRendererBackgrounded(true); memory_purge_manager_.OnPageFrozen(); FastForwardBy(kBeforeBackgroundPurgeDelay); - ExpectNoMemoryPressure(); + EXPECT_EQ(0U, MemoryPressureCount()); + memory_purge_manager_.OnPageResumed(); FastForwardBy( base::TimeDelta::FromMinutes( @@ -337,7 +342,7 @@ TEST_F(MemoryPurgeManagerTest, PageFrozenAndResumedWhileBackgrounded) { kBeforeBackgroundPurgeDelay); // Since the renderer is still backgrounded, the memory purge should happen // even though there are no frozen pages. - ExpectMemoryPressure(); + EXPECT_EQ(1U, MemoryPressureCount()); memory_purge_manager_.OnPageDestroyed(PageLifecycleState::kActive); } @@ -357,10 +362,12 @@ TEST_F(MemoryPurgeManagerTest, memory_purge_manager_.SetRendererBackgrounded(true); memory_purge_manager_.OnPageFrozen(); - ExpectMemoryPressure(base::TimeDelta::FromMinutes( + FastForwardBy(base::TimeDelta::FromMinutes( MemoryPurgeManager::kDefaultMaxTimeToPurgeAfterBackgrounded)); + EXPECT_EQ(1U, MemoryPressureCount()); + FastForwardBy(kFreezePurgeDelay); - ExpectNoMemoryPressure(); + EXPECT_EQ(1U, MemoryPressureCount()); memory_purge_manager_.OnPageDestroyed(PageLifecycleState::kFrozen); } @@ -380,11 +387,29 @@ TEST_F(MemoryPurgeManagerTest, memory_purge_manager_.SetRendererBackgrounded(true); memory_purge_manager_.OnPageFrozen(); - ExpectMemoryPressure(kFreezePurgeDelay); + FastForwardBy(kFreezePurgeDelay); + EXPECT_EQ(1U, MemoryPressureCount()); + FastForwardBy(base::TimeDelta::Max()); - ExpectNoMemoryPressure(); + EXPECT_EQ(1U, MemoryPressureCount()); + + memory_purge_manager_.OnPageDestroyed(PageLifecycleState::kFrozen); +} + +TEST_F(MemoryPurgeManagerTest, NoMemoryPurgeIfNoPage) { + scoped_feature_list_.InitWithFeatures( + {features::kFreezePurgeMemoryAllPagesFrozen} /* enabled */, + {features::kPurgeRendererMemoryWhenBackgrounded} /* disabled */); + memory_purge_manager_.SetRendererBackgrounded(true); + memory_purge_manager_.OnPageCreated(PageLifecycleState::kActive); + + memory_purge_manager_.SetRendererBackgrounded(true); + memory_purge_manager_.OnPageFrozen(); memory_purge_manager_.OnPageDestroyed(PageLifecycleState::kFrozen); + + FastForwardBy(base::TimeDelta::FromMinutes(0)); + EXPECT_EQ(0U, MemoryPressureCount()); } } // namespace diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.cc new file mode 100644 index 00000000000..13d6f4a9871 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.cc @@ -0,0 +1,45 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h" + +namespace blink { +namespace scheduler { + +NonWakingTimeDomain::NonWakingTimeDomain(const base::TickClock* tick_clock) + : tick_clock_(tick_clock) {} + +NonWakingTimeDomain::~NonWakingTimeDomain() = default; + +base::sequence_manager::LazyNow NonWakingTimeDomain::CreateLazyNow() const { + return base::sequence_manager::LazyNow(tick_clock_); +} + +base::TimeTicks NonWakingTimeDomain::Now() const { + return tick_clock_->NowTicks(); +} + +base::Optional<base::TimeDelta> NonWakingTimeDomain::DelayTillNextTask( + base::sequence_manager::LazyNow* lazy_now) { + // NonWakingTimeDomain should never generate wakeups on its own. + return base::nullopt; +} + +bool NonWakingTimeDomain::MaybeFastForwardToNextTask( + bool quit_when_idle_requested) { + return false; +} + +const char* NonWakingTimeDomain::GetName() const { + return "non_waking_time_domain"; +} + +void NonWakingTimeDomain::SetNextDelayedDoWork( + base::sequence_manager::LazyNow* lazy_now, + base::TimeTicks run_time) { + // Do not request a wake-up, unlike a regular TimeDomain. +} + +} // namespace scheduler +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h new file mode 100644 index 00000000000..9641ed9aa1b --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/non_waking_time_domain.h @@ -0,0 +1,38 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_NON_WAKING_TIME_DOMAIN_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_NON_WAKING_TIME_DOMAIN_H_ + +#include "base/task/sequence_manager/time_domain.h" +#include "base/time/tick_clock.h" + +namespace blink { +namespace scheduler { + +// A time domain which never generates wake-ups on its own. Useful for tasks +// which should run only when the system is non-idle. +class NonWakingTimeDomain : public base::sequence_manager::TimeDomain { + public: + explicit NonWakingTimeDomain(const base::TickClock* tick_clock); + ~NonWakingTimeDomain() override; + + // TimeDomain: + base::sequence_manager::LazyNow CreateLazyNow() const override; + base::TimeTicks Now() const override; + base::Optional<base::TimeDelta> DelayTillNextTask( + base::sequence_manager::LazyNow* lazy_now) override; + bool MaybeFastForwardToNextTask(bool quit_when_idle_requested) override; + const char* GetName() const override; + void SetNextDelayedDoWork(base::sequence_manager::LazyNow* lazy_now, + base::TimeTicks run_time) override; + + private: + const base::TickClock* tick_clock_; +}; + +} // namespace scheduler +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_NON_WAKING_TIME_DOMAIN_H_ diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h index 64608d7eebb..62498217237 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h @@ -7,7 +7,7 @@ #include <array> -#include "third_party/blink/public/platform/web_input_event.h" +#include "third_party/blink/public/common/input/web_input_event.h" #include "third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h" #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc index 46cbd2b9c41..98927259f7b 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc @@ -107,6 +107,8 @@ const char* TaskTypeNames::TaskTypeToString(TaskType task_type) { return "MainThreadTaskQueueCleanup"; case TaskType::kMainThreadTaskQueueMemoryPurge: return "MainThreadTaskQueueMemoryPurge"; + case TaskType::kMainThreadTaskQueueNonWaking: + return "MainThreadTaskQueueNonWaking"; case TaskType::kInternalIntersectionObserver: return "InternalIntersectionObserver"; case TaskType::kCompositorThreadTaskQueueDefault: @@ -135,6 +137,8 @@ const char* TaskTypeNames::TaskTypeToString(TaskType task_type) { return "ExperimentalWebScheduling"; case TaskType::kInternalFrameLifecycleControl: return "InternalFrameLifecycleControl"; + case TaskType::kInternalFindInPage: + return "InternalFindInPage"; case TaskType::kCount: return "Count"; } diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/user_model.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/user_model.cc index 361b57ef706..0b04962ea63 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/user_model.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/user_model.cc @@ -11,7 +11,6 @@ UserModel::UserModel() : pending_input_event_count_(0), is_gesture_active_(false), is_gesture_expected_(false) {} -UserModel::~UserModel() = default; void UserModel::DidStartProcessingInputEvent(blink::WebInputEvent::Type type, const base::TimeTicks now) { @@ -99,8 +98,7 @@ bool UserModel::IsGestureExpectedSoonImpl( base::TimeDelta::FromMilliseconds(kExpectSubsequentGestureMillis); return true; } else { - // If we've have a finished a gesture then a subsequent gesture is deemed - // likely. + // If we have finished a gesture then a subsequent gesture is deemed likely. base::TimeDelta expect_subsequent_gesture_for = base::TimeDelta::FromMilliseconds(kExpectSubsequentGestureMillis); if (last_continuous_gesture_time_.is_null() || diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/user_model.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/user_model.h index e29de4b9202..fb7d2b79468 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/user_model.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/user_model.h @@ -8,8 +8,8 @@ #include "base/macros.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" +#include "third_party/blink/public/common/input/web_input_event.h" #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h" -#include "third_party/blink/public/platform/web_input_event.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -21,7 +21,6 @@ class PLATFORM_EXPORT UserModel { public: UserModel(); - ~UserModel(); // Tells us that the system started processing an input event. Must be paired // with a call to DidFinishProcessingInputEvent. diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_priority.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_priority.cc index c3c98447ec4..d680bb7669a 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_priority.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_priority.cc @@ -8,45 +8,33 @@ namespace blink { namespace { -const AtomicString& ImmediatePriorityKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, immediate_priority, ("immediate")); - return immediate_priority; +const AtomicString& UserBlockingPriorityKeyword() { + DEFINE_STATIC_LOCAL(const AtomicString, user_blocking_priority, + ("user-blocking")); + return user_blocking_priority; } -const AtomicString& HighPriorityKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, high_priority, ("high")); - return high_priority; +const AtomicString& UserVisiblePriorityKeyword() { + DEFINE_STATIC_LOCAL(const AtomicString, user_visible_priority, + ("user-visible")); + return user_visible_priority; } -const AtomicString& DefaultPriorityKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, default_priority, ("default")); - return default_priority; -} - -const AtomicString& LowPriorityKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, low_priority, ("low")); - return low_priority; -} - -const AtomicString& IdlePriorityKeyword() { - DEFINE_STATIC_LOCAL(const AtomicString, idle_priority, ("idle")); - return idle_priority; +const AtomicString& BackgroundPriorityKeyword() { + DEFINE_STATIC_LOCAL(const AtomicString, background_priority, ("background")); + return background_priority; } } // namespace AtomicString WebSchedulingPriorityToString(WebSchedulingPriority priority) { switch (priority) { - case WebSchedulingPriority::kImmediatePriority: - return ImmediatePriorityKeyword(); - case WebSchedulingPriority::kHighPriority: - return HighPriorityKeyword(); - case WebSchedulingPriority::kDefaultPriority: - return DefaultPriorityKeyword(); - case WebSchedulingPriority::kLowPriority: - return LowPriorityKeyword(); - case WebSchedulingPriority::kIdlePriority: - return IdlePriorityKeyword(); + case WebSchedulingPriority::kUserBlockingPriority: + return UserBlockingPriorityKeyword(); + case WebSchedulingPriority::kUserVisiblePriority: + return UserVisiblePriorityKeyword(); + case WebSchedulingPriority::kBackgroundPriority: + return BackgroundPriorityKeyword(); } NOTREACHED(); @@ -55,19 +43,14 @@ AtomicString WebSchedulingPriorityToString(WebSchedulingPriority priority) { WebSchedulingPriority WebSchedulingPriorityFromString( const AtomicString& priority) { - if (priority == ImmediatePriorityKeyword()) - return WebSchedulingPriority::kImmediatePriority; - if (priority == HighPriorityKeyword()) - return WebSchedulingPriority::kHighPriority; - if (priority == DefaultPriorityKeyword()) - return WebSchedulingPriority::kDefaultPriority; - if (priority == LowPriorityKeyword()) - return WebSchedulingPriority::kLowPriority; - if (priority == IdlePriorityKeyword()) - return WebSchedulingPriority::kIdlePriority; - + if (priority == UserBlockingPriorityKeyword()) + return WebSchedulingPriority::kUserBlockingPriority; + if (priority == UserVisiblePriorityKeyword()) + return WebSchedulingPriority::kUserVisiblePriority; + if (priority == BackgroundPriorityKeyword()) + return WebSchedulingPriority::kBackgroundPriority; NOTREACHED(); - return WebSchedulingPriority::kDefaultPriority; + return WebSchedulingPriority::kUserVisiblePriority; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.cc new file mode 100644 index 00000000000..0e7bc1f23a5 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.cc @@ -0,0 +1,35 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.h" + +#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h" + +namespace blink { +namespace scheduler { + +WidgetScheduler::WidgetScheduler( + MainThreadSchedulerImpl* main_thread_scheduler) { + input_task_queue_ = main_thread_scheduler->NewTaskQueue( + MainThreadTaskQueue::QueueCreationParams( + MainThreadTaskQueue::QueueType::kInput) + .SetShouldMonitorQuiescence(true) + .SetFixedPriority( + base::make_optional(TaskQueue::QueuePriority::kHighestPriority))); + input_task_runner_ = + input_task_queue_->CreateTaskRunner(TaskType::kMainThreadTaskQueueInput); + input_task_queue_enabled_voter_ = + input_task_queue_->CreateQueueEnabledVoter(); +} + +WidgetScheduler::~WidgetScheduler() { + input_task_queue_->ShutdownTaskQueue(); +} + +scoped_refptr<base::SingleThreadTaskRunner> WidgetScheduler::InputTaskRunner() { + return input_task_runner_; +} + +} // namespace scheduler +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.h new file mode 100644 index 00000000000..83a5fdecb26 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.h @@ -0,0 +1,37 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_WIDGET_SCHEDULER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_WIDGET_SCHEDULER_H_ + +#include <memory> + +#include "base/task/sequence_manager/task_queue.h" +#include "third_party/blink/public/platform/scheduler/web_widget_scheduler.h" + +namespace blink { +namespace scheduler { + +class MainThreadSchedulerImpl; +class MainThreadTaskQueue; + +class WidgetScheduler : public WebWidgetScheduler { + public: + WidgetScheduler(MainThreadSchedulerImpl*); + ~WidgetScheduler() override; + scoped_refptr<base::SingleThreadTaskRunner> InputTaskRunner() override; + + private: + scoped_refptr<MainThreadTaskQueue> input_task_queue_; + scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_; + std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> + input_task_queue_enabled_voter_; + + DISALLOW_COPY_AND_ASSIGN(WidgetScheduler); +}; + +} // namespace scheduler +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_WIDGET_SCHEDULER_H_ diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h index 7aec66e6e0f..c9f33ee112b 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h @@ -89,8 +89,8 @@ class FrameScheduler : public FrameOrWorkerScheduler { // Set whether this frame is cross origin w.r.t. the top level frame. Cross // origin frames may use a different scheduling policy from same origin // frames. - virtual void SetCrossOrigin(bool) = 0; - virtual bool IsCrossOrigin() const = 0; + virtual void SetCrossOriginToMainFrame(bool) = 0; + virtual bool IsCrossOriginToMainFrame() const = 0; virtual void SetIsAdFrame() = 0; virtual bool IsAdFrame() const = 0; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h b/chromium/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h index 17c9a3baa13..ab1b52d776a 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h @@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_PENDING_USER_INPUT_TYPE_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_PENDING_USER_INPUT_TYPE_H_ +#include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h index 9eb6426a100..20e3e743837 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h @@ -9,8 +9,8 @@ #include "base/location.h" #include "base/single_thread_task_runner.h" #include "base/time/time.h" +#include "third_party/blink/public/common/input/web_input_event.h" #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h" -#include "third_party/blink/public/platform/web_input_event.h" #include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h" #include "third_party/blink/renderer/platform/scheduler/public/thread.h" @@ -89,6 +89,11 @@ class PLATFORM_EXPORT ThreadScheduler { // Returns a task runner for kV8 tasks. Can be called from any thread. virtual scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() = 0; + // Returns a task runner which does not generate system wakeups on its own. + // This means that if a delayed task is posted to it, it will run when + // the delay expires AND another task runs. + virtual scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() = 0; + // Returns a task runner for compositor tasks. This is intended only to be // used by specific animation and rendering related tasks (e.g. animated GIFS) // and should not generally be used. diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h b/chromium/third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h index fde66f54035..049ecb25117 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h @@ -13,13 +13,11 @@ namespace blink { // Priorities for the experimental scheduling API (see // https://github.com/WICG/main-thread-scheduling). enum class WebSchedulingPriority { - kImmediatePriority = 0, - kHighPriority = 1, - kDefaultPriority = 2, - kLowPriority = 3, - kIdlePriority = 4, + kUserBlockingPriority = 0, + kUserVisiblePriority = 1, + kBackgroundPriority = 2, - kLastPriority = kIdlePriority + kLastPriority = kBackgroundPriority }; PLATFORM_EXPORT AtomicString diff --git a/chromium/third_party/blink/renderer/platform/scheduler/public/worker_pool.h b/chromium/third_party/blink/renderer/platform/scheduler/public/worker_pool.h index e801685efad..ca7c72f301f 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/public/worker_pool.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/public/worker_pool.h @@ -15,9 +15,9 @@ namespace blink { namespace worker_pool { -// These are a thin wrapper around base::ThreadPoolInstance to ensure that all -// callers use CrossThreadBindOnce instead of base::Bind to ensure that -// all non-thread-safe objects are copied properly. +// These are a thin wrapper around base::ThreadPool to ensure that all callers +// use CrossThreadBindOnce instead of base::Bind to ensure that all +// non-thread-safe objects are copied properly. // // All tasks that do not care about which thread they are running on // (e.g. compressing/uncompressing tasks) use this API. diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc index ef04e64744c..0bc371afa11 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc @@ -33,21 +33,7 @@ CompositorThreadScheduler::CompositorThreadScheduler( base::sequence_manager::SequenceManager* sequence_manager) : NonMainThreadSchedulerImpl(sequence_manager, TaskType::kCompositorThreadTaskQueueDefault), - input_task_queue_( - base::FeatureList::IsEnabled(kHighPriorityInputOnCompositorThread) - ? helper()->NewTaskQueue( - base::sequence_manager::TaskQueue::Spec("input_tq") - .SetShouldMonitorQuiescence(true)) - : nullptr), - input_task_runner_(input_task_queue_ - ? input_task_queue_->CreateTaskRunner( - TaskType::kCompositorThreadTaskQueueInput) - : nullptr), compositor_metrics_helper_(helper()->HasCPUTimingForEachTask()) { - if (input_task_queue_) { - input_task_queue_->SetQueuePriority( - base::sequence_manager::TaskQueue::QueuePriority::kHighestPriority); - } DCHECK(!g_compositor_thread_scheduler); g_compositor_thread_scheduler = this; } @@ -85,26 +71,30 @@ CompositorThreadScheduler::IdleTaskRunner() { } scoped_refptr<base::SingleThreadTaskRunner> -CompositorThreadScheduler::InputTaskRunner() { - if (input_task_runner_) - return input_task_runner_; +CompositorThreadScheduler::V8TaskRunner() { + NOTREACHED(); + return nullptr; +} + +scoped_refptr<base::SingleThreadTaskRunner> +CompositorThreadScheduler::DefaultTaskRunner() { return helper()->DefaultTaskRunner(); } scoped_refptr<base::SingleThreadTaskRunner> -CompositorThreadScheduler::V8TaskRunner() { +CompositorThreadScheduler::CompositorTaskRunner() { NOTREACHED(); return nullptr; } scoped_refptr<base::SingleThreadTaskRunner> -CompositorThreadScheduler::CompositorTaskRunner() { +CompositorThreadScheduler::IPCTaskRunner() { NOTREACHED(); return nullptr; } scoped_refptr<base::SingleThreadTaskRunner> -CompositorThreadScheduler::IPCTaskRunner() { +CompositorThreadScheduler::NonWakingTaskRunner() { NOTREACHED(); return nullptr; } @@ -128,7 +118,6 @@ void CompositorThreadScheduler::RemoveTaskObserver( } void CompositorThreadScheduler::Shutdown() { - input_task_queue_->ShutdownTaskQueue(); } void CompositorThreadScheduler::OnIdleTaskPosted() {} diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h index 984cb2d01cc..3c808170630 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h @@ -40,9 +40,10 @@ class PLATFORM_EXPORT CompositorThreadScheduler // WebThreadScheduler: scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override; + scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override; - scoped_refptr<base::SingleThreadTaskRunner> InputTaskRunner() override; + scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override; bool ShouldYieldForHighPriorityWork() override; bool CanExceedIdleDeadlineIfRequired() const override; void AddTaskObserver(base::TaskObserver* task_observer) override; @@ -66,9 +67,6 @@ class PLATFORM_EXPORT CompositorThreadScheduler void InitImpl() override; private: - scoped_refptr<NonMainThreadTaskQueue> input_task_queue_; - scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_; - CompositorMetricsHelper compositor_metrics_helper_; DISALLOW_COPY_AND_ASSIGN(CompositorThreadScheduler); diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler_unittest.cc deleted file mode 100644 index 55385f02784..00000000000 --- a/chromium/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler_unittest.cc +++ /dev/null @@ -1,121 +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 "third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h" -#include <algorithm> -#include <memory> -#include "base/bind.h" -#include "base/macros.h" -#include "base/task/sequence_manager/test/sequence_manager_for_test.h" -#include "base/test/scoped_feature_list.h" -#include "base/test/simple_test_tick_clock.h" -#include "base/test/test_mock_time_task_runner.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/scheduler/common/features.h" -#include "third_party/blink/renderer/platform/wtf/functional.h" - -using testing::ElementsAre; -using testing::ElementsAreArray; - -namespace blink { -namespace scheduler { -// To avoid symbol collisions in jumbo builds. -namespace compositor_thread_scheduler_unittest { - -class CompositorThreadSchedulerTest : public testing::Test { - public: - CompositorThreadSchedulerTest( - std::vector<base::Feature> features_to_enable, - std::vector<base::Feature> features_to_disable) { - feature_list_.InitWithFeatures(features_to_enable, features_to_disable); - } - - CompositorThreadSchedulerTest() : CompositorThreadSchedulerTest({}, {}) {} - - ~CompositorThreadSchedulerTest() override = default; - - void SetUp() override { - mock_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); - mock_task_runner_->AdvanceMockTickClock( - base::TimeDelta::FromMicroseconds(5000)); - sequence_manager_ = base::sequence_manager::SequenceManagerForTest::Create( - nullptr, mock_task_runner_, mock_task_runner_->GetMockTickClock()); - scheduler_ = - std::make_unique<CompositorThreadScheduler>(sequence_manager_.get()); - scheduler_->Init(); - } - - void TearDown() override {} - - protected: - base::test::ScopedFeatureList feature_list_; - - scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_; - std::unique_ptr<base::sequence_manager::SequenceManagerForTest> - sequence_manager_; - std::unique_ptr<CompositorThreadScheduler> scheduler_; - - DISALLOW_COPY_AND_ASSIGN(CompositorThreadSchedulerTest); -}; - -class CompositorThreadInputPriorityTest : public CompositorThreadSchedulerTest { - public: - CompositorThreadInputPriorityTest() - : CompositorThreadSchedulerTest( - {kHighPriorityInputOnCompositorThread} /* features_to_enable */, - {} /* features_to_disable */) {} - ~CompositorThreadInputPriorityTest() override = default; -}; - -namespace { - -void RunTestTask(String name, Vector<String>* log) { - log->push_back(name); -} - -} // namespace - -TEST_F(CompositorThreadInputPriorityTest, HighestPriorityInput) { - Vector<String> run_order; - - scheduler_->DefaultTaskQueue()->task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&RunTestTask, "default", base::Unretained(&run_order))); - scheduler_->InputTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce(&RunTestTask, "input", base::Unretained(&run_order))); - - mock_task_runner_->RunUntilIdle(); - - EXPECT_THAT(run_order, testing::ElementsAre("input", "default")); -} - -class CompositorThreadNoInputPriorityTest - : public CompositorThreadSchedulerTest { - public: - CompositorThreadNoInputPriorityTest() - : CompositorThreadSchedulerTest( - {} /* features_to_enable */, - {kHighPriorityInputOnCompositorThread} /* features_to_disable */) {} - ~CompositorThreadNoInputPriorityTest() override = default; -}; - -TEST_F(CompositorThreadNoInputPriorityTest, InputNotPrioritized) { - Vector<String> run_order; - - scheduler_->DefaultTaskQueue()->task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&RunTestTask, "default", base::Unretained(&run_order))); - scheduler_->InputTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce(&RunTestTask, "input", base::Unretained(&run_order))); - - mock_task_runner_->RunUntilIdle(); - - EXPECT_THAT(run_order, testing::ElementsAre("default", "input")); -} -} // namespace compositor_thread_scheduler_unittest -} // namespace scheduler -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc index b9a44ecca6e..d63485495f5 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc @@ -197,6 +197,7 @@ scoped_refptr<base::SingleThreadTaskRunner> WorkerScheduler::GetTaskRunner( case TaskType::kMainThreadTaskQueueControl: case TaskType::kMainThreadTaskQueueCleanup: case TaskType::kMainThreadTaskQueueMemoryPurge: + case TaskType::kMainThreadTaskQueueNonWaking: case TaskType::kCompositorThreadTaskQueueDefault: case TaskType::kCompositorThreadTaskQueueInput: case TaskType::kWorkerThreadTaskQueueDefault: @@ -207,6 +208,7 @@ scoped_refptr<base::SingleThreadTaskRunner> WorkerScheduler::GetTaskRunner( case TaskType::kInternalContentCapture: case TaskType::kExperimentalWebScheduling: case TaskType::kInternalFrameLifecycleControl: + case TaskType::kInternalFindInPage: case TaskType::kCount: NOTREACHED(); break; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc index 478c3cb030f..4434805d26a 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc @@ -21,7 +21,6 @@ #include "services/metrics/public/cpp/ukm_builders.h" #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/platform/platform.h" -#include "third_party/blink/renderer/platform/instrumentation/histogram.h" #include "third_party/blink/renderer/platform/scheduler/common/features.h" #include "third_party/blink/renderer/platform/scheduler/common/process_state.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h" @@ -156,6 +155,12 @@ WorkerThreadScheduler::IPCTaskRunner() { return nullptr; } +scoped_refptr<base::SingleThreadTaskRunner> +WorkerThreadScheduler::NonWakingTaskRunner() { + NOTREACHED() << "Not implemented"; + return nullptr; +} + bool WorkerThreadScheduler::CanExceedIdleDeadlineIfRequired() const { DCHECK(initialized_); return idle_helper_.CanExceedIdleDeadlineIfRequired(); diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h index fb26754d227..130a125f9fb 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h +++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h @@ -50,6 +50,7 @@ class PLATFORM_EXPORT WorkerThreadScheduler : public NonMainThreadSchedulerImpl, scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override; scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override; + scoped_refptr<base::SingleThreadTaskRunner> NonWakingTaskRunner() override; bool ShouldYieldForHighPriorityWork() override; bool CanExceedIdleDeadlineIfRequired() const override; void AddTaskObserver(base::TaskObserver* task_observer) override; diff --git a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc index d4142d207c2..76cce482184 100644 --- a/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc +++ b/chromium/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc @@ -490,10 +490,10 @@ class WorkerThreadSchedulerWithProxyTest : public testing::Test { frame_scheduler_ = FakeFrameScheduler::Builder() .SetIsPageVisible(false) .SetFrameType(FrameScheduler::FrameType::kSubframe) - .SetIsCrossOrigin(true) + .SetIsCrossOriginToMainFrame(true) .SetDelegate(frame_scheduler_delegate_.get()) .Build(); - frame_scheduler_->SetCrossOrigin(true); + frame_scheduler_->SetCrossOriginToMainFrame(true); worker_scheduler_proxy_ = std::make_unique<WorkerSchedulerProxy>(frame_scheduler_.get()); diff --git a/chromium/third_party/blink/renderer/platform/supplementable.h b/chromium/third_party/blink/renderer/platform/supplementable.h index ff971f12334..dbbd63044f5 100644 --- a/chromium/third_party/blink/renderer/platform/supplementable.h +++ b/chromium/third_party/blink/renderer/platform/supplementable.h @@ -147,9 +147,7 @@ class Supplement : public GarbageCollectedMixin { : nullptr; } - void Trace(blink::Visitor* visitor) override { - visitor->Trace(supplementable_); - } + void Trace(Visitor* visitor) override { visitor->Trace(supplementable_); } private: Member<T> supplementable_; @@ -201,7 +199,7 @@ class Supplementable : public GarbageCollectedMixin { #endif } - void Trace(blink::Visitor* visitor) override { visitor->Trace(supplements_); } + void Trace(Visitor* visitor) override { visitor->Trace(supplements_); } protected: using SupplementMap = diff --git a/chromium/third_party/blink/renderer/platform/testing/DEPS b/chromium/third_party/blink/renderer/platform/testing/DEPS index 19b3458859d..63b1f17bcd4 100644 --- a/chromium/third_party/blink/renderer/platform/testing/DEPS +++ b/chromium/third_party/blink/renderer/platform/testing/DEPS @@ -13,10 +13,12 @@ include_rules = [ "+base/process", "+base/run_loop.h", "+base/metrics/statistics_recorder.h", + "+base/strings/string_number_conversions.h", "+base/task/single_thread_task_executor.h", "+base/test/test_io_thread.h", "+cc", "+mojo/core/embedder", + "+services/network/public", '+testing', "+third_party/blink/renderer/platform/exported", diff --git a/chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.cc b/chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.cc index a01f3006487..1a8a8a1a5f0 100644 --- a/chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.cc +++ b/chromium/third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.cc @@ -10,7 +10,6 @@ #include "base/test/test_timeouts.h" #include "content/public/test/blink_test_environment.h" #include "third_party/blink/renderer/platform/heap/thread_state.h" -#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h" namespace blink { @@ -30,8 +29,6 @@ BlinkFuzzerTestSupport::BlinkFuzzerTestSupport(int argc, char** argv) { TestTimeouts::Initialize(); content::SetUpBlinkTestEnvironment(); - - blink::SchemeRegistry::Initialize(); } BlinkFuzzerTestSupport::~BlinkFuzzerTestSupport() { diff --git a/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.cc b/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.cc index 76f39b5545e..119e0ff710d 100644 --- a/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.cc +++ b/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.cc @@ -22,12 +22,12 @@ WebTimeRanges EmptyWebMediaPlayer::Seekable() const { return WebTimeRanges(); } -WebSize EmptyWebMediaPlayer::NaturalSize() const { - return WebSize(0, 0); +gfx::Size EmptyWebMediaPlayer::NaturalSize() const { + return gfx::Size(); } -WebSize EmptyWebMediaPlayer::VisibleRect() const { - return WebSize(); +gfx::Size EmptyWebMediaPlayer::VisibleSize() const { + return gfx::Size(); } WebString EmptyWebMediaPlayer::GetErrorMessage() const { diff --git a/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.h b/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.h index 1322171e57a..e6692af38be 100644 --- a/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.h +++ b/chromium/third_party/blink/renderer/platform/testing/empty_web_media_player.h @@ -30,6 +30,7 @@ class EmptyWebMediaPlayer : public WebMediaPlayer { void SetVolume(double) override {} void SetLatencyHint(double) override {} void OnRequestPictureInPicture() override {} + void OnPictureInPictureAvailabilityChanged(bool available) override {} SurfaceLayerMode GetVideoSurfaceLayerMode() const override { return SurfaceLayerMode::kNever; } @@ -39,13 +40,14 @@ class EmptyWebMediaPlayer : public WebMediaPlayer { WebSetSinkIdCompleteCallback) override {} bool HasVideo() const override { return false; } bool HasAudio() const override { return false; } - WebSize NaturalSize() const override; - WebSize VisibleRect() const override; + gfx::Size NaturalSize() const override; + gfx::Size VisibleSize() const override; bool Paused() const override { return false; } bool Seeking() const override { return false; } double Duration() const override { return 0.0; } double CurrentTime() const override { return 0.0; } - NetworkState GetNetworkState() const override { return kNetworkStateEmpty; } + bool IsEnded() const override { return false; } + NetworkState GetNetworkState() const override { return kNetworkStateIdle; } ReadyState GetReadyState() const override { return kReadyStateHaveNothing; } WebString GetErrorMessage() const override; bool DidLoadingProgress() override { return false; } diff --git a/chromium/third_party/blink/renderer/platform/testing/font_test_helpers.cc b/chromium/third_party/blink/renderer/platform/testing/font_test_helpers.cc index 08dc5f8c1b5..1272d0fb9fc 100644 --- a/chromium/third_party/blink/renderer/platform/testing/font_test_helpers.cc +++ b/chromium/third_party/blink/renderer/platform/testing/font_test_helpers.cc @@ -61,6 +61,8 @@ class TestFontSelector : public FontSelector { const AtomicString& font_family_name) override {} void ReportFailedFontFamilyMatch( const AtomicString& font_family_name) override {} + void ReportSuccessfulLocalFontMatch(const AtomicString& font_name) override {} + void ReportFailedLocalFontMatch(const AtomicString& font_name) override {} ExecutionContext* GetExecutionContext() const override { return nullptr; } FontFaceCache* GetFontFaceCache() override { return nullptr; } @@ -93,9 +95,7 @@ Font CreateTestFont(const AtomicString& family_name, if (ligatures) font_description.SetVariantLigatures(*ligatures); - Font font(font_description); - font.Update(TestFontSelector::Create(font_path)); - return font; + return Font(font_description, TestFontSelector::Create(font_path)); } } // namespace test diff --git a/chromium/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h b/chromium/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h index 9738516ca21..6da9d8037a9 100644 --- a/chromium/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h +++ b/chromium/third_party/blink/renderer/platform/testing/paint_property_test_helpers.h @@ -23,6 +23,10 @@ inline const EffectPaintPropertyNode& e0() { return EffectPaintPropertyNode::Root(); } +constexpr int c0_id = 1; +constexpr int e0_id = 1; +constexpr int t0_id = 1; + inline scoped_refptr<EffectPaintPropertyNode> CreateOpacityEffect( const EffectPaintPropertyNode& parent, const TransformPaintPropertyNode& local_transform_space, @@ -111,14 +115,13 @@ inline scoped_refptr<EffectPaintPropertyNode> CreateBackdropFilterEffect( const TransformPaintPropertyNode& local_transform_space, const ClipPaintPropertyNode* output_clip, CompositorFilterOperations backdrop_filter, - const FloatPoint& filters_origin = FloatPoint(), - CompositingReasons compositing_reasons = CompositingReason::kNone) { + const FloatPoint& filters_origin = FloatPoint()) { EffectPaintPropertyNode::State state; state.local_transform_space = &local_transform_space; state.output_clip = output_clip; state.backdrop_filter = std::move(backdrop_filter); state.filters_origin = filters_origin; - state.direct_compositing_reasons = compositing_reasons; + state.direct_compositing_reasons = CompositingReason::kBackdropFilter; state.compositor_element_id = CompositorElementIdFromUniqueObjectId( NewUniqueObjectId(), CompositorElementIdNamespace::kPrimary); return EffectPaintPropertyNode::Create(parent, std::move(state)); @@ -127,12 +130,10 @@ inline scoped_refptr<EffectPaintPropertyNode> CreateBackdropFilterEffect( inline scoped_refptr<EffectPaintPropertyNode> CreateBackdropFilterEffect( const EffectPaintPropertyNode& parent, CompositorFilterOperations backdrop_filter, - const FloatPoint& paint_offset = FloatPoint(), - CompositingReasons compositing_reasons = CompositingReason::kNone) { + const FloatPoint& paint_offset = FloatPoint()) { return CreateBackdropFilterEffect( parent, parent.Unalias().LocalTransformSpace(), - parent.Unalias().OutputClip(), backdrop_filter, paint_offset, - compositing_reasons); + parent.Unalias().OutputClip(), backdrop_filter, paint_offset); } inline scoped_refptr<EffectPaintPropertyNode> @@ -156,9 +157,17 @@ inline scoped_refptr<ClipPaintPropertyNode> CreateClip( const ClipPaintPropertyNode& parent, const TransformPaintPropertyNode& local_transform_space, const FloatRoundedRect& clip_rect) { - ClipPaintPropertyNode::State state; - state.local_transform_space = &local_transform_space; - state.clip_rect = clip_rect; + ClipPaintPropertyNode::State state(&local_transform_space, clip_rect); + return ClipPaintPropertyNode::Create(parent, std::move(state)); +} + +inline scoped_refptr<ClipPaintPropertyNode> CreateClip( + const ClipPaintPropertyNode& parent, + const TransformPaintPropertyNode& local_transform_space, + const FloatRoundedRect& clip_rect, + const FloatRoundedRect& pixel_snapped_clip_rect) { + ClipPaintPropertyNode::State state(&local_transform_space, clip_rect, + pixel_snapped_clip_rect); return ClipPaintPropertyNode::Create(parent, std::move(state)); } @@ -166,9 +175,7 @@ inline scoped_refptr<ClipPaintPropertyNode> CreateClipPathClip( const ClipPaintPropertyNode& parent, const TransformPaintPropertyNode& local_transform_space, const FloatRoundedRect& clip_rect) { - ClipPaintPropertyNode::State state; - state.local_transform_space = &local_transform_space; - state.clip_rect = clip_rect; + ClipPaintPropertyNode::State state(&local_transform_space, clip_rect); state.clip_path = base::AdoptRef(new RefCountedPath); return ClipPaintPropertyNode::Create(parent, std::move(state)); } @@ -186,8 +193,7 @@ inline scoped_refptr<TransformPaintPropertyNode> CreateTransform( const TransformationMatrix& matrix, const FloatPoint3D& origin = FloatPoint3D(), CompositingReasons compositing_reasons = CompositingReason::kNone) { - TransformPaintPropertyNode::State state{ - TransformPaintPropertyNode::TransformAndOrigin(matrix, origin)}; + TransformPaintPropertyNode::State state{{matrix, origin}}; state.direct_compositing_reasons = compositing_reasons; return TransformPaintPropertyNode::Create(parent, std::move(state)); } @@ -196,8 +202,7 @@ inline scoped_refptr<TransformPaintPropertyNode> CreateAnimatingTransform( const TransformPaintPropertyNode& parent, const TransformationMatrix& matrix = TransformationMatrix(), const FloatPoint3D& origin = FloatPoint3D()) { - TransformPaintPropertyNode::State state{ - TransformPaintPropertyNode::TransformAndOrigin(matrix, origin)}; + TransformPaintPropertyNode::State state{{matrix, origin}}; state.direct_compositing_reasons = CompositingReason::kActiveTransformAnimation; state.compositor_element_id = CompositorElementIdFromUniqueObjectId( diff --git a/chromium/third_party/blink/renderer/platform/testing/paint_test_configurations.h b/chromium/third_party/blink/renderer/platform/testing/paint_test_configurations.h index 00b0da0c002..f5b9d63ee7b 100644 --- a/chromium/third_party/blink/renderer/platform/testing/paint_test_configurations.h +++ b/chromium/third_party/blink/renderer/platform/testing/paint_test_configurations.h @@ -11,24 +11,17 @@ namespace blink { -enum { - kCompositeAfterPaint = 1 << 0, - kUnderInvalidationChecking = 1 << 1, - kFastBorderRadius = 1 << 2, - kDoNotCompositeTrivial3D = 1 << 3, -}; +enum { kCompositeAfterPaint = 1 << 0, kUnderInvalidationChecking = 1 << 1 }; class PaintTestConfigurations : public testing::WithParamInterface<unsigned>, private ScopedCompositeAfterPaintForTest, - private ScopedPaintUnderInvalidationCheckingForTest, - private ScopedFastBorderRadiusForTest { + private ScopedPaintUnderInvalidationCheckingForTest { public: PaintTestConfigurations() : ScopedCompositeAfterPaintForTest(GetParam() & kCompositeAfterPaint), - ScopedPaintUnderInvalidationCheckingForTest(GetParam() & - kUnderInvalidationChecking), - ScopedFastBorderRadiusForTest(GetParam() & kFastBorderRadius) {} + ScopedPaintUnderInvalidationCheckingForTest( + GetParam() & kUnderInvalidationChecking) {} ~PaintTestConfigurations() { // Must destruct all objects before toggling back feature flags. WebHeap::CollectAllGarbageForTesting(); @@ -43,21 +36,6 @@ class PaintTestConfigurations INSTANTIATE_TEST_SUITE_P(All, test_class, \ ::testing::Values(kCompositeAfterPaint)) -#define INSTANTIATE_LAYER_LIST_TEST_SUITE_P(test_class) \ - INSTANTIATE_TEST_SUITE_P( \ - All, test_class, \ - ::testing::Values(0, kCompositeAfterPaint, kFastBorderRadius)) - -#define INSTANTIATE_SCROLL_HIT_TEST_SUITE_P(test_class) \ - INSTANTIATE_TEST_SUITE_P(All, test_class, \ - ::testing::Values(0, kCompositeAfterPaint)) - -#define INSTANTIATE_DO_NOT_COMPOSITE_TRIVIAL_3D_P(test_class) \ - INSTANTIATE_TEST_SUITE_P( \ - All, test_class, \ - ::testing::Values(0, kCompositeAfterPaint, kDoNotCompositeTrivial3D, \ - kCompositeAfterPaint | kDoNotCompositeTrivial3D)) - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_PAINT_TEST_CONFIGURATIONS_H_ diff --git a/chromium/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc b/chromium/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc index 2e35ebbb57b..32e03c72ae4 100644 --- a/chromium/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc +++ b/chromium/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/strings/string_number_conversions.h" #include "base/time/time.h" #include "base/timer/lap_timer.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_test.h" +#include "testing/perf/perf_result_reporter.h" #include "third_party/blink/renderer/platform/fonts/font.h" #include "third_party/blink/renderer/platform/fonts/font_description.h" #include "third_party/blink/renderer/platform/testing/font_test_helpers.h" @@ -21,6 +22,14 @@ static const int kTimeLimitMillis = 3000; static const int kWarmupRuns = 10000; static const int kTimeCheckInterval = 1000000; +namespace { + +constexpr char kMetricPrefixOffsetForPosition[] = "OffsetForPosition."; +constexpr char kMetricPrefixCharacterRange[] = "CharacterRange."; +constexpr char kMetricThroughput[] = "throughput"; + +} // namespace + class ShapeResultPerfTest { USING_FAST_MALLOC(ShapeResultPerfTest); @@ -52,6 +61,14 @@ class ShapeResultPerfTest { ltr ? TextDirection::kLtr : TextDirection::kRtl, false); } + void ReportResult(const std::string& metric_prefix, + const std::string& story_prefix) { + std::string story = story_prefix + "_" + param_string; + perf_test::PerfResultReporter reporter(metric_prefix, story); + reporter.RegisterImportantMetric(kMetricThroughput, "runs/s"); + reporter.AddResult(kMetricThroughput, timer.LapsPerSecond()); + } + Font font; HashMap<FontName, String, WTF::IntHash<FontName>> font_path = { @@ -62,6 +79,7 @@ class ShapeResultPerfTest { }; base::LapTimer timer; + std::string param_string; }; class OffsetForPositionPerfTest : public ShapeResultPerfTest, @@ -72,11 +90,18 @@ class OffsetForPositionPerfTest : public ShapeResultPerfTest, BreakGlyphsOption breakopt) { timer.Reset(); float position = GetParam(); + param_string = base::NumberToString(position); do { font.OffsetForPosition(run, position, partial, breakopt); timer.NextLap(); } while (!timer.HasTimeLimitExpired()); } + + protected: + void ReportResult(const std::string& story_prefix) { + ShapeResultPerfTest::ReportResult(kMetricPrefixOffsetForPosition, + story_prefix); + } }; class CharacterRangePerfTest : public ShapeResultPerfTest, @@ -85,67 +110,66 @@ class CharacterRangePerfTest : public ShapeResultPerfTest, void GetCharacter(TextRun& run) { timer.Reset(); int endpos = GetParam(); + param_string = base::NumberToString(endpos); do { font.SelectionRectForText(run, FloatPoint(), 100, 0, endpos); timer.NextLap(); } while (!timer.HasTimeLimitExpired()); } + + protected: + void ReportResult(const std::string& story_prefix) { + ShapeResultPerfTest::ReportResult(kMetricPrefixCharacterRange, + story_prefix); + } }; TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionFullBreak) { TextRun run = SetupFont(ahem, "FURACOLO", true); OffsetForPosition(run, OnlyFullGlyphs, BreakGlyphs); - perf_test::PrintResult("OffsetForPositionPerfTest", " LTR full break", "", - timer.LapsPerSecond(), "runs/s", true); + ReportResult("LTR_full_break"); } TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionFullDontBreak) { TextRun run = SetupFont(ahem, "FURACOLO", true); OffsetForPosition(run, OnlyFullGlyphs, DontBreakGlyphs); - perf_test::PrintResult("OffsetForPositionPerfTest", " LTR full", "", - timer.LapsPerSecond(), "runs/s", true); + ReportResult("LTR_full"); } TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionIncludePartialBreak) { TextRun run = SetupFont(ahem, "FURACOLO", true); OffsetForPosition(run, IncludePartialGlyphs, BreakGlyphs); - perf_test::PrintResult("OffsetForPositionPerfTest", " LTR partial break", "", - timer.LapsPerSecond(), "runs/s", true); + ReportResult("LTR_partial_break"); } TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionIncludePartialDontBreak) { TextRun run = SetupFont(ahem, "FURACOLO", true); OffsetForPosition(run, IncludePartialGlyphs, DontBreakGlyphs); - perf_test::PrintResult("OffsetForPositionPerfTest", " LTR partial", "", - timer.LapsPerSecond(), "runs/s", true); + ReportResult("LTR_partial"); } TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionFullBreak) { TextRun run = SetupFont(ahem, "OLOCARUF", false); OffsetForPosition(run, OnlyFullGlyphs, BreakGlyphs); - perf_test::PrintResult("OffsetForPositionPerfTest", " RTL full break", "", - timer.LapsPerSecond(), "runs/s", true); + ReportResult("RTL_full_break"); } TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionFullDontBreak) { TextRun run = SetupFont(ahem, "OLOCARUF", false); OffsetForPosition(run, OnlyFullGlyphs, DontBreakGlyphs); - perf_test::PrintResult("OffsetForPositionPerfTest", " RTL full", "", - timer.LapsPerSecond(), "runs/s", true); + ReportResult("RTL_full"); } TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionIncludePartialBreak) { TextRun run = SetupFont(ahem, "OLOCARUF", false); OffsetForPosition(run, IncludePartialGlyphs, BreakGlyphs); - perf_test::PrintResult("OffsetForPositionPerfTest", " RTL partial break", "", - timer.LapsPerSecond(), "runs/s", true); + ReportResult("RTL_partial_break"); } TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionIncludePartialDontBreak) { TextRun run = SetupFont(ahem, "OLOCARUF", false); OffsetForPosition(run, IncludePartialGlyphs, DontBreakGlyphs); - perf_test::PrintResult("OffsetForPositionPerfTest", " RTL partial", "", - timer.LapsPerSecond(), "runs/s", true); + ReportResult("RTL_partial"); } INSTANTIATE_TEST_SUITE_P(OffsetForPosition, @@ -155,15 +179,13 @@ INSTANTIATE_TEST_SUITE_P(OffsetForPosition, TEST_P(CharacterRangePerfTest, LTRCharacterForPosition) { TextRun run = SetupFont(ahem, "FURACOLO", true); GetCharacter(run); - perf_test::PrintResult("CharacterRangePerfTest", " LTR", "", - timer.LapsPerSecond(), "runs/s", true); + ReportResult("LTR"); } TEST_P(CharacterRangePerfTest, RTLCharacterForPosition) { TextRun run = SetupFont(ahem, "OLOCARUF", false); GetCharacter(run); - perf_test::PrintResult("CharacterRangePerfTest", " RTL", "", - timer.LapsPerSecond(), "runs/s", true); + ReportResult("RTL"); } INSTANTIATE_TEST_SUITE_P(CharacterRange, diff --git a/chromium/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc b/chromium/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc index c265e21a38e..4fb43e6a2cb 100644 --- a/chromium/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc +++ b/chromium/third_party/blink/renderer/platform/testing/shaping_line_breaker_perf_test.cc @@ -17,7 +17,7 @@ #include "third_party/blink/renderer/platform/text/text_run.h" #include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_test.h" +#include "testing/perf/perf_result_reporter.h" namespace blink { namespace { @@ -26,6 +26,16 @@ static const int kTimeLimitMillis = 2000; static const int kWarmupRuns = 5; static const int kTimeCheckInterval = 10; +constexpr char kMetricPrefixShapingLineBreaker[] = "ShapingLineBreaker."; +constexpr char kMetricThroughput[] = "throughput"; + +perf_test::PerfResultReporter SetUpReporter(const std::string& story) { + perf_test::PerfResultReporter reporter(kMetricPrefixShapingLineBreaker, + story); + reporter.RegisterImportantMetric(kMetricThroughput, "runs/s"); + return reporter; +} + struct HarfBuzzShaperCallbackContext { const HarfBuzzShaper* shaper; const Font* font; @@ -67,7 +77,6 @@ class ShapingLineBreakerPerfTest : public testing::Test { void SetUp() override { font_description.SetComputedSize(12.0); font = Font(font_description); - font.Update(nullptr); } void TearDown() override {} @@ -164,8 +173,8 @@ TEST_F(ShapingLineBreakerPerfTest, ShapeLatinText) { timer_.NextLap(); } while (!timer_.HasTimeLimitExpired()); - perf_test::PrintResult("ShapingLineBreakerPerfTest", "shape latin text", "", - timer_.LapsPerSecond(), "runs/s", true); + SetUpReporter("latin_text") + .AddResult(kMetricThroughput, timer_.LapsPerSecond()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/testing/stub_graphics_layer_client.h b/chromium/third_party/blink/renderer/platform/testing/stub_graphics_layer_client.h index 36ccdd9b42d..9d3841aa77b 100644 --- a/chromium/third_party/blink/renderer/platform/testing/stub_graphics_layer_client.h +++ b/chromium/third_party/blink/renderer/platform/testing/stub_graphics_layer_client.h @@ -11,7 +11,6 @@ class StubGraphicsLayerClient : public GraphicsLayerClient { ~StubGraphicsLayerClient() override = default; // GraphicsLayerClient implementation. - void InvalidateTargetElementForTesting() override {} IntRect ComputeInterestRect( const GraphicsLayer*, const IntRect& previous_interest_rect) const override { diff --git a/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.cc b/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.cc index 1f0238b9157..badc81ac3fe 100644 --- a/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.cc +++ b/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.cc @@ -13,38 +13,34 @@ #include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h" -#include "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h" #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" namespace blink { -class TestPaintArtifact::DummyRectClient : public FakeDisplayItemClient { - public: - IntRect VisualRect() const final { return rect_; } - void SetVisualRect(const IntRect& rect) { rect_ = rect; } - - sk_sp<PaintRecord> MakeRecord(const FloatRect& rect, Color color) { - rect_ = EnclosingIntRect(rect); - PaintRecorder recorder; - cc::PaintCanvas* canvas = recorder.beginRecording(rect); - PaintFlags flags; - flags.setColor(color.Rgb()); - canvas->drawRect(rect, flags); - return recorder.finishRecordingAsPicture(); - } - - private: - IntRect rect_; -}; +sk_sp<PaintRecord> DummyRectClient::MakeRecord(const IntRect& rect, + Color color) { + rect_ = rect; + PaintRecorder recorder; + cc::PaintCanvas* canvas = recorder.beginRecording(rect); + PaintFlags flags; + flags.setColor(color.Rgb()); + canvas->drawRect(rect, flags); + return recorder.finishRecordingAsPicture(); +} TestPaintArtifact::TestPaintArtifact() : display_item_list_(0) {} TestPaintArtifact::~TestPaintArtifact() = default; -TestPaintArtifact& TestPaintArtifact::Chunk(int id) { +static DummyRectClient& StaticDummyClient() { DEFINE_STATIC_LOCAL(DummyRectClient, client, ()); - Chunk(client, + client.Validate(); + return client; +} + +TestPaintArtifact& TestPaintArtifact::Chunk(int id) { + Chunk(StaticDummyClient(), static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + id)); // The default bounds with magic numbers make the chunks have different bounds // from each other, for e.g. RasterInvalidatorTest to check the tracked raster @@ -55,13 +51,11 @@ TestPaintArtifact& TestPaintArtifact::Chunk(int id) { return *this; } -TestPaintArtifact& TestPaintArtifact::Chunk(FakeDisplayItemClient& client, +TestPaintArtifact& TestPaintArtifact::Chunk(DummyRectClient& client, DisplayItem::Type type) { - if (!paint_chunks_.IsEmpty()) - paint_chunks_.back().end_index = display_item_list_.size(); - paint_chunks_.push_back(PaintChunk(display_item_list_.size(), 0, - PaintChunk::Id(client, type), - PropertyTreeState::Root())); + paint_chunks_.push_back( + PaintChunk(display_item_list_.size(), display_item_list_.size(), + PaintChunk::Id(client, type), PropertyTreeState::Root())); // Assume PaintController has processed this chunk. paint_chunks_.back().client_is_just_created = false; return *this; @@ -73,7 +67,7 @@ TestPaintArtifact& TestPaintArtifact::Properties( return *this; } -TestPaintArtifact& TestPaintArtifact::RectDrawing(const FloatRect& bounds, +TestPaintArtifact& TestPaintArtifact::RectDrawing(const IntRect& bounds, Color color) { return RectDrawing(NewClient(), bounds, color); } @@ -84,15 +78,6 @@ TestPaintArtifact& TestPaintArtifact::ScrollHitTest( return ScrollHitTest(NewClient(), scroll_offset, scroll_container_bounds); } -TestPaintArtifact& TestPaintArtifact::RectDrawing(FakeDisplayItemClient& client, - const FloatRect& bounds, - Color color) { - display_item_list_.AllocateAndConstruct<DrawingDisplayItem>( - client, DisplayItem::kDrawingFirst, - static_cast<DummyRectClient&>(client).MakeRecord(bounds, color)); - return *this; -} - TestPaintArtifact& TestPaintArtifact::ForeignLayer( scoped_refptr<cc::Layer> layer, const FloatPoint& offset) { @@ -100,16 +85,25 @@ TestPaintArtifact& TestPaintArtifact::ForeignLayer( display_item_list_.AllocateAndConstruct<ForeignLayerDisplayItem>( client, DisplayItem::kForeignLayerFirst, std::move(layer), offset, nullptr); + DidAddDisplayItem(); + return *this; +} + +TestPaintArtifact& TestPaintArtifact::RectDrawing(DummyRectClient& client, + const IntRect& bounds, + Color color) { + display_item_list_.AllocateAndConstruct<DrawingDisplayItem>( + client, DisplayItem::kDrawingFirst, client.MakeRecord(bounds, color)); + DidAddDisplayItem(); return *this; } TestPaintArtifact& TestPaintArtifact::ScrollHitTest( - FakeDisplayItemClient& client, - const TransformPaintPropertyNode* scroll_offset, + DummyRectClient& client, + const TransformPaintPropertyNode* scroll_translation, const IntRect& scroll_container_bounds) { - display_item_list_.AllocateAndConstruct<ScrollHitTestDisplayItem>( - client, DisplayItem::kScrollHitTest, scroll_offset, - scroll_container_bounds); + paint_chunks_.back().EnsureHitTestData().scroll_translation = + scroll_translation; return *this; } @@ -125,6 +119,14 @@ TestPaintArtifact& TestPaintArtifact::KnownToBeOpaque() { TestPaintArtifact& TestPaintArtifact::Bounds(const IntRect& bounds) { paint_chunks_.back().bounds = bounds; + paint_chunks_.back().drawable_bounds = bounds; + return *this; +} + +TestPaintArtifact& TestPaintArtifact::DrawableBounds( + const IntRect& drawable_bounds) { + paint_chunks_.back().drawable_bounds = drawable_bounds; + DCHECK(paint_chunks_.back().bounds.Contains(drawable_bounds)); return *this; } @@ -134,19 +136,27 @@ TestPaintArtifact& TestPaintArtifact::Uncacheable() { } scoped_refptr<PaintArtifact> TestPaintArtifact::Build() { - if (!paint_chunks_.IsEmpty()) - paint_chunks_.back().end_index = display_item_list_.size(); return PaintArtifact::Create(std::move(display_item_list_), std::move(paint_chunks_)); } -FakeDisplayItemClient& TestPaintArtifact::NewClient() { +DummyRectClient& TestPaintArtifact::NewClient() { dummy_clients_.push_back(std::make_unique<DummyRectClient>()); return *dummy_clients_.back(); } -FakeDisplayItemClient& TestPaintArtifact::Client(wtf_size_t i) const { +DummyRectClient& TestPaintArtifact::Client(wtf_size_t i) const { return *dummy_clients_[i]; } +void TestPaintArtifact::DidAddDisplayItem() { + auto& chunk = paint_chunks_.back(); + DCHECK_EQ(chunk.end_index, display_item_list_.size() - 1); + const auto& item = display_item_list_.Last(); + chunk.bounds.Unite(item.VisualRect()); + if (item.DrawsContent()) + chunk.drawable_bounds.Unite(item.VisualRect()); + chunk.end_index++; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.h b/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.h index 838ab969e8d..2e7a1f1108d 100644 --- a/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.h +++ b/chromium/third_party/blink/renderer/platform/testing/test_paint_artifact.h @@ -22,10 +22,20 @@ namespace blink { class ClipPaintPropertyNode; class EffectPaintPropertyNode; -class FloatRect; class PaintArtifact; class TransformPaintPropertyNode; +class DummyRectClient : public FakeDisplayItemClient { + public: + IntRect VisualRect() const final { return rect_; } + void SetVisualRect(const IntRect& rect) { rect_ = rect; } + + sk_sp<PaintRecord> MakeRecord(const IntRect& rect, Color color); + + private: + IntRect rect_; +}; + // Useful for quickly making a paint artifact in unit tests. // // If any method that automatically creates display item client is called, the @@ -58,7 +68,7 @@ class TestPaintArtifact { TestPaintArtifact& Chunk() { return Chunk(NewClient()); } // Add a chunk with the specified client. - TestPaintArtifact& Chunk(FakeDisplayItemClient&, + TestPaintArtifact& Chunk(DummyRectClient&, DisplayItem::Type = DisplayItem::kDrawingFirst); // This is for RasterInvalidatorTest, to create a chunk with specific id and @@ -92,7 +102,7 @@ class TestPaintArtifact { // Add display item in the chunk. Each display item will have a different // automatically created client. - TestPaintArtifact& RectDrawing(const FloatRect& bounds, Color color); + TestPaintArtifact& RectDrawing(const IntRect& bounds, Color color); TestPaintArtifact& ScrollHitTest( const TransformPaintPropertyNode* scroll_offset, const IntRect& scroll_container_bounds); @@ -101,18 +111,20 @@ class TestPaintArtifact { const FloatPoint& offset); // Add display item with the specified client in the chunk. - TestPaintArtifact& RectDrawing(FakeDisplayItemClient&, - const FloatRect& bounds, - Color); + TestPaintArtifact& RectDrawing(DummyRectClient&, + const IntRect& bounds, + Color color); TestPaintArtifact& ScrollHitTest( - FakeDisplayItemClient&, + DummyRectClient&, const TransformPaintPropertyNode* scroll_offset, const IntRect& scroll_container_bounds); // Sets fake bounds for the last paint chunk. Note that the bounds will be // overwritten when the PaintArtifact is constructed if the chunk has any - // display items. + // display items. Bounds() sets both bounds and drawable_bounds, while + // DrawableBounds() sets drawable_bounds only. TestPaintArtifact& Bounds(const IntRect&); + TestPaintArtifact& DrawableBounds(const IntRect&); TestPaintArtifact& OutsetForRasterEffects(float); TestPaintArtifact& KnownToBeOpaque(); @@ -124,12 +136,13 @@ class TestPaintArtifact { scoped_refptr<PaintArtifact> Build(); // Create a new display item client which is owned by this TestPaintArtifact. - FakeDisplayItemClient& NewClient(); + DummyRectClient& NewClient(); - FakeDisplayItemClient& Client(wtf_size_t) const; + DummyRectClient& Client(wtf_size_t) const; private: - class DummyRectClient; + void DidAddDisplayItem(); + Vector<std::unique_ptr<DummyRectClient>> dummy_clients_; DisplayItemList display_item_list_; diff --git a/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.cc b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.cc index 70a486d1bcb..7464c9cd52f 100644 --- a/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.cc +++ b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.cc @@ -39,8 +39,7 @@ #include "base/test/icu_test_util.h" #include "base/test/test_discardable_memory_allocator.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" -#include "mojo/public/cpp/bindings/strong_binding.h" -#include "third_party/blink/public/platform/interface_provider.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/platform/web_runtime_features.h" #include "third_party/blink/renderer/platform/font_family_names.h" #include "third_party/blink/renderer/platform/heap/heap.h" @@ -55,23 +54,24 @@ namespace blink { -class TestingPlatformSupport::TestingInterfaceProvider - : public blink::InterfaceProvider { +class TestingPlatformSupport::TestingBrowserInterfaceBroker + : public ThreadSafeBrowserInterfaceBrokerProxy { public: - TestingInterfaceProvider() = default; - virtual ~TestingInterfaceProvider() = default; + TestingBrowserInterfaceBroker() = default; + ~TestingBrowserInterfaceBroker() override = default; - void GetInterface(const char* name, - mojo::ScopedMessagePipeHandle handle) override { + void GetInterfaceImpl(mojo::GenericPendingReceiver receiver) override { auto& override_callback = GetOverrideCallback(); + auto interface_name = receiver.interface_name().value_or(""); if (!override_callback.is_null()) { - override_callback.Run(name, std::move(handle)); + override_callback.Run(interface_name.c_str(), receiver.PassPipe()); return; } - if (std::string(name) == mojom::blink::MimeRegistry::Name_) { + if (interface_name == mojom::blink::MimeRegistry::Name_) { mojo::MakeSelfOwnedReceiver( std::make_unique<MockMimeRegistry>(), - mojo::PendingReceiver<mojom::blink::MimeRegistry>(std::move(handle))); + mojo::PendingReceiver<mojom::blink::MimeRegistry>( + receiver.PassPipe())); return; } } @@ -86,7 +86,7 @@ class TestingPlatformSupport::TestingInterfaceProvider TestingPlatformSupport::ScopedOverrideMojoInterface:: ScopedOverrideMojoInterface(GetInterfaceCallback callback) - : auto_reset_(&TestingInterfaceProvider::GetOverrideCallback(), + : auto_reset_(&TestingBrowserInterfaceBroker::GetOverrideCallback(), std::move(callback)) {} TestingPlatformSupport::ScopedOverrideMojoInterface:: @@ -94,7 +94,7 @@ TestingPlatformSupport::ScopedOverrideMojoInterface:: TestingPlatformSupport::TestingPlatformSupport() : old_platform_(Platform::Current()), - interface_provider_(new TestingInterfaceProvider) { + interface_broker_(base::MakeRefCounted<TestingBrowserInterfaceBroker>()) { DCHECK(old_platform_); DCHECK(WTF::IsMainThread()); } @@ -129,11 +129,13 @@ WebData TestingPlatformSupport::UncompressDataResource(int resource_id) { : WebData(); } -InterfaceProvider* TestingPlatformSupport::GetInterfaceProvider() { - return interface_provider_.get(); +ThreadSafeBrowserInterfaceBrokerProxy* +TestingPlatformSupport::GetBrowserInterfaceBroker() { + return interface_broker_.get(); } void TestingPlatformSupport::RunUntilIdle() { + ThreadState::HeapPointersOnStackScope scan_stack(ThreadState::Current()); base::RunLoop().RunUntilIdle(); } @@ -169,7 +171,7 @@ ScopedUnittestsEnvironmentSetup::ScopedUnittestsEnvironmentSetup(int argc, Platform::SetCurrentPlatformForTesting(dummy_platform_.get()); WTF::Partitions::Initialize(); - WTF::Initialize(nullptr); + WTF::Initialize(); // This must be called after WTF::Initialize(), because ThreadSpecific<> // used in this function depends on WTF::IsMainThread(). diff --git a/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.h b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.h index eb1f8a94884..17ea8b4e183 100644 --- a/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.h +++ b/chromium/third_party/blink/renderer/platform/testing/testing_platform_support.h @@ -37,6 +37,7 @@ #include "base/auto_reset.h" #include "base/callback.h" #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/testing/code_cache_loader_mock.h" @@ -69,7 +70,7 @@ class TestingPlatformSupport : public Platform { WebData GetDataResource(int resource_id, ui::ScaleFactor scale_factor) override; WebData UncompressDataResource(int resource_id) override; - InterfaceProvider* GetInterfaceProvider() override; + ThreadSafeBrowserInterfaceBrokerProxy* GetBrowserInterfaceBroker() override; bool IsThreadedAnimationEnabled() override; virtual void RunUntilIdle(); @@ -90,10 +91,10 @@ class TestingPlatformSupport : public Platform { }; protected: - class TestingInterfaceProvider; + class TestingBrowserInterfaceBroker; Platform* const old_platform_; - std::unique_ptr<TestingInterfaceProvider> interface_provider_; + scoped_refptr<TestingBrowserInterfaceBroker> interface_broker_; private: bool is_threaded_animation_enabled_ = false; diff --git a/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.cc b/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.cc index f78ee319d2c..1215543c54b 100644 --- a/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.cc +++ b/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.cc @@ -64,9 +64,9 @@ void RunPendingTasks() { Thread::Current()->GetTaskRunner()->PostTask(FROM_HERE, WTF::Bind(&ExitRunLoop)); - // We forbid GC in the tasks. Otherwise the registered GCTaskObserver tries - // to run GC with NoHeapPointerOnStack. - ThreadState::GCForbiddenScope gc_forbidden(ThreadState::Current()); + // The following runloop can execute non-nested tasks with heap pointers + // living on stack, so we force both Oilpan and Unified GC to visit the stack. + ThreadState::HeapPointersOnStackScope scan_stack(ThreadState::Current()); EnterRunLoop(); } @@ -131,6 +131,13 @@ scoped_refptr<SharedBuffer> ReadFromFile(const String& path) { return SharedBuffer::Create(buffer.data(), buffer.size()); } +String BlinkWebTestsFontsTestDataPath(const String& relative_path) { + return FilePathToWebString( + WebTestsFilePath() + .Append(FILE_PATH_LITERAL("external/wpt/fonts")) + .Append(WebStringToFilePath(relative_path))); +} + LineReader::LineReader(const String& text) : text_(text), index_(0) {} bool LineReader::GetNextLine(String* line) { diff --git a/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.h b/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.h index 87d71599901..a964623e478 100644 --- a/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.h +++ b/chromium/third_party/blink/renderer/platform/testing/unit_test_helpers.h @@ -75,6 +75,12 @@ String PlatformTestDataPath(const String& relative_path = String()); // specified. String AccessibilityTestDataPath(const String& relative_path = String()); +// Returns Blink web_tests fonts as an absolute path, i.e. +// <blinkRootDir>/src/third_party/blink/web_tests/external/wpt/fonts/<relative_path>. +// It returns the top fonts test directory if |relative_path| was not +// specified. +String BlinkWebTestsFontsTestDataPath(const String& relative_path = String()); + scoped_refptr<SharedBuffer> ReadFromFile(const String& path); class LineReader { diff --git a/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.cc b/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.cc index 653acd538f2..e07b21c2cf3 100644 --- a/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.cc +++ b/chromium/third_party/blink/renderer/platform/testing/url_test_helpers.cc @@ -33,10 +33,10 @@ #include <string> #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "services/network/public/mojom/load_timing_info.mojom.h" #include "third_party/blink/public/platform/file_path_conversion.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_url_error.h" -#include "third_party/blink/public/platform/web_url_load_timing.h" #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h" #include "third_party/blink/renderer/platform/network/http_names.h" @@ -64,27 +64,27 @@ WebURL RegisterMockedURLLoadFromBase(const WebString& base_url, void RegisterMockedURLLoad(const WebURL& full_url, const WebString& file_path, const WebString& mime_type) { - WebURLLoadTiming timing; - timing.Initialize(); + network::mojom::LoadTimingInfoPtr timing = + network::mojom::LoadTimingInfo::New(); WebURLResponse response(full_url); response.SetMimeType(mime_type); response.SetHttpHeaderField(http_names::kContentType, mime_type); response.SetHttpStatusCode(200); - response.SetLoadTiming(timing); + response.SetLoadTiming(*timing); RegisterMockedURLLoadWithCustomResponse(full_url, file_path, response); } void RegisterMockedErrorURLLoad(const WebURL& full_url) { - WebURLLoadTiming timing; - timing.Initialize(); + network::mojom::LoadTimingInfoPtr timing = + network::mojom::LoadTimingInfo::New(); WebURLResponse response; response.SetMimeType("image/png"); response.SetHttpHeaderField(http_names::kContentType, "image/png"); response.SetHttpStatusCode(404); - response.SetLoadTiming(timing); + response.SetLoadTiming(*timing); ResourceError error = ResourceError::Failure(full_url); Platform::Current()->GetURLLoaderMockFactory()->RegisterErrorURL( diff --git a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc index a58efcf47f6..a4bf13cebbe 100644 --- a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc +++ b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc @@ -6,6 +6,8 @@ #include <utility> +#include "net/cookies/site_for_cookies.h" +#include "services/network/public/cpp/resource_request.h" #include "third_party/blink/public/platform/url_conversion.h" #include "third_party/blink/public/platform/web_data.h" #include "third_party/blink/public/platform/web_security_origin.h" @@ -70,7 +72,7 @@ void WebURLLoaderMock::ServeAsynchronousRequest( } WebURL WebURLLoaderMock::ServeRedirect( - const WebURLRequest& request, + const WebString& method, const WebURLResponse& redirect_response) { KURL redirect_url(redirect_response.HttpHeaderField("Location")); @@ -78,9 +80,9 @@ WebURL WebURLLoaderMock::ServeRedirect( bool report_raw_headers = false; bool follow = client_->WillFollowRedirect( - redirect_url, redirect_url, WebString(), - network::mojom::ReferrerPolicy::kDefault, request.HttpMethod(), - redirect_response, report_raw_headers); + redirect_url, net::SiteForCookies::FromUrl(redirect_url), WebString(), + network::mojom::ReferrerPolicy::kDefault, method, redirect_response, + report_raw_headers); // |this| might be deleted in willFollowRedirect(). if (!self) return redirect_url; @@ -92,7 +94,13 @@ WebURL WebURLLoaderMock::ServeRedirect( } void WebURLLoaderMock::LoadSynchronously( - const WebURLRequest& request, + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequest::ExtraData> request_extra_data, + int requestor_id, + bool download_to_network_cache_only, + bool pass_response_pipe_to_client, + bool no_mime_sniffing, + base::TimeDelta timeout_interval, WebURLLoaderClient* client, WebURLResponse& response, base::Optional<WebURLError>& error, @@ -100,17 +108,22 @@ void WebURLLoaderMock::LoadSynchronously( int64_t& encoded_data_length, int64_t& encoded_body_length, blink::WebBlobInfo& downloaded_blob) { - DCHECK(factory_->IsMockedURL(request.Url())); - factory_->LoadSynchronously(request, &response, &error, &data, + DCHECK(factory_->IsMockedURL(WebURL(KURL(request->url)))) << request->url; + factory_->LoadSynchronously(std::move(request), &response, &error, &data, &encoded_data_length); } -void WebURLLoaderMock::LoadAsynchronously(const WebURLRequest& request, - WebURLLoaderClient* client) { +void WebURLLoaderMock::LoadAsynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequest::ExtraData> request_extra_data, + int requestor_id, + bool download_to_network_cache_only, + bool no_mime_sniffing, + WebURLLoaderClient* client) { DCHECK(client); - DCHECK(factory_->IsMockedURL(request.Url())) << request.Url(); + DCHECK(factory_->IsMockedURL(WebURL(KURL(request->url)))) << request->url; client_ = client; - factory_->LoadAsynchronouly(request, this); + factory_->LoadAsynchronouly(std::move(request), this); } void WebURLLoaderMock::Cancel() { diff --git a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.h b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.h index 45d418e8766..0181a0889f9 100644 --- a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.h +++ b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock.h @@ -39,20 +39,32 @@ class WebURLLoaderMock : public WebURLLoader { const base::Optional<WebURLError>& error); // Simulates the redirect being served. - WebURL ServeRedirect(const WebURLRequest& request, + WebURL ServeRedirect(const WebString& method, const WebURLResponse& redirect_response); // WebURLLoader methods: - void LoadSynchronously(const WebURLRequest&, - WebURLLoaderClient* client, - WebURLResponse&, - base::Optional<WebURLError>&, - WebData&, - int64_t& encoded_data_length, - int64_t& encoded_body_length, - blink::WebBlobInfo& downloaded_blob) override; - void LoadAsynchronously(const WebURLRequest& request, - WebURLLoaderClient* client) override; + void LoadSynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequest::ExtraData> request_extra_data, + int requestor_id, + bool download_to_network_cache_only, + bool pass_response_pipe_to_client, + bool no_mime_sniffing, + base::TimeDelta timeout_interval, + WebURLLoaderClient* client, + WebURLResponse&, + base::Optional<WebURLError>&, + WebData&, + int64_t& encoded_data_length, + int64_t& encoded_body_length, + blink::WebBlobInfo& downloaded_blob) override; + void LoadAsynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequest::ExtraData> request_extra_data, + int requestor_id, + bool download_to_network_cache_only, + bool no_mime_sniffing, + WebURLLoaderClient* client) override; void SetDefersLoading(bool defer) override; void DidChangePriority(WebURLRequest::Priority new_priority, int intra_priority_value) override; diff --git a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc index 71d4ab676d5..c8d802cd025 100644 --- a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc +++ b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.cc @@ -118,17 +118,18 @@ void WebURLLoaderMockFactoryImpl::ServeAsynchronousRequests() { while (!pending_loaders_.IsEmpty()) { LoaderToRequestMap::iterator iter = pending_loaders_.begin(); base::WeakPtr<WebURLLoaderMock> loader(iter->key->GetWeakPtr()); - const WebURLRequest request = iter->value; + std::unique_ptr<network::ResourceRequest> request = std::move(iter->value); pending_loaders_.erase(loader.get()); WebURLResponse response; base::Optional<WebURLError> error; WebData data; - LoadRequest(request.Url(), &response, &error, &data); + LoadRequest(WebURL(KURL(request->url)), &response, &error, &data); // Follow any redirects while the loader is still active. while (response.HttpStatusCode() >= 300 && response.HttpStatusCode() < 400) { - WebURL new_url = loader->ServeRedirect(request, response); + WebURL new_url = loader->ServeRedirect( + WebString::FromLatin1(request->method), response); RunUntilIdle(); if (!loader || loader->is_cancelled() || loader->is_deferred()) break; @@ -204,20 +205,20 @@ void WebURLLoaderMockFactoryImpl::CancelLoad(WebURLLoaderMock* loader) { } void WebURLLoaderMockFactoryImpl::LoadSynchronously( - const WebURLRequest& request, + std::unique_ptr<network::ResourceRequest> request, WebURLResponse* response, base::Optional<WebURLError>* error, WebData* data, int64_t* encoded_data_length) { - LoadRequest(request.Url(), response, error, data); + LoadRequest(WebURL(KURL(request->url)), response, error, data); *encoded_data_length = data->size(); } void WebURLLoaderMockFactoryImpl::LoadAsynchronouly( - const WebURLRequest& request, + std::unique_ptr<network::ResourceRequest> request, WebURLLoaderMock* loader) { DCHECK(!pending_loaders_.Contains(loader)); - pending_loaders_.Set(loader, request); + pending_loaders_.Set(loader, std::move(request)); } void WebURLLoaderMockFactoryImpl::RunUntilIdle() { diff --git a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h index 2af483ea74d..3fdebfd5142 100644 --- a/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h +++ b/chromium/third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h @@ -18,6 +18,10 @@ #include "third_party/blink/renderer/platform/weborigin/kurl_hash.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" +namespace network { +struct ResourceRequest; +} // namespace network + namespace blink { class TestingPlatformSupport; @@ -58,12 +62,12 @@ class WebURLLoaderMockFactoryImpl : public WebURLLoaderMockFactory { bool IsMockedURL(const WebURL& url); // Called by the loader to load a resource. - void LoadSynchronously(const WebURLRequest& request, + void LoadSynchronously(std::unique_ptr<network::ResourceRequest> request, WebURLResponse* response, base::Optional<WebURLError>* error, WebData* data, int64_t* encoded_data_length); - void LoadAsynchronouly(const WebURLRequest& request, + void LoadAsynchronouly(std::unique_ptr<network::ResourceRequest> request, WebURLLoaderMock* loader); // Removes the loader from the list of pending loaders. @@ -101,7 +105,8 @@ class WebURLLoaderMockFactoryImpl : public WebURLLoaderMockFactory { WebURLLoaderTestDelegate* delegate_ = nullptr; // The loaders that have not being served data yet. - using LoaderToRequestMap = HashMap<WebURLLoaderMock*, WebURLRequest>; + using LoaderToRequestMap = + HashMap<WebURLLoaderMock*, std::unique_ptr<network::ResourceRequest>>; LoaderToRequestMap pending_loaders_; // All values must be valid, but we use Optional because HashMap requires diff --git a/chromium/third_party/blink/renderer/platform/text/DEPS b/chromium/third_party/blink/renderer/platform/text/DEPS index e6709d4f7a1..8cfc5a48108 100644 --- a/chromium/third_party/blink/renderer/platform/text/DEPS +++ b/chromium/third_party/blink/renderer/platform/text/DEPS @@ -6,6 +6,7 @@ include_rules = [ "+third_party/blink/renderer/platform/text", # Dependencies. + "+base/i18n/rtl.h", "+base/mac", "+third_party/blink/renderer/platform/text/date_components.h", "+third_party/blink/renderer/platform/heap", @@ -16,4 +17,5 @@ include_rules = [ "+third_party/blink/renderer/platform/web_test_support.h", "+third_party/blink/renderer/platform/weborigin", "+third_party/blink/renderer/platform/wtf", + "+ui/base/ui_base_features.h", ] diff --git a/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc b/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc index abe78aa5062..4d24ad93a59 100644 --- a/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc +++ b/chromium/third_party/blink/renderer/platform/text/hyphenation/hyphenation_minikin.cc @@ -12,8 +12,8 @@ #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" #include "third_party/blink/public/mojom/hyphenation/hyphenation.mojom-blink.h" -#include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/text/character.h" #include "third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h" @@ -51,7 +51,7 @@ using Hyphenator = android::Hyphenator; static mojo::Remote<mojom::blink::Hyphenation> ConnectToRemoteService() { mojo::Remote<mojom::blink::Hyphenation> service; - Platform::Current()->GetInterfaceProvider()->GetInterface( + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( service.BindNewPipeAndPassReceiver()); return service; } diff --git a/chromium/third_party/blink/renderer/platform/text/icu_error.cc b/chromium/third_party/blink/renderer/platform/text/icu_error.cc index 1aad8529944..ec72aac3447 100644 --- a/chromium/third_party/blink/renderer/platform/text/icu_error.cc +++ b/chromium/third_party/blink/renderer/platform/text/icu_error.cc @@ -11,7 +11,7 @@ namespace blink { // Distinguish memory allocation failures from other errors. // https://groups.google.com/a/chromium.org/d/msg/platform-architecture-dev/MP0k9WGnCjA/zIBiJtilBwAJ static NOINLINE void ICUOutOfMemory() { - OOM_CRASH(); + OOM_CRASH(0); } void ICUError::HandleFailure() { diff --git a/chromium/third_party/blink/renderer/platform/text/locale_icu.cc b/chromium/third_party/blink/renderer/platform/text/locale_icu.cc index 514931f881c..92fb99e0cc0 100644 --- a/chromium/third_party/blink/renderer/platform/text/locale_icu.cc +++ b/chromium/third_party/blink/renderer/platform/text/locale_icu.cc @@ -42,6 +42,7 @@ #include "third_party/blink/renderer/platform/wtf/date_math.h" #include "third_party/blink/renderer/platform/wtf/text/string_buffer.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" +#include "ui/base/ui_base_features.h" namespace blink { @@ -249,7 +250,7 @@ void LocaleICU::InitializeCalendar() { UCAL_FIRST_DAY_OF_WEEK) - UCAL_SUNDAY; - if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (features::IsFormControlsRefreshEnabled()) { week_day_short_labels_ = CreateLabelVector( short_date_format_, UDAT_NARROW_WEEKDAYS, UCAL_SUNDAY, 7); } else { diff --git a/chromium/third_party/blink/renderer/platform/text/locale_mac.mm b/chromium/third_party/blink/renderer/platform/text/locale_mac.mm index 5f721423e39..c72f103fa1a 100644 --- a/chromium/third_party/blink/renderer/platform/text/locale_mac.mm +++ b/chromium/third_party/blink/renderer/platform/text/locale_mac.mm @@ -41,6 +41,7 @@ #include "third_party/blink/renderer/platform/web_test_support.h" #include "third_party/blink/renderer/platform/wtf/date_math.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" +#include "ui/base/ui_base_features.h" namespace blink { @@ -136,7 +137,7 @@ const Vector<String>& LocaleMac::WeekDayShortLabels() { if (!week_day_short_labels_.IsEmpty()) return week_day_short_labels_; week_day_short_labels_.ReserveCapacity(7); - NSArray* array = RuntimeEnabledFeatures::FormControlsRefreshEnabled() + NSArray* array = features::IsFormControlsRefreshEnabled() ? [ShortDateFormatter() veryShortWeekdaySymbols] : [ShortDateFormatter() shortWeekdaySymbols]; if ([array count] == 7) { diff --git a/chromium/third_party/blink/renderer/platform/text/locale_mac_test.mm b/chromium/third_party/blink/renderer/platform/text/locale_mac_test.mm index 5daadce443d..588ebb56372 100644 --- a/chromium/third_party/blink/renderer/platform/text/locale_mac_test.mm +++ b/chromium/third_party/blink/renderer/platform/text/locale_mac_test.mm @@ -291,13 +291,13 @@ TEST_F(LocaleMacTest, monthLabels) { } TEST_F(LocaleMacTest, weekDayShortLabels) { - EXPECT_EQ("Sun", WeekDayShortLabel("en_US", kSunday)); - EXPECT_EQ("Wed", WeekDayShortLabel("en_US", kWednesday)); - EXPECT_EQ("Sat", WeekDayShortLabel("en_US", kSaturday)); + EXPECT_EQ("S", WeekDayShortLabel("en_US", kSunday)); + EXPECT_EQ("W", WeekDayShortLabel("en_US", kWednesday)); + EXPECT_EQ("S", WeekDayShortLabel("en_US", kSaturday)); - EXPECT_EQ("dim.", WeekDayShortLabel("fr_FR", kSunday)); - EXPECT_EQ("mer.", WeekDayShortLabel("fr_FR", kWednesday)); - EXPECT_EQ("sam.", WeekDayShortLabel("fr_FR", kSaturday)); + EXPECT_EQ("D", WeekDayShortLabel("fr_FR", kSunday)); + EXPECT_EQ("M", WeekDayShortLabel("fr_FR", kWednesday)); + EXPECT_EQ("S", WeekDayShortLabel("fr_FR", kSaturday)); EXPECT_EQ("\xE6\x97\xA5", WeekDayShortLabel("ja_JP", kSunday).Utf8()); EXPECT_EQ("\xE6\xB0\xB4", WeekDayShortLabel("ja_JP", kWednesday).Utf8()); diff --git a/chromium/third_party/blink/renderer/platform/text/locale_win.cc b/chromium/third_party/blink/renderer/platform/text/locale_win.cc index 2464478de20..3c86b59300d 100644 --- a/chromium/third_party/blink/renderer/platform/text/locale_win.cc +++ b/chromium/third_party/blink/renderer/platform/text/locale_win.cc @@ -45,6 +45,7 @@ #include "third_party/blink/renderer/platform/wtf/text/string_buffer.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/text/string_hash.h" +#include "ui/base/ui_base_features.h" namespace blink { @@ -304,7 +305,7 @@ void LocaleWin::EnsureWeekDayShortLabels() { LOCALE_SSHORTESTDAYNAME6}; week_day_short_labels_.ReserveCapacity(base::size(kTypes)); for (unsigned i = 0; i < base::size(kTypes); ++i) { - if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (features::IsFormControlsRefreshEnabled()) { week_day_short_labels_.push_back(GetLocaleInfoString(kTypesRefresh[i])); } else { week_day_short_labels_.push_back(GetLocaleInfoString(kTypes[i])); diff --git a/chromium/third_party/blink/renderer/platform/text/locale_win_test.cc b/chromium/third_party/blink/renderer/platform/text/locale_win_test.cc index d614168111c..2b132b226c7 100644 --- a/chromium/third_party/blink/renderer/platform/text/locale_win_test.cc +++ b/chromium/third_party/blink/renderer/platform/text/locale_win_test.cc @@ -35,6 +35,7 @@ #include "third_party/blink/renderer/platform/text/date_components.h" #include "third_party/blink/renderer/platform/wtf/date_math.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" +#include "ui/base/ui_base_features.h" namespace blink { @@ -188,14 +189,23 @@ TEST_F(LocaleWinTest, monthLabels) { } TEST_F(LocaleWinTest, weekDayShortLabels) { - EXPECT_EQ("Sun", WeekDayShortLabel(kEnglishUS, kSunday)); - EXPECT_EQ("Wed", WeekDayShortLabel(kEnglishUS, kWednesday)); - EXPECT_EQ("Sat", WeekDayShortLabel(kEnglishUS, kSaturday)); - - EXPECT_EQ("dim.", WeekDayShortLabel(kFrenchFR, kSunday)); - EXPECT_EQ("mer.", WeekDayShortLabel(kFrenchFR, kWednesday)); - EXPECT_EQ("sam.", WeekDayShortLabel(kFrenchFR, kSaturday)); - + if (features::IsFormControlsRefreshEnabled()) { + EXPECT_EQ("Su", WeekDayShortLabel(kEnglishUS, kSunday)); + EXPECT_EQ("We", WeekDayShortLabel(kEnglishUS, kWednesday)); + EXPECT_EQ("Sa", WeekDayShortLabel(kEnglishUS, kSaturday)); + + EXPECT_EQ("di", WeekDayShortLabel(kFrenchFR, kSunday)); + EXPECT_EQ("me", WeekDayShortLabel(kFrenchFR, kWednesday)); + EXPECT_EQ("sa", WeekDayShortLabel(kFrenchFR, kSaturday)); + } else { + EXPECT_EQ("Sun", WeekDayShortLabel(kEnglishUS, kSunday)); + EXPECT_EQ("Wed", WeekDayShortLabel(kEnglishUS, kWednesday)); + EXPECT_EQ("Sat", WeekDayShortLabel(kEnglishUS, kSaturday)); + + EXPECT_EQ("dim.", WeekDayShortLabel(kFrenchFR, kSunday)); + EXPECT_EQ("mer.", WeekDayShortLabel(kFrenchFR, kWednesday)); + EXPECT_EQ("sam.", WeekDayShortLabel(kFrenchFR, kSaturday)); + } EXPECT_EQ("\xE6\x97\xA5", WeekDayShortLabel(kJapaneseJP, kSunday).Utf8()); EXPECT_EQ("\xE6\xB0\xB4", WeekDayShortLabel(kJapaneseJP, kWednesday).Utf8()); EXPECT_EQ("\xE5\x9C\x9F", WeekDayShortLabel(kJapaneseJP, kSaturday).Utf8()); diff --git a/chromium/third_party/blink/renderer/platform/text/text_direction.h b/chromium/third_party/blink/renderer/platform/text/text_direction.h index a3f71b81b58..e92eed0ad8a 100644 --- a/chromium/third_party/blink/renderer/platform/text/text_direction.h +++ b/chromium/third_party/blink/renderer/platform/text/text_direction.h @@ -28,6 +28,8 @@ #include <cstdint> #include <iosfwd> +#include "base/i18n/rtl.h" +#include "base/logging.h" #include "third_party/blink/renderer/platform/platform_export.h" namespace blink { @@ -57,6 +59,17 @@ inline TextDirection DirectionFromLevel(unsigned level) { PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, TextDirection); +inline base::i18n::TextDirection ToBaseTextDirection(TextDirection direction) { + switch (direction) { + case TextDirection::kLtr: + return base::i18n::TextDirection::LEFT_TO_RIGHT; + case TextDirection::kRtl: + return base::i18n::TextDirection::RIGHT_TO_LEFT; + } + NOTREACHED(); + return base::i18n::TextDirection::UNKNOWN_DIRECTION; +} + } // namespace blink -#endif +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_TEXT_DIRECTION_H_ diff --git a/chromium/third_party/blink/renderer/platform/theme_types.h b/chromium/third_party/blink/renderer/platform/theme_types.h index 76f20d1e953..b5ed8c14e6e 100644 --- a/chromium/third_party/blink/renderer/platform/theme_types.h +++ b/chromium/third_party/blink/renderer/platform/theme_types.h @@ -45,8 +45,10 @@ enum ControlState { typedef unsigned ControlStates; // Must follow css_value_keywords.json5 order +// kAutoPart is never returned by ComputedStyle::EffectiveAppearance() enum ControlPart { kNoControlPart, + kAutoPart, kCheckboxPart, kRadioPart, kPushButtonPart, diff --git a/chromium/third_party/blink/renderer/platform/timer_test.cc b/chromium/third_party/blink/renderer/platform/timer_test.cc index 0511f0f9bda..54e9b1c6792 100644 --- a/chromium/third_party/blink/renderer/platform/timer_test.cc +++ b/chromium/third_party/blink/renderer/platform/timer_test.cc @@ -119,7 +119,7 @@ class OnHeapTimerOwner final : public GarbageCollected<OnHeapTimerOwner> { timer_.StartOneShot(interval, caller); } - void Trace(blink::Visitor* visitor) {} + void Trace(Visitor* visitor) {} private: void Fired(TimerBase*) { @@ -655,8 +655,8 @@ TEST_F(TimerTest, MarkOnHeapTimerAsUnreachable) { owner = nullptr; // Explicit regular GC call to allow lazy sweeping. ThreadState::Current()->CollectGarbage( - BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking, - BlinkGC::kConcurrentAndLazySweeping, + BlinkGC::CollectionType::kMajor, BlinkGC::kNoHeapPointersOnStack, + BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping, BlinkGC::GCReason::kForcedGCForTesting); // Since the heap is laziy swept, owner is not yet destructed. EXPECT_FALSE(record->OwnerIsDestructed()); @@ -667,6 +667,7 @@ TEST_F(TimerTest, MarkOnHeapTimerAsUnreachable) { platform_->RunUntilIdle(); EXPECT_FALSE(record->TimerHasFired()); EXPECT_FALSE(record->OwnerIsDestructed()); + ThreadState::Current()->CompleteSweep(); } } diff --git a/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc index 1ee49fe627f..2d8da85b813 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc +++ b/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.cc @@ -35,7 +35,7 @@ namespace blink { scoped_refptr<TransformOperation> Matrix3DTransformOperation::Accumulate( const TransformOperation& other_op) { DCHECK(other_op.IsSameType(*this)); - const auto& other = ToMatrix3DTransformOperation(other_op); + const auto& other = To<Matrix3DTransformOperation>(other_op); // If either matrix is non-invertible, fail and fallback to replace. if (!matrix_.IsInvertible() || !other.matrix_.IsInvertible()) diff --git a/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h index 9c61b1265e2..81d3fd814f5 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h +++ b/chromium/third_party/blink/renderer/platform/transforms/matrix_3d_transform_operation.h @@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_MATRIX_3D_TRANSFORM_OPERATION_H_ #include "third_party/blink/renderer/platform/transforms/transform_operation.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -81,7 +82,13 @@ class PLATFORM_EXPORT Matrix3DTransformOperation final TransformationMatrix matrix_; }; -DEFINE_TRANSFORM_TYPE_CASTS(Matrix3DTransformOperation); +template <> +struct DowncastTraits<Matrix3DTransformOperation> { + static bool AllowFrom(const TransformOperation& transform) { + return Matrix3DTransformOperation::IsMatchingOperationType( + transform.GetType()); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc index 7ac6a31d386..ba42dc2947a 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc +++ b/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.cc @@ -28,7 +28,7 @@ namespace blink { scoped_refptr<TransformOperation> MatrixTransformOperation::Accumulate( const TransformOperation& other_op) { DCHECK(other_op.IsSameType(*this)); - const MatrixTransformOperation& other = ToMatrixTransformOperation(other_op); + const auto& other = To<MatrixTransformOperation>(other_op); TransformationMatrix from_t(other.a_, other.b_, other.c_, other.d_, other.e_, other.f_); diff --git a/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h index bb943c2a603..d601a30c3fd 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h +++ b/chromium/third_party/blink/renderer/platform/transforms/matrix_transform_operation.h @@ -27,6 +27,7 @@ #include "third_party/blink/renderer/platform/transforms/transform_operation.h" #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -109,7 +110,13 @@ class PLATFORM_EXPORT MatrixTransformOperation final double f_; }; -DEFINE_TRANSFORM_TYPE_CASTS(MatrixTransformOperation); +template <> +struct DowncastTraits<MatrixTransformOperation> { + static bool AllowFrom(const TransformOperation& transform) { + return MatrixTransformOperation::IsMatchingOperationType( + transform.GetType()); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc index 7a491ab9d84..83fe6f2b0a0 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc +++ b/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.cc @@ -33,7 +33,7 @@ namespace blink { scoped_refptr<TransformOperation> PerspectiveTransformOperation::Accumulate( const TransformOperation& other) { DCHECK(other.IsSameType(*this)); - double other_p = ToPerspectiveTransformOperation(other).p_; + double other_p = To<PerspectiveTransformOperation>(other).p_; if (p_ == 0 && other_p == 0) return nullptr; diff --git a/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h index 24628650440..e89ec30853f 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h +++ b/chromium/third_party/blink/renderer/platform/transforms/perspective_transform_operation.h @@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_PERSPECTIVE_TRANSFORM_OPERATION_H_ #include "third_party/blink/renderer/platform/transforms/transform_operation.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -78,7 +79,13 @@ class PLATFORM_EXPORT PerspectiveTransformOperation final double p_; }; -DEFINE_TRANSFORM_TYPE_CASTS(PerspectiveTransformOperation); +template <> +struct DowncastTraits<PerspectiveTransformOperation> { + static bool AllowFrom(const TransformOperation& transform) { + return PerspectiveTransformOperation::IsMatchingOperationType( + transform.GetType()); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc index 4910951d0e3..648e24665ae 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc +++ b/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc @@ -43,7 +43,7 @@ bool RotateTransformOperation::operator==( const TransformOperation& other) const { if (!IsSameType(other)) return false; - const Rotation& other_rotation = ToRotateTransformOperation(other).rotation_; + const auto& other_rotation = To<RotateTransformOperation>(other).rotation_; return rotation_.axis == other_rotation.axis && rotation_.angle == other_rotation.angle; } @@ -62,7 +62,7 @@ scoped_refptr<TransformOperation> RotateTransformOperation::Accumulate( const TransformOperation& other) { DCHECK(IsMatchingOperationType(other.GetType())); Rotation new_rotation = - Rotation::Add(rotation_, ToRotateTransformOperation(other).rotation_); + Rotation::Add(rotation_, To<RotateTransformOperation>(other).rotation_); return RotateTransformOperation::Create(new_rotation, GetTypeForRotation(new_rotation)); } @@ -91,8 +91,7 @@ scoped_refptr<TransformOperation> RotateTransformOperation::Blend( DCHECK(from->PrimitiveType() == OperationType::kRotate3D); OperationType type = from->IsSameType(*this) ? type_ : OperationType::kRotate3D; - const RotateTransformOperation& from_rotate = - ToRotateTransformOperation(*from); + const auto& from_rotate = To<RotateTransformOperation>(*from); return RotateTransformOperation::Create( Rotation::Slerp(from_rotate.rotation_, rotation_, progress), type); } @@ -123,8 +122,7 @@ bool RotateAroundOriginTransformOperation::operator==( const TransformOperation& other) const { if (!IsSameType(other)) return false; - const RotateAroundOriginTransformOperation& other_rotate = - ToRotateAroundOriginTransformOperation(other); + const auto& other_rotate = To<RotateAroundOriginTransformOperation>(other); const Rotation& other_rotation = other_rotate.rotation_; return rotation_.axis == other_rotation.axis && rotation_.angle == other_rotation.angle && @@ -146,8 +144,7 @@ scoped_refptr<TransformOperation> RotateAroundOriginTransformOperation::Blend( return RotateAroundOriginTransformOperation::Create(Angle() * progress, origin_x_, origin_y_); } - const RotateAroundOriginTransformOperation& from_rotate = - ToRotateAroundOriginTransformOperation(*from); + const auto& from_rotate = To<RotateAroundOriginTransformOperation>(*from); return RotateAroundOriginTransformOperation::Create( blink::Blend(from_rotate.Angle(), Angle(), progress), blink::Blend(from_rotate.origin_x_, origin_x_, progress), diff --git a/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h index d5f7bdd8d1e..b0e054ca1fe 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h +++ b/chromium/third_party/blink/renderer/platform/transforms/rotate_transform_operation.h @@ -28,6 +28,7 @@ #include "third_party/blink/renderer/platform/geometry/float_point_3d.h" #include "third_party/blink/renderer/platform/transforms/rotation.h" #include "third_party/blink/renderer/platform/transforms/transform_operation.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -107,7 +108,13 @@ class PLATFORM_EXPORT RotateTransformOperation : public TransformOperation { const OperationType type_; }; -DEFINE_TRANSFORM_TYPE_CASTS(RotateTransformOperation); +template <> +struct DowncastTraits<RotateTransformOperation> { + static bool AllowFrom(const TransformOperation& transform) { + return RotateTransformOperation::IsMatchingOperationType( + transform.GetType()); + } +}; class PLATFORM_EXPORT RotateAroundOriginTransformOperation final : public RotateTransformOperation { @@ -141,7 +148,13 @@ class PLATFORM_EXPORT RotateAroundOriginTransformOperation final double origin_y_; }; -DEFINE_TRANSFORM_TYPE_CASTS(RotateAroundOriginTransformOperation); +template <> +struct DowncastTraits<RotateAroundOriginTransformOperation> { + static bool AllowFrom(const TransformOperation& transform) { + return RotateAroundOriginTransformOperation::IsMatchingOperationType( + transform.GetType()); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc index 0f326e4ce8a..b6fb9a86cd5 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc +++ b/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.cc @@ -49,7 +49,7 @@ TransformOperation::OperationType GetTypeForScale(double x, scoped_refptr<TransformOperation> ScaleTransformOperation::Accumulate( const TransformOperation& other) { DCHECK(other.CanBlendWith(*this)); - const auto& other_op = ToScaleTransformOperation(other); + const auto& other_op = To<ScaleTransformOperation>(other); // Scale parameters are one in the identity transform function so use // accumulation for one-based values. double new_x = x_ + other_op.x_ - 1; @@ -76,9 +76,11 @@ scoped_refptr<TransformOperation> ScaleTransformOperation::Blend( double from_x = from_op ? from_op->x_ : 1.0; double from_y = from_op ? from_op->y_ : 1.0; double from_z = from_op ? from_op->z_ : 1.0; + + bool is_3d = Is3DOperation() || (from && from->Is3DOperation()); return ScaleTransformOperation::Create( blink::Blend(from_x, x_, progress), blink::Blend(from_y, y_, progress), - blink::Blend(from_z, z_, progress), type_); + blink::Blend(from_z, z_, progress), is_3d ? kScale3D : kScale); } bool ScaleTransformOperation::CanBlendWith( diff --git a/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.h index 874e3efd558..39c678804c4 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.h +++ b/chromium/third_party/blink/renderer/platform/transforms/scale_transform_operation.h @@ -26,6 +26,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_SCALE_TRANSFORM_OPERATION_H_ #include "third_party/blink/renderer/platform/transforms/transform_operation.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -99,7 +100,13 @@ class PLATFORM_EXPORT ScaleTransformOperation final OperationType type_; }; -DEFINE_TRANSFORM_TYPE_CASTS(ScaleTransformOperation); +template <> +struct DowncastTraits<ScaleTransformOperation> { + static bool AllowFrom(const TransformOperation& transform) { + return ScaleTransformOperation::IsMatchingOperationType( + transform.GetType()); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc index 15eeabb0ed9..30c27055dfd 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc +++ b/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.cc @@ -28,7 +28,7 @@ namespace blink { scoped_refptr<TransformOperation> SkewTransformOperation::Accumulate( const TransformOperation& other) { DCHECK(other.CanBlendWith(*this)); - const SkewTransformOperation& skew_other = ToSkewTransformOperation(other); + const auto& skew_other = To<SkewTransformOperation>(other); return SkewTransformOperation::Create(angle_x_ + skew_other.angle_x_, angle_y_ + skew_other.angle_y_, type_); } diff --git a/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.h index 368ddfcb371..ab38b71f6ef 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.h +++ b/chromium/third_party/blink/renderer/platform/transforms/skew_transform_operation.h @@ -27,6 +27,7 @@ #include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/platform/transforms/transform_operation.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -78,7 +79,12 @@ class PLATFORM_EXPORT SkewTransformOperation final : public TransformOperation { OperationType type_; }; -DEFINE_TRANSFORM_TYPE_CASTS(SkewTransformOperation); +template <> +struct DowncastTraits<SkewTransformOperation> { + static bool AllowFrom(const TransformOperation& transform) { + return SkewTransformOperation::IsMatchingOperationType(transform.GetType()); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/transforms/transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/transform_operation.h index 6ccfd1d7c04..9ab8a4cd9bf 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/transform_operation.h +++ b/chromium/third_party/blink/renderer/platform/transforms/transform_operation.h @@ -114,11 +114,6 @@ class PLATFORM_EXPORT TransformOperation DISALLOW_COPY_AND_ASSIGN(TransformOperation); }; -#define DEFINE_TRANSFORM_TYPE_CASTS(thisType) \ - DEFINE_TYPE_CASTS(thisType, TransformOperation, transform, \ - thisType::IsMatchingOperationType(transform->GetType()), \ - thisType::IsMatchingOperationType(transform.GetType())) - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_TRANSFORMS_TRANSFORM_OPERATION_H_ diff --git a/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc b/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc index 86fc8ff89ec..43dfa1f4ef9 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc +++ b/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.cc @@ -46,15 +46,15 @@ TransformOperation::OperationType GetTypeForTranslate(const Length& x, bool x_zero = x.IsZero(); bool y_zero = x.IsZero(); bool z_zero = !z; - if (!x_zero && !y_zero && !z_zero) - return TransformOperation::kTranslate3D; if (y_zero && z_zero) return TransformOperation::kTranslateX; if (x_zero && z_zero) return TransformOperation::kTranslateY; if (x_zero && y_zero) return TransformOperation::kTranslateZ; - return TransformOperation::kTranslate; + if (z_zero) + return TransformOperation::kTranslate; + return TransformOperation::kTranslate3D; } } // namespace @@ -62,7 +62,7 @@ scoped_refptr<TransformOperation> TranslateTransformOperation::Accumulate( const TransformOperation& other) { DCHECK(other.CanBlendWith(*this)); - const auto& other_op = ToTranslateTransformOperation(other); + const auto& other_op = To<TranslateTransformOperation>(other); Length new_x = AddLengths(x_, other_op.x_); Length new_y = AddLengths(y_, other_op.y_); double new_z = z_ + other_op.z_; @@ -85,14 +85,16 @@ scoped_refptr<TransformOperation> TranslateTransformOperation::Blend( blink::Blend(z_, 0., progress), type_); } - const auto* from_op = ToTranslateTransformOperation(from); + const auto* from_op = To<TranslateTransformOperation>(from); const Length& from_x = from_op ? from_op->x_ : zero_length; const Length& from_y = from_op ? from_op->y_ : zero_length; double from_z = from_op ? from_op->z_ : 0; + + bool is_3d = Is3DOperation() || (from && from->Is3DOperation()); return TranslateTransformOperation::Create( x_.Blend(from_x, progress, kValueRangeAll), y_.Blend(from_y, progress, kValueRangeAll), - blink::Blend(from_z, z_, progress), type_); + blink::Blend(from_z, z_, progress), is_3d ? kTranslate3D : kTranslate); } bool TranslateTransformOperation::CanBlendWith( diff --git a/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.h b/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.h index b73cb80331e..6cdf6d9d37b 100644 --- a/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.h +++ b/chromium/third_party/blink/renderer/platform/transforms/translate_transform_operation.h @@ -28,6 +28,7 @@ #include "third_party/blink/renderer/platform/geometry/length.h" #include "third_party/blink/renderer/platform/geometry/length_functions.h" #include "third_party/blink/renderer/platform/transforms/transform_operation.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -119,7 +120,13 @@ class PLATFORM_EXPORT TranslateTransformOperation final OperationType type_; }; -DEFINE_TRANSFORM_TYPE_CASTS(TranslateTransformOperation); +template <> +struct DowncastTraits<TranslateTransformOperation> { + static bool AllowFrom(const TransformOperation& transform) { + return TranslateTransformOperation::IsMatchingOperationType( + transform.GetType()); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc b/chromium/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc index e88c93054d6..4be9b4f4f6a 100644 --- a/chromium/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc +++ b/chromium/third_party/blink/renderer/platform/video_capture/video_capture_impl.cc @@ -17,6 +17,7 @@ #include <utility> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/macros.h" #include "base/stl_util.h" #include "base/trace_event/trace_event.h" @@ -553,17 +554,16 @@ void VideoCaptureImpl::OnBufferReady( uint8_t* u_data = y_data + (media::VideoFrame::Rows(media::VideoFrame::kYPlane, info->pixel_format, - info->coded_size.height) * + info->coded_size.height()) * info->strides->stride_by_plane[0]); uint8_t* v_data = u_data + (media::VideoFrame::Rows(media::VideoFrame::kUPlane, info->pixel_format, - info->coded_size.height) * + info->coded_size.height()) * info->strides->stride_by_plane[1]); frame = media::VideoFrame::WrapExternalYuvData( info->pixel_format, gfx::Size(info->coded_size), - gfx::Rect(info->visible_rect), - gfx::Size(info->visible_rect.width, info->visible_rect.height), + gfx::Rect(info->visible_rect), info->visible_rect.size(), info->strides->stride_by_plane[0], info->strides->stride_by_plane[1], info->strides->stride_by_plane[2], y_data, u_data, v_data, @@ -571,8 +571,7 @@ void VideoCaptureImpl::OnBufferReady( } else { frame = media::VideoFrame::WrapExternalData( info->pixel_format, gfx::Size(info->coded_size), - gfx::Rect(info->visible_rect), - gfx::Size(info->visible_rect.width, info->visible_rect.height), + gfx::Rect(info->visible_rect), info->visible_rect.size(), const_cast<uint8_t*>(buffer_context->data()), buffer_context->data_size(), info->timestamp); } @@ -582,8 +581,7 @@ void VideoCaptureImpl::OnBufferReady( // the data without attaching the shared region to the frame. frame = media::VideoFrame::WrapExternalData( info->pixel_format, gfx::Size(info->coded_size), - gfx::Rect(info->visible_rect), - gfx::Size(info->visible_rect.width, info->visible_rect.height), + gfx::Rect(info->visible_rect), info->visible_rect.size(), const_cast<uint8_t*>(buffer_context->data()), buffer_context->data_size(), info->timestamp); break; @@ -600,8 +598,7 @@ void VideoCaptureImpl::OnBufferReady( frame = media::VideoFrame::WrapNativeTextures( info->pixel_format, mailbox_holder_array, media::VideoFrame::ReleaseMailboxCB(), gfx::Size(info->coded_size), - gfx::Rect(info->visible_rect), - gfx::Size(info->visible_rect.width, info->visible_rect.height), + gfx::Rect(info->visible_rect), info->visible_rect.size(), info->timestamp); break; } @@ -641,8 +638,7 @@ void VideoCaptureImpl::OnBufferReady( FROM_HERE, base::BindOnce( &BufferContext::BindBufferToTextureOnMediaThread, - std::move(buffer_context), base::Passed(&info), - base::Passed(&gmb), frame, + std::move(buffer_context), std::move(info), std::move(gmb), frame, media::BindToCurrentLoop(base::BindOnce( &VideoCaptureImpl::OnVideoFrameReady, weak_factory_.GetWeakPtr(), buffer_id, reference_time)))); diff --git a/chromium/third_party/blink/renderer/platform/video_capture/video_capture_impl_test.cc b/chromium/third_party/blink/renderer/platform/video_capture/video_capture_impl_test.cc index 2a1682941ce..650f0da130d 100644 --- a/chromium/third_party/blink/renderer/platform/video_capture/video_capture_impl_test.cc +++ b/chromium/third_party/blink/renderer/platform/video_capture/video_capture_impl_test.cc @@ -148,10 +148,10 @@ class VideoCaptureImplTest : public ::testing::Test { void(const Vector<media::VideoCaptureFormat>&)); void StartCapture(int client_id, const media::VideoCaptureParams& params) { - const auto state_update_callback = base::Bind( + const auto state_update_callback = WTF::BindRepeating( &VideoCaptureImplTest::OnStateUpdate, base::Unretained(this)); - const auto frame_ready_callback = - base::Bind(&VideoCaptureImplTest::OnFrameReady, base::Unretained(this)); + const auto frame_ready_callback = WTF::BindRepeating( + &VideoCaptureImplTest::OnFrameReady, base::Unretained(this)); video_capture_impl_->StartCapture(client_id, params, state_update_callback, frame_ready_callback); @@ -199,8 +199,8 @@ class VideoCaptureImplTest : public ::testing::Test { info->timestamp = now - base::TimeTicks(); info->pixel_format = pixel_format; - info->coded_size = WebSize(size); - info->visible_rect = WebRect(gfx::Rect(size)); + info->coded_size = size; + info->visible_rect = gfx::Rect(size); info->color_space = gfx::ColorSpace(); video_capture_impl_->OnBufferReady(buffer_id, std::move(info)); @@ -211,17 +211,14 @@ class VideoCaptureImplTest : public ::testing::Test { } void GetDeviceSupportedFormats() { - const base::Callback<void(const Vector<media::VideoCaptureFormat>&)> - callback = base::Bind(&VideoCaptureImplTest::OnDeviceSupportedFormats, - base::Unretained(this)); - video_capture_impl_->GetDeviceSupportedFormats(callback); + video_capture_impl_->GetDeviceSupportedFormats( + WTF::Bind(&VideoCaptureImplTest::OnDeviceSupportedFormats, + base::Unretained(this))); } void GetDeviceFormatsInUse() { - const base::Callback<void(const Vector<media::VideoCaptureFormat>&)> - callback = base::Bind(&VideoCaptureImplTest::OnDeviceFormatsInUse, - base::Unretained(this)); - video_capture_impl_->GetDeviceFormatsInUse(callback); + video_capture_impl_->GetDeviceFormatsInUse(WTF::Bind( + &VideoCaptureImplTest::OnDeviceFormatsInUse, base::Unretained(this))); } void OnStateChanged(media::mojom::VideoCaptureState state) { diff --git a/chromium/third_party/blink/renderer/platform/weborigin/DEPS b/chromium/third_party/blink/renderer/platform/weborigin/DEPS index 0d5faf41ba0..ee4bd6ccd3e 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/DEPS +++ b/chromium/third_party/blink/renderer/platform/weborigin/DEPS @@ -18,3 +18,7 @@ include_rules = [ "+third_party/blink/renderer/platform/testing", "+third_party/blink/renderer/platform/wtf", ] + +specific_include_rules = { + "kurl_test.cc": [ "+third_party/blink/renderer/platform/scheduler/public/thread.h" ], +}
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc b/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc index 4ab44fc6c7d..ce96d1bef63 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc +++ b/chromium/third_party/blink/renderer/platform/weborigin/kurl.cc @@ -36,6 +36,7 @@ #include "third_party/blink/renderer/platform/wtf/text/string_statics.h" #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h" #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h" +#include "third_party/blink/renderer/platform/wtf/thread_specific.h" #include "url/gurl.h" #include "url/url_util.h" #ifndef NDEBUG @@ -125,12 +126,6 @@ bool IsValidProtocol(const String& protocol) { return true; } -void KURL::Initialize() { - // This must be called before we create other threads to - // avoid racy static local initialization. - BlankURL(); -} - String KURL::StrippedForUseAsReferrer() const { if (!ProtocolIsInHTTPFamily()) return String(); @@ -169,8 +164,11 @@ bool ProtocolIsJavaScript(const String& url) { } const KURL& BlankURL() { - DEFINE_STATIC_LOCAL(KURL, static_blank_url, ("about:blank")); - return static_blank_url; + DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<KURL>, static_blank_url, ()); + KURL& blank_url = *static_blank_url; + if (blank_url.IsNull()) + blank_url = KURL(AtomicString("about:blank")); + return blank_url; } bool KURL::IsAboutBlankURL() const { @@ -178,8 +176,11 @@ bool KURL::IsAboutBlankURL() const { } const KURL& SrcdocURL() { - DEFINE_STATIC_LOCAL(KURL, static_srcdoc_url, ("about:srcdoc")); - return static_srcdoc_url; + DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<KURL>, static_srcdoc_url, ()); + KURL& srcdoc_url = *static_srcdoc_url; + if (srcdoc_url.IsNull()) + srcdoc_url = KURL(AtomicString("about:srcdoc")); + return srcdoc_url; } bool KURL::IsAboutSrcdocURL() const { @@ -187,8 +188,8 @@ bool KURL::IsAboutSrcdocURL() const { } const KURL& NullURL() { - DEFINE_THREAD_SAFE_STATIC_LOCAL(KURL, static_null_url, ()); - return static_null_url; + DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<KURL>, static_null_url, ()); + return *static_null_url; } String KURL::ElidedString() const { diff --git a/chromium/third_party/blink/renderer/platform/weborigin/kurl.h b/chromium/third_party/blink/renderer/platform/weborigin/kurl.h index 6500b29d892..8ddb31a682b 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/kurl.h +++ b/chromium/third_party/blink/renderer/platform/weborigin/kurl.h @@ -77,10 +77,6 @@ class PLATFORM_EXPORT KURL { USING_FAST_MALLOC(KURL); public: - // This must be called during initialization (before we create - // other threads). - static void Initialize(); - KURL(); KURL(const KURL&); diff --git a/chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc b/chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc index 32b3ae8a580..94a471da823 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc +++ b/chromium/third_party/blink/renderer/platform/weborigin/kurl_test.cc @@ -37,6 +37,7 @@ #include "base/stl_util.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "url/url_util.h" @@ -873,6 +874,43 @@ TEST(KURLTest, strippedForUseAsReferrer) { } } +TEST(KURLTest, ThreadSafesStaticKurlGetters) { +#if DCHECK_IS_ON() + // Simulate the static getters being called during/after threads have been + // started, so that StaticSingleton's thread checks will be applied. + WTF::WillCreateThread(); +#endif + + // Take references to the static KURLs, so that each has two references to + // its internal StringImpl, rather than one. + KURL blank_url = BlankURL(); + EXPECT_FALSE(blank_url.IsEmpty()); + KURL srcdoc_url = SrcdocURL(); + EXPECT_FALSE(srcdoc_url.IsEmpty()); + KURL null_url = NullURL(); + EXPECT_TRUE(null_url.IsNull()); + + auto thread = + Thread::CreateThread(ThreadCreationParams(ThreadType::kTestThread)); + thread->GetTaskRunner()->PostTask(FROM_HERE, base::BindOnce([]() { + // Reference each of the static KURLs + // again, from the background thread, + // which should succeed without thread + // verifier checks firing. + KURL blank_url = BlankURL(); + EXPECT_FALSE(blank_url.IsEmpty()); + KURL srcdoc_url = SrcdocURL(); + EXPECT_FALSE(srcdoc_url.IsEmpty()); + KURL null_url = NullURL(); + EXPECT_TRUE(null_url.IsNull()); + })); + +#if DCHECK_IS_ON() + // Restore the IsBeforeThreadCreated() flag. + WTF::SetIsBeforeThreadCreatedForTest(); +#endif +} + enum class PortIsValid { // The constructor does strict checking. Ports which are considered valid by // the constructor are kAlways valid. diff --git a/chromium/third_party/blink/renderer/platform/weborigin/reporting_disposition.h b/chromium/third_party/blink/renderer/platform/weborigin/reporting_disposition.h new file mode 100644 index 00000000000..54060e075c4 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/weborigin/reporting_disposition.h @@ -0,0 +1,17 @@ +// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REPORTING_DISPOSITION_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REPORTING_DISPOSITION_H_ + +namespace blink { + +enum class ReportingDisposition { + kSuppressReporting, + kReport, +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_REPORTING_DISPOSITION_H_ diff --git a/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.cc b/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.cc index 3903a94f5df..5e53886a855 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.cc +++ b/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.cc @@ -112,12 +112,6 @@ URLSchemesRegistry& GetMutableURLSchemesRegistry() { } // namespace -// Must be called before we create other threads to avoid racy static local -// initialization. -void SchemeRegistry::Initialize() { - GetURLSchemesRegistry(); -} - void SchemeRegistry::RegisterURLSchemeAsLocal(const String& scheme) { DCHECK_EQ(scheme, scheme.LowerASCII()); GetMutableURLSchemesRegistry().local_schemes.insert(scheme); diff --git a/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.h b/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.h index 4ef1bdfd07f..3a6f7734f19 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.h +++ b/chromium/third_party/blink/renderer/platform/weborigin/scheme_registry.h @@ -49,8 +49,6 @@ class PLATFORM_EXPORT SchemeRegistry { STATIC_ONLY(SchemeRegistry); public: - static void Initialize(); - static void RegisterURLSchemeAsLocal(const String&); static bool ShouldTreatURLSchemeAsLocal(const String&); diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_origin.cc b/chromium/third_party/blink/renderer/platform/weborigin/security_origin.cc index e71853c7d3c..c448d326226 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/security_origin.cc +++ b/chromium/third_party/blink/renderer/platform/weborigin/security_origin.cc @@ -237,7 +237,7 @@ scoped_refptr<SecurityOrigin> SecurityOrigin::CreateFromUrlOrigin( DCHECK(String::FromUTF8(tuple.host()).ContainsOnlyASCIIOrEmpty()); scoped_refptr<SecurityOrigin> tuple_origin; - if (!tuple.IsInvalid()) { + if (tuple.IsValid()) { String scheme = String::FromUTF8(tuple.scheme()); String host = String::FromUTF8(tuple.host()); uint16_t port = tuple.port(); @@ -327,58 +327,7 @@ bool SecurityOrigin::CanAccess(const SecurityOrigin* other, return true; } - // This is needed to ensure an origin can access to itself under nullified - // document.domain. - // TODO(tzik): Update the nulled domain handling and remove this condition. - if (this == other) { - detail = AccessResultDomainDetail::kDomainNotRelevant; - return true; - } - - if (IsOpaque() || other->IsOpaque()) { - detail = AccessResultDomainDetail::kDomainNotRelevant; - return nonce_if_opaque_ == other->nonce_if_opaque_; - } - - // document.domain handling, as per - // https://html.spec.whatwg.org/C/#dom-document-domain: - // - // 1) Neither document has set document.domain. In this case, we insist - // that the scheme, host, and port of the URLs match. - // - // 2) Both documents have set document.domain. In this case, we insist - // that the documents have set document.domain to the same value and - // that the scheme of the URLs match. Ports do not need to match. - bool can_access = false; - if (protocol_ == other->protocol_) { - if (!domain_was_set_in_dom_ && !other->domain_was_set_in_dom_) { - detail = AccessResultDomainDetail::kDomainNotSet; - if (host_ == other->host_ && port_ == other->port_) - can_access = true; - } else if (domain_was_set_in_dom_ && other->domain_was_set_in_dom_) { - if (domain_ == other->domain_) { - can_access = true; - detail = (host_ == other->host_ && port_ == other->port_) - ? AccessResultDomainDetail::kDomainMatchUnnecessary - : AccessResultDomainDetail::kDomainMatchNecessary; - } else { - detail = (host_ == other->host_ && port_ == other->port_) - ? AccessResultDomainDetail::kDomainMismatch - : AccessResultDomainDetail::kDomainNotRelevant; - } - } else { - detail = (host_ == other->host_ && port_ == other->port_) - ? AccessResultDomainDetail::kDomainSetByOnlyOneOrigin - : AccessResultDomainDetail::kDomainNotRelevant; - } - } else { - detail = AccessResultDomainDetail::kDomainNotRelevant; - } - - if (can_access && IsLocal() && !PassesFileCheck(other)) { - detail = AccessResultDomainDetail::kDomainNotRelevant; - can_access = false; - } + bool can_access = IsSameOriginDomainWith(other, detail); // Compare that the clusters are the same. if (can_access && !cross_agent_cluster_access_ && @@ -629,6 +578,65 @@ bool SecurityOrigin::AreSameOrigin(const KURL& a, const KURL& b) { return origin_b->IsSameOriginWith(origin_a.get()); } +bool SecurityOrigin::IsSameOriginDomainWith( + const SecurityOrigin* other, + AccessResultDomainDetail& detail) const { + // This is needed to ensure an origin can access to itself under nullified + // document.domain. + // TODO(tzik): Update the nulled domain handling and remove this condition. + if (this == other) { + detail = AccessResultDomainDetail::kDomainNotRelevant; + return true; + } + + if (IsOpaque() || other->IsOpaque()) { + detail = AccessResultDomainDetail::kDomainNotRelevant; + return nonce_if_opaque_ == other->nonce_if_opaque_; + } + + // document.domain handling, as per + // https://html.spec.whatwg.org/C/#dom-document-domain: + // + // 1) Neither document has set document.domain. In this case, we insist + // that the scheme, host, and port of the URLs match. + // + // 2) Both documents have set document.domain. In this case, we insist + // that the documents have set document.domain to the same value and + // that the scheme of the URLs match. Ports do not need to match. + bool can_access = false; + if (protocol_ == other->protocol_) { + if (!domain_was_set_in_dom_ && !other->domain_was_set_in_dom_) { + detail = AccessResultDomainDetail::kDomainNotSet; + if (host_ == other->host_ && port_ == other->port_) + can_access = true; + } else if (domain_was_set_in_dom_ && other->domain_was_set_in_dom_) { + if (domain_ == other->domain_) { + can_access = true; + detail = (host_ == other->host_ && port_ == other->port_) + ? AccessResultDomainDetail::kDomainMatchUnnecessary + : AccessResultDomainDetail::kDomainMatchNecessary; + } else { + detail = (host_ == other->host_ && port_ == other->port_) + ? AccessResultDomainDetail::kDomainMismatch + : AccessResultDomainDetail::kDomainNotRelevant; + } + } else { + detail = (host_ == other->host_ && port_ == other->port_) + ? AccessResultDomainDetail::kDomainSetByOnlyOneOrigin + : AccessResultDomainDetail::kDomainNotRelevant; + } + } else { + detail = AccessResultDomainDetail::kDomainNotRelevant; + } + + if (can_access && IsLocal() && !PassesFileCheck(other)) { + detail = AccessResultDomainDetail::kDomainNotRelevant; + can_access = false; + } + + return can_access; +} + const KURL& SecurityOrigin::UrlWithUniqueOpaqueOrigin() { DCHECK(IsMainThread()); DEFINE_STATIC_LOCAL(const KURL, url, ("data:,")); diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_origin.h b/chromium/third_party/blink/renderer/platform/weborigin/security_origin.h index 7a02c33ca25..f2ee4da33cf 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/security_origin.h +++ b/chromium/third_party/blink/renderer/platform/weborigin/security_origin.h @@ -139,19 +139,22 @@ class PLATFORM_EXPORT SecurityOrigin : public RefCounted<SecurityOrigin> { static bool IsSecure(const KURL&); // Returns true if this SecurityOrigin can script objects in the given - // SecurityOrigin. For example, call this function before allowing - // script from one security origin to read or write objects from - // another SecurityOrigin. + // SecurityOrigin. This check is similar to `IsSameOriginDomainWith()`, but + // additionally takes "universal access" flag into account, as well as the + // origin's agent cluster (see https://tc39.es/ecma262/#sec-agent-clusters). + // + // Note: This kind of access check should be rare; `IsSameOriginWith()` is + // almost certainly the right choice for new security checks. + // + // TODO(1027191): We're currently calling this method in a number of places + // where either `IsSameOriginWith()` or `IsSameOriginDomainWith()` might + // be more appropriate. We should audit its existing usage, and it might + // make sense to move it out of SecurityOrigin entirely to align it more + // tightly with `BindingSecurity` where it's clearly necessary. bool CanAccess(const SecurityOrigin* other) const { AccessResultDomainDetail unused_detail; return CanAccess(other, unused_detail); } - - // Returns true if this SecurityOrigin can script objects in |other|, just - // as above, but also returns the category into which the access check fell. - // - // TODO(crbug.com/787905): Remove this variant once we have enough data to - // make decisions about `document.domain`. bool CanAccess(const SecurityOrigin* other, AccessResultDomainDetail&) const; // Returns true if this SecurityOrigin can read content retrieved from @@ -294,6 +297,25 @@ class PLATFORM_EXPORT SecurityOrigin : public RefCounted<SecurityOrigin> { bool IsSameOriginWith(const SecurityOrigin*) const; static bool AreSameOrigin(const KURL& a, const KURL& b); + // This method implements HTML's "same origin-domain" check, which takes + // `document.domain` into account when comparing two origins. + // + // This method does not take the "universal access" flag into account. It does + // take the "local access" flag into account, considering `file:` origins that + // set the flag to be same origin-domain with all other `file:` origins that + // set the flag (assuming no `document.domain` mismatch). + // + // Note: Same origin-domain checks should be rare, and `IsSameOriginWith()` + // is almost certainly the right choice for new security checks. + // + // https://html.spec.whatwg.org/#same-origin-domain + bool IsSameOriginDomainWith(const SecurityOrigin* other) const { + AccessResultDomainDetail unused_detail; + return IsSameOriginDomainWith(other, unused_detail); + } + bool IsSameOriginDomainWith(const SecurityOrigin*, + AccessResultDomainDetail&) const; + static const KURL& UrlWithUniqueOpaqueOrigin(); // Transfer origin privileges from another security origin. diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/chromium/third_party/blink/renderer/platform/weborigin/security_origin_test.cc index 3c10ca1288f..879eef2c67e 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/security_origin_test.cc +++ b/chromium/third_party/blink/renderer/platform/weborigin/security_origin_test.cc @@ -662,6 +662,7 @@ TEST_F(SecurityOriginTest, CanonicalizeHost) { } TEST_F(SecurityOriginTest, UrlOriginConversions) { + url::ScopedSchemeRegistryForTests scoped_registry; url::AddLocalScheme("nonstandard-but-local"); SchemeRegistry::RegisterURLSchemeAsLocal("nonstandard-but-local"); struct TestCases { @@ -884,6 +885,7 @@ TEST_F(SecurityOriginTest, NonStandardScheme) { } TEST_F(SecurityOriginTest, NonStandardSchemeWithAndroidWebViewHack) { + url::ScopedSchemeRegistryForTests scoped_registry; url::EnableNonStandardSchemesForAndroidWebView(); scoped_refptr<const SecurityOrigin> origin = SecurityOrigin::CreateFromString("cow://"); @@ -891,7 +893,6 @@ TEST_F(SecurityOriginTest, NonStandardSchemeWithAndroidWebViewHack) { EXPECT_EQ("cow", origin->Protocol()); EXPECT_EQ("", origin->Host()); EXPECT_EQ(0, origin->Port()); - url::ResetForTests(); } TEST_F(SecurityOriginTest, OpaqueIsolatedCopy) { @@ -960,7 +961,7 @@ TEST_F(SecurityOriginTest, IsSameOriginWith) { // Opaque {false, "data:text/html,whatever", "data:text/html,whatever"}}; - for (auto test : tests) { + for (const auto& test : tests) { SCOPED_TRACE(testing::Message() << "Origin 1: `" << test.a << "` " << "Origin 2: `" << test.b << "`\n"); scoped_refptr<SecurityOrigin> a = SecurityOrigin::CreateFromString(test.a); @@ -1036,4 +1037,136 @@ TEST_F(SecurityOriginTest, IsSameOriginWithWithLocalScheme) { EXPECT_FALSE(b->IsSameOriginWith(a.get())); } +TEST_F(SecurityOriginTest, IsSameOriginDomainWith) { + struct TestCase { + bool same_origin_domain; + const char* a; + const char* domain_a; // empty string === no `domain` set. + const char* b; + const char* domain_b; + } tests[] = { + {true, "https://a.com", "", "https://a.com", ""}, + {false, "https://a.com", "a.com", "https://a.com", ""}, + {true, "https://a.com", "a.com", "https://a.com", "a.com"}, + {false, "https://sub.a.com", "", "https://a.com", ""}, + {false, "https://sub.a.com", "a.com", "https://a.com", ""}, + {true, "https://sub.a.com", "a.com", "https://a.com", "a.com"}, + {true, "https://sub.a.com", "a.com", "https://sub.a.com", "a.com"}, + {true, "https://sub.a.com", "a.com", "https://sub.sub.a.com", "a.com"}, + + // Schemes. + {false, "https://a.com", "", "http://a.com", ""}, + {false, "https://a.com", "a.com", "http://a.com", ""}, + {false, "https://a.com", "a.com", "http://a.com", "a.com"}, + {false, "https://sub.a.com", "a.com", "http://a.com", ""}, + {false, "https://a.com", "a.com", "http://sub.a.com", "a.com"}, + + // Ports? Why would they matter? + {true, "https://a.com:443", "", "https://a.com", ""}, + {false, "https://a.com:444", "", "https://a.com", ""}, + {false, "https://a.com:444", "", "https://a.com:442", ""}, + + {false, "https://a.com:443", "a.com", "https://a.com", ""}, + {false, "https://a.com:444", "a.com", "https://a.com", ""}, + {false, "https://a.com:444", "a.com", "https://a.com:442", ""}, + + {true, "https://a.com:443", "a.com", "https://a.com", "a.com"}, + {true, "https://a.com:444", "a.com", "https://a.com", "a.com"}, + {true, "https://a.com:444", "a.com", "https://a.com:442", "a.com"}, + + {false, "https://sub.a.com:443", "", "https://a.com", ""}, + {false, "https://sub.a.com:444", "", "https://a.com", ""}, + {false, "https://sub.a.com:444", "", "https://a.com:442", ""}, + + {false, "https://sub.a.com:443", "a.com", "https://a.com", ""}, + {false, "https://sub.a.com:444", "a.com", "https://a.com", ""}, + {false, "https://sub.a.com:444", "a.com", "https://a.com:442", ""}, + + {true, "https://sub.a.com:443", "a.com", "https://a.com", "a.com"}, + {true, "https://sub.a.com:444", "a.com", "https://a.com", "a.com"}, + {true, "https://sub.a.com:444", "a.com", "https://a.com:442", "a.com"}, + }; + + for (const auto& test : tests) { + SCOPED_TRACE(testing::Message() + << "Origin 1: `" << test.a << "` (`" << test.domain_a << "`)\n" + << "Origin 2: `" << test.b << "` (`" << test.domain_b + << "`)\n"); + scoped_refptr<SecurityOrigin> a = SecurityOrigin::CreateFromString(test.a); + if (strlen(test.domain_a)) + a->SetDomainFromDOM(test.domain_a); + scoped_refptr<SecurityOrigin> b = SecurityOrigin::CreateFromString(test.b); + if (strlen(test.domain_b)) + b->SetDomainFromDOM(test.domain_b); + EXPECT_EQ(test.same_origin_domain, a->IsSameOriginDomainWith(b.get())); + EXPECT_EQ(test.same_origin_domain, b->IsSameOriginDomainWith(a.get())); + + // Self-comparison + EXPECT_TRUE(a->IsSameOriginDomainWith(a.get())); + EXPECT_TRUE(b->IsSameOriginDomainWith(b.get())); + + // DeriveNewOpaqueOrigin + EXPECT_FALSE(a->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith(a.get())); + EXPECT_FALSE(b->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith(a.get())); + EXPECT_FALSE(a->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith(b.get())); + EXPECT_FALSE(b->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith(b.get())); + EXPECT_FALSE(b->IsSameOriginDomainWith(a->DeriveNewOpaqueOrigin().get())); + EXPECT_FALSE(b->IsSameOriginDomainWith(a->DeriveNewOpaqueOrigin().get())); + EXPECT_FALSE(a->IsSameOriginDomainWith(b->DeriveNewOpaqueOrigin().get())); + EXPECT_FALSE(b->IsSameOriginDomainWith(b->DeriveNewOpaqueOrigin().get())); + EXPECT_FALSE(a->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith( + a->DeriveNewOpaqueOrigin().get())); + EXPECT_FALSE(b->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith( + b->DeriveNewOpaqueOrigin().get())); + + // UniversalAccess does not override. + a->GrantUniversalAccess(); + EXPECT_EQ(test.same_origin_domain, a->IsSameOriginDomainWith(b.get())); + EXPECT_EQ(test.same_origin_domain, b->IsSameOriginDomainWith(a.get())); + } +} + +TEST_F(SecurityOriginTest, IsSameOriginDomainWithWithLocalScheme) { + scoped_refptr<SecurityOrigin> a = + SecurityOrigin::CreateFromString("file:///etc/passwd"); + scoped_refptr<SecurityOrigin> b = + SecurityOrigin::CreateFromString("file:///etc/hosts"); + + // Self-comparison + EXPECT_TRUE(a->IsSameOriginDomainWith(a.get())); + EXPECT_TRUE(b->IsSameOriginDomainWith(b.get())); + + // block_local_access_from_local_origin_ defaults to `false`: + EXPECT_TRUE(a->IsSameOriginDomainWith(b.get())); + EXPECT_TRUE(b->IsSameOriginDomainWith(a.get())); + + // DeriveNewOpaqueOrigin + EXPECT_FALSE(a->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith(a.get())); + EXPECT_FALSE(b->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith(a.get())); + EXPECT_FALSE(a->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith(b.get())); + EXPECT_FALSE(b->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith(b.get())); + EXPECT_FALSE(b->IsSameOriginDomainWith(a->DeriveNewOpaqueOrigin().get())); + EXPECT_FALSE(b->IsSameOriginDomainWith(a->DeriveNewOpaqueOrigin().get())); + EXPECT_FALSE(a->IsSameOriginDomainWith(b->DeriveNewOpaqueOrigin().get())); + EXPECT_FALSE(b->IsSameOriginDomainWith(b->DeriveNewOpaqueOrigin().get())); + EXPECT_FALSE(a->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith( + a->DeriveNewOpaqueOrigin().get())); + EXPECT_FALSE(b->DeriveNewOpaqueOrigin()->IsSameOriginDomainWith( + b->DeriveNewOpaqueOrigin().get())); + + // Set block_local_access_from_local_origin_ to `true`: + a->BlockLocalAccessFromLocalOrigin(); + EXPECT_FALSE(a->IsSameOriginDomainWith(b.get())); + EXPECT_FALSE(b->IsSameOriginDomainWith(a.get())); + + // Self-comparison should still be true. + EXPECT_TRUE(a->IsSameOriginDomainWith(a.get())); + EXPECT_TRUE(b->IsSameOriginDomainWith(b.get())); + + // UniversalAccess does not override + a->GrantUniversalAccess(); + EXPECT_FALSE(a->IsSameOriginDomainWith(b.get())); + EXPECT_FALSE(b->IsSameOriginDomainWith(a.get())); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc b/chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc index 6f93952427c..a1d26ce91c1 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc +++ b/chromium/third_party/blink/renderer/platform/weborigin/security_policy.cc @@ -72,8 +72,7 @@ network::mojom::ReferrerPolicy ReferrerPolicyResolveDefault( network::mojom::ReferrerPolicy referrer_policy) { if (referrer_policy == network::mojom::ReferrerPolicy::kDefault) { if (RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled()) { - return network::mojom::ReferrerPolicy:: - kNoReferrerWhenDowngradeOriginWhenCrossOrigin; + return network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin; } else { return network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade; } @@ -160,8 +159,7 @@ Referrer SecurityPolicy::GenerateReferrer( : origin + "/", referrer_policy_no_default); } - case network::mojom::ReferrerPolicy:: - kNoReferrerWhenDowngradeOriginWhenCrossOrigin: { + case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin: { scoped_refptr<const SecurityOrigin> referrer_origin = SecurityOrigin::Create(referrer_url); scoped_refptr<const SecurityOrigin> url_origin = @@ -328,8 +326,7 @@ bool SecurityPolicy::ReferrerPolicyFromString( return true; } if (EqualIgnoringASCIICase(policy, "strict-origin-when-cross-origin")) { - *result = network::mojom::ReferrerPolicy:: - kNoReferrerWhenDowngradeOriginWhenCrossOrigin; + *result = network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin; return true; } if (EqualIgnoringASCIICase(policy, "no-referrer-when-downgrade") || diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_policy_test.cc b/chromium/third_party/blink/renderer/platform/weborigin/security_policy_test.cc index 2c6e996fef8..ecc706ea8ea 100644 --- a/chromium/third_party/blink/renderer/platform/weborigin/security_policy_test.cc +++ b/chromium/third_party/blink/renderer/platform/weborigin/security_policy_test.cc @@ -32,10 +32,15 @@ #include "services/network/public/mojom/cors.mojom-blink.h" #include "services/network/public/mojom/cors_origin_pattern.mojom-blink.h" +#include "services/network/public/mojom/referrer_policy.mojom-shared.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h" #include "third_party/blink/renderer/platform/weborigin/security_origin.h" +#include "url/url_canon.h" +#include "url/url_util.h" namespace blink { @@ -97,6 +102,9 @@ TEST(SecurityPolicyTest, GenerateReferrer) { "blob:http://a.test/b3aae9c8-7f90-440d-8d7c-43aa20d72fde"; const char kFilesystemURL[] = "filesystem:http://a.test/path/t/file.html"; + bool reduced_granularity = + RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled(); + TestCase inputs[] = { // HTTP -> HTTP: Same Origin {network::mojom::ReferrerPolicy::kAlways, kInsecureURLA, kInsecureURLA, @@ -115,15 +123,14 @@ TEST(SecurityPolicyTest, GenerateReferrer) { kInsecureURLA, kInsecureURLA}, {network::mojom::ReferrerPolicy::kStrictOrigin, kInsecureURLA, kInsecureURLA, kInsecureOriginA}, - {network::mojom::ReferrerPolicy:: - kNoReferrerWhenDowngradeOriginWhenCrossOrigin, + {network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin, kInsecureURLA, kInsecureURLA, kInsecureURLA}, // HTTP -> HTTP: Cross Origin {network::mojom::ReferrerPolicy::kAlways, kInsecureURLA, kInsecureURLB, kInsecureURLA}, {network::mojom::ReferrerPolicy::kDefault, kInsecureURLA, kInsecureURLB, - kInsecureURLA}, + reduced_granularity ? kInsecureOriginA : kInsecureURLA}, {network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade, kInsecureURLA, kInsecureURLB, kInsecureURLA}, {network::mojom::ReferrerPolicy::kNever, kInsecureURLA, kInsecureURLB, @@ -136,8 +143,7 @@ TEST(SecurityPolicyTest, GenerateReferrer) { kInsecureURLB, nullptr}, {network::mojom::ReferrerPolicy::kStrictOrigin, kInsecureURLA, kInsecureURLB, kInsecureOriginA}, - {network::mojom::ReferrerPolicy:: - kNoReferrerWhenDowngradeOriginWhenCrossOrigin, + {network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin, kInsecureURLA, kInsecureURLB, kInsecureOriginA}, // HTTPS -> HTTPS: Same Origin @@ -157,15 +163,14 @@ TEST(SecurityPolicyTest, GenerateReferrer) { kSecureURLA}, {network::mojom::ReferrerPolicy::kStrictOrigin, kSecureURLA, kSecureURLA, kSecureOriginA}, - {network::mojom::ReferrerPolicy:: - kNoReferrerWhenDowngradeOriginWhenCrossOrigin, + {network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin, kSecureURLA, kSecureURLA, kSecureURLA}, // HTTPS -> HTTPS: Cross Origin {network::mojom::ReferrerPolicy::kAlways, kSecureURLA, kSecureURLB, kSecureURLA}, {network::mojom::ReferrerPolicy::kDefault, kSecureURLA, kSecureURLB, - kSecureURLA}, + reduced_granularity ? kSecureOriginA : kSecureURLA}, {network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade, kSecureURLA, kSecureURLB, kSecureURLA}, {network::mojom::ReferrerPolicy::kNever, kSecureURLA, kSecureURLB, @@ -178,15 +183,14 @@ TEST(SecurityPolicyTest, GenerateReferrer) { nullptr}, {network::mojom::ReferrerPolicy::kStrictOrigin, kSecureURLA, kSecureURLB, kSecureOriginA}, - {network::mojom::ReferrerPolicy:: - kNoReferrerWhenDowngradeOriginWhenCrossOrigin, + {network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin, kSecureURLA, kSecureURLB, kSecureOriginA}, // HTTP -> HTTPS {network::mojom::ReferrerPolicy::kAlways, kInsecureURLA, kSecureURLB, kInsecureURLA}, {network::mojom::ReferrerPolicy::kDefault, kInsecureURLA, kSecureURLB, - kInsecureURLA}, + reduced_granularity ? kInsecureOriginA : kInsecureURLA}, {network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade, kInsecureURLA, kSecureURLB, kInsecureURLA}, {network::mojom::ReferrerPolicy::kNever, kInsecureURLA, kSecureURLB, @@ -199,8 +203,7 @@ TEST(SecurityPolicyTest, GenerateReferrer) { nullptr}, {network::mojom::ReferrerPolicy::kStrictOrigin, kInsecureURLA, kSecureURLB, kInsecureOriginA}, - {network::mojom::ReferrerPolicy:: - kNoReferrerWhenDowngradeOriginWhenCrossOrigin, + {network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin, kInsecureURLA, kSecureURLB, kInsecureOriginA}, // HTTPS -> HTTP @@ -220,8 +223,7 @@ TEST(SecurityPolicyTest, GenerateReferrer) { nullptr}, {network::mojom::ReferrerPolicy::kStrictOrigin, kSecureURLA, kInsecureURLB, nullptr}, - {network::mojom::ReferrerPolicy:: - kNoReferrerWhenDowngradeOriginWhenCrossOrigin, + {network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin, kSecureURLA, kInsecureURLB, nullptr}, // blob and filesystem URL handling @@ -250,10 +252,18 @@ TEST(SecurityPolicyTest, GenerateReferrer) { << "' should have been empty: was '" << result.referrer.Utf8() << "'."; } - EXPECT_EQ(test.policy == network::mojom::ReferrerPolicy::kDefault - ? network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade - : test.policy, - result.referrer_policy); + + network::mojom::ReferrerPolicy expected_policy = test.policy; + if (expected_policy == network::mojom::ReferrerPolicy::kDefault) { + if (RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled()) { + expected_policy = + network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin; + } else { + expected_policy = + network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade; + } + } + EXPECT_EQ(expected_policy, result.referrer_policy); } } @@ -550,4 +560,47 @@ TEST_F(SecurityPolicyAccessTest, IsOriginAccessAllowedPriority) { https_chromium_origin(), https_sub_example_origin())); } +// Test that referrers for custom hierarchical (standard) schemes are correctly +// handled by the new policy. (For instance, this covers android-app://.) +TEST(SecurityPolicyTest, ReferrerForCustomScheme) { + url::ScopedSchemeRegistryForTests scoped_registry; + const char kCustomStandardScheme[] = "my-new-scheme"; + url::AddStandardScheme(kCustomStandardScheme, url::SCHEME_WITH_HOST); + SchemeRegistry::RegisterURLSchemeAsAllowedForReferrer(kCustomStandardScheme); + + String kFullReferrer = "my-new-scheme://com.foo.me/this-should-be-truncated"; + String kTruncatedReferrer = "my-new-scheme://com.foo.me/"; + + bool initially_enabled = + RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled(); + + { + // With the feature off, the old default policy of + // no-referrer-when-downgrade should preserve the entire URL. + RuntimeEnabledFeatures::SetReducedReferrerGranularityEnabled(false); + + EXPECT_EQ(SecurityPolicy::GenerateReferrer( + network::mojom::ReferrerPolicy::kDefault, + KURL("https://www.example.com/"), kFullReferrer) + .referrer, + kFullReferrer); + } + + { + // With the feature on, the new default policy of + // strict-origin-when-cross-origin should truncate the referrer. + RuntimeEnabledFeatures::SetReducedReferrerGranularityEnabled(true); + + ASSERT_TRUE(RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled()); + EXPECT_EQ(SecurityPolicy::GenerateReferrer( + network::mojom::ReferrerPolicy::kDefault, + KURL("https://www.example.com/"), kFullReferrer) + .referrer, + kTruncatedReferrer); + } + + RuntimeEnabledFeatures::SetReducedReferrerGranularityEnabled( + initially_enabled); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h b/chromium/third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h deleted file mode 100644 index 363658cbca8..00000000000 --- a/chromium/third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h +++ /dev/null @@ -1,17 +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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_VIOLATION_REPORTING_POLICY_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_VIOLATION_REPORTING_POLICY_H_ - -namespace blink { - -enum class SecurityViolationReportingPolicy { - kSuppressReporting, - kReport, -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBORIGIN_SECURITY_VIOLATION_REPORTING_POLICY_H_ diff --git a/chromium/third_party/blink/renderer/platform/webrtc/peer_connection_remote_audio_source.cc b/chromium/third_party/blink/renderer/platform/webrtc/peer_connection_remote_audio_source.cc index 0fc04e446e0..7a775eb7467 100644 --- a/chromium/third_party/blink/renderer/platform/webrtc/peer_connection_remote_audio_source.cc +++ b/chromium/third_party/blink/renderer/platform/webrtc/peer_connection_remote_audio_source.cc @@ -4,9 +4,14 @@ #include "third_party/blink/renderer/platform/webrtc/peer_connection_remote_audio_source.h" +#include <string> +#include <utility> + #include "base/logging.h" +#include "base/strings/stringprintf.h" #include "base/time/time.h" #include "media/base/audio_bus.h" +#include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" namespace blink { @@ -14,19 +19,26 @@ namespace { // Used as an identifier for the down-casters. void* const kPeerConnectionRemoteTrackIdentifier = const_cast<void**>(&kPeerConnectionRemoteTrackIdentifier); + +void SendLogMessage(const std::string& message) { + blink::WebRtcLogMessage("PCRAS::" + message); +} + } // namespace PeerConnectionRemoteAudioTrack::PeerConnectionRemoteAudioTrack( scoped_refptr<webrtc::AudioTrackInterface> track_interface) : MediaStreamAudioTrack(false /* is_local_track */), track_interface_(std::move(track_interface)) { - DVLOG(1) - << "PeerConnectionRemoteAudioTrack::PeerConnectionRemoteAudioTrack()"; + blink::WebRtcLogMessage( + base::StringPrintf("PCRAT::PeerConnectionRemoteAudioTrack({id=%s})", + track_interface_->id().c_str())); } PeerConnectionRemoteAudioTrack::~PeerConnectionRemoteAudioTrack() { - DVLOG(1) - << "PeerConnectionRemoteAudioTrack::~PeerConnectionRemoteAudioTrack()"; + blink::WebRtcLogMessage( + base::StringPrintf("PCRAT::~PeerConnectionRemoteAudioTrack([id=%s])", + track_interface_->id().c_str())); // Ensure the track is stopped. MediaStreamAudioTrack::Stop(); } @@ -42,6 +54,9 @@ PeerConnectionRemoteAudioTrack* PeerConnectionRemoteAudioTrack::From( void PeerConnectionRemoteAudioTrack::SetEnabled(bool enabled) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + blink::WebRtcLogMessage(base::StringPrintf( + "PCRAT::SetEnabled([id=%s] {enabled=%s})", track_interface_->id().c_str(), + (enabled ? "true" : "false"))); // This affects the shared state of the source for whether or not it's a part // of the mixed audio that's rendered for remote tracks from WebRTC. @@ -67,13 +82,13 @@ PeerConnectionRemoteAudioSource::PeerConnectionRemoteAudioSource( track_interface_(std::move(track_interface)), is_sink_of_peer_connection_(false) { DCHECK(track_interface_); - DVLOG(1) - << "PeerConnectionRemoteAudioSource::PeerConnectionRemoteAudioSource()"; + SendLogMessage(base::StringPrintf("PeerConnectionRemoteAudioSource([id=%s])", + track_interface_->id().c_str())); } PeerConnectionRemoteAudioSource::~PeerConnectionRemoteAudioSource() { - DVLOG(1) - << "PeerConnectionRemoteAudioSource::~PeerConnectionRemoteAudioSource()"; + SendLogMessage(base::StringPrintf("~PeerConnectionRemoteAudioSource([id=%s])", + track_interface_->id().c_str())); EnsureSourceIsStopped(); } @@ -88,8 +103,8 @@ bool PeerConnectionRemoteAudioSource::EnsureSourceIsStarted() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (is_sink_of_peer_connection_) return true; - VLOG(1) << "Starting PeerConnection remote audio source with id=" - << track_interface_->id(); + SendLogMessage(base::StringPrintf("EnsureSourceIsStarted([id=%s])", + track_interface_->id().c_str())); track_interface_->AddSink(this); is_sink_of_peer_connection_ = true; return true; @@ -98,10 +113,10 @@ bool PeerConnectionRemoteAudioSource::EnsureSourceIsStarted() { void PeerConnectionRemoteAudioSource::EnsureSourceIsStopped() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (is_sink_of_peer_connection_) { + SendLogMessage(base::StringPrintf("EnsureSourceIsStopped([id=%s])", + track_interface_->id().c_str())); track_interface_->RemoveSink(this); is_sink_of_peer_connection_ = false; - VLOG(1) << "Stopped PeerConnection remote audio source with id=" - << track_interface_->id(); } } @@ -149,7 +164,8 @@ void PeerConnectionRemoteAudioSource::OnData(const void* audio_data, MediaStreamAudioSource::DeliverDataToTracks(*audio_bus_, playout_time); #ifndef NDEBUG - single_audio_thread_guard_.Release(); + if (is_only_thread_here) + single_audio_thread_guard_.Release(); #endif } diff --git a/chromium/third_party/blink/renderer/platform/webrtc/peer_connection_remote_audio_source.h b/chromium/third_party/blink/renderer/platform/webrtc/peer_connection_remote_audio_source.h index 46305d1b6b4..b831df797b2 100644 --- a/chromium/third_party/blink/renderer/platform/webrtc/peer_connection_remote_audio_source.h +++ b/chromium/third_party/blink/renderer/platform/webrtc/peer_connection_remote_audio_source.h @@ -9,8 +9,8 @@ #include "base/memory/scoped_refptr.h" #include "base/synchronization/lock.h" -#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_track.h" #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h" +#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_track.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/webrtc/api/media_stream_interface.h" diff --git a/chromium/third_party/blink/renderer/platform/webrtc/webrtc_source.h b/chromium/third_party/blink/renderer/platform/webrtc/webrtc_source.h index 8ef336fe2e4..ac942e2642a 100644 --- a/chromium/third_party/blink/renderer/platform/webrtc/webrtc_source.h +++ b/chromium/third_party/blink/renderer/platform/webrtc/webrtc_source.h @@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBRTC_WEBRTC_SOURCE_H_ #include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace base { class UnguessableToken; @@ -39,7 +40,7 @@ class PLATFORM_EXPORT WebRtcAudioRendererSource { virtual void AudioRendererThreadStopped() = 0; // Callback to notify the client of the output device the renderer is using. - virtual void SetOutputDeviceForAec(const std::string& output_device_id) = 0; + virtual void SetOutputDeviceForAec(const String& output_device_id) = 0; // Returns the UnguessableToken used to connect this stream to an input stream // for echo cancellation. diff --git a/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc b/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc index bda303b71c9..d73e43710f4 100644 --- a/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc +++ b/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc @@ -6,6 +6,9 @@ #include "base/bind_helpers.h" #include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/webrtc/common_video/include/video_frame_buffer.h" #include "third_party/webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "third_party/webrtc/rtc_base/ref_counted_object.h" @@ -82,7 +85,8 @@ void IsValidFrame(const media::VideoFrame& frame) { } scoped_refptr<media::VideoFrame> ConstructI420VideoFrame( - const media::VideoFrame& source_frame) { + const media::VideoFrame& source_frame, + blink::WebRtcVideoFrameAdapter::LogStatus log_to_webrtc) { // NV12 is the only supported format. DCHECK_EQ(source_frame.format(), media::PIXEL_FORMAT_NV12); DCHECK_EQ(source_frame.storage_type(), @@ -101,6 +105,22 @@ scoped_refptr<media::VideoFrame> ConstructI420VideoFrame( ((source_frame.visible_rect().x() / 2) * 2) + ((source_frame.visible_rect().y() / 2) * gmb->stride(1))); + if (log_to_webrtc == + blink::WebRtcVideoFrameAdapter::LogStatus::kLogToWebRtc) { + blink::WebRtcLogMessage(base::StringPrintf( + "VFC::WebRtcVideoFrameAdapter : ConstructI420VideoFrame " + "pixel_format %d " + "natural_size %s coded_size %s visible_rect %s " + "source_plane_y %p source_plane_uv %p " + "source_stride_y %d, source_stride_uv %d " + "visible_y %p visible_uv %p", + static_cast<int>(source_frame.format()), + source_frame.natural_size().ToString().c_str(), + source_frame.coded_size().ToString().c_str(), + source_frame.visible_rect().ToString().c_str(), gmb->memory(0), + gmb->memory(1), gmb->stride(0), gmb->stride(1), src_y, src_uv)); + } + // Convert to I420 and scale to the natural size specified in |source_frame|. scoped_refptr<media::VideoFrame> i420_frame = media::VideoFrame::CreateFrame( media::PIXEL_FORMAT_I420, source_frame.natural_size(), @@ -130,8 +150,9 @@ scoped_refptr<media::VideoFrame> ConstructI420VideoFrame( namespace blink { WebRtcVideoFrameAdapter::WebRtcVideoFrameAdapter( - scoped_refptr<media::VideoFrame> frame) - : frame_(std::move(frame)) {} + scoped_refptr<media::VideoFrame> frame, + LogStatus log_to_webrtc) + : frame_(std::move(frame)), log_to_webrtc_(log_to_webrtc) {} WebRtcVideoFrameAdapter::~WebRtcVideoFrameAdapter() {} @@ -157,8 +178,12 @@ rtc::scoped_refptr<webrtc::I420BufferInterface> WebRtcVideoFrameAdapter::CreateFrameAdapter() const { if (frame_->storage_type() == media::VideoFrame::StorageType::STORAGE_GPU_MEMORY_BUFFER) { - auto i420_frame = ConstructI420VideoFrame(*frame_); + auto i420_frame = ConstructI420VideoFrame(*frame_, log_to_webrtc_); if (!i420_frame) { + if (log_to_webrtc_ == LogStatus::kLogToWebRtc) { + blink::WebRtcLogMessage( + "VFC::WebRtcVideoFrameAdapter couldn't contruct I420 frame"); + } return new rtc::RefCountedObject< FrameAdapter<webrtc::I420BufferInterface>>( media::VideoFrame::CreateColorFrame(frame_->natural_size(), 0u, 0x80, @@ -166,8 +191,10 @@ WebRtcVideoFrameAdapter::CreateFrameAdapter() const { } // Keep |frame_| alive until |i420_frame| is destroyed. - i420_frame->AddDestructionObserver(base::BindOnce( - base::DoNothing::Once<scoped_refptr<media::VideoFrame>>(), frame_)); + i420_frame->AddDestructionObserver( + ConvertToBaseOnceCallback(CrossThreadBindOnce( + base::DoNothing::Once<scoped_refptr<media::VideoFrame>>(), + frame_))); IsValidFrame(*i420_frame); return new rtc::RefCountedObject<FrameAdapter<webrtc::I420BufferInterface>>( @@ -185,7 +212,29 @@ WebRtcVideoFrameAdapter::CreateFrameAdapter() const { } IsValidFrame(*frame_); + if (log_to_webrtc_ == LogStatus::kLogToWebRtc) { + blink::WebRtcLogMessage(base::StringPrintf( + "VFC::WebRtcVideoFrameAdapter created I420 adapter: " + "natural_size %s coded_size %s visible_rect %s " + "PlaneY %p PlaneU %p PlaneY %p StrideY %d StrideU %d StrideY %d ", + frame_->natural_size().ToString().c_str(), + frame_->coded_size().ToString().c_str(), + frame_->visible_rect().ToString().c_str(), + frame_->visible_data(media::VideoFrame::kYPlane), + frame_->visible_data(media::VideoFrame::kUPlane), + frame_->visible_data(media::VideoFrame::kVPlane), + frame_->stride(media::VideoFrame::kYPlane), + frame_->stride(media::VideoFrame::kUPlane), + frame_->stride(media::VideoFrame::kVPlane))); + } if (media::PIXEL_FORMAT_I420A == frame_->format()) { + if (log_to_webrtc_ == LogStatus::kLogToWebRtc) { + blink::WebRtcLogMessage(base::StringPrintf( + "VFC::WebRtcVideoFrameAdapter pixel format is I420A. " + "PlaneA %p StrideA %d", + frame_->visible_data(media::VideoFrame::kAPlane), + frame_->stride(media::VideoFrame::kAPlane))); + } return new rtc::RefCountedObject< FrameAdapterWithA<webrtc::I420ABufferInterface>>(frame_); } diff --git a/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h b/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h index 3b674a25230..e0cb044a583 100644 --- a/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h +++ b/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h @@ -12,6 +12,7 @@ #include "third_party/webrtc/api/video/video_frame_buffer.h" namespace blink { + // Thin adapter from media::VideoFrame to webrtc::VideoFrameBuffer. This // implementation is read-only and will return null if trying to get a // non-const pointer to the pixel data. This object will be accessed from @@ -19,7 +20,10 @@ namespace blink { class PLATFORM_EXPORT WebRtcVideoFrameAdapter : public webrtc::VideoFrameBuffer { public: - explicit WebRtcVideoFrameAdapter(scoped_refptr<media::VideoFrame> frame); + enum class LogStatus { kNoLogging, kLogToWebRtc }; + + WebRtcVideoFrameAdapter(scoped_refptr<media::VideoFrame> frame, + LogStatus log_to_webrtc); scoped_refptr<media::VideoFrame> getMediaVideoFrame() const { return frame_; } @@ -41,6 +45,8 @@ class PLATFORM_EXPORT WebRtcVideoFrameAdapter mutable rtc::scoped_refptr<webrtc::I420BufferInterface> frame_adapter_; scoped_refptr<media::VideoFrame> frame_; + + const LogStatus log_to_webrtc_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc b/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc index d65df0e8adb..f7380df85e6 100644 --- a/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc +++ b/chromium/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc @@ -25,7 +25,8 @@ TEST(WebRtcVideoFrameAdapterTest, WidthAndHeight) { media::VideoFrame::STORAGE_OWNED_MEMORY); rtc::scoped_refptr<webrtc::VideoFrameBuffer> owned_memory_frame_adapter( new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( - std::move(owned_memory_frame))); + std::move(owned_memory_frame), + WebRtcVideoFrameAdapter::LogStatus::kNoLogging)); EXPECT_EQ(owned_memory_frame_adapter->width(), kVisibleRect.width()); EXPECT_EQ(owned_memory_frame_adapter->height(), kVisibleRect.height()); @@ -35,7 +36,9 @@ TEST(WebRtcVideoFrameAdapterTest, WidthAndHeight) { CreateTestFrame(kCodedSize, kVisibleRect, kNaturalSize, media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER); rtc::scoped_refptr<webrtc::VideoFrameBuffer> gmb_frame_adapter( - new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(std::move(gmb_frame))); + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( + std::move(gmb_frame), + WebRtcVideoFrameAdapter::LogStatus::kNoLogging)); EXPECT_EQ(gmb_frame_adapter->width(), kNaturalSize.width()); EXPECT_EQ(gmb_frame_adapter->height(), kNaturalSize.height()); } @@ -52,7 +55,9 @@ TEST(WebRtcVideoFrameAdapterTest, ToI420DownScale) { // The adapter should report width and height from the natural size for // VideoFrame backed by GpuMemoryBuffer. rtc::scoped_refptr<webrtc::VideoFrameBuffer> gmb_frame_adapter( - new rtc::RefCountedObject<WebRtcVideoFrameAdapter>(std::move(gmb_frame))); + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( + std::move(gmb_frame), + WebRtcVideoFrameAdapter::LogStatus::kNoLogging)); EXPECT_EQ(gmb_frame_adapter->width(), kNaturalSize.width()); EXPECT_EQ(gmb_frame_adapter->height(), kNaturalSize.height()); diff --git a/chromium/third_party/blink/renderer/platform/widget/frame_widget.cc b/chromium/third_party/blink/renderer/platform/widget/frame_widget.cc new file mode 100644 index 00000000000..2f471eeeba8 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/widget/frame_widget.cc @@ -0,0 +1,11 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/widget/frame_widget.h" + +namespace blink { + +FrameWidget::~FrameWidget() = default; + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/widget/frame_widget.h b/chromium/third_party/blink/renderer/platform/widget/frame_widget.h new file mode 100644 index 00000000000..fffcf36f899 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/widget/frame_widget.h @@ -0,0 +1,81 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_FRAME_WIDGET_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_FRAME_WIDGET_H_ + +#include "third_party/blink/public/web/web_swap_result.h" +#include "third_party/blink/public/web/web_widget_client.h" +#include "third_party/blink/renderer/platform/platform_export.h" + +namespace cc { +class AnimationHost; +class Layer; +class PaintImage; +} // namespace cc + +namespace blink { + +// In interface exposed within Blink from local root frames that provides +// local-root specific things related to compositing and input. +class PLATFORM_EXPORT FrameWidget { + public: + virtual ~FrameWidget(); + + // Returns the WebWidgetClient, which is implemented outside of blink. + virtual WebWidgetClient* Client() const = 0; + + // Returns the compositors's AnimationHost for the widget. + virtual cc::AnimationHost* AnimationHost() const = 0; + + // Set the browser's behavior when overscroll happens, e.g. whether to glow + // or navigate. + virtual void SetOverscrollBehavior( + const cc::OverscrollBehavior& overscroll_behavior) = 0; + + // Posts a task with the given delay, then calls ScheduleAnimation() on the + // Client(). + virtual void RequestAnimationAfterDelay(const base::TimeDelta&) = 0; + + // Sets the root layer. The |layer| can be null when detaching the root layer. + virtual void SetRootLayer(scoped_refptr<cc::Layer> layer) = 0; + + // Used to update the active selection bounds. Pass a default-constructed + // LayerSelection to clear it. + virtual void RegisterSelection(cc::LayerSelection selection) = 0; + + // Image decode functionality. + virtual void RequestDecode(const cc::PaintImage&, + base::OnceCallback<void(bool)>) = 0; + + // Forwards to WebFrameWidget::NotifySwapAndPresentationTime(). + // The |callback| will be fired when the corresponding renderer frame is + // submitted (still called "swapped") to the display compositor (either with + // DidSwap or DidNotSwap). + virtual void NotifySwapAndPresentationTimeInBlink( + WebReportTimeCallback swap_callback, + WebReportTimeCallback presentation_callback) = 0; + + // Enable or disable BeginMainFrameNotExpected signals from the compositor, + // which are consumed by the blink scheduler. + virtual void RequestBeginMainFrameNotExpected(bool request) = 0; + + // A stable numeric Id for the local root's compositor. For tracing/debugging + // purposes. + virtual int GetLayerTreeId() = 0; + + // Set or get what event handlers exist in the document contained in the + // WebWidget in order to inform the compositor thread if it is able to handle + // an input event, or it needs to pass it to the main thread to be handled. + // The class is the type of input event, and for each class there is a + // properties defining if the compositor thread can handle the event. + virtual void SetEventListenerProperties(cc::EventListenerClass, + cc::EventListenerProperties) = 0; + virtual cc::EventListenerProperties EventListenerProperties( + cc::EventListenerClass) const = 0; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_FRAME_WIDGET_H_ diff --git a/chromium/third_party/blink/renderer/platform/widget/widget_base.cc b/chromium/third_party/blink/renderer/platform/widget/widget_base.cc new file mode 100644 index 00000000000..800bb29a8d5 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/widget/widget_base.cc @@ -0,0 +1,79 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/widget/widget_base.h" + +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" +#include "mojo/public/cpp/bindings/pending_associated_remote.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread.h" +#include "third_party/blink/renderer/platform/widget/widget_base_client.h" + +namespace blink { + +WidgetBase::WidgetBase( + WidgetBaseClient* client, + CrossVariantMojoAssociatedRemote<mojom::WidgetHostInterfaceBase> + widget_host, + CrossVariantMojoAssociatedReceiver<mojom::WidgetInterfaceBase> widget) + : client_(client), + widget_host_(std::move(widget_host)), + receiver_(this, std::move(widget)) {} + +WidgetBase::~WidgetBase() = default; + +void WidgetBase::SetCompositorHosts(cc::LayerTreeHost* layer_tree_host, + cc::AnimationHost* animation_host) { + layer_tree_host_ = layer_tree_host; + animation_host_ = animation_host; +} + +cc::LayerTreeHost* WidgetBase::LayerTreeHost() const { + return layer_tree_host_; +} + +cc::AnimationHost* WidgetBase::AnimationHost() const { + return animation_host_; +} + +void WidgetBase::SetCompositorVisible(bool visible) { + if (visible) + was_shown_time_ = base::TimeTicks::Now(); + else + first_update_visual_state_after_hidden_ = true; +} + +void WidgetBase::UpdateVisualState() { + // When recording main frame metrics set the lifecycle reason to + // kBeginMainFrame, because this is the calller of UpdateLifecycle + // for the main frame. Otherwise, set the reason to kTests, which is + // the only other reason this method is called. + DocumentUpdateReason lifecycle_reason = + ShouldRecordBeginMainFrameMetrics() + ? DocumentUpdateReason::kBeginMainFrame + : DocumentUpdateReason::kTest; + client_->UpdateLifecycle(WebLifecycleUpdate::kAll, lifecycle_reason); + client_->SetSuppressFrameRequestsWorkaroundFor704763Only(false); + if (first_update_visual_state_after_hidden_) { + client_->RecordTimeToFirstActivePaint(base::TimeTicks::Now() - + was_shown_time_); + first_update_visual_state_after_hidden_ = false; + } +} + +void WidgetBase::WillBeginCompositorFrame() { + client_->SetSuppressFrameRequestsWorkaroundFor704763Only(true); +} + +void WidgetBase::BeginMainFrame(base::TimeTicks frame_time) { + client_->DispatchRafAlignedInput(frame_time); + client_->BeginMainFrame(frame_time); +} + +bool WidgetBase::ShouldRecordBeginMainFrameMetrics() { + // We record metrics only when running in multi-threaded mode, not + // single-thread mode for testing. + return Thread::CompositorThread(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/widget/widget_base.h b/chromium/third_party/blink/renderer/platform/widget/widget_base.h new file mode 100644 index 00000000000..3d70fae9378 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/widget/widget_base.h @@ -0,0 +1,75 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_WIDGET_BASE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_WIDGET_BASE_H_ + +#include "base/time/time.h" +#include "mojo/public/cpp/bindings/associated_receiver.h" +#include "mojo/public/cpp/bindings/associated_remote.h" +#include "third_party/blink/public/mojom/page/widget.mojom-blink.h" +#include "third_party/blink/public/platform/cross_variant_mojo_util.h" +#include "third_party/blink/renderer/platform/platform_export.h" + +namespace cc { +class AnimationHost; +class LayerTreeHost; +} // namespace cc + +namespace blink { +class WidgetBaseClient; + +// This class is the foundational class for all widgets that blink creates. +// (WebPagePopupImpl, WebFrameWidgetBase) will contain an instance of this +// class. For simplicity purposes this class will be a member of those classes. +// It will eventually host compositing, input and emulation. See design doc: +// https://docs.google.com/document/d/10uBnSWBaitGsaROOYO155Wb83rjOPtrgrGTrQ_pcssY/edit?ts=5e3b26f7 +class PLATFORM_EXPORT WidgetBase : public mojom::blink::Widget { + public: + WidgetBase( + WidgetBaseClient* client, + CrossVariantMojoAssociatedRemote<mojom::WidgetHostInterfaceBase> + widget_host, + CrossVariantMojoAssociatedReceiver<mojom::WidgetInterfaceBase> widget); + ~WidgetBase() override; + + // Set the current compositor hosts. + void SetCompositorHosts(cc::LayerTreeHost*, cc::AnimationHost*); + + // Set the compositor as visible. If |visible| is true, then the compositor + // will request a new layer frame sink, begin producing frames from the + // compositor scheduler, and in turn will update the document lifecycle. + void SetCompositorVisible(bool visible); + + // Called to update the document lifecycle, advance the state of animations + // and dispatch rAF. + void BeginMainFrame(base::TimeTicks frame_time); + + // Update the visual state of the document, running the document lifecycle. + void UpdateVisualState(); + + // Called when a compositor frame will begin. + void WillBeginCompositorFrame(); + + cc::AnimationHost* AnimationHost() const; + cc::LayerTreeHost* LayerTreeHost() const; + + // Returns if we should gather begin main frame metrics. If there is no + // compositor thread this returns false. + static bool ShouldRecordBeginMainFrameMetrics(); + + private: + // Not owned, they are owned by the RenderWidget. + cc::LayerTreeHost* layer_tree_host_ = nullptr; + cc::AnimationHost* animation_host_ = nullptr; + WidgetBaseClient* client_; + mojo::AssociatedRemote<mojom::blink::WidgetHost> widget_host_; + mojo::AssociatedReceiver<mojom::blink::Widget> receiver_; + bool first_update_visual_state_after_hidden_ = false; + base::TimeTicks was_shown_time_ = base::TimeTicks::Now(); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_WIDGET_BASE_H_ diff --git a/chromium/third_party/blink/renderer/platform/widget/widget_base_client.h b/chromium/third_party/blink/renderer/platform/widget/widget_base_client.h new file mode 100644 index 00000000000..4d9d8036357 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/widget/widget_base_client.h @@ -0,0 +1,41 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_WIDGET_BASE_CLIENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_WIDGET_BASE_CLIENT_H_ + +#include "base/time/time.h" +#include "third_party/blink/public/common/metrics/document_update_reason.h" +#include "third_party/blink/public/web/web_lifecycle_update.h" + +namespace blink { + +// This class is part of the foundation of all widgets. It provides +// callbacks from the compositing infrastructure that the individual widgets +// will need to implement. +class WidgetBaseClient { + public: + // Dispatch any pending input. This method will called before + // dispatching a RequestAnimationFrame to the widget. + virtual void DispatchRafAlignedInput(base::TimeTicks frame_time) = 0; + + // Called to update the document lifecycle, advance the state of animations + // and dispatch rAF. + virtual void BeginMainFrame(base::TimeTicks frame_time) = 0; + + // Called to record the time between when the widget was marked visible + // until the compositor begain producing a frame. + virtual void RecordTimeToFirstActivePaint(base::TimeDelta duration) = 0; + + // Requests that the lifecycle of the widget be updated. + virtual void UpdateLifecycle(WebLifecycleUpdate requested_update, + DocumentUpdateReason reason) = 0; + + // TODO(crbug.com/704763): Remove the need for this. + virtual void SetSuppressFrameRequestsWorkaroundFor704763Only(bool) {} +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_WIDGET_BASE_CLIENT_H_ diff --git a/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn b/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn index 6e9a1056bfa..4c07d2e881b 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn +++ b/chromium/third_party/blink/renderer/platform/wtf/BUILD.gn @@ -4,6 +4,7 @@ assert(!is_ios) +import("//build/config/compiler/compiler.gni") import("//build/config/jumbo.gni") import("//testing/test.gni") import("//third_party/blink/renderer/config.gni") @@ -42,6 +43,7 @@ jumbo_component("wtf") { "allocator/partitions.h", "assertions.cc", "assertions.h", + "bit_field.h", "bloom_filter.h", "casting.h", "conditional_destructor.h", @@ -74,6 +76,7 @@ jumbo_component("wtf") { "leak_annotations.h", "linked_hash_set.h", "list_hash_set.h", + "lru_cache.h", "math_extras.h", "pod_arena.h", "pod_free_list_arena.h", @@ -174,6 +177,7 @@ jumbo_component("wtf") { "uuid.cc", "uuid.h", "vector.h", + "vector_backed_linked_list.h", "vector_traits.h", "wtf.cc", "wtf.h", @@ -235,14 +239,17 @@ jumbo_component("wtf") { sources -= [ "text/atomic_string_cf.cc" ] } + if (!is_debug && !optimize_for_size) { + configs -= [ "//build/config/compiler:default_optimization" ] + configs += [ "//build/config/compiler:optimize_max" ] + } + configs -= [ "//build/config/compiler:default_symbols" ] configs += blink_symbols_config } test("wtf_unittests") { - deps = [ - ":wtf_unittests_sources", - ] + deps = [ ":wtf_unittests_sources" ] } jumbo_source_set("wtf_unittests_sources") { @@ -251,9 +258,11 @@ jumbo_source_set("wtf_unittests_sources") { testonly = true sources = [ + "allocator/atomic_operations_test.cc", "allocator/partitions_test.cc", "ascii_ctype_test.cc", "assertions_test.cc", + "bit_field_test.cc", "cross_thread_functional_test.cc", "decimal_test.cc", "deque_test.cc", @@ -262,7 +271,9 @@ jumbo_source_set("wtf_unittests_sources") { "functional_test.cc", "hash_map_test.cc", "hash_set_test.cc", + "linked_hash_set_test.cc", "list_hash_set_test.cc", + "lru_cache_test.cc", "math_extras_test.cc", "pod_arena_test.cc", "pod_arena_test_helpers.h", @@ -294,6 +305,7 @@ jumbo_source_set("wtf_unittests_sources") { "tree_node_test.cc", "type_traits_test.cc", "uuid_test.cc", + "vector_backed_linked_list_test.cc", "vector_test.cc", ] diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.cc b/chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.cc index b69702742d8..11349c5e44f 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.cc @@ -18,3 +18,57 @@ static_assert(WTF::IsStackAllocatedType<StackAllocatedType>::value, "Failed to detect STACK_ALLOCATED macro."); } // namespace + +namespace WTF { + +void AtomicReadMemcpy(void* to, const void* from, size_t bytes) { + // Check alignment of |to| and |from| + DCHECK_EQ(0u, reinterpret_cast<size_t>(to) & (sizeof(size_t) - 1)); + DCHECK_EQ(0u, reinterpret_cast<size_t>(from) & (sizeof(size_t) - 1)); + size_t* sizet_to = reinterpret_cast<size_t*>(to); + const size_t* sizet_from = reinterpret_cast<const size_t*>(from); + for (; bytes > sizeof(size_t); + bytes -= sizeof(size_t), ++sizet_to, ++sizet_from) { + *sizet_to = AsAtomicPtr(sizet_from)->load(std::memory_order_relaxed); + } + uint8_t* uint8t_to = reinterpret_cast<uint8_t*>(sizet_to); + const uint8_t* uint8t_from = reinterpret_cast<const uint8_t*>(sizet_from); + for (; bytes > 0; bytes -= sizeof(uint8_t), ++uint8t_to, ++uint8t_from) { + *uint8t_to = AsAtomicPtr(uint8t_from)->load(std::memory_order_relaxed); + } + DCHECK_EQ(0u, bytes); +} + +void AtomicWriteMemcpy(void* to, const void* from, size_t bytes) { + // Check alignment of |to| and |from| + DCHECK_EQ(0u, reinterpret_cast<size_t>(to) & (sizeof(size_t) - 1)); + DCHECK_EQ(0u, reinterpret_cast<size_t>(from) & (sizeof(size_t) - 1)); + size_t* sizet_to = reinterpret_cast<size_t*>(to); + const size_t* sizet_from = reinterpret_cast<const size_t*>(from); + for (; bytes > sizeof(size_t); + bytes -= sizeof(size_t), ++sizet_to, ++sizet_from) { + AsAtomicPtr(sizet_to)->store(*sizet_from, std::memory_order_relaxed); + } + uint8_t* uint8t_to = reinterpret_cast<uint8_t*>(sizet_to); + const uint8_t* uint8t_from = reinterpret_cast<const uint8_t*>(sizet_from); + for (; bytes > 0; bytes -= sizeof(uint8_t), ++uint8t_to, ++uint8t_from) { + AsAtomicPtr(uint8t_to)->store(*uint8t_from, std::memory_order_relaxed); + } + DCHECK_EQ(0u, bytes); +} + +void AtomicMemzero(void* buf, size_t bytes) { + // Check alignment of |buf| + DCHECK_EQ(0u, reinterpret_cast<size_t>(buf) & (sizeof(size_t) - 1)); + size_t* sizet_buf = reinterpret_cast<size_t*>(buf); + for (; bytes > sizeof(size_t); bytes -= sizeof(size_t), ++sizet_buf) { + AsAtomicPtr(sizet_buf)->store(0, std::memory_order_relaxed); + } + uint8_t* uint8t_buf = reinterpret_cast<uint8_t*>(sizet_buf); + for (; bytes > 0; bytes -= sizeof(uint8_t), ++uint8t_buf) { + AsAtomicPtr(uint8t_buf)->store(0, std::memory_order_relaxed); + } + DCHECK_EQ(0u, bytes); +} + +} // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.h b/chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.h index af536529a6d..66fc9943d46 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.h +++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/allocator.h @@ -5,6 +5,9 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_ALLOCATOR_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_ALLOCATOR_ALLOCATOR_H_ +#include <atomic> + +#include "build/build_config.h" #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/type_traits.h" @@ -147,6 +150,205 @@ class __thisIsHereToForceASemicolonAfterThisMacro; #define USING_FAST_MALLOC_WITH_TYPE_NAME(type) \ USING_FAST_MALLOC_INTERNAL(type, #type) +// TOOD(omerkatz): replace these casts with std::atomic_ref (C++20) once it +// becomes available +template <typename T> +ALWAYS_INLINE std::atomic<T>* AsAtomicPtr(T* t) { + return reinterpret_cast<std::atomic<T>*>(t); +} +template <typename T> +ALWAYS_INLINE const std::atomic<T>* AsAtomicPtr(const T* t) { + return reinterpret_cast<const std::atomic<T>*>(t); +} + +// Copies |bytes| bytes from |from| to |to| using atomic reads. Assumes |to| +// and |from| are size_t-aligned and point to buffers of size |bytes|. Note +// that atomicity is guaranteed only per word, not for the entire |bytes| +// bytes as a whole. When copying arrays of elements, If |to| and |from| +// are overlapping, should move the elements one by one. +WTF_EXPORT void AtomicReadMemcpy(void* to, const void* from, size_t bytes); +template <size_t bytes> +ALWAYS_INLINE void AtomicReadMemcpy(void* to, const void* from) { + static_assert(bytes > 0, "Number of copied bytes should be greater than 0"); + AtomicReadMemcpy(to, from, bytes); +} + +// AtomicReadMemcpy specializations: + +#if defined(ARCH_CPU_X86_64) +template <> +ALWAYS_INLINE void AtomicReadMemcpy<sizeof(uint32_t)>(void* to, + const void* from) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(to) & (sizeof(uint32_t) - 1)); + DCHECK_EQ(0u, reinterpret_cast<size_t>(from) & (sizeof(uint32_t) - 1)); + *reinterpret_cast<uint32_t*>(to) = + AsAtomicPtr(reinterpret_cast<const uint32_t*>(from)) + ->load(std::memory_order_relaxed); +} +#endif // ARCH_CPU_X86_64 + +template <> +ALWAYS_INLINE void AtomicReadMemcpy<sizeof(size_t)>(void* to, + const void* from) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(to) & (sizeof(size_t) - 1)); + DCHECK_EQ(0u, reinterpret_cast<size_t>(from) & (sizeof(size_t) - 1)); + *reinterpret_cast<size_t*>(to) = + AsAtomicPtr(reinterpret_cast<const size_t*>(from)) + ->load(std::memory_order_relaxed); +} + +template <> +ALWAYS_INLINE void AtomicReadMemcpy<2 * sizeof(size_t)>(void* to, + const void* from) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(to) & (sizeof(size_t) - 1)); + DCHECK_EQ(0u, reinterpret_cast<size_t>(from) & (sizeof(size_t) - 1)); + *reinterpret_cast<size_t*>(to) = + AsAtomicPtr(reinterpret_cast<const size_t*>(from)) + ->load(std::memory_order_relaxed); + *(reinterpret_cast<size_t*>(to) + 1) = + AsAtomicPtr(reinterpret_cast<const size_t*>(from) + 1) + ->load(std::memory_order_relaxed); +} + +template <> +ALWAYS_INLINE void AtomicReadMemcpy<3 * sizeof(size_t)>(void* to, + const void* from) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(to) & (sizeof(size_t) - 1)); + DCHECK_EQ(0u, reinterpret_cast<size_t>(from) & (sizeof(size_t) - 1)); + *reinterpret_cast<size_t*>(to) = + AsAtomicPtr(reinterpret_cast<const size_t*>(from)) + ->load(std::memory_order_relaxed); + *(reinterpret_cast<size_t*>(to) + 1) = + AsAtomicPtr(reinterpret_cast<const size_t*>(from) + 1) + ->load(std::memory_order_relaxed); + *(reinterpret_cast<size_t*>(to) + 2) = + AsAtomicPtr(reinterpret_cast<const size_t*>(from) + 2) + ->load(std::memory_order_relaxed); +} + +// Copies |bytes| bytes from |from| to |to| using atomic writes. Assumes |to| +// and |from| are size_t-aligned and point to buffers of size |bytes|. Note +// that atomicity is guaranteed only per word, not for the entire |bytes| +// bytes as a whole. When copying arrays of elements, If |to| and |from| are +// overlapping, should move the elements one by one. +WTF_EXPORT void AtomicWriteMemcpy(void* to, const void* from, size_t bytes); +template <size_t bytes> +ALWAYS_INLINE void AtomicWriteMemcpy(void* to, const void* from) { + static_assert(bytes > 0, "Number of copied bytes should be greater than 0"); + AtomicWriteMemcpy(to, from, bytes); +} + +// AtomicReadMemcpy specializations: + +#if defined(ARCH_CPU_X86_64) +template <> +ALWAYS_INLINE void AtomicWriteMemcpy<sizeof(uint32_t)>(void* to, + const void* from) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(to) & (sizeof(uint32_t) - 1)); + DCHECK_EQ(0u, reinterpret_cast<size_t>(from) & (sizeof(uint32_t) - 1)); + AsAtomicPtr(reinterpret_cast<uint32_t*>(to)) + ->store(*reinterpret_cast<const uint32_t*>(from), + std::memory_order_relaxed); +} +#endif // ARCH_CPU_X86_64 + +template <> +ALWAYS_INLINE void AtomicWriteMemcpy<sizeof(size_t)>(void* to, + const void* from) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(to) & (sizeof(size_t) - 1)); + DCHECK_EQ(0u, reinterpret_cast<size_t>(from) & (sizeof(size_t) - 1)); + AsAtomicPtr(reinterpret_cast<size_t*>(to)) + ->store(*reinterpret_cast<const size_t*>(from), + std::memory_order_relaxed); +} + +template <> +ALWAYS_INLINE void AtomicWriteMemcpy<2 * sizeof(size_t)>(void* to, + const void* from) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(to) & (sizeof(size_t) - 1)); + DCHECK_EQ(0u, reinterpret_cast<size_t>(from) & (sizeof(size_t) - 1)); + AsAtomicPtr(reinterpret_cast<size_t*>(to)) + ->store(*reinterpret_cast<const size_t*>(from), + std::memory_order_relaxed); + AsAtomicPtr(reinterpret_cast<size_t*>(to) + 1) + ->store(*(reinterpret_cast<const size_t*>(from) + 1), + std::memory_order_relaxed); +} + +template <> +ALWAYS_INLINE void AtomicWriteMemcpy<3 * sizeof(size_t)>(void* to, + const void* from) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(to) & (sizeof(size_t) - 1)); + DCHECK_EQ(0u, reinterpret_cast<size_t>(from) & (sizeof(size_t) - 1)); + AsAtomicPtr(reinterpret_cast<size_t*>(to)) + ->store(*reinterpret_cast<const size_t*>(from), + std::memory_order_relaxed); + AsAtomicPtr(reinterpret_cast<size_t*>(to) + 1) + ->store(*(reinterpret_cast<const size_t*>(from) + 1), + std::memory_order_relaxed); + AsAtomicPtr(reinterpret_cast<size_t*>(to) + 2) + ->store(*(reinterpret_cast<const size_t*>(from) + 2), + std::memory_order_relaxed); +} + +// Set the first |bytes| bytes of |buf| to 0 using atomic writes. Assumes |buf| +// is size_t-aligned and points to a buffer of size at least |bytes|. Note +// that atomicity is guaranteed only per word, not for the entire |bytes| bytes +// as a whole. +WTF_EXPORT void AtomicMemzero(void* buf, size_t bytes); + +template <size_t bytes> +ALWAYS_INLINE void AtomicMemzero(void* buf) { + static_assert(bytes > 0, "Number of copied bytes should be greater than 0"); + AtomicMemzero(buf, bytes); +} + +// AtomicReadMemcpy specializations: + +#if defined(ARCH_CPU_X86_64) +template <> +ALWAYS_INLINE void AtomicMemzero<sizeof(uint32_t)>(void* buf) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(buf) & (sizeof(uint32_t) - 1)); + AsAtomicPtr(reinterpret_cast<uint32_t*>(buf)) + ->store(0, std::memory_order_relaxed); +} +#endif // ARCH_CPU_X86_64 + +template <> +ALWAYS_INLINE void AtomicMemzero<sizeof(size_t)>(void* buf) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(buf) & (sizeof(size_t) - 1)); + AsAtomicPtr(reinterpret_cast<size_t*>(buf)) + ->store(0, std::memory_order_relaxed); +} + +template <> +ALWAYS_INLINE void AtomicMemzero<2 * sizeof(size_t)>(void* buf) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(buf) & (sizeof(size_t) - 1)); + AsAtomicPtr(reinterpret_cast<size_t*>(buf)) + ->store(0, std::memory_order_relaxed); + AsAtomicPtr(reinterpret_cast<size_t*>(buf) + 1) + ->store(0, std::memory_order_relaxed); +} + +template <> +ALWAYS_INLINE void AtomicMemzero<3 * sizeof(size_t)>(void* buf) { + DCHECK_EQ(0u, reinterpret_cast<size_t>(buf) & (sizeof(size_t) - 1)); + AsAtomicPtr(reinterpret_cast<size_t*>(buf)) + ->store(0, std::memory_order_relaxed); + AsAtomicPtr(reinterpret_cast<size_t*>(buf) + 1) + ->store(0, std::memory_order_relaxed); + AsAtomicPtr(reinterpret_cast<size_t*>(buf) + 2) + ->store(0, std::memory_order_relaxed); +} + +// Swaps values using atomic writes. +template <typename T> +ALWAYS_INLINE void AtomicWriteSwap(T& lhs, T& rhs) { + T tmp_val = rhs; + AsAtomicPtr(&rhs)->store(lhs, std::memory_order_relaxed); + AsAtomicPtr(&lhs)->store(tmp_val, std::memory_order_relaxed); +} + } // namespace WTF // This version of placement new omits a 0 check. @@ -156,20 +358,4 @@ inline void* operator new(size_t, NotNullTag, void* location) { return location; } -#if defined(__clang__) && __has_attribute(uninitialized) -// Attribute "uninitialized" disables -ftrivial-auto-var-init=pattern for -// the specified variable. -// -// -ftrivial-auto-var-init is security risk mitigation feature, so attribute -// should not be used "just in case", but only to fix real performance -// bottlenecks when other approaches do not work. In general the compiler is -// quite effective at eliminating unneeded initializations introduced by the -// flag, e.g. when they are followed by actual initialization by a program. -// However if compiler optimization fails and code refactoring is hard, the -// attribute can be used as a workaround. -#define STACK_UNINITIALIZED __attribute__((uninitialized)) -#else -#define STACK_UNINITIALIZED -#endif - #endif /* WTF_Allocator_h */ diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/atomic_operations_test.cc b/chromium/third_party/blink/renderer/platform/wtf/allocator/atomic_operations_test.cc new file mode 100644 index 00000000000..819b3bff584 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/atomic_operations_test.cc @@ -0,0 +1,139 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace WTF { + +class AtomicOperationsTest : public ::testing::Test {}; + +template <size_t buffer_size, typename CopyMethod> +void TestCopyImpl(CopyMethod copy) { + alignas(sizeof(size_t)) unsigned char src[buffer_size]; + for (size_t i = 0; i < buffer_size; ++i) + src[i] = static_cast<char>(i + 1); + // Allocating extra memory before and after the buffer to make sure the + // atomic memcpy doesn't exceed the buffer in any direction. + alignas(sizeof(size_t)) unsigned char tgt[buffer_size + (2 * sizeof(size_t))]; + memset(tgt, 0, buffer_size + (2 * sizeof(size_t))); + copy(tgt + sizeof(size_t), src); + // Check nothing before the buffer was changed + EXPECT_EQ(0u, *reinterpret_cast<size_t*>(&tgt[0])); + // Check buffer was copied correctly + EXPECT_TRUE(!memcmp(src, tgt + sizeof(size_t), buffer_size)); + // Check nothing after the buffer was changed + EXPECT_EQ(0u, *reinterpret_cast<size_t*>(&tgt[sizeof(size_t) + buffer_size])); +} + +// Tests for AtomicReadMemcpy +template <size_t buffer_size> +void TestAtomicReadMemcpy() { + TestCopyImpl<buffer_size>(AtomicReadMemcpy<buffer_size>); +} + +TEST_F(AtomicOperationsTest, AtomicReadMemcpy_UINT8T) { + TestAtomicReadMemcpy<sizeof(uint8_t)>(); +} +TEST_F(AtomicOperationsTest, AtomicReadMemcpy_UINT16T) { + TestAtomicReadMemcpy<sizeof(uint16_t)>(); +} +TEST_F(AtomicOperationsTest, AtomicReadMemcpy_UINT32T) { + TestAtomicReadMemcpy<sizeof(uint32_t)>(); +} +TEST_F(AtomicOperationsTest, AtomicReadMemcpy_UINT64T) { + TestAtomicReadMemcpy<sizeof(uint64_t)>(); +} + +TEST_F(AtomicOperationsTest, AtomicReadMemcpy_17Bytes) { + TestAtomicReadMemcpy<17>(); +} +TEST_F(AtomicOperationsTest, AtomicReadMemcpy_34Bytes) { + TestAtomicReadMemcpy<34>(); +} +TEST_F(AtomicOperationsTest, AtomicReadMemcpy_68Bytes) { + TestAtomicReadMemcpy<68>(); +} +TEST_F(AtomicOperationsTest, AtomicReadMemcpy_127Bytes) { + TestAtomicReadMemcpy<127>(); +} + +// Tests for AtomicWriteMemcpy +template <size_t buffer_size> +void TestAtomicWriteMemcpy() { + TestCopyImpl<buffer_size>(AtomicWriteMemcpy<buffer_size>); +} + +TEST_F(AtomicOperationsTest, AtomicWriteMemcpy_UINT8T) { + TestAtomicWriteMemcpy<sizeof(uint8_t)>(); +} +TEST_F(AtomicOperationsTest, AtomicWriteMemcpy_UINT16T) { + TestAtomicWriteMemcpy<sizeof(uint16_t)>(); +} +TEST_F(AtomicOperationsTest, AtomicWriteMemcpy_UINT32T) { + TestAtomicWriteMemcpy<sizeof(uint32_t)>(); +} +TEST_F(AtomicOperationsTest, AtomicWriteMemcpy_UINT64T) { + TestAtomicWriteMemcpy<sizeof(uint64_t)>(); +} + +TEST_F(AtomicOperationsTest, AtomicWriteMemcpy_17Bytes) { + TestAtomicWriteMemcpy<17>(); +} +TEST_F(AtomicOperationsTest, AtomicWriteMemcpy_34Bytes) { + TestAtomicWriteMemcpy<34>(); +} +TEST_F(AtomicOperationsTest, AtomicWriteMemcpy_68Bytes) { + TestAtomicWriteMemcpy<68>(); +} +TEST_F(AtomicOperationsTest, AtomicWriteMemcpy_127Bytes) { + TestAtomicWriteMemcpy<127>(); +} + +// Tests for AtomicMemzero +template <size_t buffer_size> +void TestAtomicMemzero() { + // Allocating extra memory before and after the buffer to make sure the + // AtomicMemzero doesn't exceed the buffer in any direction. + alignas(sizeof(size_t)) unsigned char buf[buffer_size + (2 * sizeof(size_t))]; + memset(buf, ~uint8_t{0}, buffer_size + (2 * sizeof(size_t))); + AtomicMemzero<buffer_size>(buf + sizeof(size_t)); + // Check nothing before the buffer was changed + EXPECT_EQ(~size_t{0}, *reinterpret_cast<size_t*>(&buf[0])); + // Check buffer was copied correctly + static const unsigned char for_comparison[buffer_size] = {0}; + EXPECT_TRUE(!memcmp(buf + sizeof(size_t), for_comparison, buffer_size)); + // Check nothing after the buffer was changed + EXPECT_EQ(~size_t{0}, + *reinterpret_cast<size_t*>(&buf[sizeof(size_t) + buffer_size])); +} + +TEST_F(AtomicOperationsTest, AtomicMemzero_UINT8T) { + TestAtomicMemzero<sizeof(uint8_t)>(); +} +TEST_F(AtomicOperationsTest, AtomicMemzero_UINT16T) { + TestAtomicMemzero<sizeof(uint16_t)>(); +} +TEST_F(AtomicOperationsTest, AtomicMemzero_UINT32T) { + TestAtomicMemzero<sizeof(uint32_t)>(); +} +TEST_F(AtomicOperationsTest, AtomicMemzero_UINT64T) { + TestAtomicMemzero<sizeof(uint64_t)>(); +} + +TEST_F(AtomicOperationsTest, AtomicMemzero_17Bytes) { + TestAtomicMemzero<17>(); +} +TEST_F(AtomicOperationsTest, AtomicMemzero_34Bytes) { + TestAtomicMemzero<34>(); +} +TEST_F(AtomicOperationsTest, AtomicMemzero_68Bytes) { + TestAtomicMemzero<68>(); +} +TEST_F(AtomicOperationsTest, AtomicMemzero_127Bytes) { + TestAtomicMemzero<127>(); +} + +} // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h b/chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h index 143b0584ce5..e1bc3a994e4 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h +++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h @@ -76,14 +76,16 @@ class WTF_EXPORT PartitionAllocator { Free(ptr); // Not the system free, the one from this class. } - static void TraceMarkedBackingStore(void*) {} - static void BackingWriteBarrier(void*) {} - template <typename> - static void BackingWriteBarrierForHashTable(void*) {} + static void TraceBackingStoreIfMarked(const void*) {} + template <typename T> + static void BackingWriteBarrier(T**) {} + template <typename, typename T> + static void BackingWriteBarrierForHashTable(T**) {} static bool IsAllocationAllowed() { return true; } static bool IsObjectResurrectionForbidden() { return false; } static bool IsSweepForbidden() { return false; } + static bool IsIncrementalMarking() { return false; } static void EnterGCForbiddenScope() {} static void LeaveGCForbiddenScope() {} diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc index cde57169e59..abb17cad6d9 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.cc @@ -36,7 +36,6 @@ #include "base/allocator/partition_allocator/partition_alloc.h" #include "base/allocator/partition_allocator/partition_root_base.h" #include "base/debug/alias.h" -#include "base/lazy_instance.h" #include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h" #include "third_party/blink/renderer/platform/wtf/wtf.h" @@ -45,63 +44,54 @@ namespace WTF { const char* const Partitions::kAllocatedObjectPoolName = "partition_alloc/allocated_objects"; -static base::LazyInstance<base::subtle::SpinLock>::Leaky initialization_lock_ = - LAZY_INSTANCE_INITIALIZER; bool Partitions::initialized_ = false; -// These statics are inlined, so cannot be LazyInstances. We create -// LazyInstances below, and then set the pointers correctly in Initialize(). +// These statics are inlined, so cannot be LazyInstances. We create the values, +// and then set the pointers correctly in Initialize(). base::PartitionRootGeneric* Partitions::fast_malloc_root_ = nullptr; base::PartitionRootGeneric* Partitions::array_buffer_root_ = nullptr; base::PartitionRootGeneric* Partitions::buffer_root_ = nullptr; base::PartitionRoot* Partitions::layout_root_ = nullptr; -static base::LazyInstance<base::PartitionAllocatorGeneric>::Leaky - lazy_fast_malloc = LAZY_INSTANCE_INITIALIZER; -static base::LazyInstance<base::PartitionAllocatorGeneric>::Leaky - lazy_array_buffer = LAZY_INSTANCE_INITIALIZER; -static base::LazyInstance<base::PartitionAllocatorGeneric>::Leaky lazy_buffer = - LAZY_INSTANCE_INITIALIZER; -static base::LazyInstance<base::SizeSpecificPartitionAllocator<1024>>::Leaky - lazy_layout = LAZY_INSTANCE_INITIALIZER; - +// static void Partitions::Initialize() { - base::subtle::SpinLock::Guard guard(initialization_lock_.Get()); - - if (!initialized_) { - base::PartitionAllocatorGeneric* fast_malloc_allocator = - lazy_fast_malloc.Pointer(); - base::PartitionAllocatorGeneric* array_buffer_allocator = - lazy_array_buffer.Pointer(); - base::PartitionAllocatorGeneric* buffer_allocator = lazy_buffer.Pointer(); - base::SizeSpecificPartitionAllocator<1024>* layout_allocator = - lazy_layout.Pointer(); - - base::PartitionAllocGlobalInit(&Partitions::HandleOutOfMemory); - fast_malloc_allocator->init(); - array_buffer_allocator->init(); - buffer_allocator->init(); - layout_allocator->init(); - - fast_malloc_root_ = fast_malloc_allocator->root(); - array_buffer_root_ = array_buffer_allocator->root(); - buffer_root_ = buffer_allocator->root(); - layout_root_ = layout_allocator->root(); - - initialized_ = true; - } + static bool initialized = InitializeOnce(); + DCHECK(initialized); +} + +// static +bool Partitions::InitializeOnce() { + static base::PartitionAllocatorGeneric fast_malloc_allocator{}; + static base::PartitionAllocatorGeneric array_buffer_allocator{}; + static base::PartitionAllocatorGeneric buffer_allocator{}; + static base::SizeSpecificPartitionAllocator<1024> layout_allocator{}; + + base::PartitionAllocGlobalInit(&Partitions::HandleOutOfMemory); + + fast_malloc_allocator.init(); + array_buffer_allocator.init(); + buffer_allocator.init(); + layout_allocator.init(); + + fast_malloc_root_ = fast_malloc_allocator.root(); + array_buffer_root_ = array_buffer_allocator.root(); + buffer_root_ = buffer_allocator.root(); + layout_root_ = layout_allocator.root(); + + initialized_ = true; + return initialized_; } // static void Partitions::StartPeriodicReclaim( scoped_refptr<base::SequencedTaskRunner> task_runner) { CHECK(IsMainThread()); - if (!initialized_) - return; + DCHECK(initialized_); base::PartitionAllocMemoryReclaimer::Instance()->Start(task_runner); } +// static void Partitions::DumpMemoryStats( bool is_light_dump, base::PartitionStatsDumper* partition_stats_dumper) { @@ -141,6 +131,7 @@ class LightPartitionStatsDumperImpl : public base::PartitionStatsDumper { } // namespace +// static size_t Partitions::TotalSizeOfCommittedPages() { DCHECK(initialized_); size_t total_size = 0; @@ -151,118 +142,128 @@ size_t Partitions::TotalSizeOfCommittedPages() { return total_size; } +// static size_t Partitions::TotalActiveBytes() { LightPartitionStatsDumperImpl dumper; WTF::Partitions::DumpMemoryStats(true, &dumper); return dumper.TotalActiveBytes(); } -static NOINLINE void PartitionsOutOfMemoryUsing2G() { +static NOINLINE void PartitionsOutOfMemoryUsing2G(size_t size) { size_t signature = 2UL * 1024 * 1024 * 1024; base::debug::Alias(&signature); - OOM_CRASH(); + OOM_CRASH(size); } -static NOINLINE void PartitionsOutOfMemoryUsing1G() { +static NOINLINE void PartitionsOutOfMemoryUsing1G(size_t size) { size_t signature = 1UL * 1024 * 1024 * 1024; base::debug::Alias(&signature); - OOM_CRASH(); + OOM_CRASH(size); } -static NOINLINE void PartitionsOutOfMemoryUsing512M() { +static NOINLINE void PartitionsOutOfMemoryUsing512M(size_t size) { size_t signature = 512 * 1024 * 1024; base::debug::Alias(&signature); - OOM_CRASH(); + OOM_CRASH(size); } -static NOINLINE void PartitionsOutOfMemoryUsing256M() { +static NOINLINE void PartitionsOutOfMemoryUsing256M(size_t size) { size_t signature = 256 * 1024 * 1024; base::debug::Alias(&signature); - OOM_CRASH(); + OOM_CRASH(size); } -static NOINLINE void PartitionsOutOfMemoryUsing128M() { +static NOINLINE void PartitionsOutOfMemoryUsing128M(size_t size) { size_t signature = 128 * 1024 * 1024; base::debug::Alias(&signature); - OOM_CRASH(); + OOM_CRASH(size); } -static NOINLINE void PartitionsOutOfMemoryUsing64M() { +static NOINLINE void PartitionsOutOfMemoryUsing64M(size_t size) { size_t signature = 64 * 1024 * 1024; base::debug::Alias(&signature); - OOM_CRASH(); + OOM_CRASH(size); } -static NOINLINE void PartitionsOutOfMemoryUsing32M() { +static NOINLINE void PartitionsOutOfMemoryUsing32M(size_t size) { size_t signature = 32 * 1024 * 1024; base::debug::Alias(&signature); - OOM_CRASH(); + OOM_CRASH(size); } -static NOINLINE void PartitionsOutOfMemoryUsing16M() { +static NOINLINE void PartitionsOutOfMemoryUsing16M(size_t size) { size_t signature = 16 * 1024 * 1024; base::debug::Alias(&signature); - OOM_CRASH(); + OOM_CRASH(size); } -static NOINLINE void PartitionsOutOfMemoryUsingLessThan16M() { +static NOINLINE void PartitionsOutOfMemoryUsingLessThan16M(size_t size) { size_t signature = 16 * 1024 * 1024 - 1; base::debug::Alias(&signature); - DLOG(FATAL) << "ParitionAlloc: out of memory with < 16M usage (error:" + DLOG(FATAL) << "PartitionAlloc: out of memory with < 16M usage (error:" << base::GetAllocPageErrorCode() << ")"; + OOM_CRASH(size); } +// static void* Partitions::BufferMalloc(size_t n, const char* type_name) { return BufferPartition()->Alloc(n, type_name); } +// static void* Partitions::BufferTryRealloc(void* p, size_t n, const char* type_name) { return BufferPartition()->TryRealloc(p, n, type_name); } +// static void Partitions::BufferFree(void* p) { BufferPartition()->Free(p); } +// static size_t Partitions::BufferActualSize(size_t n) { return BufferPartition()->ActualSize(n); } +// static void* Partitions::FastMalloc(size_t n, const char* type_name) { return FastMallocPartition()->Alloc(n, type_name); } +// static void* Partitions::FastZeroedMalloc(size_t n, const char* type_name) { return FastMallocPartition()->AllocFlags(base::PartitionAllocZeroFill, n, type_name); } +// static void Partitions::FastFree(void* p) { FastMallocPartition()->Free(p); } -void Partitions::HandleOutOfMemory() { +// static +void Partitions::HandleOutOfMemory(size_t size) { volatile size_t total_usage = TotalSizeOfCommittedPages(); uint32_t alloc_page_error_code = base::GetAllocPageErrorCode(); base::debug::Alias(&alloc_page_error_code); if (total_usage >= 2UL * 1024 * 1024 * 1024) - PartitionsOutOfMemoryUsing2G(); + PartitionsOutOfMemoryUsing2G(size); if (total_usage >= 1UL * 1024 * 1024 * 1024) - PartitionsOutOfMemoryUsing1G(); + PartitionsOutOfMemoryUsing1G(size); if (total_usage >= 512 * 1024 * 1024) - PartitionsOutOfMemoryUsing512M(); + PartitionsOutOfMemoryUsing512M(size); if (total_usage >= 256 * 1024 * 1024) - PartitionsOutOfMemoryUsing256M(); + PartitionsOutOfMemoryUsing256M(size); if (total_usage >= 128 * 1024 * 1024) - PartitionsOutOfMemoryUsing128M(); + PartitionsOutOfMemoryUsing128M(size); if (total_usage >= 64 * 1024 * 1024) - PartitionsOutOfMemoryUsing64M(); + PartitionsOutOfMemoryUsing64M(size); if (total_usage >= 32 * 1024 * 1024) - PartitionsOutOfMemoryUsing32M(); + PartitionsOutOfMemoryUsing32M(size); if (total_usage >= 16 * 1024 * 1024) - PartitionsOutOfMemoryUsing16M(); - PartitionsOutOfMemoryUsingLessThan16M(); + PartitionsOutOfMemoryUsing16M(size); + PartitionsOutOfMemoryUsingLessThan16M(size); } } // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h index 48e833a2467..4dcc3564ed8 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h +++ b/chromium/third_party/blink/renderer/platform/wtf/allocator/partitions.h @@ -50,6 +50,7 @@ class WTF_EXPORT Partitions { // memory snapshots. static const char* const kAllocatedObjectPoolName; + // Should be called on the thread which is or will become the main one. static void Initialize(); static void StartPeriodicReclaim( scoped_refptr<base::SequencedTaskRunner> task_runner); @@ -90,7 +91,7 @@ class WTF_EXPORT Partitions { static void* FastZeroedMalloc(size_t n, const char* type_name); static void FastFree(void* p); - static void HandleOutOfMemory(); + static void HandleOutOfMemory(size_t size); private: ALWAYS_INLINE static base::PartitionRootGeneric* FastMallocPartition() { @@ -98,8 +99,9 @@ class WTF_EXPORT Partitions { return fast_malloc_root_; } - static bool initialized_; + static bool InitializeOnce(); + static bool initialized_; // See Allocator.md for a description of these partitions. static base::PartitionRootGeneric* fast_malloc_root_; static base::PartitionRootGeneric* array_buffer_root_; diff --git a/chromium/third_party/blink/renderer/platform/wtf/bit_field.h b/chromium/third_party/blink/renderer/platform/wtf/bit_field.h new file mode 100644 index 00000000000..e65e183b475 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/wtf/bit_field.h @@ -0,0 +1,153 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIT_FIELD_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIT_FIELD_H_ + +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +namespace WTF { + +enum class BitFieldValueConstness { + kNonConst, + kConst, +}; + +namespace internal { + +template <class BitFieldType> +class BitFieldBase; + +// Helper class for defining values in a bit field. This helper provides +// utilities to read, write and update the value in the bit field. +template <class ValueType, + size_t offset, + size_t size, + class BitFieldType, + BitFieldValueConstness is_const = BitFieldValueConstness::kNonConst> +class BitFieldValue final { + static_assert(std::is_fundamental<ValueType>::value, + "Fields in a bit field must be of a primitive type."); + static_assert(std::is_fundamental<BitFieldType>::value, + "Bit fields must be of a primitive type."); + static_assert(std::is_unsigned<BitFieldType>::value, + "Bit field must be of an unsigned type"); + static_assert(sizeof(ValueType) <= sizeof(BitFieldType), + "Value in bit field cannot be bigger than the bit field"); + static_assert( + offset < 8 * sizeof(BitFieldType), + "Field offset in bit field must be smaller than the bit field size"); + static_assert( + size < 8 * sizeof(BitFieldType), + "Field size in bit field must be smaller than the bit field size"); + static_assert(offset + size <= 8 * sizeof(BitFieldType), + "Field in bit field cannot overflow the bit field"); + static_assert(size > 0, "Bit field fields cannot have 0 size."); + + public: + using Type = ValueType; + + template <class OtherValueType, + int other_size, + BitFieldValueConstness other_is_const = + BitFieldValueConstness::kNonConst> + using DefineNextValue = BitFieldValue<OtherValueType, + offset + size, + other_size, + BitFieldType, + other_is_const>; + + // Create a bit field with the given value. + static constexpr BitFieldType encode(ValueType value) { + DCHECK(is_valid(value)); + return static_cast<BitFieldType>(value) << offset; + } + + // Update a bit field with the given value. + static constexpr BitFieldType update(BitFieldType previous, ValueType value) { + return (previous & ~kMask) | encode(value); + } + + // Read the value from the bit field. + static constexpr ValueType decode(BitFieldType value) { + return static_cast<ValueType>((value & kMask) >> offset); + } + + private: + static constexpr BitFieldValueConstness kIsConst = is_const; + + static constexpr BitFieldType kValidationMask = + (BitFieldType{1} << size) - BitFieldType{1}; + static constexpr BitFieldType kMask = (kValidationMask) << offset; + static_assert(kMask != 0, "Mask in which all bits are 0 is not allowed."); + static_assert(~kMask != 0, "Mask in which all bits are 1 is not allowed."); + + // Confirm that the provided value fits into the bit field. + static constexpr bool is_valid(ValueType value) { + return (static_cast<BitFieldType>(value) & ~kValidationMask) == 0; + } + + friend class BitFieldBase<BitFieldType>; +}; + +} // namespace internal + +// BitField intended to be used by a single thread. +template <class BitFieldType> +class WTF_EXPORT SingleThreadedBitField { + static_assert(std::is_fundamental<BitFieldType>::value, + "Bit fields must be of a primitive type."); + static_assert(std::is_unsigned<BitFieldType>::value, + "Bit field must be of an unsigned type"); + + public: + template <class Type, + int size, + BitFieldValueConstness is_const = BitFieldValueConstness::kNonConst> + using DefineFirstValue = + internal::BitFieldValue<Type, 0, size, BitFieldType, is_const>; + + explicit SingleThreadedBitField() : SingleThreadedBitField(0) {} + explicit SingleThreadedBitField(BitFieldType bits) : bits_(bits) {} + + template <typename Value> + typename Value::Type get() const { + return Value::decode(bits_); + } + + template <typename Value> + void set(typename Value::Type value) { + bits_ = Value::update(bits_, value); + } + + protected: + BitFieldType bits_; +}; + +// BitField that can be written by a single thread but read by multiple threads. +template <class BitFieldType> +class WTF_EXPORT ConcurrentlyReadBitField + : public SingleThreadedBitField<BitFieldType> { + using Base = SingleThreadedBitField<BitFieldType>; + using Base::bits_; + + public: + explicit ConcurrentlyReadBitField() : Base(0) {} + explicit ConcurrentlyReadBitField(BitFieldType bits) : Base(bits) {} + + template <typename Value> + typename Value::Type get_concurrently() const { + return Value::decode(AsAtomicPtr(&bits_)->load(std::memory_order_relaxed)); + } + + template <typename Value> + void set(typename Value::Type value) { + AsAtomicPtr(&bits_)->store(Value::update(bits_, value), + std::memory_order_relaxed); + } +}; + +} // namespace WTF + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_BIT_FIELD_H_ diff --git a/chromium/third_party/blink/renderer/platform/wtf/bit_field_test.cc b/chromium/third_party/blink/renderer/platform/wtf/bit_field_test.cc new file mode 100644 index 00000000000..795db25fbe5 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/wtf/bit_field_test.cc @@ -0,0 +1,105 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/wtf/bit_field.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace WTF { + +class BitFieldTest : public ::testing::Test {}; + +TEST_F(BitFieldTest, BitFieldDefaultCtor) { + using BitField = SingleThreadedBitField<uint32_t>; + using Value1 = BitField::DefineFirstValue<uint32_t, 31>; + using Value2 = Value1::DefineNextValue<uint32_t, 1>; + + SingleThreadedBitField<uint32_t> bit_field; + EXPECT_EQ(0u, bit_field.get<Value1>()); + EXPECT_EQ(0u, bit_field.get<Value2>()); +} + +TEST_F(BitFieldTest, BitFieldCtor) { + using BitField = SingleThreadedBitField<uint32_t>; + using Value1 = BitField::DefineFirstValue<uint32_t, 31>; + using Value2 = Value1::DefineNextValue<uint32_t, 1>; + + SingleThreadedBitField<uint32_t> bit_field(0xdeadbeef); + EXPECT_EQ(0x5eadbeefu, bit_field.get<Value1>()); + EXPECT_EQ(1u, bit_field.get<Value2>()); +} + +TEST_F(BitFieldTest, SplitBitField) { + using BitField = SingleThreadedBitField<uint32_t>; + using Value1 = BitField::DefineFirstValue<uint16_t, 16>; + using Value2 = Value1::DefineNextValue<uint16_t, 8>; + using Value3 = Value2::DefineNextValue<uint16_t, 8>; + + SingleThreadedBitField<uint32_t> bit_field(0xdeadbeef); + EXPECT_EQ(0xde, bit_field.get<Value3>()); + EXPECT_EQ(0xad, bit_field.get<Value2>()); + EXPECT_EQ(0xbeef, bit_field.get<Value1>()); +} + +TEST_F(BitFieldTest, BitFieldBits) { + using BitField = SingleThreadedBitField<uint8_t>; + using Value1 = BitField::DefineFirstValue<bool, 1>; + using Value2 = Value1::DefineNextValue<bool, 1>; + using Value3 = Value2::DefineNextValue<bool, 1>; + using Value4 = Value3::DefineNextValue<bool, 1>; + using Value5 = Value4::DefineNextValue<bool, 1>; + using Value6 = Value5::DefineNextValue<bool, 1>; + using Value7 = Value6::DefineNextValue<bool, 1>; + using Value8 = Value7::DefineNextValue<bool, 1>; + + SingleThreadedBitField<uint32_t> bit_field(0b10101010); + EXPECT_FALSE(bit_field.get<Value1>()); + EXPECT_TRUE(bit_field.get<Value2>()); + EXPECT_FALSE(bit_field.get<Value3>()); + EXPECT_TRUE(bit_field.get<Value4>()); + EXPECT_FALSE(bit_field.get<Value5>()); + EXPECT_TRUE(bit_field.get<Value6>()); + EXPECT_FALSE(bit_field.get<Value7>()); + EXPECT_TRUE(bit_field.get<Value8>()); +} + +TEST_F(BitFieldTest, BitFieldSetValue) { + using BitField = SingleThreadedBitField<uint32_t>; + using Value1 = BitField::DefineFirstValue<uint16_t, 16>; + using Value2 = Value1::DefineNextValue<uint16_t, 16>; + + SingleThreadedBitField<uint32_t> bit_field; + CHECK_EQ(0u, bit_field.get<Value1>()); + CHECK_EQ(0u, bit_field.get<Value2>()); + bit_field.set<Value1>(1337); + EXPECT_EQ(1337u, bit_field.get<Value1>()); + EXPECT_EQ(0u, bit_field.get<Value2>()); +} + +TEST_F(BitFieldTest, ConcurrentBitFieldGettersReturnTheSame) { + using BitField = SingleThreadedBitField<uint32_t>; + using Value1 = BitField::DefineFirstValue<uint16_t, 16>; + using Value2 = Value1::DefineNextValue<uint16_t, 16>; + + ConcurrentlyReadBitField<uint32_t> bit_field(0xdeadbeef); + CHECK_EQ(0xbeef, bit_field.get<Value1>()); + CHECK_EQ(0xdead, bit_field.get<Value2>()); + EXPECT_EQ(bit_field.get_concurrently<Value1>(), bit_field.get<Value1>()); + EXPECT_EQ(bit_field.get_concurrently<Value2>(), bit_field.get<Value2>()); +} + +TEST_F(BitFieldTest, ConcurrentBitFieldSetValue) { + using BitField = SingleThreadedBitField<uint32_t>; + using Value1 = BitField::DefineFirstValue<uint16_t, 16>; + using Value2 = Value1::DefineNextValue<uint16_t, 16>; + + ConcurrentlyReadBitField<uint32_t> bit_field; + CHECK_EQ(0u, bit_field.get_concurrently<Value1>()); + CHECK_EQ(0u, bit_field.get_concurrently<Value2>()); + bit_field.set<Value1>(1337); + EXPECT_EQ(1337u, bit_field.get_concurrently<Value1>()); + EXPECT_EQ(0u, bit_field.get_concurrently<Value2>()); +} + +} // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/construct_traits.h b/chromium/third_party/blink/renderer/platform/wtf/construct_traits.h index 7812accbc0b..02741c5c9c6 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/construct_traits.h +++ b/chromium/third_party/blink/renderer/platform/wtf/construct_traits.h @@ -11,9 +11,9 @@ namespace WTF { -// ConstructTraits is used to construct elements in WTF collections. All -// in-place constructions that may assign Oilpan objects must be dispatched -// through ConstructAndNotifyElement. +// ConstructTraits is used to construct elements in WTF collections. +// All in-place constructions that may assign Oilpan objects must be +// dispatched through ConstructAndNotifyElement. template <typename T, typename Traits, typename Allocator> class ConstructTraits { STATIC_ONLY(ConstructTraits); diff --git a/chromium/third_party/blink/renderer/platform/wtf/cross_thread_copier.h b/chromium/third_party/blink/renderer/platform/wtf/cross_thread_copier.h index 51b1575473f..1aeaf9b843b 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/cross_thread_copier.h +++ b/chromium/third_party/blink/renderer/platform/wtf/cross_thread_copier.h @@ -69,6 +69,12 @@ struct SyncToken; namespace mojo { template <typename Interface> class PendingReceiver; +template <typename Interface> +class PendingRemote; +template <typename Interface> +class PendingAssociatedRemote; +template <typename Interface> +class PendingAssociatedReceiver; } namespace WTF { @@ -80,6 +86,15 @@ struct CrossThreadCopierPassThrough { static Type Copy(const T& parameter) { return parameter; } }; +template <typename T> +struct CrossThreadCopierByValuePassThrough { + STATIC_ONLY(CrossThreadCopierByValuePassThrough); + typedef T Type; + static Type Copy(T receiver) { + return receiver; // This is in fact a move. + } +}; + template <typename T, bool isArithmeticOrEnum> struct CrossThreadCopierBase; @@ -273,12 +288,31 @@ struct CrossThreadCopier<String> { }; template <typename Interface> -struct CrossThreadCopier<mojo::PendingReceiver<Interface>> { +struct CrossThreadCopier<mojo::PendingReceiver<Interface>> + : public CrossThreadCopierByValuePassThrough< + mojo::PendingReceiver<Interface>> { + STATIC_ONLY(CrossThreadCopier); +}; + +template <typename Interface> +struct CrossThreadCopier<mojo::PendingRemote<Interface>> + : public CrossThreadCopierByValuePassThrough< + mojo::PendingRemote<Interface>> { + STATIC_ONLY(CrossThreadCopier); +}; + +template <typename Interface> +struct CrossThreadCopier<mojo::PendingAssociatedRemote<Interface>> + : public CrossThreadCopierByValuePassThrough< + mojo::PendingAssociatedRemote<Interface>> { + STATIC_ONLY(CrossThreadCopier); +}; + +template <typename Interface> +struct CrossThreadCopier<mojo::PendingAssociatedReceiver<Interface>> + : public CrossThreadCopierByValuePassThrough< + mojo::PendingAssociatedReceiver<Interface>> { STATIC_ONLY(CrossThreadCopier); - using Type = mojo::PendingReceiver<Interface>; - static Type Copy(Type receiver) { - return receiver; // This is in fact a move. - } }; template <> diff --git a/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional.h b/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional.h index 86f58560098..973b8138f7d 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional.h +++ b/chromium/third_party/blink/renderer/platform/wtf/cross_thread_functional.h @@ -75,10 +75,10 @@ auto CrossThreadBindRepeating(FunctionType&& function, Ps&&... parameters) { std::decay_t<Ps>...>::ok, "A bound argument uses a bad pattern."); return internal::MakeCrossThreadFunction( - base::Bind(internal::CoerceFunctorForCrossThreadBind( - std::forward<FunctionType>(function)), - CrossThreadCopier<std::decay_t<Ps>>::Copy( - std::forward<Ps>(parameters))...)); + base::BindRepeating(internal::CoerceFunctorForCrossThreadBind( + std::forward<FunctionType>(function)), + CrossThreadCopier<std::decay_t<Ps>>::Copy( + std::forward<Ps>(parameters))...)); } template <typename FunctionType, typename... Ps> diff --git a/chromium/third_party/blink/renderer/platform/wtf/deque.h b/chromium/third_party/blink/renderer/platform/wtf/deque.h index e3e444e701b..4f2d25a8895 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/deque.h +++ b/chromium/third_party/blink/renderer/platform/wtf/deque.h @@ -146,7 +146,7 @@ class Deque : public ConditionalDestructor<Deque<T, INLINE_CAPACITY, Allocator>, void clear(); template <typename VisitorDispatcher, typename A = Allocator> - std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher); + std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher) const; static_assert(!std::is_polymorphic<T>::value || !VectorTraits<T>::kCanInitializeWithMemset, @@ -162,6 +162,7 @@ class Deque : public ConditionalDestructor<Deque<T, INLINE_CAPACITY, Allocator>, protected: T** GetBufferSlot() { return buffer_.BufferSlot(); } + const T* const* GetBufferSlot() const { return buffer_.BufferSlot(); } private: friend class DequeIteratorBase<T, inlineCapacity, Allocator>; @@ -169,8 +170,11 @@ class Deque : public ConditionalDestructor<Deque<T, INLINE_CAPACITY, Allocator>, class BackingBuffer : public VectorBuffer<T, INLINE_CAPACITY, Allocator> { private: using Base = VectorBuffer<T, INLINE_CAPACITY, Allocator>; + using Base::BufferSafe; using Base::size_; + friend class Deque; + public: BackingBuffer() : Base() {} explicit BackingBuffer(wtf_size_t capacity) : Base(capacity) {} @@ -236,6 +240,7 @@ class DequeIterator : public DequeIteratorBase<T, inlineCapacity, Allocator> { typedef T& reference; typedef std::bidirectional_iterator_tag iterator_category; + DequeIterator() = default; DequeIterator(Deque<T, inlineCapacity, Allocator>* deque, wtf_size_t index) : Base(deque, index) {} @@ -676,39 +681,23 @@ inline T* DequeIteratorBase<T, inlineCapacity, Allocator>::Before() const { template <typename T, wtf_size_t inlineCapacity, typename Allocator> template <typename VisitorDispatcher, typename A> std::enable_if_t<A::kIsGarbageCollected> -Deque<T, inlineCapacity, Allocator>::Trace(VisitorDispatcher visitor) { +Deque<T, inlineCapacity, Allocator>::Trace(VisitorDispatcher visitor) const { + // Bail out for concurrent marking. + if (visitor->ConcurrentTracingBailOut( + {this, [](blink::Visitor* visitor, const void* object) { + reinterpret_cast<const Deque<T, inlineCapacity, Allocator>*>( + object) + ->Trace(visitor); + }})) + return; + + static_assert(inlineCapacity == 0, + "Heap allocated Deque should not use inline buffer"); static_assert(Allocator::kIsGarbageCollected, "Garbage collector must be enabled."); - if (buffer_.HasOutOfLineBuffer()) { - Allocator::TraceVectorBacking(visitor, buffer_.Buffer(), - buffer_.BufferSlot()); - } else { - Allocator::TraceVectorBacking(visitor, static_cast<T*>(nullptr), - buffer_.BufferSlot()); - const T* buffer_begin = buffer_.Buffer(); - const T* end = buffer_begin + end_; - if (IsTraceableInCollectionTrait<VectorTraits<T>>::value) { - if (start_ <= end_) { - for (const T* buffer_entry = buffer_begin + start_; buffer_entry != end; - buffer_entry++) { - Allocator::template Trace<T, VectorTraits<T>>( - visitor, *const_cast<T*>(buffer_entry)); - } - } else { - for (const T* buffer_entry = buffer_begin; buffer_entry != end; - buffer_entry++) { - Allocator::template Trace<T, VectorTraits<T>>( - visitor, *const_cast<T*>(buffer_entry)); - } - const T* buffer_end = buffer_.Buffer() + buffer_.capacity(); - for (const T* buffer_entry = buffer_begin + start_; - buffer_entry != buffer_end; buffer_entry++) { - Allocator::template Trace<T, VectorTraits<T>>( - visitor, *const_cast<T*>(buffer_entry)); - } - } - } - } + const T* buffer = buffer_.BufferSafe(); + DCHECK(!buffer || buffer_.IsOutOfLineBuffer(buffer)); + Allocator::TraceVectorBacking(visitor, buffer, buffer_.BufferSlot()); } template <typename T, wtf_size_t inlineCapacity, typename Allocator> diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_counted_set.h b/chromium/third_party/blink/renderer/platform/wtf/hash_counted_set.h index 9e15db75c15..23d62960ec9 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/hash_counted_set.h +++ b/chromium/third_party/blink/renderer/platform/wtf/hash_counted_set.h @@ -106,7 +106,8 @@ class HashCountedSet { Vector<Value> AsVector() const; template <typename VisitorDispatcher, typename A = Allocator> - std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) { + std::enable_if_t<A::kIsGarbageCollected> Trace( + VisitorDispatcher visitor) const { impl_.Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_map.h b/chromium/third_party/blink/renderer/platform/wtf/hash_map.h index 46b39e0697f..88fc20f0ea1 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/hash_map.h +++ b/chromium/third_party/blink/renderer/platform/wtf/hash_map.h @@ -22,6 +22,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_MAP_H_ #include <initializer_list> +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h" #include "third_party/blink/renderer/platform/wtf/construct_traits.h" #include "third_party/blink/renderer/platform/wtf/hash_table.h" @@ -43,6 +44,12 @@ struct KeyValuePairKeyExtractor { static const typename T::KeyType& Extract(const T& p) { return p.key; } + // Assumes out points to a buffer of size at least sizeof(T::KeyType). + template <typename T> + static const typename T::KeyType& ExtractSafe(const T& p, void* out) { + AtomicReadMemcpy<sizeof(typename T::KeyType)>(out, &p.key); + return *reinterpret_cast<typename T::KeyType*>(out); + } }; // Note: empty or deleted key values are not allowed, using them may lead to @@ -202,7 +209,8 @@ class HashMap { static bool IsValidKey(const IncomingKeyType&); template <typename VisitorDispatcher, typename A = Allocator> - std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) { + std::enable_if_t<A::kIsGarbageCollected> Trace( + VisitorDispatcher visitor) const { impl_.Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_set.h b/chromium/third_party/blink/renderer/platform/wtf/hash_set.h index d7cdeeddf1c..abef9643fb0 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/hash_set.h +++ b/chromium/third_party/blink/renderer/platform/wtf/hash_set.h @@ -22,6 +22,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_SET_H_ #include <initializer_list> +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_table.h" #include "third_party/blink/renderer/platform/wtf/wtf_size_t.h" @@ -134,7 +135,8 @@ class HashSet { ValueType TakeAny(); template <typename VisitorDispatcher, typename A = Allocator> - std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) { + std::enable_if_t<A::kIsGarbageCollected> Trace( + VisitorDispatcher visitor) const { impl_.Trace(visitor); } @@ -151,6 +153,12 @@ struct IdentityExtractor { static const T& Extract(const T& t) { return t; } + // Assumes out points to a buffer of size at least sizeof(T). + template <typename T> + static const T& ExtractSafe(const T& t, void* out) { + AtomicReadMemcpy<sizeof(T)>(out, &t); + return *reinterpret_cast<T*>(out); + } }; template <typename Translator> diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_table.h b/chromium/third_party/blink/renderer/platform/wtf/hash_table.h index 472e2232846..1d195f1218b 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/hash_table.h +++ b/chromium/third_party/blink/renderer/platform/wtf/hash_table.h @@ -23,9 +23,9 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TABLE_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TABLE_H_ -#include <atomic> #include <memory> +#include "base/bits.h" #include "base/numerics/checked_math.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h" @@ -92,13 +92,6 @@ #endif #endif -namespace { -template <typename T> -ALWAYS_INLINE std::atomic<T>& AsAtomic(T& t) { - return reinterpret_cast<std::atomic<T>&>(t); -} -} // namespace - namespace WTF { // This is for tracing inside collections that have special support for weak @@ -144,7 +137,7 @@ struct WTF_EXPORT HashTableStats { static HashTableStats& instance(); template <typename VisitorDispatcher> - void trace(VisitorDispatcher) {} + void trace(VisitorDispatcher) const {} private: void RecordCollisionAtCountWithoutLock(int count); @@ -657,15 +650,32 @@ struct HashTableAddResult final { template <typename Value, typename Extractor, typename KeyTraits> struct HashTableHelper { + template <typename T> + struct AddConstToPtrType { + using type = T; + }; + template <typename T> + struct AddConstToPtrType<T*> { + using type = const T*; + }; + + using Key = typename AddConstToPtrType<typename KeyTraits::TraitType>::type; + STATIC_ONLY(HashTableHelper); - static bool IsEmptyBucket(const Value& value) { - return IsHashTraitsEmptyValue<KeyTraits>(Extractor::Extract(value)); + static bool IsEmptyBucket(const Key& key) { + return IsHashTraitsEmptyValue<KeyTraits>(key); } - static bool IsDeletedBucket(const Value& value) { - return KeyTraits::IsDeletedValue(Extractor::Extract(value)); + static bool IsDeletedBucket(const Key& key) { + return KeyTraits::IsDeletedValue(key); } static bool IsEmptyOrDeletedBucket(const Value& value) { - return IsEmptyBucket(value) || IsDeletedBucket(value); + const Key& key = Extractor::Extract(value); + return IsEmptyBucket(key) || IsDeletedBucket(key); + } + static bool IsEmptyOrDeletedBucketSafe(const Value& value) { + alignas(std::max(alignof(Key), sizeof(size_t))) char buf[sizeof(Key)]; + const Key& key = Extractor::ExtractSafe(value, &buf); + return IsEmptyBucket(key) || IsDeletedBucket(key); } }; @@ -846,7 +856,7 @@ class HashTable final ValueType** GetBufferSlot() { return &table_; } template <typename VisitorDispatcher, typename A = Allocator> - std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher); + std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher) const; #if DCHECK_IS_ON() void EnterAccessForbiddenScope() { @@ -953,6 +963,22 @@ class HashTable final void ClearEnqueued() { queue_flag_ = false; } bool Enqueued() { return queue_flag_; } + // Constructor for hash tables with raw storage. + struct RawStorageTag {}; + HashTable(RawStorageTag, ValueType* table, unsigned size) + : table_(table), + table_size_(size), + key_count_(0), + deleted_count_(0), + queue_flag_(0) +#if DCHECK_IS_ON() + , + access_forbidden_(0), + modifications_(0) +#endif + { + } + ValueType* table_; unsigned table_size_; unsigned key_count_; @@ -1664,7 +1690,17 @@ void HashTable<Key, KeyTraits, Allocator>::DeleteAllBucketsAndDeallocate(ValueType* table, unsigned size) { - if (!std::is_trivially_destructible<ValueType>::value) { + // We delete a bucket in the following cases: + // - It is not trivially destructible. + // - The table is weak (thus garbage collected) and we are currently marking. + // This is to handle the case where a backing store is removed from the + // HashTable after HashTable has been enqueued for processing. If we remove + // the backing in that case it stays unprocessed which upsets the marking + // verifier that checks that all backings are in consistent state. + const bool needs_bucket_deletion = + !std::is_trivially_destructible<ValueType>::value || + (WTF::IsWeak<ValueType>::value && Allocator::IsIncrementalMarking()); + if (needs_bucket_deletion) { for (unsigned i = 0; i < size; ++i) { // This code is called when the hash table is cleared or resized. We // have allocated a new backing store and we need to run the @@ -1751,7 +1787,7 @@ HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: } } table_ = temporary_table; - Allocator::template BackingWriteBarrierForHashTable<HashTable>(table_); + Allocator::template BackingWriteBarrierForHashTable<HashTable>(&table_); if (Traits::kEmptyValueIsZero) { memset(original_table, 0, new_table_size * sizeof(ValueType)); @@ -1761,10 +1797,6 @@ HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: } new_entry = RehashTo(original_table, new_table_size, new_entry); - EnterAccessForbiddenScope(); - DeleteAllBucketsAndDeallocate(temporary_table, old_table_size); - LeaveAccessForbiddenScope(); - return new_entry; } @@ -1778,41 +1810,50 @@ template <typename Key, Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: RehashTo(ValueType* new_table, unsigned new_table_size, Value* entry) { - unsigned old_table_size = table_size_; - ValueType* old_table = table_; - #if DUMP_HASHTABLE_STATS - if (old_table_size != 0) { + if (table_size_ != 0) { HashTableStats::instance().numRehashes.fetch_add(1, std::memory_order_relaxed); } #endif #if DUMP_HASHTABLE_STATS_PER_TABLE - if (old_table_size != 0) + if (table_size_ != 0) stats_->numRehashes.fetch_add(1, std::memory_order_relaxed); #endif - AsAtomic<ValueType*>(table_).store(new_table, std::memory_order_relaxed); - Allocator::template BackingWriteBarrierForHashTable<HashTable>(new_table); - table_size_ = new_table_size; + HashTable new_hash_table(RawStorageTag{}, new_table, new_table_size); Value* new_entry = nullptr; - for (unsigned i = 0; i != old_table_size; ++i) { - if (IsEmptyOrDeletedBucket(old_table[i])) { - DCHECK_NE(&old_table[i], entry); + for (unsigned i = 0; i != table_size_; ++i) { + if (IsEmptyOrDeletedBucket(table_[i])) { + DCHECK_NE(&table_[i], entry); continue; } - Value* reinserted_entry = Reinsert(std::move(old_table[i])); - if (&old_table[i] == entry) { + Value* reinserted_entry = new_hash_table.Reinsert(std::move(table_[i])); + if (&table_[i] == entry) { DCHECK(!new_entry); new_entry = reinserted_entry; } } - // Rescan the contents of the backing store as no write barriers were emitted - // during re-insertion. Traits::NeedsToForbidGCOnMove ensures that no - // garbage collection is triggered during moving. - Allocator::TraceMarkedBackingStore(new_table); + + Allocator::TraceBackingStoreIfMarked(new_hash_table.table_); + + ValueType* old_table = table_; + unsigned old_table_size = table_size_; + + // This swaps the newly allocated buffer with the current one. The store to + // the current table has to be atomic to prevent races with concurrent marker. + AsAtomicPtr(&table_)->store(new_hash_table.table_, std::memory_order_relaxed); + Allocator::template BackingWriteBarrierForHashTable<HashTable>(&table_); + table_size_ = new_table_size; + + new_hash_table.table_ = old_table; + new_hash_table.table_size_ = old_table_size; + + // Explicitly clear since garbage collected HashTables don't do this on + // destruction. + new_hash_table.clear(); deleted_count_ = 0; @@ -1835,7 +1876,6 @@ Value* HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: Rehash(unsigned new_table_size, Value* entry) { unsigned old_table_size = table_size_; - ValueType* old_table = table_; #if DUMP_HASHTABLE_STATS if (old_table_size != 0) { @@ -1862,10 +1902,6 @@ HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: ValueType* new_table = AllocateTable(new_table_size); Value* new_entry = RehashTo(new_table, new_table_size, entry); - EnterAccessForbiddenScope(); - DeleteAllBucketsAndDeallocate(old_table, old_table_size); - LeaveAccessForbiddenScope(); - return new_entry; } @@ -1890,7 +1926,7 @@ void HashTable<Key, EnterAccessForbiddenScope(); DeleteAllBucketsAndDeallocate(table_, table_size_); LeaveAccessForbiddenScope(); - table_ = nullptr; + AsAtomicPtr(&table_)->store(nullptr, std::memory_order_relaxed); table_size_ = 0; key_count_ = 0; } @@ -1976,11 +2012,17 @@ void HashTable<Key, // race). Atomic reads are not needed here because this method is only called // on the mutator thread, which is also the only one that writes to them, so // there is *no* risk of data races when reading. - Value* tmp_table = other.table_; - AsAtomic<Value*>(other.table_).store(table_, std::memory_order_relaxed); - AsAtomic<Value*>(table_).store(tmp_table, std::memory_order_relaxed); - Allocator::template BackingWriteBarrierForHashTable<HashTable>(table_); - Allocator::template BackingWriteBarrierForHashTable<HashTable>(other.table_); + AtomicWriteSwap(table_, other.table_); + Allocator::template BackingWriteBarrierForHashTable<HashTable>(&table_); + Allocator::template BackingWriteBarrierForHashTable<HashTable>(&other.table_); + if (IsWeak<ValueType>::value) { + // Weak processing is omitted when no backing store is present. In case such + // an empty table is later on used it needs to be strongified. + if (table_) + Allocator::TraceBackingStoreIfMarked(table_); + if (other.table_) + Allocator::TraceBackingStoreIfMarked(other.table_); + } std::swap(table_size_, other.table_size_); std::swap(key_count_, other.key_count_); // std::swap does not work for bit fields. @@ -2038,7 +2080,8 @@ template <WeakHandlingFlag weakHandlingFlag, typename Allocator> struct WeakProcessingHashTableHelper { STATIC_ONLY(WeakProcessingHashTableHelper); - static void Process(const typename Allocator::WeakCallbackInfo&, void*) {} + static void Process(const typename Allocator::WeakCallbackInfo&, + const void*) {} }; template <typename Key, @@ -2069,8 +2112,9 @@ struct WeakProcessingHashTableHelper<kWeakHandling, // Used for purely weak and for weak-and-strong tables (ephemerons). static void Process(const typename Allocator::WeakCallbackInfo&, - void* parameter) { - HashTableType* table = reinterpret_cast<HashTableType*>(parameter); + const void* parameter) { + HashTableType* table = + reinterpret_cast<HashTableType*>(const_cast<void*>(parameter)); // During incremental marking, the table may be freed after the callback has // been registered. if (!table->table_) @@ -2105,12 +2149,22 @@ template <typename Key, template <typename VisitorDispatcher, typename A> std::enable_if_t<A::kIsGarbageCollected> HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>:: - Trace(VisitorDispatcher visitor) { + Trace(VisitorDispatcher visitor) const { + // bail out for concurrent marking + if (visitor->ConcurrentTracingBailOut( + {this, [](blink::Visitor* visitor, const void* object) { + reinterpret_cast< + const HashTable<Key, Value, Extractor, HashFunctions, Traits, + KeyTraits, Allocator>*>(object) + ->Trace(visitor); + }})) + return; + static_assert(WTF::IsWeak<ValueType>::value || IsTraceableInCollectionTrait<Traits>::value, "Value should not be traced"); - ValueType* table = - AsAtomic<ValueType*>(table_).load(std::memory_order_relaxed); + const ValueType* table = + AsAtomicPtr(&table_)->load(std::memory_order_relaxed); if (!WTF::IsWeak<ValueType>::value) { // Strong HashTable. Allocator::template TraceHashTableBackingStrongly<ValueType, HashTable>( diff --git a/chromium/third_party/blink/renderer/platform/wtf/hash_traits.h b/chromium/third_party/blink/renderer/platform/wtf/hash_traits.h index fe2bea5d8d8..58833a13d2b 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/hash_traits.h +++ b/chromium/third_party/blink/renderer/platform/wtf/hash_traits.h @@ -22,7 +22,6 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TRAITS_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_HASH_TRAITS_H_ -#include <string.h> // For memset. #include <limits> #include <memory> #include <type_traits> @@ -43,6 +42,27 @@ struct EnumOrGenericHashTraits; template <typename T> struct HashTraits; +namespace { +template <typename T, bool use_atomic_writes = IsTraceable<T>::value> +void ClearMemoryAtomically(T* slot, size_t size) { + size_t* address = reinterpret_cast<size_t*>(slot); + // This method is called for clearing hash table entires that are removed. In + // case Oilpan concurrent marking is tracing the hash table at the same time, + // there might be a data race between the marker reading the entry and zeroing + // the entry. Using atomic reads here resolves any possible races. + // Note that sizeof(T) might not be a multiple of sizeof(size_t). The last + // sizeof(T)%sizeof(size_t) bytes don't require atomic write as it cannot hold + // a pointer (i.e it will not be traceable). + if (use_atomic_writes) { + for (; size >= sizeof(size_t); size -= sizeof(size_t), ++address) { + WTF::AsAtomicPtr(address)->store(0, std::memory_order_relaxed); + } + } + DCHECK(!use_atomic_writes || (size < sizeof(size_t))); + memset(address, 0, size); +} +} // namespace + template <typename T> struct GenericHashTraitsBase<false, T> { // The emptyValueIsZero flag is used to optimize allocation of empty hash @@ -196,7 +216,7 @@ struct HashTraits<P*> : GenericHashTraits<P*> { static void ConstructDeletedValue(P*& slot, bool) { slot = reinterpret_cast<P*>(-1); } - static bool IsDeletedValue(P* value) { + static bool IsDeletedValue(const P* value) { return value == reinterpret_cast<P*>(-1); } }; @@ -374,8 +394,9 @@ struct PairHashTraits // at a later point, the same assumptions around memory zeroing must // hold as they did at the initial allocation. Therefore we zero the // value part of the slot here for GC collections. - if (zero_value) - memset(reinterpret_cast<void*>(&slot.second), 0, sizeof(slot.second)); + if (zero_value) { + ClearMemoryAtomically(&slot.second, sizeof(slot.second)); + } } static bool IsDeletedValue(const TraitType& value) { return FirstTraits::IsDeletedValue(value.first); @@ -446,8 +467,9 @@ struct KeyValuePairHashTraits static void ConstructDeletedValue(TraitType& slot, bool zero_value) { KeyTraits::ConstructDeletedValue(slot.key, zero_value); // See similar code in this file for why we need to do this. - if (zero_value) - memset(reinterpret_cast<void*>(&slot.value), 0, sizeof(slot.value)); + if (zero_value) { + ClearMemoryAtomically(&slot.value, sizeof(slot.value)); + } } static bool IsDeletedValue(const TraitType& value) { return KeyTraits::IsDeletedValue(value.key); diff --git a/chromium/third_party/blink/renderer/platform/wtf/linked_hash_set.h b/chromium/third_party/blink/renderer/platform/wtf/linked_hash_set.h index 9c12afcda68..0c4308e3ee1 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/linked_hash_set.h +++ b/chromium/third_party/blink/renderer/platform/wtf/linked_hash_set.h @@ -25,8 +25,10 @@ #include "base/macros.h" #include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h" +#include "third_party/blink/renderer/platform/wtf/hash_map.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" #include "third_party/blink/renderer/platform/wtf/sanitizers.h" +#include "third_party/blink/renderer/platform/wtf/vector_backed_linked_list.h" namespace WTF { @@ -49,20 +51,51 @@ template <typename Value, class LinkedHashSet; template <typename LinkedHashSet> -class LinkedHashSetIterator; -template <typename LinkedHashSet> class LinkedHashSetConstIterator; template <typename LinkedHashSet> -class LinkedHashSetReverseIterator; -template <typename LinkedHashSet> class LinkedHashSetConstReverseIterator; -template <typename Value, typename HashFunctions, typename Allocator> +template <typename Value, typename HashFunctions> struct LinkedHashSetTranslator; -template <typename Value, typename Allocator> +template <typename Value> struct LinkedHashSetExtractor; template <typename Value, typename ValueTraits, typename Allocator> struct LinkedHashSetTraits; +class LinkedHashSetNodeBase; + +class LinkedHashSetNodeBasePointer { + public: + LinkedHashSetNodeBasePointer(LinkedHashSetNodeBase* node) : node_(node) {} + + LinkedHashSetNodeBasePointer& operator=( + const LinkedHashSetNodeBasePointer& other) { + SetSafe(other); + return *this; + } + + LinkedHashSetNodeBasePointer& operator=(LinkedHashSetNodeBase* other) { + SetSafe(other); + return *this; + } + + LinkedHashSetNodeBasePointer& operator=(std::nullptr_t) { + SetSafe(nullptr); + return *this; + } + + LinkedHashSetNodeBase* Get() const { return node_; } + explicit operator bool() const { return Get(); } + operator LinkedHashSetNodeBase*() const { return Get(); } + LinkedHashSetNodeBase* operator->() const { return Get(); } + LinkedHashSetNodeBase& operator*() const { return *Get(); } + + private: + void SetSafe(LinkedHashSetNodeBase* node) { + AsAtomicPtr(&node_)->store(node, std::memory_order_relaxed); + } + + LinkedHashSetNodeBase* node_ = nullptr; +}; class LinkedHashSetNodeBase { DISALLOW_NEW(); @@ -75,10 +108,16 @@ class LinkedHashSetNodeBase { if (!next_) return; DCHECK(prev_); - DCHECK(next_->prev_ == this); - DCHECK(prev_->next_ == this); - next_->prev_ = prev_; - prev_->next_ = next_; + { + AsanUnpoisonScope unpoison_scope(next_, sizeof(LinkedHashSetNodeBase)); + DCHECK(next_->prev_ == this); + next_->prev_ = prev_; + } + { + AsanUnpoisonScope unpoison_scope(prev_, sizeof(LinkedHashSetNodeBase)); + DCHECK(prev_->next_ == this); + prev_->next_ = next_; + } } ~LinkedHashSetNodeBase() { Unlink(); } @@ -111,8 +150,8 @@ class LinkedHashSetNodeBase { DCHECK((prev && next) || (!prev && !next)); } - LinkedHashSetNodeBase* prev_; - LinkedHashSetNodeBase* next_; + LinkedHashSetNodeBasePointer prev_; + LinkedHashSetNodeBasePointer next_; protected: // If we take a copy of a node we can't copy the next and prev pointers, @@ -177,8 +216,7 @@ class LinkedHashSet { typedef TraitsArg Traits; typedef LinkedHashSetNode<Value> Node; typedef LinkedHashSetNodeBase NodeBase; - typedef LinkedHashSetTranslator<Value, HashFunctions, Allocator> - NodeHashFunctions; + typedef LinkedHashSetTranslator<Value, HashFunctions> NodeHashFunctions; typedef LinkedHashSetTraits<Value, Traits, Allocator> NodeHashTraits; typedef HashTable<Node, @@ -191,13 +229,11 @@ class LinkedHashSet { ImplType; public: - typedef LinkedHashSetIterator<LinkedHashSet> iterator; - friend class LinkedHashSetIterator<LinkedHashSet>; + typedef LinkedHashSetConstIterator<LinkedHashSet> iterator; typedef LinkedHashSetConstIterator<LinkedHashSet> const_iterator; friend class LinkedHashSetConstIterator<LinkedHashSet>; - typedef LinkedHashSetReverseIterator<LinkedHashSet> reverse_iterator; - friend class LinkedHashSetReverseIterator<LinkedHashSet>; + typedef LinkedHashSetConstReverseIterator<LinkedHashSet> reverse_iterator; typedef LinkedHashSetConstReverseIterator<LinkedHashSet> const_reverse_iterator; friend class LinkedHashSetConstReverseIterator<LinkedHashSet>; @@ -210,7 +246,7 @@ class LinkedHashSet { : stored_value(&hash_table_add_result.stored_value->value_), is_new_entry(hash_table_add_result.is_new_entry) {} - Value* stored_value; + const Value* stored_value; bool is_new_entry; }; @@ -277,13 +313,6 @@ class LinkedHashSet { template <typename IncomingValueType> AddResult insert(IncomingValueType&&); - // Same as insert() except that the return value is an - // iterator. Useful in cases where it's needed to have the - // same return value as find() and where it's not possible to - // use a pointer to the storedValue. - template <typename IncomingValueType> - iterator AddReturnIterator(IncomingValueType&&); - // Add the value to the end of the collection. If the value was already in // the list, it is moved to the end. template <typename IncomingValueType> @@ -298,13 +327,13 @@ class LinkedHashSet { AddResult InsertBefore(ValuePeekInType before_value, IncomingValueType&& new_value); template <typename IncomingValueType> - AddResult InsertBefore(iterator it, IncomingValueType&& new_value) { + AddResult InsertBefore(const_iterator it, IncomingValueType&& new_value) { return impl_.template insert<NodeHashFunctions>( std::forward<IncomingValueType>(new_value), it.GetNode()); } void erase(ValuePeekInType); - void erase(iterator); + void erase(const_iterator); void clear() { impl_.clear(); } template <typename Collection> void RemoveAll(const Collection& other) { @@ -312,13 +341,24 @@ class LinkedHashSet { } template <typename VisitorDispatcher> - void Trace(VisitorDispatcher visitor) { + void Trace(VisitorDispatcher visitor) const { + if (visitor->ConcurrentTracingBailOut( + {this, [](blink::Visitor* visitor, const void* object) { + reinterpret_cast<const LinkedHashSet<ValueArg, HashFunctions, + TraitsArg, Allocator>*>( + object) + ->Trace(visitor); + }})) + return; + impl_.Trace(visitor); // Should the underlying table be moved by GC, register a callback // that fixes up the interior pointers that the (Heap)LinkedHashSet keeps. - if (impl_.table_) { + const auto* table = + AsAtomicPtr(&impl_.table_)->load(std::memory_order_relaxed); + if (table) { Allocator::RegisterBackingStoreCallback( - visitor, impl_.table_, + visitor, table, NodeHashTraits::template MoveBackingCallback<ImplType>); } } @@ -336,13 +376,13 @@ class LinkedHashSet { private: Node* Anchor() { return reinterpret_cast<Node*>(&anchor_); } const Node* Anchor() const { return reinterpret_cast<const Node*>(&anchor_); } - Node* FirstNode() { return reinterpret_cast<Node*>(anchor_.next_); } + Node* FirstNode() { return reinterpret_cast<Node*>(anchor_.next_.Get()); } const Node* FirstNode() const { - return reinterpret_cast<const Node*>(anchor_.next_); + return reinterpret_cast<const Node*>(anchor_.next_.Get()); } - Node* LastNode() { return reinterpret_cast<Node*>(anchor_.prev_); } + Node* LastNode() { return reinterpret_cast<Node*>(anchor_.prev_.Get()); } const Node* LastNode() const { - return reinterpret_cast<const Node*>(anchor_.prev_); + return reinterpret_cast<const Node*>(anchor_.prev_.Get()); } iterator MakeIterator(const Node* position) { @@ -362,7 +402,7 @@ class LinkedHashSet { NodeBase anchor_; }; -template <typename Value, typename HashFunctions, typename Allocator> +template <typename Value, typename HashFunctions> struct LinkedHashSetTranslator { STATIC_ONLY(LinkedHashSetTranslator); typedef LinkedHashSetNode<Value> Node; @@ -395,7 +435,7 @@ struct LinkedHashSetTranslator { static const bool safe_to_compare_to_empty_or_deleted = false; }; -template <typename Value, typename Allocator> +template <typename Value> struct LinkedHashSetExtractor { STATIC_ONLY(LinkedHashSetExtractor); static const Value& Extract(const LinkedHashSetNode<Value>& node) { @@ -449,10 +489,14 @@ struct LinkedHashSetTraits } template <typename HashTable> - static void MoveBackingCallback(void* from, void* to, size_t size) { + static void MoveBackingCallback(const void* const_from, + const void* const_to, + size_t size) { // Note: the hash table move may have been overlapping; linearly scan the // entire table and fixup interior pointers into the old region with // correspondingly offset ones into the new. + void* from = const_cast<void*>(const_from); + void* to = const_cast<void*>(const_to); const size_t table_size = size / sizeof(Node); Node* table = reinterpret_cast<Node*>(to); NodeBase* from_start = reinterpret_cast<NodeBase*>(from); @@ -464,7 +508,7 @@ struct LinkedHashSetTraits if (HashTable::IsEmptyOrDeletedBucket(node)) continue; if (node.next_ >= from_start && node.next_ < from_end) { - const size_t diff = reinterpret_cast<uintptr_t>(node.next_) - + const size_t diff = reinterpret_cast<uintptr_t>(node.next_.Get()) - reinterpret_cast<uintptr_t>(from); node.next_ = reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff); @@ -473,7 +517,7 @@ struct LinkedHashSetTraits anchor_node = node.next_; } if (node.prev_ >= from_start && node.prev_ < from_end) { - const size_t diff = reinterpret_cast<uintptr_t>(node.prev_) - + const size_t diff = reinterpret_cast<uintptr_t>(node.prev_.Get()) - reinterpret_cast<uintptr_t>(from); node.prev_ = reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff); @@ -490,15 +534,17 @@ struct LinkedHashSetTraits } { DCHECK(anchor_node->prev_ >= from_start && anchor_node->prev_ < from_end); - const size_t diff = reinterpret_cast<uintptr_t>(anchor_node->prev_) - - reinterpret_cast<uintptr_t>(from); + const size_t diff = + reinterpret_cast<uintptr_t>(anchor_node->prev_.Get()) - + reinterpret_cast<uintptr_t>(from); anchor_node->prev_ = reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff); } { DCHECK(anchor_node->next_ >= from_start && anchor_node->next_ < from_end); - const size_t diff = reinterpret_cast<uintptr_t>(anchor_node->next_) - - reinterpret_cast<uintptr_t>(from); + const size_t diff = + reinterpret_cast<uintptr_t>(anchor_node->next_.Get()) - + reinterpret_cast<uintptr_t>(from); anchor_node->next_ = reinterpret_cast<NodeBase*>(reinterpret_cast<uintptr_t>(to) + diff); } @@ -506,59 +552,6 @@ struct LinkedHashSetTraits }; template <typename LinkedHashSetType> -class LinkedHashSetIterator { - DISALLOW_NEW(); - - private: - typedef typename LinkedHashSetType::Node Node; - typedef typename LinkedHashSetType::Traits Traits; - - typedef typename LinkedHashSetType::Value& ReferenceType; - typedef typename LinkedHashSetType::Value* PointerType; - - typedef LinkedHashSetConstIterator<LinkedHashSetType> const_iterator; - - Node* GetNode() { return const_cast<Node*>(iterator_.GetNode()); } - - protected: - LinkedHashSetIterator(const Node* position, LinkedHashSetType* container) - : iterator_(position, container) {} - - public: - // Default copy, assignment and destructor are OK. - - PointerType Get() const { return const_cast<PointerType>(iterator_.Get()); } - ReferenceType operator*() const { return *Get(); } - PointerType operator->() const { return Get(); } - - LinkedHashSetIterator& operator++() { - ++iterator_; - return *this; - } - LinkedHashSetIterator& operator--() { - --iterator_; - return *this; - } - - // Postfix ++ and -- intentionally omitted. - - // Comparison. - bool operator==(const LinkedHashSetIterator& other) const { - return iterator_ == other.iterator_; - } - bool operator!=(const LinkedHashSetIterator& other) const { - return iterator_ != other.iterator_; - } - - operator const_iterator() const { return iterator_; } - - protected: - const_iterator iterator_; - template <typename T, typename U, typename V, typename W> - friend class LinkedHashSet; -}; - -template <typename LinkedHashSetType> class LinkedHashSetConstIterator { DISALLOW_NEW(); @@ -569,7 +562,9 @@ class LinkedHashSetConstIterator { typedef const typename LinkedHashSetType::Value& ReferenceType; typedef const typename LinkedHashSetType::Value* PointerType; - const Node* GetNode() const { return static_cast<const Node*>(position_); } + Node* GetNode() const { + return const_cast<Node*>(static_cast<const Node*>(position_)); + } protected: LinkedHashSetConstIterator(const LinkedHashSetNodeBase* position, @@ -628,42 +623,6 @@ class LinkedHashSetConstIterator { #endif template <typename T, typename U, typename V, typename W> friend class LinkedHashSet; - friend class LinkedHashSetIterator<LinkedHashSetType>; -}; - -template <typename LinkedHashSetType> -class LinkedHashSetReverseIterator - : public LinkedHashSetIterator<LinkedHashSetType> { - typedef LinkedHashSetReverseIterator<LinkedHashSetType> reverse_iterator; - typedef LinkedHashSetIterator<LinkedHashSetType> Superclass; - typedef LinkedHashSetConstReverseIterator<LinkedHashSetType> - const_reverse_iterator; - typedef typename LinkedHashSetType::Node Node; - - protected: - LinkedHashSetReverseIterator(const Node* position, - LinkedHashSetType* container) - : Superclass(position, container) {} - - public: - LinkedHashSetReverseIterator& operator++() { - Superclass::operator--(); - return *this; - } - LinkedHashSetReverseIterator& operator--() { - Superclass::operator++(); - return *this; - } - - // Postfix ++ and -- intentionally omitted. - - operator const_reverse_iterator() const { - return *reinterpret_cast<const_reverse_iterator*>( - const_cast<reverse_iterator*>(this)); - } - - template <typename T, typename U, typename V, typename W> - friend class LinkedHashSet; }; template <typename LinkedHashSetType> @@ -797,7 +756,7 @@ inline const T& LinkedHashSet<T, U, V, W>::front() const { template <typename T, typename U, typename V, typename W> inline void LinkedHashSet<T, U, V, W>::RemoveFirst() { DCHECK(!IsEmpty()); - impl_.erase(static_cast<Node*>(anchor_.next_)); + impl_.erase(static_cast<Node*>(anchor_.next_.Get())); } template <typename T, typename U, typename V, typename W> @@ -815,7 +774,7 @@ inline const T& LinkedHashSet<T, U, V, W>::back() const { template <typename T, typename U, typename V, typename W> inline void LinkedHashSet<T, U, V, W>::pop_back() { DCHECK(!IsEmpty()); - impl_.erase(static_cast<Node*>(anchor_.prev_)); + impl_.erase(static_cast<Node*>(anchor_.prev_.Get())); } template <typename T, typename U, typename V, typename W> @@ -903,16 +862,6 @@ LinkedHashSet<Value, HashFunctions, Traits, Allocator>::insert( template <typename T, typename U, typename V, typename W> template <typename IncomingValueType> -typename LinkedHashSet<T, U, V, W>::iterator -LinkedHashSet<T, U, V, W>::AddReturnIterator(IncomingValueType&& value) { - typename ImplType::AddResult result = - impl_.template insert<NodeHashFunctions>( - std::forward<IncomingValueType>(value), &anchor_); - return MakeIterator(result.stored_value); -} - -template <typename T, typename U, typename V, typename W> -template <typename IncomingValueType> typename LinkedHashSet<T, U, V, W>::AddResult LinkedHashSet<T, U, V, W>::AppendOrMoveToLast(IncomingValueType&& value) { typename ImplType::AddResult result = @@ -951,7 +900,7 @@ LinkedHashSet<T, U, V, W>::InsertBefore(ValuePeekInType before_value, } template <typename T, typename U, typename V, typename W> -inline void LinkedHashSet<T, U, V, W>::erase(iterator it) { +inline void LinkedHashSet<T, U, V, W>::erase(const_iterator it) { if (it == end()) return; impl_.erase(it.GetNode()); @@ -964,20 +913,284 @@ inline void LinkedHashSet<T, U, V, W>::erase(ValuePeekInType value) { template <typename T, typename Allocator> inline void swap(LinkedHashSetNode<T>& a, LinkedHashSetNode<T>& b) { - typedef LinkedHashSetNodeBase Base; // The key and value cannot be swapped atomically, and it would be // wrong to have a GC when only one was swapped and the other still // contained garbage (eg. from a previous use of the same slot). // Therefore we forbid a GC until both the key and the value are // swapped. Allocator::EnterGCForbiddenScope(); - swap(static_cast<Base&>(a), static_cast<Base&>(b)); + swap(static_cast<LinkedHashSetNodeBase&>(a), + static_cast<LinkedHashSetNodeBase&>(b)); swap(a.value_, b.value_); Allocator::LeaveGCForbiddenScope(); } +// TODO(keinakashima): replace existing LinkedHashSet with NewLinkedHashSet +// after completion + +// This class is still experimental. Do not use this class. + +// LinkedHashSet provides a Set interface like HashSet, but also has a +// predictable iteration order. It has O(1) insertion, removal, and test for +// containership. It maintains a linked list through its contents such that +// iterating it yields values in the order in which they were inserted. +// The linked list is implementing in a vector (with links being indexes instead +// of pointers), to simplify the move of backing during GC compaction. + +// TODO(keinakashima): implement NewLinkedHashTraits (now we cannot insert +// deleted/empty value) and add it to template parameter + +template <typename ValueArg, typename Allocator = PartitionAllocator> +class NewLinkedHashSet { + USE_ALLOCATOR(NewLinkedHashSet, Allocator); + + private: + using Value = ValueArg; + using Map = HashMap<Value, + wtf_size_t, + typename DefaultHash<Value>::Hash, + HashTraits<Value>, + HashTraits<wtf_size_t>, + Allocator>; + using ListType = VectorBackedLinkedList<Value, Allocator>; + + public: + using iterator = typename ListType::const_iterator; + using reverse_iterator = typename ListType::const_reverse_iterator; + using const_iterator = typename ListType::const_iterator; + using const_reverse_iterator = typename ListType::const_reverse_iterator; + + // TODO(keinakashima): add security check + struct AddResult final { + STACK_ALLOCATED(); + + public: + AddResult(const Value* stored_value, bool is_new_entry) + : stored_value(stored_value), is_new_entry(is_new_entry) {} + const Value* stored_value; + bool is_new_entry; + }; + + typedef typename HashTraits<Value>::PeekInType ValuePeekInType; + + NewLinkedHashSet(); + NewLinkedHashSet(const NewLinkedHashSet&) = default; + NewLinkedHashSet(NewLinkedHashSet&&) = default; + NewLinkedHashSet& operator=(const NewLinkedHashSet&) = default; + NewLinkedHashSet& operator=(NewLinkedHashSet&&) = default; + + ~NewLinkedHashSet() = default; + + void Swap(NewLinkedHashSet&); + + wtf_size_t size() const { return list_.size(); } + bool IsEmpty() const { return list_.empty(); } + + iterator begin() { return list_.begin(); } + const_iterator begin() const { return list_.cbegin(); } + const_iterator cbegin() const { return list_.cbegin(); } + iterator end() { return list_.end(); } + const_iterator end() const { return list_.cend(); } + const_iterator cend() const { return list_.cend(); } + + reverse_iterator rbegin() { return list_.rbegin(); } + const_reverse_iterator rbegin() const { return list_.crbegin(); } + const_reverse_iterator crbegin() const { return list_.crbegin(); } + reverse_iterator rend() { return list_.rend(); } + const_reverse_iterator rend() const { return list_.crend(); } + const_reverse_iterator crend() const { return list_.crend(); } + + const Value& front() const { return list_.front(); } + const Value& back() const { return list_.back(); } + + iterator find(ValuePeekInType); + const_iterator find(ValuePeekInType) const; + bool Contains(ValuePeekInType) const; + + template <typename IncomingValueType> + AddResult insert(IncomingValueType&&); + + // If |value| already exists in the set, nothing happens. + // If |before_value| doesn't exist in the set, appends |value|. + template <typename IncomingValueType> + AddResult InsertBefore(ValuePeekInType before_value, + IncomingValueType&& value); + + template <typename IncomingValueType> + AddResult InsertBefore(const_iterator it, IncomingValueType&& value); + + template <typename IncomingValueType> + AddResult AppendOrMoveToLast(IncomingValueType&&); + + template <typename IncomingValueType> + AddResult PrependOrMoveToFirst(IncomingValueType&&); + + void erase(ValuePeekInType); + void erase(const_iterator); + void RemoveFirst(); + void pop_back(); + + void clear() { + value_to_index_.clear(); + list_.clear(); + } + + template <typename VisitorDispatcher, typename A = Allocator> + std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) { + value_to_index_.Trace(visitor); + list_.Trace(visitor); + } + + private: + enum class MoveType { + kMoveIfValueExists, + kDontMove, + }; + + template <typename IncomingValueType> + AddResult InsertOrMoveBefore(const_iterator, IncomingValueType&&, MoveType); + + Map value_to_index_; + ListType list_; +}; + +template <typename T, typename Allocator> +inline NewLinkedHashSet<T, Allocator>::NewLinkedHashSet() { + static_assert(Allocator::kIsGarbageCollected || + !IsPointerToGarbageCollectedType<T>::value, + "Cannot put raw pointers to garbage-collected classes into " + "an off-heap NewLinkedHashSet. Use " + "HeapNewLinkedHashSet<Member<T>> instead."); +} + +template <typename T, typename Allocator> +inline void NewLinkedHashSet<T, Allocator>::Swap(NewLinkedHashSet& other) { + value_to_index_.swap(other.value_to_index_); + list_.swap(other.list_); +} + +template <typename T, typename Allocator> +typename NewLinkedHashSet<T, Allocator>::iterator +NewLinkedHashSet<T, Allocator>::find(ValuePeekInType value) { + typename Map::const_iterator it = value_to_index_.find(value); + + if (it == value_to_index_.end()) + return end(); + return list_.MakeIterator(it->value); +} + +template <typename T, typename Allocator> +typename NewLinkedHashSet<T, Allocator>::const_iterator +NewLinkedHashSet<T, Allocator>::find(ValuePeekInType value) const { + typename Map::const_iterator it = value_to_index_.find(value); + + if (it == value_to_index_.end()) + return end(); + return list_.MakeConstIterator(it->value); +} + +template <typename T, typename Allocator> +bool NewLinkedHashSet<T, Allocator>::Contains(ValuePeekInType value) const { + return value_to_index_.Contains(value); +} + +template <typename T, typename Allocator> +template <typename IncomingValueType> +typename NewLinkedHashSet<T, Allocator>::AddResult +NewLinkedHashSet<T, Allocator>::insert(IncomingValueType&& value) { + return InsertOrMoveBefore(end(), std::forward<IncomingValueType>(value), + MoveType::kDontMove); +} + +template <typename T, typename Allocator> +template <typename IncomingValueType> +typename NewLinkedHashSet<T, Allocator>::AddResult +NewLinkedHashSet<T, Allocator>::InsertBefore(ValuePeekInType before_value, + IncomingValueType&& value) { + return InsertOrMoveBefore(find(before_value), + std::forward<IncomingValueType>(value), + MoveType::kDontMove); +} + +template <typename T, typename Allocator> +template <typename IncomingValueType> +typename NewLinkedHashSet<T, Allocator>::AddResult +NewLinkedHashSet<T, Allocator>::InsertBefore(const_iterator it, + IncomingValueType&& value) { + return InsertOrMoveBefore(it, std::forward<IncomingValueType>(value), + MoveType::kDontMove); +} + +template <typename T, typename Allocator> +template <typename IncomingValueType> +typename NewLinkedHashSet<T, Allocator>::AddResult +NewLinkedHashSet<T, Allocator>::AppendOrMoveToLast(IncomingValueType&& value) { + return InsertOrMoveBefore(end(), std::forward<IncomingValueType>(value), + MoveType::kMoveIfValueExists); +} + +template <typename T, typename Allocator> +template <typename IncomingValueType> +typename NewLinkedHashSet<T, Allocator>::AddResult +NewLinkedHashSet<T, Allocator>::PrependOrMoveToFirst( + IncomingValueType&& value) { + return InsertOrMoveBefore(begin(), std::forward<IncomingValueType>(value), + MoveType::kMoveIfValueExists); +} + +template <typename T, typename Allocator> +inline void NewLinkedHashSet<T, Allocator>::erase(ValuePeekInType value) { + erase(find(value)); +} + +template <typename T, typename Allocator> +inline void NewLinkedHashSet<T, Allocator>::erase(const_iterator it) { + if (it == end()) + return; + value_to_index_.erase(*it); + list_.erase(it); +} + +template <typename T, typename Allocator> +inline void NewLinkedHashSet<T, Allocator>::RemoveFirst() { + DCHECK(!IsEmpty()); + erase(begin()); +} + +template <typename T, typename Allocator> +inline void NewLinkedHashSet<T, Allocator>::pop_back() { + DCHECK(!IsEmpty()); + erase(--end()); +} + +template <typename T, typename Allocator> +template <typename IncomingValueType> +typename NewLinkedHashSet<T, Allocator>::AddResult +NewLinkedHashSet<T, Allocator>::InsertOrMoveBefore(const_iterator position, + IncomingValueType&& value, + MoveType type) { + typename Map::AddResult result = value_to_index_.insert(value, kNotFound); + + if (result.is_new_entry) { + const_iterator stored_position_iterator = + list_.insert(position, std::forward<IncomingValueType>(value)); + result.stored_value->value = stored_position_iterator.GetIndex(); + return AddResult(stored_position_iterator.Get(), true); + } + + const_iterator stored_position_iterator = + list_.MakeConstIterator(result.stored_value->value); + if (type == MoveType::kDontMove) + return AddResult(stored_position_iterator.Get(), false); + + const_iterator moved_position_iterator = + list_.MoveTo(stored_position_iterator, position); + return AddResult(moved_position_iterator.Get(), false); +} + } // namespace WTF using WTF::LinkedHashSet; +using WTF::NewLinkedHashSet; #endif /* WTF_LinkedHashSet_h */ diff --git a/chromium/third_party/blink/renderer/platform/wtf/linked_hash_set_test.cc b/chromium/third_party/blink/renderer/platform/wtf/linked_hash_set_test.cc new file mode 100644 index 00000000000..bcfb0be1d0f --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/wtf/linked_hash_set_test.cc @@ -0,0 +1,463 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/wtf/linked_hash_set.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/wtf_test_helper.h" + +namespace WTF { + +TEST(NewLinkedHashSetTest, CopyConstructAndAssignInt) { + NewLinkedHashSet<int> set1; + EXPECT_EQ(set1.size(), 0u); + EXPECT_TRUE(set1.IsEmpty()); + set1.insert(1); + set1.insert(2); + set1.insert(3); + EXPECT_EQ(set1.size(), 3u); + NewLinkedHashSet<int> set2(set1); + EXPECT_EQ(set2.size(), 3u); + NewLinkedHashSet<int> set3; + EXPECT_EQ(set3.size(), 0u); + set3 = set2; + EXPECT_EQ(set3.size(), 3u); + auto it1 = set1.begin(); + auto it2 = set2.begin(); + auto it3 = set3.begin(); + for (int i = 0; i < 3; i++) { + EXPECT_EQ(*it1, i + 1); + EXPECT_EQ(*it2, i + 1); + EXPECT_EQ(*it3, i + 1); + ++it1; + ++it2; + ++it3; + } +} + +TEST(NewLinkedHashSetTest, CopyConstructAndAssignIntPtr) { + NewLinkedHashSet<int*> set1; + EXPECT_EQ(set1.size(), 0u); + EXPECT_TRUE(set1.IsEmpty()); + std::unique_ptr<int> int1 = std::make_unique<int>(1); + std::unique_ptr<int> int2 = std::make_unique<int>(2); + std::unique_ptr<int> int3 = std::make_unique<int>(3); + set1.insert(int1.get()); + set1.insert(int2.get()); + set1.insert(int3.get()); + EXPECT_EQ(set1.size(), 3u); + NewLinkedHashSet<int*> set2(set1); + EXPECT_EQ(set2.size(), 3u); + NewLinkedHashSet<int*> set3; + EXPECT_EQ(set3.size(), 0u); + set3 = set2; + EXPECT_EQ(set3.size(), 3u); + auto it1 = set1.begin(); + auto it2 = set2.begin(); + auto it3 = set3.begin(); + for (int i = 0; i < 3; i++) { + EXPECT_EQ(**it1, i + 1); + EXPECT_EQ(**it2, i + 1); + EXPECT_EQ(**it3, i + 1); + ++it1; + ++it2; + ++it3; + } + + for (int* ptr : set1) + *ptr += 1000; + it1 = set1.begin(); + it2 = set2.begin(); + it3 = set3.begin(); + for (int i = 0; i < 3; i++) { + EXPECT_EQ(**it1, i + 1001); + EXPECT_EQ(**it2, i + 1001); + EXPECT_EQ(**it3, i + 1001); + ++it1; + ++it2; + ++it3; + } +} + +TEST(NewLinkedHashSetTest, CopyConstructAndAssignString) { + NewLinkedHashSet<String> set1; + EXPECT_EQ(set1.size(), 0u); + EXPECT_TRUE(set1.IsEmpty()); + set1.insert("1"); + set1.insert("2"); + set1.insert("3"); + EXPECT_EQ(set1.size(), 3u); + NewLinkedHashSet<String> set2(set1); + EXPECT_EQ(set2.size(), 3u); + NewLinkedHashSet<String> set3; + EXPECT_EQ(set3.size(), 0u); + set3 = set2; + EXPECT_EQ(set3.size(), 3u); + auto it1 = set1.begin(); + auto it2 = set2.begin(); + auto it3 = set3.begin(); + for (int i = 0; i < 3; i++) { + EXPECT_EQ(*it1, String(Vector<UChar>({'1' + i}))); + EXPECT_EQ(*it2, String(Vector<UChar>({'1' + i}))); + EXPECT_EQ(*it3, String(Vector<UChar>({'1' + i}))); + ++it1; + ++it2; + ++it3; + } +} + +TEST(NewLinkedHashSetTest, MoveConstructAndAssignInt) { + NewLinkedHashSet<ValueInstanceCount<int>> set1; + EXPECT_EQ(set1.size(), 0u); + EXPECT_TRUE(set1.IsEmpty()); + int counter1 = 0; + int counter2 = 0; + int counter3 = 0; + set1.insert(ValueInstanceCount<int>(&counter1, 1)); + set1.insert(ValueInstanceCount<int>(&counter2, 2)); + set1.insert(ValueInstanceCount<int>(&counter3, 3)); + EXPECT_EQ(set1.size(), 3u); + NewLinkedHashSet<ValueInstanceCount<int>> set2(std::move(set1)); + EXPECT_EQ(set2.size(), 3u); + NewLinkedHashSet<ValueInstanceCount<int>> set3; + EXPECT_EQ(set3.size(), 0u); + set3 = std::move(set2); + EXPECT_EQ(set3.size(), 3u); + auto it = set3.begin(); + for (int i = 0; i < 3; i++) { + EXPECT_EQ(it->Value(), i + 1); + ++it; + } + + // Only move constructors were used, each object is only in set3. + // Count 2x because each set uses hash map and vector. + EXPECT_EQ(counter1, 2); + EXPECT_EQ(counter2, 2); + EXPECT_EQ(counter3, 2); + + NewLinkedHashSet<ValueInstanceCount<int>> set4(set3); + // Copy constructor was used, each object is in set3 and set4. + EXPECT_EQ(counter1, 4); + EXPECT_EQ(counter2, 4); + EXPECT_EQ(counter3, 4); +} + +TEST(NewLinkedHashSetTest, MoveConstructAndAssignString) { + NewLinkedHashSet<ValueInstanceCount<String>> set1; + EXPECT_EQ(set1.size(), 0u); + EXPECT_TRUE(set1.IsEmpty()); + int counter1 = 0; + int counter2 = 0; + int counter3 = 0; + set1.insert(ValueInstanceCount<String>(&counter1, "1")); + set1.insert(ValueInstanceCount<String>(&counter2, "2")); + set1.insert(ValueInstanceCount<String>(&counter3, "3")); + EXPECT_EQ(set1.size(), 3u); + NewLinkedHashSet<ValueInstanceCount<String>> set2(std::move(set1)); + EXPECT_EQ(set2.size(), 3u); + NewLinkedHashSet<ValueInstanceCount<String>> set3; + EXPECT_EQ(set3.size(), 0u); + set3 = std::move(set2); + EXPECT_EQ(set3.size(), 3u); + auto it = set3.begin(); + for (int i = 0; i < 3; i++) { + EXPECT_EQ(it->Value(), String(Vector<UChar>({'1' + i}))); + ++it; + } + + // Only move constructors were used, each object is only in set3. + // Count 2x because each set uses hash map and vector. + EXPECT_EQ(counter1, 2); + EXPECT_EQ(counter2, 2); + EXPECT_EQ(counter3, 2); + + NewLinkedHashSet<ValueInstanceCount<String>> set4(set3); + // Copy constructor was used, each object is in set3 and set4. + EXPECT_EQ(counter1, 4); + EXPECT_EQ(counter2, 4); + EXPECT_EQ(counter3, 4); +} + +TEST(NewLinkedHashSetTest, Iterator) { + using Set = NewLinkedHashSet<int>; + Set set; + EXPECT_TRUE(set.begin() == set.end()); + EXPECT_TRUE(set.rbegin() == set.rend()); +} + +TEST(NewLinkedHashSetTest, FrontAndBack) { + using Set = NewLinkedHashSet<int>; + Set set; + EXPECT_EQ(set.size(), 0u); + EXPECT_TRUE(set.IsEmpty()); + + set.PrependOrMoveToFirst(1); + EXPECT_EQ(set.front(), 1); + EXPECT_EQ(set.back(), 1); + + set.insert(2); + EXPECT_EQ(set.front(), 1); + EXPECT_EQ(set.back(), 2); + + set.AppendOrMoveToLast(3); + EXPECT_EQ(set.front(), 1); + EXPECT_EQ(set.back(), 3); + + set.PrependOrMoveToFirst(3); + EXPECT_EQ(set.front(), 3); + EXPECT_EQ(set.back(), 2); + + set.AppendOrMoveToLast(1); + EXPECT_EQ(set.front(), 3); + EXPECT_EQ(set.back(), 1); +} + +TEST(NewLinkedHashSetTest, FindAndContains) { + using Set = NewLinkedHashSet<int>; + Set set; + set.insert(2); + set.AppendOrMoveToLast(2); + set.PrependOrMoveToFirst(1); + set.insert(3); + set.AppendOrMoveToLast(4); + set.insert(5); + + int i = 1; + for (auto element : set) { + EXPECT_EQ(element, i); + i++; + } + + Set::const_iterator it = set.find(2); + EXPECT_EQ(*it, 2); + it = set.find(3); + EXPECT_EQ(*it, 3); + it = set.find(10); + EXPECT_TRUE(it == set.end()); + + EXPECT_TRUE(set.Contains(1)); + EXPECT_TRUE(set.Contains(2)); + EXPECT_TRUE(set.Contains(3)); + EXPECT_TRUE(set.Contains(4)); + EXPECT_TRUE(set.Contains(5)); + + EXPECT_FALSE(set.Contains(10)); +} + +TEST(NewLinkedHashSetTest, Insert) { + using Set = NewLinkedHashSet<int>; + Set set; + Set::AddResult result = set.insert(1); + EXPECT_TRUE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 1); + + result = set.insert(1); + EXPECT_FALSE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 1); + + result = set.insert(2); + EXPECT_TRUE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 2); + + result = set.insert(3); + EXPECT_TRUE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 3); + + result = set.insert(2); + EXPECT_FALSE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 2); + + Set::const_iterator it = set.begin(); + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 2); + ++it; + EXPECT_EQ(*it, 3); + ++it; + EXPECT_TRUE(it == set.end()); +} + +TEST(NewLinkedHashSetTest, InsertBefore) { + using Set = NewLinkedHashSet<int>; + Set set; + + set.InsertBefore(set.begin(), 1); + set.InsertBefore(10, 3); + set.InsertBefore(3, 2); + set.InsertBefore(set.end(), 6); + set.InsertBefore(--set.end(), 5); + set.InsertBefore(5, 4); + + Set::const_iterator it = set.begin(); + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 2); + ++it; + EXPECT_EQ(*it, 3); + ++it; + EXPECT_EQ(*it, 4); + ++it; + EXPECT_EQ(*it, 5); + ++it; + EXPECT_EQ(*it, 6); + ++it; + EXPECT_TRUE(it == set.end()); +} + +TEST(NewLinkedHashSetTest, AppendOrMoveToLast) { + using Set = NewLinkedHashSet<int>; + Set set; + Set::AddResult result = set.AppendOrMoveToLast(1); + EXPECT_TRUE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 1); + + result = set.AppendOrMoveToLast(2); + EXPECT_TRUE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 2); + + result = set.AppendOrMoveToLast(1); + EXPECT_FALSE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 1); + + result = set.AppendOrMoveToLast(3); + EXPECT_TRUE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 3); + + Set::const_iterator it = set.begin(); + EXPECT_EQ(*it, 2); + ++it; + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 3); +} + +TEST(NewLinkedHashSetTest, PrependOrMoveToFirst) { + using Set = NewLinkedHashSet<int>; + Set set; + Set::AddResult result = set.PrependOrMoveToFirst(1); + EXPECT_TRUE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 1); + + result = set.PrependOrMoveToFirst(2); + EXPECT_TRUE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 2); + + result = set.PrependOrMoveToFirst(1); + EXPECT_FALSE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 1); + + result = set.PrependOrMoveToFirst(3); + EXPECT_TRUE(result.is_new_entry); + EXPECT_EQ(*result.stored_value, 3); + + Set::const_iterator it = set.begin(); + EXPECT_EQ(*it, 3); + ++it; + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 2); +} + +TEST(NewLinkedHashSetTest, Erase) { + using Set = NewLinkedHashSet<int>; + Set set; + set.insert(1); + set.insert(2); + set.insert(3); + set.insert(4); + set.insert(5); + + Set::const_iterator it = set.begin(); + ++it; + set.erase(it); + it = set.begin(); + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 3); + ++it; + EXPECT_EQ(*it, 4); + ++it; + EXPECT_EQ(*it, 5); + + set.erase(3); + it = set.begin(); + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 4); + ++it; + EXPECT_EQ(*it, 5); + + set.insert(6); + it = set.begin(); + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 4); + ++it; + EXPECT_EQ(*it, 5); + ++it; + EXPECT_EQ(*it, 6); +} + +TEST(NewLinkedHashSetTest, RemoveFirst) { + using Set = NewLinkedHashSet<int>; + Set set; + set.insert(1); + set.insert(2); + set.insert(3); + + set.RemoveFirst(); + Set::const_iterator it = set.begin(); + EXPECT_EQ(*it, 2); + ++it; + EXPECT_EQ(*it, 3); + + set.RemoveFirst(); + it = set.begin(); + EXPECT_EQ(*it, 3); + + set.RemoveFirst(); + EXPECT_TRUE(set.begin() == set.end()); +} + +TEST(NewLinkedHashSetTest, pop_back) { + using Set = NewLinkedHashSet<int>; + Set set; + set.insert(1); + set.insert(2); + set.insert(3); + + set.pop_back(); + Set::const_iterator it = set.begin(); + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 2); + + set.pop_back(); + it = set.begin(); + EXPECT_EQ(*it, 1); + + set.pop_back(); + EXPECT_TRUE(set.begin() == set.end()); +} + +TEST(NewLinkedHashSetTest, Clear) { + using Set = NewLinkedHashSet<int>; + Set set; + set.insert(1); + set.insert(2); + set.insert(3); + + set.clear(); + EXPECT_TRUE(set.begin() == set.end()); + + set.insert(1); + Set::const_iterator it = set.begin(); + EXPECT_EQ(*it, 1); + ++it; + EXPECT_TRUE(it == set.end()); +} + +} // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/list_hash_set.h b/chromium/third_party/blink/renderer/platform/wtf/list_hash_set.h index 39fd2a0dfdb..2792dbc6b97 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/list_hash_set.h +++ b/chromium/third_party/blink/renderer/platform/wtf/list_hash_set.h @@ -58,7 +58,7 @@ class ListHashSetReverseIterator; template <typename Set> class ListHashSetConstReverseIterator; -template <typename ValueArg> +template <typename ValueArg, typename Allocator> class ListHashSetNodeBase; template <typename ValueArg, typename Allocator> class ListHashSetNode; @@ -70,6 +70,17 @@ struct ListHashSetNodeHashFunctions; template <typename HashArg> struct ListHashSetTranslator; +template <typename Value, typename Allocator> +struct ListHashSetTraits + : public HashTraits<ListHashSetNode<Value, Allocator>*> { + using Node = ListHashSetNode<Value, Allocator>; + + static void ConstructDeletedValue(Node*& slot, bool) { + AsAtomicPtr(&slot)->store(reinterpret_cast<Node*>(-1), + std::memory_order_relaxed); + } +}; + // Note that for a ListHashSet you cannot specify the HashTraits as a template // argument. It uses the default hash traits for the ValueArg type. template <typename ValueArg, @@ -85,7 +96,7 @@ class ListHashSet USE_ALLOCATOR(ListHashSet, Allocator); typedef ListHashSetNode<ValueArg, Allocator> Node; - typedef HashTraits<Node*> NodeTraits; + typedef ListHashSetTraits<ValueArg, Allocator> NodeTraits; typedef ListHashSetNodeHashFunctions<HashArg> NodeHash; typedef ListHashSetTranslator<HashArg> BaseTranslator; @@ -137,14 +148,9 @@ class ListHashSet public: friend class ListHashSet<ValueArg, inlineCapacity, HashArg, AllocatorArg>; AddResult(Node* node, bool is_new_entry) - : stored_value(&node->value_), - is_new_entry(is_new_entry), - node_(node) {} + : stored_value(&node->value_), is_new_entry(is_new_entry) {} ValueType* stored_value; bool is_new_entry; - - private: - Node* node_; }; ListHashSet(); @@ -201,12 +207,6 @@ class ListHashSet template <typename IncomingValueType> AddResult insert(IncomingValueType&&); - // Same as insert() except that the return value is an iterator. Useful in - // cases where it's needed to have the same return value as find() and where - // it's not possible to use a pointer to the storedValue. - template <typename IncomingValueType> - iterator AddReturnIterator(IncomingValueType&&); - // Add the value to the end of the collection. If the value was already in // the list, it is moved to the end. template <typename IncomingValueType> @@ -235,8 +235,8 @@ class ListHashSet ValueType Take(ValuePeekInType); ValueType TakeFirst(); - template <typename VisitorDispatcher> - void Trace(VisitorDispatcher); + template <typename VisitorDispatcher, typename A = AllocatorArg> + std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher) const; protected: typename ImplType::ValueType** GetBufferSlot() { @@ -272,10 +272,62 @@ class ListHashSet typename Allocator::AllocatorProvider allocator_provider_; }; +template <typename T, typename Allocator> +class ListHashSetNodeBasePointer { + using NodeType = ListHashSetNodeBase<T, Allocator>; + + public: + ListHashSetNodeBasePointer& operator=( + const ListHashSetNodeBasePointer& other) { + SetSafe(other); + return *this; + } + + template <typename U> + ListHashSetNodeBasePointer& operator=( + const ListHashSetNodeBasePointer<U, Allocator>& other) { + SetSafe(other); + return *this; + } + + template <typename U> + ListHashSetNodeBasePointer& operator=(U* other) { + SetSafe(other); + return *this; + } + + ListHashSetNodeBasePointer& operator=(std::nullptr_t) { + SetSafe(nullptr); + return *this; + } + + NodeType* Get() const { return node_; } + explicit operator bool() const { return Get(); } + operator NodeType*() const { return Get(); } + NodeType* operator->() const { return Get(); } + NodeType& operator*() const { return *Get(); } + + private: + void SetSafe(NodeType* node) { + AsAtomicPtr(&node_)->store(node, std::memory_order_relaxed); + } + + NodeType* GetSafe() const { + if (Allocator::kIsGarbageCollected) + return AsAtomicPtr(&node_)->load(std::memory_order_relaxed); + return node_; + } + + NodeType* node_ = nullptr; + + template <typename ValueArg, typename AllocatorArg> + friend class ListHashSetNode; +}; + // ListHashSetNode has this base class to hold the members because the MSVC // compiler otherwise gets into circular template dependencies when trying to do // sizeof on a node. -template <typename ValueArg> +template <typename ValueArg, typename Allocator> class ListHashSetNodeBase { DISALLOW_NEW(); @@ -285,8 +337,8 @@ class ListHashSetNodeBase { public: ValueArg value_; - ListHashSetNodeBase* prev_ = nullptr; - ListHashSetNodeBase* next_ = nullptr; + ListHashSetNodeBasePointer<ValueArg, Allocator> prev_; + ListHashSetNodeBasePointer<ValueArg, Allocator> next_; #if DCHECK_IS_ON() bool is_allocated_ = true; #endif @@ -297,7 +349,7 @@ template <typename ValueArg, size_t inlineCapacity> struct ListHashSetAllocator : public PartitionAllocator { typedef PartitionAllocator TableAllocator; typedef ListHashSetNode<ValueArg, ListHashSetAllocator> Node; - typedef ListHashSetNodeBase<ValueArg> NodeBase; + typedef ListHashSetNodeBase<ValueArg, ListHashSetAllocator> NodeBase; class AllocatorProvider { DISALLOW_NEW(); @@ -382,7 +434,7 @@ struct ListHashSetAllocator : public PartitionAllocator { bool InPool(Node* node) { return node >= Pool() && node < PastPool(); } template <typename VisitorDispatcher> - static void TraceValue(VisitorDispatcher, Node*) {} + static void TraceValue(VisitorDispatcher, const Node*) {} private: Node* Pool() { return reinterpret_cast_ptr<Node*>(pool_); } @@ -402,19 +454,19 @@ struct ListHashSetAllocator : public PartitionAllocator { }; template <typename ValueArg, typename AllocatorArg> -class ListHashSetNode : public ListHashSetNodeBase<ValueArg> { +class ListHashSetNode : public ListHashSetNodeBase<ValueArg, AllocatorArg> { public: typedef AllocatorArg NodeAllocator; typedef ValueArg Value; template <typename U> ListHashSetNode(U&& value) - : ListHashSetNodeBase<ValueArg>(std::forward<U>(value)) {} + : ListHashSetNodeBase<ValueArg, AllocatorArg>(std::forward<U>(value)) {} void* operator new(size_t, NodeAllocator* allocator) { - static_assert( - sizeof(ListHashSetNode) == sizeof(ListHashSetNodeBase<ValueArg>), - "please add any fields to the base"); + static_assert(sizeof(ListHashSetNode) == + sizeof(ListHashSetNodeBase<ValueArg, AllocatorArg>), + "please add any fields to the base"); return allocator->AllocateNode(); } @@ -452,7 +504,16 @@ class ListHashSetNode : public ListHashSetNodeBase<ValueArg> { } template <typename VisitorDispatcher, typename A = NodeAllocator> - std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) { + std::enable_if_t<A::kIsGarbageCollected> Trace( + VisitorDispatcher visitor) const { + if (visitor->ConcurrentTracingBailOut( + {this, [](blink::Visitor* visitor, const void* object) { + reinterpret_cast<const ListHashSetNode<ValueArg, AllocatorArg>*>( + object) + ->Trace(visitor); + }})) + return; + // The conservative stack scan can find nodes that have been removed // from the set and destructed. We don't need to trace these, and it // would be wrong to do so, because the class will not expect the trace @@ -460,18 +521,20 @@ class ListHashSetNode : public ListHashSetNodeBase<ValueArg> { // node from the ListHashSet while an iterator is positioned at that // node, so there should be no valid pointers from the stack to a // destructed node. - if (WasAlreadyDestructed()) + if (WasAlreadyDestructedSafe()) return; NodeAllocator::TraceValue(visitor, this); - visitor->Trace(Next()); - visitor->Trace(Prev()); + visitor->Trace( + reinterpret_cast<const ListHashSetNode*>(this->next_.GetSafe())); + visitor->Trace( + reinterpret_cast<const ListHashSetNode*>(this->prev_.GetSafe())); } ListHashSetNode* Next() const { - return reinterpret_cast<ListHashSetNode*>(this->next_); + return reinterpret_cast<ListHashSetNode*>(this->next_.Get()); } ListHashSetNode* Prev() const { - return reinterpret_cast<ListHashSetNode*>(this->prev_); + return reinterpret_cast<ListHashSetNode*>(this->prev_.Get()); } // Don't add fields here, the ListHashSetNodeBase and this should have the @@ -483,6 +546,12 @@ class ListHashSetNode : public ListHashSetNodeBase<ValueArg> { template <typename HashArg> friend struct ListHashSetNodeHashFunctions; + + private: + bool WasAlreadyDestructedSafe() const { + DCHECK(NodeAllocator::kIsGarbageCollected); + return this->prev_.GetSafe() == UnlinkedNodePointer(); + } }; template <typename HashArg> @@ -544,7 +613,7 @@ class ListHashSetIterator { operator const_iterator() const { return iterator_; } template <typename VisitorDispatcher> - void Trace(VisitorDispatcher visitor) { + void Trace(VisitorDispatcher visitor) const { iterator_.Trace(visitor); } @@ -606,7 +675,7 @@ class ListHashSetConstIterator { } template <typename VisitorDispatcher> - void Trace(VisitorDispatcher visitor) { + void Trace(VisitorDispatcher visitor) const { visitor->Trace(*set_); visitor->Trace(position_); } @@ -666,7 +735,7 @@ class ListHashSetReverseIterator { operator const_reverse_iterator() const { return iterator_; } template <typename VisitorDispatcher> - void Trace(VisitorDispatcher visitor) { + void Trace(VisitorDispatcher visitor) const { iterator_.trace(visitor); } @@ -728,7 +797,7 @@ class ListHashSetConstReverseIterator { } template <typename VisitorDispatcher> - void Trace(VisitorDispatcher visitor) { + void Trace(VisitorDispatcher visitor) const { visitor->Trace(*set_); visitor->Trace(position_); } @@ -756,7 +825,9 @@ struct ListHashSetTranslator { } template <typename T, typename U, typename V> static void Translate(T*& location, U&& key, const V& allocator) { - location = new (const_cast<V*>(&allocator)) T(std::forward<U>(key)); + AsAtomicPtr(&location)->store(new (const_cast<V*>(&allocator)) + T(std::forward<U>(key)), + std::memory_order_relaxed); } }; @@ -939,14 +1010,6 @@ ListHashSet<T, inlineCapacity, U, V>::insert(IncomingValueType&& value) { template <typename T, size_t inlineCapacity, typename U, typename V> template <typename IncomingValueType> -typename ListHashSet<T, inlineCapacity, U, V>::iterator -ListHashSet<T, inlineCapacity, U, V>::AddReturnIterator( - IncomingValueType&& value) { - return MakeIterator(insert(std::forward<IncomingValueType>(value)).node_); -} - -template <typename T, size_t inlineCapacity, typename U, typename V> -template <typename IncomingValueType> typename ListHashSet<T, inlineCapacity, U, V>::AddResult ListHashSet<T, inlineCapacity, U, V>::AppendOrMoveToLast( IncomingValueType&& value) { @@ -1125,8 +1188,17 @@ void ListHashSet<T, inlineCapacity, U, V>::DeleteAllNodes() { } template <typename T, size_t inlineCapacity, typename U, typename V> -template <typename VisitorDispatcher> -void ListHashSet<T, inlineCapacity, U, V>::Trace(VisitorDispatcher visitor) { +template <typename VisitorDispatcher, typename A> +std::enable_if_t<A::kIsGarbageCollected> +ListHashSet<T, inlineCapacity, U, V>::Trace(VisitorDispatcher visitor) const { + if (visitor->ConcurrentTracingBailOut( + {this, [](blink::Visitor* visitor, const void* object) { + reinterpret_cast<const ListHashSet<T, inlineCapacity, U, V>*>( + object) + ->Trace(visitor); + }})) + return; + static_assert(!IsWeak<T>::value, "HeapListHashSet does not support weakness, consider using " "HeapLinkedHashSet instead."); diff --git a/chromium/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc b/chromium/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc index cd1be0089b9..f9cb5f8270c 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/list_hash_set_test.cc @@ -41,12 +41,17 @@ namespace { template <typename Set> class ListOrLinkedHashSetTest : public testing::Test {}; -using SetTypes = - testing::Types<ListHashSet<int>, ListHashSet<int, 1>, LinkedHashSet<int>>; +using SetTypes = testing::Types<ListHashSet<int>, + ListHashSet<int, 1>, + LinkedHashSet<int>, + NewLinkedHashSet<int>>; TYPED_TEST_SUITE(ListOrLinkedHashSetTest, SetTypes); TYPED_TEST(ListOrLinkedHashSetTest, RemoveFirst) { using Set = TypeParam; + // TODO(bartekn): Make the test work. Fails due to empty value. + if (std::is_same<Set, NewLinkedHashSet<int>>::value) + return; Set list; list.insert(-1); list.insert(0); @@ -197,6 +202,9 @@ TYPED_TEST(ListOrLinkedHashSetTest, PrependOrMoveToLastWithDuplicates) { TYPED_TEST(ListOrLinkedHashSetTest, Find) { using Set = TypeParam; + // TODO(bartekn): Make the test work. Fails due to empty value. + if (std::is_same<Set, NewLinkedHashSet<int>>::value) + return; Set set; set.insert(-1); set.insert(0); @@ -228,6 +236,9 @@ TYPED_TEST(ListOrLinkedHashSetTest, Find) { TYPED_TEST(ListOrLinkedHashSetTest, InsertBefore) { using Set = TypeParam; + // TODO(bartekn): Make the test work. Fails due to empty value. + if (std::is_same<Set, NewLinkedHashSet<int>>::value) + return; bool can_modify_while_iterating = !std::is_same<Set, LinkedHashSet<int>>::value; Set set; @@ -273,54 +284,6 @@ TYPED_TEST(ListOrLinkedHashSetTest, InsertBefore) { EXPECT_EQ(7u, set.size()); } -TYPED_TEST(ListOrLinkedHashSetTest, AddReturnIterator) { - using Set = TypeParam; - bool can_modify_while_iterating = - !std::is_same<Set, LinkedHashSet<int>>::value; - Set set; - set.insert(-1); - set.insert(0); - set.insert(1); - set.insert(2); - - typename Set::iterator it = set.AddReturnIterator(3); - EXPECT_EQ(3, *it); - --it; - EXPECT_EQ(2, *it); - EXPECT_EQ(5u, set.size()); - --it; - EXPECT_EQ(1, *it); - --it; - EXPECT_EQ(0, *it); - it = set.AddReturnIterator(4); - if (can_modify_while_iterating) { - set.erase(3); - set.erase(2); - set.erase(1); - set.erase(0); - set.erase(-1); - EXPECT_EQ(1u, set.size()); - } - EXPECT_EQ(4, *it); - ++it; - EXPECT_EQ(it, set.end()); - --it; - EXPECT_EQ(4, *it); - if (can_modify_while_iterating) { - set.InsertBefore(it, -1); - set.InsertBefore(it, 0); - set.InsertBefore(it, 1); - set.InsertBefore(it, 2); - set.InsertBefore(it, 3); - } - EXPECT_EQ(6u, set.size()); - it = set.AddReturnIterator(5); - EXPECT_EQ(7u, set.size()); - set.erase(it); - EXPECT_EQ(6u, set.size()); - EXPECT_EQ(4, set.back()); -} - TYPED_TEST(ListOrLinkedHashSetTest, Swap) { using Set = TypeParam; int num = 10; @@ -409,11 +372,17 @@ class ListOrLinkedHashSetRefPtrTest : public testing::Test {}; using RefPtrSetTypes = testing::Types<ListHashSet<scoped_refptr<DummyRefCounted>>, ListHashSet<scoped_refptr<DummyRefCounted>, 1>, - LinkedHashSet<scoped_refptr<DummyRefCounted>>>; + LinkedHashSet<scoped_refptr<DummyRefCounted>>, + NewLinkedHashSet<scoped_refptr<DummyRefCounted>>>; TYPED_TEST_SUITE(ListOrLinkedHashSetRefPtrTest, RefPtrSetTypes); TYPED_TEST(ListOrLinkedHashSetRefPtrTest, WithRefPtr) { using Set = TypeParam; + int expected = 1; + // NewLinkedHashSet stores each object twice. + if (std::is_same<Set, + NewLinkedHashSet<scoped_refptr<DummyRefCounted>>>::value) + expected = 2; bool is_deleted = false; DummyRefCounted::ref_invokes_count_ = 0; scoped_refptr<DummyRefCounted> ptr = @@ -423,24 +392,24 @@ TYPED_TEST(ListOrLinkedHashSetRefPtrTest, WithRefPtr) { Set set; set.insert(ptr); // Referenced only once (to store a copy in the container). - EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_); + EXPECT_EQ(expected, DummyRefCounted::ref_invokes_count_); EXPECT_EQ(ptr, set.front()); - EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_); + EXPECT_EQ(expected, DummyRefCounted::ref_invokes_count_); DummyRefCounted* raw_ptr = ptr.get(); EXPECT_TRUE(set.Contains(ptr)); EXPECT_TRUE(set.Contains(raw_ptr)); - EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_); + EXPECT_EQ(expected, DummyRefCounted::ref_invokes_count_); ptr = nullptr; EXPECT_FALSE(is_deleted); - EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_); + EXPECT_EQ(expected, DummyRefCounted::ref_invokes_count_); set.erase(raw_ptr); EXPECT_TRUE(is_deleted); - EXPECT_EQ(1, DummyRefCounted::ref_invokes_count_); + EXPECT_EQ(expected, DummyRefCounted::ref_invokes_count_); } TYPED_TEST(ListOrLinkedHashSetRefPtrTest, ExerciseValuePeekInType) { @@ -460,11 +429,10 @@ TYPED_TEST(ListOrLinkedHashSetRefPtrTest, ExerciseValuePeekInType) { const Set& const_set(set); const_set.find(ptr); EXPECT_TRUE(set.Contains(ptr)); - typename Set::iterator it = set.AddReturnIterator(ptr); + set.insert(ptr); set.AppendOrMoveToLast(ptr); set.PrependOrMoveToFirst(ptr); set.InsertBefore(ptr, ptr); - set.InsertBefore(it, ptr); EXPECT_EQ(1u, set.size()); set.insert(ptr2); ptr2 = nullptr; @@ -516,6 +484,7 @@ struct ComplexityTranslator { template <typename Set> class ListOrLinkedHashSetTranslatorTest : public testing::Test {}; +// TODO(bartekn): Add NewLinkedHashSet once it supports custom hash function. using TranslatorSetTypes = testing::Types<ListHashSet<Complicated, 256, ComplicatedHashFunctions>, ListHashSet<Complicated, 1, ComplicatedHashFunctions>, @@ -625,7 +594,8 @@ class ListOrLinkedHashSetCountCopyTest : public testing::Test {}; using CountCopySetTypes = testing::Types<ListHashSet<CountCopy>, ListHashSet<CountCopy, 1>, - LinkedHashSet<CountCopy>>; + LinkedHashSet<CountCopy>, + NewLinkedHashSet<CountCopy>>; TYPED_TEST_SUITE(ListOrLinkedHashSetCountCopyTest, CountCopySetTypes); TYPED_TEST(ListOrLinkedHashSetCountCopyTest, @@ -655,6 +625,7 @@ TYPED_TEST(ListOrLinkedHashSetCountCopyTest, MoveAssignmentShouldNotMakeACopy) { template <typename Set> class ListOrLinkedHashSetMoveOnlyTest : public testing::Test {}; +// TODO(bartekn): Add NewLinkedHashSet once it supports move-only type. using MoveOnlySetTypes = testing::Types<ListHashSet<MoveOnlyHashValue>, ListHashSet<MoveOnlyHashValue, 1>, LinkedHashSet<MoveOnlyHashValue>>; @@ -685,14 +656,6 @@ TYPED_TEST(ListOrLinkedHashSetMoveOnlyTest, MoveOnlyValue) { EXPECT_TRUE(iter == set.end()); // ListHashSet and LinkedHashSet have several flavors of add(). - iter = set.AddReturnIterator(MoveOnlyHashValue(2, 2)); - EXPECT_EQ(2, iter->Value()); - EXPECT_EQ(2, iter->Id()); - - iter = set.AddReturnIterator(MoveOnlyHashValue(2, 222)); - EXPECT_EQ(2, iter->Value()); - EXPECT_EQ(2, iter->Id()); - { AddResult add_result = set.AppendOrMoveToLast(MoveOnlyHashValue(3, 3)); EXPECT_TRUE(add_result.is_new_entry); @@ -758,6 +721,7 @@ struct DefaultHash<InvalidZeroValue> { template <typename Set> class ListOrLinkedHashSetInvalidZeroTest : public testing::Test {}; +// TODO(bartekn): Add NewLinkedHashSet once it supports custom hash traits. using InvalidZeroValueSetTypes = testing::Types<ListHashSet<InvalidZeroValue>, ListHashSet<InvalidZeroValue, 1>, diff --git a/chromium/third_party/blink/renderer/platform/wtf/lru_cache.h b/chromium/third_party/blink/renderer/platform/wtf/lru_cache.h new file mode 100644 index 00000000000..c2d27f0e430 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/wtf/lru_cache.h @@ -0,0 +1,148 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LRU_CACHE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LRU_CACHE_H_ + +#include <memory> + +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" +#include "third_party/blink/renderer/platform/wtf/construct_traits.h" +#include "third_party/blink/renderer/platform/wtf/doubly_linked_list.h" +#include "third_party/blink/renderer/platform/wtf/hash_map.h" +#include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h" + +namespace WTF { + +// LruCache is a simple least-recently-used cache based on HashMap and +// DoublyLinkedList. Useful in situations where caching by using a HashMap is +// desirable but needs to be limited in size to not grow out of proportions. +// LruCache uses a HashMap to store cache entries, and uses a DoublyLinkedList +// in parallel to keep track of the age of entries. Accessing an entry using +// Get() refreshes its age, Put() places a new entry into the HashMap with +// youngest age as well. The least recently used entry of the list is pruned +// when a Put() call would otherwise exceed the max_size limit. +// +// Example: +// const size_t kMaxSize = 2; +// LruCache<uint16_t, String> my_cache(kMaxSize); +// my_cache.Put(13, "first string"); +// my_cache.Put(42, "second string"); +// my_cache.Put(256, "third string"); +// my_cache.Get(13) // -> nullptr, has been removed due to kMaxSize == 2. +// my_cache.Get(42) // -> String* "second string" +// my_cache.Get(256) // -> String* "third_string" +// +// See lru_cache_test.cc for more examples. +template <typename KeyArg, + typename MappedArg, + typename HashArg = typename DefaultHash<KeyArg>::Hash, + typename KeyTraitsArg = HashTraits<KeyArg>> +class LruCache { + USING_FAST_MALLOC(LruCache); + + private: + class MappedListNodeWithKey final + : public DoublyLinkedListNode<MappedListNodeWithKey> { + USING_FAST_MALLOC(MappedListNodeWithKey); + + public: + friend class DoublyLinkedListNode<MappedListNodeWithKey>; + + MappedListNodeWithKey(const KeyArg& key, MappedArg&& mapped_arg) + : key_(key), mapped_value_(std::move(mapped_arg)) {} + + MappedArg* Value() { return &mapped_value_; } + + void SetValue(MappedArg&& mapped_arg) { + mapped_value_ = std::move(mapped_arg); + } + + const KeyArg& Key() const { return key_; } + + private: + KeyArg key_; + MappedArg mapped_value_; + MappedListNodeWithKey* prev_{nullptr}; + MappedListNodeWithKey* next_{nullptr}; + }; + + using MappedListNode = std::unique_ptr<MappedListNodeWithKey>; + + using HashMapType = HashMap<KeyArg, MappedListNode, HashArg, KeyTraitsArg>; + + public: + LruCache(size_t max_size) : max_size_(max_size) { + static_assert(!IsGarbageCollectedType<KeyArg>::value || + !IsGarbageCollectedType<MappedArg>::value, + "Cannot use LruCache<> with garbage collected types."); + CHECK_GT(max_size_, 0u); + } + + // Retrieve cache entry under |key| if it exists and refresh its age. + // Returns: pointer to cache entry or nullptr if no entry is found for that + // key. + MappedArg* Get(const KeyArg& key) { + if (map_.IsEmpty()) + return nullptr; + + typename HashMapType::iterator find_result = map_.find(key); + if (find_result == map_.end()) + return nullptr; + + // Move result to beginning of list. + MappedListNodeWithKey* node = find_result->value.get(); + ordering_.Remove(node); + ordering_.Push(node); + return find_result->value->Value(); + } + + // Place entry in cache as new / youngest. Multiple calls to Put() with an + // identical key but differing MappedArg will override the stored value and + // refresh the age. + void Put(const KeyArg& key, MappedArg&& arg) { + { + typename HashMapType::iterator find_result = map_.find(key); + if (find_result != map_.end()) { + find_result->value->SetValue(std::move(arg)); + ordering_.Remove(find_result->value.get()); + ordering_.Push(find_result->value.get()); + } else { + auto list_node = + std::make_unique<MappedListNodeWithKey>(key, std::move(arg)); + typename HashMapType::AddResult add_result = + map_.insert(key, std::move(list_node)); + DCHECK(add_result.is_new_entry); + ordering_.Push(add_result.stored_value->value.get()); + } + } + + if (map_.size() > max_size_) { + RemoveLeastRecentlyUsed(); + } + } + + // Clear the cache, remove all elements. + void Clear() { + map_.clear(); + ordering_.Clear(); + } + + size_t size() { return map_.size(); } + + private: + void RemoveLeastRecentlyUsed() { + MappedListNodeWithKey* tail = ordering_.Tail(); + ordering_.Remove(tail); + map_.erase(tail->Key()); + } + + HashMapType map_; + DoublyLinkedList<MappedListNodeWithKey> ordering_; + size_t max_size_; +}; + +} // namespace WTF + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_LRU_CACHE_H_ diff --git a/chromium/third_party/blink/renderer/platform/wtf/lru_cache_test.cc b/chromium/third_party/blink/renderer/platform/wtf/lru_cache_test.cc new file mode 100644 index 00000000000..914b15db53f --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/wtf/lru_cache_test.cc @@ -0,0 +1,110 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/wtf/lru_cache.h" + +#include "third_party/blink/renderer/platform/wtf/text/string_builder.h" +#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace WTF { + +TEST(LruCacheTest, TestEmpty) { + LruCache<int, int> test_cache(1); + EXPECT_EQ(test_cache.Get(0), nullptr); +} + +TEST(LruCacheTest, TestInstantiation) { + const int kMaxSize = 10; + const int kOffset = 1000; + LruCache<int, int> test_cache(kMaxSize); + EXPECT_EQ(test_cache.size(), 0u); + for (size_t i = 1; i < kMaxSize * 10; ++i) { + test_cache.Put(i, kOffset + i); + } + EXPECT_EQ(test_cache.size(), kMaxSize * 1u); + EXPECT_EQ(*test_cache.Get(kMaxSize * 10 - 1), kOffset + (kMaxSize * 10 - 1)); + EXPECT_EQ(*test_cache.Get(kMaxSize * 10 - 2), kOffset + (kMaxSize * 10 - 2)); + EXPECT_EQ(test_cache.Get(89), nullptr); + EXPECT_EQ(test_cache.Get(1), nullptr); + test_cache.Clear(); + EXPECT_EQ(test_cache.size(), 0u); +} + +TEST(LruCacheTest, TestString) { + const size_t kMaxSize = 4; + const char* test_strings[] = {"1_testing", "2_LruCache", "3_using", + "4_several", "5_random", "6_strings"}; + LruCache<uint16_t, String> test_cache(kMaxSize); + uint16_t counter = 1; + EXPECT_EQ(test_cache.size(), 0u); + for (auto* test_string : test_strings) { + test_cache.Put(counter, test_string); + counter++; + } + EXPECT_EQ(test_cache.size(), kMaxSize); + EXPECT_EQ(test_cache.Get(1), nullptr); + EXPECT_EQ(test_cache.Get(2), nullptr); + EXPECT_EQ(*test_cache.Get(3), String(test_strings[2])); + EXPECT_EQ(*test_cache.Get(4), String(test_strings[3])); + EXPECT_EQ(*test_cache.Get(5), String(test_strings[4])); + EXPECT_EQ(*test_cache.Get(6), String(test_strings[5])); + test_cache.Put(1, test_strings[0]); + EXPECT_EQ(*test_cache.Get(1), String(test_strings[0])); + EXPECT_EQ(test_cache.Get(3), nullptr); + EXPECT_EQ(*test_cache.Get(4), String(test_strings[3])); + EXPECT_EQ(*test_cache.Get(5), String(test_strings[4])); + EXPECT_EQ(*test_cache.Get(6), String(test_strings[5])); + test_cache.Clear(); + EXPECT_EQ(test_cache.size(), 0u); +} + +TEST(LruCacheTest, TestOverrideKey) { + const size_t kMaxSize = 2; + const char* test_strings[] = {"original_value", "override"}; + LruCache<uint16_t, String> test_cache(kMaxSize); + EXPECT_EQ(test_cache.size(), 0u); + test_cache.Put(1, test_strings[0]); + test_cache.Put(1, test_strings[1]); + EXPECT_EQ(*test_cache.Get(1), String(test_strings[1])); +} + +TEST(LruCacheTest, StringToVector) { + const size_t kMaxSize = 4u; + LruCache<String, Vector<String>> typeface_cache(kMaxSize); + + struct FontFallbackExample { + String locale; + String typeface_name; + } example_typefaces[] = {{"en_us", "Arial"}, + {"ko", "Malgun Gothic"}, + {"ja", "Yu Gothic UI"}, + {"en_us", "Times New Roman"}, + {"en_us", "Calibri"}, + {"km", "Leelawadee UI"}, + {"zh-Hans", "Microsoft Yahei UI"}, + {"bn", "Nirmala UI"}}; + for (auto& example : example_typefaces) { + Vector<String>* cache_for_locale = typeface_cache.Get(example.locale); + if (cache_for_locale) { + cache_for_locale->push_back(example.typeface_name); + } else { + Vector<String> new_cache_for_locale; + new_cache_for_locale.push_back(example.typeface_name); + typeface_cache.Put(String(example.locale), + std::move(new_cache_for_locale)); + } + } + Vector<String>* vector_for_latin = typeface_cache.Get("en_us"); + EXPECT_TRUE(vector_for_latin); + EXPECT_EQ(vector_for_latin->size(), 3u); + EXPECT_EQ(typeface_cache.Get("zh-Hant"), nullptr); + EXPECT_EQ(typeface_cache.Get("ko"), nullptr); + EXPECT_EQ(typeface_cache.Get("ja"), nullptr); +} + +} // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/pod_interval.h b/chromium/third_party/blink/renderer/platform/wtf/pod_interval.h index 93a5a345991..ef6972acf3d 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/pod_interval.h +++ b/chromium/third_party/blink/renderer/platform/wtf/pod_interval.h @@ -86,11 +86,11 @@ class PODInterval { // UserData type is a pointer or other type which can be initialized // with 0. PODInterval(const T& low, const T& high) - : low_(low), high_(high), data_(0), max_high_(high) {} + : low_(low), high_(high), data_(0), min_low_(low), max_high_(high) {} // Constructor from two endpoints plus explicit user data. PODInterval(const T& low, const T& high, const UserData data) - : low_(low), high_(high), data_(data), max_high_(high) {} + : low_(low), high_(high), data_(data), min_low_(low), max_high_(high) {} const T& Low() const { return low_; } const T& High() const { return high_; } @@ -119,6 +119,9 @@ class PODInterval { Data() == other.Data()); } + const T& MinLow() const { return min_low_; } + void SetMinLow(const T& min_low) { min_low_ = min_low; } + const T& MaxHigh() const { return max_high_; } void SetMaxHigh(const T& max_high) { max_high_ = max_high; } @@ -132,6 +135,8 @@ class PODInterval { builder.Append(ValueToString<T>::ToString(High())); builder.Append("), data="); builder.Append(ValueToString<UserData>::ToString(Data())); + builder.Append(", minLow="); + builder.Append(ValueToString<T>::ToString(MinLow())); builder.Append(", maxHigh="); builder.Append(ValueToString<T>::ToString(MaxHigh())); builder.Append(']'); @@ -148,6 +153,7 @@ class PODInterval { #else UserData data_; #endif + T min_low_; T max_high_; }; diff --git a/chromium/third_party/blink/renderer/platform/wtf/pod_interval_tree.h b/chromium/third_party/blink/renderer/platform/wtf/pod_interval_tree.h index 79af877b225..7db780f2500 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/pod_interval_tree.h +++ b/chromium/third_party/blink/renderer/platform/wtf/pod_interval_tree.h @@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_POD_INTERVAL_TREE_H_ #include "base/macros.h" +#include "base/optional.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/pod_arena.h" #include "third_party/blink/renderer/platform/wtf/pod_interval.h" @@ -105,14 +106,14 @@ class PODIntervalTree final : public PODRedBlackTree<PODInterval<T, UserData>> { // Explicit dereference of "this" required because of // inheritance rules in template classes. IntervalSearchAdapterType adapter(result, interval.Low(), interval.High()); - SearchForOverlapsFrom<IntervalSearchAdapterType>(this->Root(), adapter); + SearchForOverlapsFrom(this->Root(), adapter); } template <class AdapterType> void AllOverlapsWithAdapter(AdapterType& adapter) const { // Explicit dereference of "this" required because of // inheritance rules in template classes. - SearchForOverlapsFrom<AdapterType>(this->Root(), adapter); + SearchForOverlapsFrom(this->Root(), adapter); } // Helper to create interval objects. @@ -127,7 +128,13 @@ class PODIntervalTree final : public PODRedBlackTree<PODInterval<T, UserData>> { return false; if (!this->Root()) return true; - return CheckInvariantsFromNode(this->Root(), nullptr); + return CheckInvariantsFromNode(this->Root()); + } + + // Returns the next interval point (start or end) after the given starting + // point (non-inclusive). If there is no such point, returns |base::nullopt|. + base::Optional<T> NextIntervalPoint(T start) const { + return NextIntervalPoint(start, this->Root()); } private: @@ -144,102 +151,184 @@ class PODIntervalTree final : public PODRedBlackTree<PODInterval<T, UserData>> { // interval to the result vector. The intervals are sorted by // increasing low endpoint. template <class AdapterType> - DISABLE_CFI_PERF void SearchForOverlapsFrom(IntervalNode* node, - AdapterType& adapter) const { - if (!node) + DISABLE_CFI_PERF static void SearchForOverlapsFrom(IntervalNode const* node, + AdapterType& adapter) { + // This is phrased this way to avoid the need for operator + // <= on type T. + if (!node || adapter.HighValue() < node->Data().MinLow() || + node->Data().MaxHigh() < adapter.LowValue()) { return; + } // Because the intervals are sorted by left endpoint, inorder // traversal produces results sorted as desired. - // See whether we need to traverse the left subtree. - IntervalNode* left = node->Left(); - if (left - // This is phrased this way to avoid the need for operator - // <= on type T. - && !(left->Data().MaxHigh() < adapter.LowValue())) - SearchForOverlapsFrom<AdapterType>(left, adapter); + // Attempt to traverse left subtree + SearchForOverlapsFrom(node->Left(), adapter); // Check for overlap with current node. adapter.CollectIfNeeded(node->Data()); - // See whether we need to traverse the right subtree. - // This is phrased this way to avoid the need for operator <= - // on type T. - if (!(adapter.HighValue() < node->Data().Low())) - SearchForOverlapsFrom<AdapterType>(node->Right(), adapter); + // Attempt to traverse right subtree + SearchForOverlapsFrom(node->Right(), adapter); + } + + static base::Optional<T> NextIntervalPoint(T start, + IntervalNode const* node) { + // If this node doesn't exist or is entirely out of scope, just return. This + // prevents recursing deeper than necessary on the left. + if (!node || node->Data().MaxHigh() < start) { + return base::nullopt; + } + // Easy shortcut: If the lowest point in this subtree is in scope, just + // return that. This prevents recursing deeper than necessary on the right. + if (start < node->Data().MinLow()) { + return node->Data().MinLow(); + } + + auto left_candidate = NextIntervalPoint(start, node->Left()); + + // If the current node's low point isn't out of scope, we don't even need to + // look at the right branch. + if (start < node->Data().Low()) { + if (left_candidate.has_value()) { + return std::min(node->Data().Low(), left_candidate.value()); + } else { + return node->Data().Low(); + } + } + + // If the current node's high point is in scope, consider that against the + // left branch + base::Optional<T> current_candidate; + if (start < node->Data().High()) { + if (left_candidate.has_value()) { + current_candidate = + std::min(node->Data().High(), left_candidate.value()); + } else { + current_candidate = node->Data().High(); + } + } else { + current_candidate = left_candidate; + } + + // If the current (and left) nodes fail, tail-recurse on the right node + if (!current_candidate.has_value()) { + return NextIntervalPoint(start, node->Right()); + } + + // Otherwise, pick the min between the |current_candidate| and the right + // node + auto right_candidate = NextIntervalPoint(start, node->Right()); + if (right_candidate.has_value()) { + return std::min(current_candidate.value(), right_candidate.value()); + } else { + return current_candidate; + } } bool UpdateNode(IntervalNode* node) override { - // Would use const T&, but need to reassign this reference in this - // function. - const T* cur_max = &node->Data().High(); + T cur_max(node->Data().High()); + T cur_min(node->Data().Low()); + IntervalNode* left = node->Left(); if (left) { - if (*cur_max < left->Data().MaxHigh()) - cur_max = &left->Data().MaxHigh(); + // Left node will always have a lower MinLow than the right node, so just + // reassign immediately. + cur_min = left->Data().MinLow(); + cur_max = std::max(cur_max, left->Data().MaxHigh()); } + IntervalNode* right = node->Right(); if (right) { - if (*cur_max < right->Data().MaxHigh()) - cur_max = &right->Data().MaxHigh(); + // Right node will always have greater min than current node or left + // branch, so don't bother checking it. + cur_max = std::max(cur_max, right->Data().MaxHigh()); } - // This is phrased like this to avoid needing operator!= on type T. - if (!(*cur_max == node->Data().MaxHigh())) { - node->Data().SetMaxHigh(*cur_max); - return true; + + bool updated = false; + if (!(cur_min == node->Data().MinLow())) { + node->Data().SetMinLow(cur_min); + updated = true; + } + if (!(cur_max == node->Data().MaxHigh())) { + node->Data().SetMaxHigh(cur_max); + updated = true; } - return false; + + return updated; } - bool CheckInvariantsFromNode(IntervalNode* node, T* current_max_value) const { - // These assignments are only done in order to avoid requiring - // a default constructor on type T. - T left_max_value(node->Data().MaxHigh()); - T right_max_value(node->Data().MaxHigh()); - IntervalNode* left = node->Left(); - IntervalNode* right = node->Right(); + static bool CheckInvariantsFromNode(IntervalNode const* node) { + IntervalNode const* left = node->Left(); + IntervalNode const* right = node->Right(); + + T observed_min_value(node->Data().Low()); + T observed_max_value(node->Data().High()); + if (left) { - if (!CheckInvariantsFromNode(left, &left_max_value)) + // Ensure left branch is entirely valid + if (!CheckInvariantsFromNode(left)) { + return false; + } + // Ensure that this node's MinLow is equal to MinLow of the left branch + if (!(left->Data().MinLow() == node->Data().MinLow())) { + LogVerificationFailedAtNode(node); return false; + } + // Ensure that this node's MaxHigh is at least MaxHigh of left branch + if (node->Data().MaxHigh() < left->Data().MaxHigh()) { + LogVerificationFailedAtNode(node); + return false; + } + + observed_min_value = left->Data().MinLow(); + observed_max_value = std::max(observed_max_value, left->Data().MaxHigh()); } + if (right) { - if (!CheckInvariantsFromNode(right, &right_max_value)) + // Ensure right branch is entirely valid + if (!CheckInvariantsFromNode(right)) { return false; + } + // Ensure this node's MinLow is not greater than the right node's MinLow + if (right->Data().MinLow() < node->Data().MinLow()) { + LogVerificationFailedAtNode(node); + return false; + } + // Ensure that this node's MaxHigh is at least MaxHigh of right branch + if (node->Data().MaxHigh() < right->Data().MaxHigh()) { + LogVerificationFailedAtNode(node); + return false; + } + + observed_max_value = + std::max(observed_max_value, right->Data().MaxHigh()); } - if (!left && !right) { - // Base case. - if (current_max_value) - *current_max_value = node->Data().High(); - return (node->Data().High() == node->Data().MaxHigh()); - } - T local_max_value(node->Data().MaxHigh()); - if (!left || !right) { - if (left) - local_max_value = left_max_value; - else - local_max_value = right_max_value; - } else { - local_max_value = - (left_max_value < right_max_value) ? right_max_value : left_max_value; + + // Ensure this node's MinLow is the min we actually observed + if (!(observed_min_value == node->Data().MinLow())) { + LogVerificationFailedAtNode(node); + return false; } - if (local_max_value < node->Data().High()) - local_max_value = node->Data().High(); - if (!(local_max_value == node->Data().MaxHigh())) { -#ifndef NDEBUG - String local_max_value_string = - ValueToString<T>::ToString(local_max_value); - DLOG(ERROR) << "PODIntervalTree verification failed at node " << node - << ": localMaxValue=" << local_max_value_string - << " and data=" << node->Data().ToString(); -#endif + // Ensure that this node's MaxHigh is the max we actually observed + if (!(observed_max_value == node->Data().MaxHigh())) { + LogVerificationFailedAtNode(node); return false; } - if (current_max_value) - *current_max_value = local_max_value; + return true; } +#ifndef NDEBUG + static void LogVerificationFailedAtNode(IntervalNode const* node) { + DLOG(ERROR) << "PODIntervalTree verification failed at node " << node + << ": data=" << node->Data().ToString(); + } +#else + static void LogVerificationFailedAtNode(IntervalNode const*) {} +#endif + DISALLOW_COPY_AND_ASSIGN(PODIntervalTree); }; diff --git a/chromium/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc b/chromium/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc index ad4ffa6f016..cdf01a2b602 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc @@ -57,11 +57,26 @@ TEST(PODIntervalTreeTest, TestInsertionAndQuery) { PODIntervalTree<float> tree; tree.Add(PODInterval<float>(2, 4)); ASSERT_TRUE(tree.CheckInvariants()); - Vector<PODInterval<float>> result = + Vector<PODInterval<float>> overlap = tree.AllOverlaps(PODInterval<float>(1, 3)); - EXPECT_EQ(1U, result.size()); - EXPECT_EQ(2, result[0].Low()); - EXPECT_EQ(4, result[0].High()); + EXPECT_EQ(1U, overlap.size()); + EXPECT_EQ(2, overlap[0].Low()); + EXPECT_EQ(4, overlap[0].High()); + + auto next_point = tree.NextIntervalPoint(1); + EXPECT_TRUE(next_point.has_value()); + EXPECT_EQ(2, next_point.value()); + + next_point = tree.NextIntervalPoint(2); + EXPECT_TRUE(next_point.has_value()); + EXPECT_EQ(4, next_point.value()); + + next_point = tree.NextIntervalPoint(3); + EXPECT_TRUE(next_point.has_value()); + EXPECT_EQ(4, next_point.value()); + + next_point = tree.NextIntervalPoint(4); + EXPECT_FALSE(next_point.has_value()); } TEST(PODIntervalTreeTest, TestQueryAgainstZeroSizeInterval) { @@ -214,7 +229,7 @@ void TreeInsertionAndDeletionTest(int32_t seed, int tree_size) { tree.Add(interval); #ifdef DEBUG_INSERTION_AND_DELETION_TEST DLOG(ERROR) << "*** Adding element " - << ValueToString<PODInterval<int>>::string(interval); + << ValueToString<PODInterval<int>>::ToString(interval); #endif added_elements.push_back(interval); } @@ -224,8 +239,8 @@ void TreeInsertionAndDeletionTest(int32_t seed, int tree_size) { int index = NextRandom(added_elements.size()); #ifdef DEBUG_INSERTION_AND_DELETION_TEST DLOG(ERROR) << "*** Removing element " - << ValueToString<PODInterval<int>>::string( - addedElements[index]); + << ValueToString<PODInterval<int>>::ToString( + added_elements[index]); #endif ASSERT_TRUE(tree.Contains(added_elements[index])) << "Test failed for seed " << seed; @@ -247,8 +262,8 @@ void TreeInsertionAndDeletionTest(int32_t seed, int tree_size) { int index = NextRandom(removed_elements.size()); #ifdef DEBUG_INSERTION_AND_DELETION_TEST DLOG(ERROR) << "*** Adding element " - << ValueToString<PODInterval<int>>::string( - removedElements[index]); + << ValueToString<PODInterval<int>>::ToString( + removed_elements[index]); #endif tree.Add(removed_elements[index]); added_elements.push_back(removed_elements[index]); @@ -257,8 +272,8 @@ void TreeInsertionAndDeletionTest(int32_t seed, int tree_size) { int index = NextRandom(added_elements.size()); #ifdef DEBUG_INSERTION_AND_DELETION_TEST DLOG(ERROR) << "*** Removing element " - << ValueToString<PODInterval<int>>::string( - addedElements[index]); + << ValueToString<PODInterval<int>>::ToString( + added_elements[index]); #endif ASSERT_TRUE(tree.Contains(added_elements[index])) << "Test failed for seed " << seed; diff --git a/chromium/third_party/blink/renderer/platform/wtf/pod_red_black_tree.h b/chromium/third_party/blink/renderer/platform/wtf/pod_red_black_tree.h index 2ba83d373e1..d4b7a8a6b9c 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/pod_red_black_tree.h +++ b/chromium/third_party/blink/renderer/platform/wtf/pod_red_black_tree.h @@ -256,6 +256,7 @@ class PODRedBlackTree { // Fetches the user data. T& Data() { return data_; } + T const& Data() const { return data_; } // Copies all user-level fields from the source node, but not // internal fields. For example, the base implementation of this @@ -265,13 +266,16 @@ class PODRedBlackTree { // superclass implementation. virtual void CopyFrom(Node* src) { data_ = src->Data(); } - Node* Left() const { return left_; } + Node* Left() { return left_; } + Node const* Left() const { return left_; } void SetLeft(Node* node) { left_ = node; } - Node* Right() const { return right_; } + Node const* Right() const { return right_; } + Node* Right() { return right_; } void SetRight(Node* node) { right_ = node; } - Node* Parent() const { return parent_; } + Node const* Parent() const { return parent_; } + Node* Parent() { return parent_; } void SetParent(Node* node) { parent_ = node; } private: diff --git a/chromium/third_party/blink/renderer/platform/wtf/sanitizers.h b/chromium/third_party/blink/renderer/platform/wtf/sanitizers.h index 847424195f8..1916939fb30 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/sanitizers.h +++ b/chromium/third_party/blink/renderer/platform/wtf/sanitizers.h @@ -11,12 +11,36 @@ #define ASAN_REGION_IS_POISONED(addr, size) \ __asan_region_is_poisoned(addr, size) #define NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) +class AsanUnpoisonScope { + public: + AsanUnpoisonScope(const void* addr, size_t size) + : addr_(addr), size_(size), was_poisoned_(false) { + if (!ASAN_REGION_IS_POISONED(const_cast<void*>(addr_), size_)) + return; + ASAN_UNPOISON_MEMORY_REGION(addr_, size_); + was_poisoned_ = true; + } + ~AsanUnpoisonScope() { + if (was_poisoned_) + ASAN_POISON_MEMORY_REGION(addr_, size_); + } + + private: + const void* addr_; + size_t size_; + bool was_poisoned_; +}; #else #define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) #define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) #define ASAN_REGION_IS_POISONED(addr, size) \ ((void)(addr), (void)(size), (void*)nullptr) #define NO_SANITIZE_ADDRESS +class AsanUnpoisonScope { + public: + AsanUnpoisonScope(const void*, size_t) {} + ~AsanUnpoisonScope() {} +}; #endif #if defined(LEAK_SANITIZER) diff --git a/chromium/third_party/blink/renderer/platform/wtf/shared_buffer.cc b/chromium/third_party/blink/renderer/platform/wtf/shared_buffer.cc index 9d3eb78f42a..cb8c3f8ad3b 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/shared_buffer.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/shared_buffer.cc @@ -28,6 +28,7 @@ #include <memory> +#include "base/numerics/safe_conversions.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/text/unicode.h" #include "third_party/blink/renderer/platform/wtf/text/utf8.h" @@ -171,7 +172,8 @@ SharedBuffer::Iterator SharedBuffer::end() const { } void SharedBuffer::MergeSegmentsIntoBuffer() { - wtf_size_t bytes_left = size_ - buffer_.size(); + wtf_size_t bytes_left = + base::checked_cast<wtf_size_t>(size_ - buffer_.size()); for (const auto& segment : segments_) { wtf_size_t bytes_to_copy = std::min<wtf_size_t>(bytes_left, kSegmentSize); buffer_.Append(segment.get(), bytes_to_copy); diff --git a/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc b/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc index b2421649ff3..1aaaa1c60d2 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/stack_util.cc @@ -24,12 +24,11 @@ size_t GetUnderestimatedStackSize() { // and its size is different from the value which APIs tells us. #if defined(ADDRESS_SANITIZER) return 0; -#endif // FIXME: On Mac OSX and Linux, this method cannot estimate stack size // correctly for the main thread. -#if defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \ +#elif defined(__GLIBC__) || defined(OS_ANDROID) || defined(OS_FREEBSD) || \ defined(OS_FUCHSIA) // pthread_getattr_np() can fail if the thread is not invoked by // pthread_create() (e.g., the main thread of blink_unittests). diff --git a/chromium/third_party/blink/renderer/platform/wtf/testing/run_all_tests.cc b/chromium/third_party/blink/renderer/platform/wtf/testing/run_all_tests.cc index 5a8c45de8a1..4a54c7d144f 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/testing/run_all_tests.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/testing/run_all_tests.cc @@ -35,6 +35,6 @@ int main(int argc, char** argv) { WTF::Partitions::Initialize(); - WTF::Initialize(nullptr); + WTF::Initialize(); return base::RunUnitTestsUsingBaseTestSuite(argc, argv); } diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_builder.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_builder.h index a853cc40e4d..1af520552af 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/string_builder.h +++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_builder.h @@ -160,6 +160,14 @@ class WTF_EXPORT StringBuilder { AtomicString ToAtomicString(); String Substring(unsigned start, unsigned length) const; + operator StringView() const { + if (Is8Bit()) { + return StringView(Characters8(), length()); + } else { + return StringView(Characters16(), length()); + } + } + unsigned length() const { return length_; } bool IsEmpty() const { return !length_; } diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.cc index 2e992dfac85..a02a45ef137 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.cc @@ -701,6 +701,15 @@ wtf_size_t StringImpl::HexToUIntStrict(bool* ok) { NumberParsingOptions::kStrict, ok); } +uint64_t StringImpl::HexToUInt64Strict(bool* ok) { + if (Is8Bit()) { + return HexCharactersToUInt64(Characters8(), length_, + NumberParsingOptions::kStrict, ok); + } + return HexCharactersToUInt64(Characters16(), length_, + NumberParsingOptions::kStrict, ok); +} + int64_t StringImpl::ToInt64(NumberParsingOptions options, bool* ok) const { if (Is8Bit()) return CharactersToInt64(Characters8(), length_, options, ok); diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.h index 4228268fcf3..2083ac0ffe3 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.h +++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_impl.h @@ -345,6 +345,7 @@ class WTF_EXPORT StringImpl { uint64_t ToUInt64(NumberParsingOptions, bool* ok) const; wtf_size_t HexToUIntStrict(bool* ok); + uint64_t HexToUInt64Strict(bool* ok); // FIXME: Like NumberParsingOptions::kStrict, these give false for "ok" when // there is trailing garbage. Like NumberParsingOptions::kLoose, these return diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.cc index d00bd2605f6..d915df41740 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.cc @@ -175,6 +175,20 @@ unsigned HexCharactersToUInt(const UChar* data, return ToIntegralType<unsigned, UChar, 16>(data, length, options, ok); } +uint64_t HexCharactersToUInt64(const LChar* data, + size_t length, + NumberParsingOptions options, + bool* ok) { + return ToIntegralType<uint64_t, LChar, 16>(data, length, options, ok); +} + +uint64_t HexCharactersToUInt64(const UChar* data, + size_t length, + NumberParsingOptions options, + bool* ok) { + return ToIntegralType<uint64_t, UChar, 16>(data, length, options, ok); +} + int CharactersToInt(const LChar* data, size_t length, NumberParsingOptions options, diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.h index ae9fdedebe7..f6e78041261 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.h +++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_to_number.h @@ -40,6 +40,14 @@ WTF_EXPORT unsigned HexCharactersToUInt(const UChar*, size_t, NumberParsingOptions, bool* ok); +WTF_EXPORT uint64_t HexCharactersToUInt64(const UChar*, + size_t, + NumberParsingOptions, + bool* ok); +WTF_EXPORT uint64_t HexCharactersToUInt64(const LChar*, + size_t, + NumberParsingOptions, + bool* ok); WTF_EXPORT unsigned CharactersToUInt(const LChar*, size_t, NumberParsingOptions, diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_view.h b/chromium/third_party/blink/renderer/platform/wtf/text/string_view.h index 3d92473d79e..06611ab2a58 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/string_view.h +++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_view.h @@ -58,12 +58,12 @@ class WTF_EXPORT StringView { StringView(StringImpl&, unsigned offset); StringView(StringImpl&, unsigned offset, unsigned length); - // From a String, implemented in String.h + // From a String, implemented in wtf_string.h inline StringView(const String&, unsigned offset, unsigned length); inline StringView(const String&, unsigned offset); inline StringView(const String&); - // From an AtomicString, implemented in AtomicString.h + // From an AtomicString, implemented in atomic_string.h inline StringView(const AtomicString&, unsigned offset, unsigned length); inline StringView(const AtomicString&, unsigned offset); inline StringView(const AtomicString&); @@ -253,6 +253,15 @@ WTF_EXPORT bool DeprecatedEqualIgnoringCaseAndNullity(const StringView&, WTF_EXPORT bool EqualIgnoringASCIICase(const StringView&, const StringView&); +template <size_t N> +inline bool EqualIgnoringASCIICase(const StringView& a, + const char (&literal)[N]) { + if (a.length() != N - 1 || (N == 1 && a.IsNull())) + return false; + return a.Is8Bit() ? EqualIgnoringASCIICase(a.Characters8(), literal, N - 1) + : EqualIgnoringASCIICase(a.Characters16(), literal, N - 1); +} + // TODO(esprehn): Can't make this an overload of WTF::equal since that makes // calls to equal() that pass literal strings ambiguous. Figure out if we can // replace all the callers with equalStringView and then rename it to equal(). diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/string_view_test.cc b/chromium/third_party/blink/renderer/platform/wtf/text/string_view_test.cc index 924e1d55a99..143fd2b7813 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/string_view_test.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/text/string_view_test.cc @@ -460,4 +460,30 @@ TEST(StringViewTest, EqualIgnoringASCIICase) { EXPECT_TRUE(EqualIgnoringASCIICase(StringView(""), "")); } +TEST(StringViewTest, DeprecatedEqualIgnoringCase) { + constexpr UChar kLongSAndKelvin[] = {0x017F, 0x212A, 0}; + EXPECT_TRUE(DeprecatedEqualIgnoringCase("SK", kLongSAndKelvin)); + EXPECT_TRUE(DeprecatedEqualIgnoringCase("sk", kLongSAndKelvin)); + + // Turkish-specific mappings are not applied. + constexpr UChar kSmallDotlessI[] = {0x0131, 0}; + constexpr UChar kCapitalDotI[] = {0x0130, 0}; + EXPECT_FALSE(DeprecatedEqualIgnoringCase("i", kSmallDotlessI)); + EXPECT_FALSE(DeprecatedEqualIgnoringCase("i", kCapitalDotI)); + + // DeprecatedEqualIgnoringCase() has length-equality check. + constexpr UChar kSmallSharpS[] = {0x00DF, 0}; + constexpr UChar kCapitalSharpS[] = {0x1E9E, 0}; + EXPECT_FALSE(DeprecatedEqualIgnoringCase("ss", kSmallSharpS)); + EXPECT_FALSE(DeprecatedEqualIgnoringCase("SS", kSmallSharpS)); + EXPECT_FALSE(DeprecatedEqualIgnoringCase("ss", kCapitalSharpS)); + EXPECT_FALSE(DeprecatedEqualIgnoringCase("SS", kCapitalSharpS)); + constexpr UChar kLigatureFFI[] = {0xFB03, 0}; + EXPECT_FALSE(DeprecatedEqualIgnoringCase("ffi", kLigatureFFI)); + + constexpr UChar kLigatureFFIAndSSSS[] = {0xFB03, 's', 's', 's', 's', 0}; + constexpr UChar kFFIAndSharpSs[] = {'f', 'f', 'i', 0x00DF, 0x00DF, 0}; + EXPECT_TRUE(DeprecatedEqualIgnoringCase(kLigatureFFIAndSSSS, kFFIAndSharpSs)); +} + } // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.h index b6926170494..3d4f4108d57 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.h +++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec.h @@ -82,7 +82,7 @@ class WTF_EXPORT TextCodec { struct EncodeIntoResult { wtf_size_t code_units_read; - wtf_size_t bytes_written; + size_t bytes_written; }; String Decode(const char* str, @@ -108,14 +108,14 @@ class WTF_EXPORT TextCodec { virtual EncodeIntoResult EncodeInto(const LChar*, wtf_size_t length, unsigned char* destination, - wtf_size_t capacity) { + size_t capacity) { NOTREACHED(); return EncodeIntoResult{0, 0}; } virtual EncodeIntoResult EncodeInto(const UChar*, wtf_size_t length, unsigned char* destination, - wtf_size_t capacity) { + size_t capacity) { NOTREACHED(); return EncodeIntoResult{0, 0}; } diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc index 6874eca2d10..71fc2ac2aae 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc @@ -497,7 +497,7 @@ TextCodec::EncodeIntoResult TextCodecUTF8::EncodeIntoCommon( const CharType* characters, wtf_size_t length, unsigned char* destination, - wtf_size_t capacity) { + size_t capacity) { TextCodec::EncodeIntoResult encode_into_result{0, 0}; wtf_size_t i = 0; @@ -544,7 +544,7 @@ TextCodec::EncodeIntoResult TextCodecUTF8::EncodeInto( const UChar* characters, wtf_size_t length, unsigned char* destination, - wtf_size_t capacity) { + size_t capacity) { return EncodeIntoCommon(characters, length, destination, capacity); } @@ -552,7 +552,7 @@ TextCodec::EncodeIntoResult TextCodecUTF8::EncodeInto( const LChar* characters, wtf_size_t length, unsigned char* destination, - wtf_size_t capacity) { + size_t capacity) { return EncodeIntoCommon(characters, length, destination, capacity); } diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h index 688a40439b6..65806a9beb1 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h +++ b/chromium/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.h @@ -62,11 +62,11 @@ class TextCodecUTF8 : public TextCodec { EncodeIntoResult EncodeInto(const UChar*, wtf_size_t length, unsigned char* destination, - wtf_size_t capacity) override; + size_t capacity) override; EncodeIntoResult EncodeInto(const LChar*, wtf_size_t length, unsigned char* destination, - wtf_size_t capacity) override; + size_t capacity) override; template <typename CharType> std::string EncodeCommon(const CharType* characters, wtf_size_t length); @@ -74,7 +74,7 @@ class TextCodecUTF8 : public TextCodec { EncodeIntoResult EncodeIntoCommon(const CharType* characters, wtf_size_t length, unsigned char* destination, - wtf_size_t capacity); + size_t capacity); template <typename CharType> bool HandlePartialSequence(CharType*& destination, diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.cc b/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.cc index 5d7213c8503..c32a41c8f00 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.cc @@ -295,6 +295,15 @@ unsigned String::HexToUIntStrict(bool* ok) const { return impl_->HexToUIntStrict(ok); } +uint64_t String::HexToUInt64Strict(bool* ok) const { + if (!impl_) { + if (ok) + *ok = false; + return 0; + } + return impl_->HexToUInt64Strict(ok); +} + int64_t String::ToInt64Strict(bool* ok) const { if (!impl_) { if (ok) diff --git a/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.h b/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.h index df24384c5be..a50371b7b56 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.h +++ b/chromium/third_party/blink/renderer/platform/wtf/text/wtf_string.h @@ -422,6 +422,7 @@ class WTF_EXPORT String { int ToIntStrict(bool* ok = nullptr) const; unsigned ToUIntStrict(bool* ok = nullptr) const; unsigned HexToUIntStrict(bool* ok) const; + uint64_t HexToUInt64Strict(bool* ok) const; int64_t ToInt64Strict(bool* ok = nullptr) const; uint64_t ToUInt64Strict(bool* ok = nullptr) const; diff --git a/chromium/third_party/blink/renderer/platform/wtf/threading.cc b/chromium/third_party/blink/renderer/platform/wtf/threading.cc index 828eee6c27c..d6636df7991 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/threading.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/threading.cc @@ -29,6 +29,10 @@ bool IsBeforeThreadCreated() { void WillCreateThread() { g_thread_created = true; } + +void SetIsBeforeThreadCreatedForTest() { + g_thread_created = false; +} #endif ThreadSpecific<Threading>* Threading::static_data_; diff --git a/chromium/third_party/blink/renderer/platform/wtf/threading.h b/chromium/third_party/blink/renderer/platform/wtf/threading.h index 197bfe06e53..0a21c3fb321 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/threading.h +++ b/chromium/third_party/blink/renderer/platform/wtf/threading.h @@ -48,6 +48,7 @@ WTF_EXPORT base::PlatformThreadId CurrentThread(); #if DCHECK_IS_ON() WTF_EXPORT bool IsBeforeThreadCreated(); WTF_EXPORT void WillCreateThread(); +WTF_EXPORT void SetIsBeforeThreadCreatedForTest(); #endif class AtomicStringTable; diff --git a/chromium/third_party/blink/renderer/platform/wtf/threading_primitives_test.cc b/chromium/third_party/blink/renderer/platform/wtf/threading_primitives_test.cc index 6ce2b8bb560..2393e012756 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/threading_primitives_test.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/threading_primitives_test.cc @@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/synchronization/waitable_event.h" #include "base/threading/scoped_blocking_call.h" +#include "base/threading/scoped_blocking_call_internal.h" #include "base/threading/thread.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/third_party/blink/renderer/platform/wtf/vector.h b/chromium/third_party/blink/renderer/platform/wtf/vector.h index 1b321ec9462..632d30883c9 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/vector.h +++ b/chromium/third_party/blink/renderer/platform/wtf/vector.h @@ -108,7 +108,7 @@ template <typename T> struct VectorUnusedSlotClearer<true, T> { STATIC_ONLY(VectorUnusedSlotClearer); static void Clear(T* begin, T* end) { - memset(reinterpret_cast<void*>(begin), 0, sizeof(T) * (end - begin)); + AtomicMemzero(reinterpret_cast<void*>(begin), sizeof(T) * (end - begin)); } #if DCHECK_IS_ON() @@ -204,14 +204,40 @@ struct VectorMover<true, T, Allocator> { Traits::NotifyNewElements(dst, src_end - src); } } + + template <bool = Allocator::kIsGarbageCollected> + static void MoveOverlappingImpl(const T* src, const T* src_end, T* dst); + template <> + static void MoveOverlappingImpl<false>(const T* src, + const T* src_end, + T* dst) { + memmove(dst, src, + reinterpret_cast<const char*>(src_end) - + reinterpret_cast<const char*>(src)); + } + template <> + static void MoveOverlappingImpl<true>(const T* src, + const T* src_end, + T* dst) { + if (src == dst) + return; + if (dst < src) { + for (; src < src_end; ++src, ++dst) + AtomicWriteMemcpy<sizeof(T)>(dst, src); + } else { + --src_end; + T* dst_end = dst + (src_end - src); + for (; src_end >= src; --src_end, --dst_end) + AtomicWriteMemcpy<sizeof(T)>(dst_end, src_end); + } + } + static void MoveOverlapping(const T* src, const T* src_end, T* dst, bool has_inline_buffer) { if (LIKELY(dst && src)) { - memmove(dst, src, - reinterpret_cast<const char*>(src_end) - - reinterpret_cast<const char*>(src)); + MoveOverlappingImpl(src, src_end, dst); if (has_inline_buffer) Traits::NotifyNewElements(dst, src_end - src); } @@ -407,13 +433,15 @@ class VectorBufferBase { DCHECK_LE(new_capacity, Allocator::template MaxElementCountInBackingStore<T>()); size_t size_to_allocate = AllocationSize(new_capacity); - buffer_ = Allocator::template AllocateVectorBacking<T>(size_to_allocate); + AsAtomicPtr(&buffer_)->store( + Allocator::template AllocateVectorBacking<T>(size_to_allocate), + std::memory_order_relaxed); capacity_ = static_cast<wtf_size_t>(size_to_allocate / sizeof(T)); } void AllocateBuffer(wtf_size_t new_capacity) { AllocateBufferNoBarrier(new_capacity); - Allocator::BackingWriteBarrier(buffer_); + Allocator::BackingWriteBarrier(&buffer_); } size_t AllocationSize(size_t capacity) const { @@ -446,7 +474,7 @@ class VectorBufferBase { } void MoveBufferInto(VectorBufferBase& other) { - other.buffer_ = buffer_; + AsAtomicPtr(&other.buffer_)->store(buffer_, std::memory_order_relaxed); other.capacity_ = capacity_; } @@ -481,6 +509,10 @@ class VectorBufferBase { return buffer_ == reinterpret_cast<T*>(-1); } + const T* BufferSafe() const { + return AsAtomicPtr(&buffer_)->load(std::memory_order_relaxed); + } + T* buffer_; wtf_size_t capacity_; wtf_size_t size_; @@ -540,7 +572,7 @@ class VectorBuffer<T, 0, Allocator> : protected VectorBufferBase<T, Allocator> { } void ResetBufferPointer() { - buffer_ = nullptr; + AsAtomicPtr(&buffer_)->store(nullptr, std::memory_order_relaxed); capacity_ = 0; } @@ -551,11 +583,11 @@ class VectorBuffer<T, 0, Allocator> : protected VectorBufferBase<T, Allocator> { OffsetRange other_hole) { static_assert(VectorTraits<T>::kCanSwapUsingCopyOrMove, "Cannot swap using copy or move."); - std::swap(buffer_, other.buffer_); + AtomicWriteSwap(buffer_, other.buffer_); std::swap(capacity_, other.capacity_); std::swap(size_, other.size_); - Allocator::BackingWriteBarrier(buffer_); - Allocator::BackingWriteBarrier(other.buffer_); + Allocator::BackingWriteBarrier(&buffer_); + Allocator::BackingWriteBarrier(&other.buffer_); } using Base::AllocateBuffer; @@ -570,14 +602,19 @@ class VectorBuffer<T, 0, Allocator> : protected VectorBufferBase<T, Allocator> { bool HasOutOfLineBuffer() const { // When inlineCapacity is 0 we have an out of line buffer if we have a // buffer. - return Buffer(); + return IsOutOfLineBuffer(Buffer()); } T** BufferSlot() { return &buffer_; } + const T* const* BufferSlot() const { return &buffer_; } protected: + using Base::BufferSafe; + using Base::size_; + bool IsOutOfLineBuffer(const T* buffer) const { return buffer; } + private: using Base::buffer_; using Base::capacity_; @@ -648,7 +685,7 @@ class VectorBuffer : protected VectorBufferBase<T, Allocator> { } void ResetBufferPointer() { - buffer_ = InlineBuffer(); + AsAtomicPtr(&buffer_)->store(InlineBuffer(), std::memory_order_relaxed); capacity_ = inlineCapacity; } @@ -694,8 +731,8 @@ class VectorBuffer : protected VectorBufferBase<T, Allocator> { std::swap(buffer_, other.buffer_); std::swap(capacity_, other.capacity_); std::swap(size_, other.size_); - Allocator::BackingWriteBarrier(buffer_); - Allocator::BackingWriteBarrier(other.buffer_); + Allocator::BackingWriteBarrier(&buffer_); + Allocator::BackingWriteBarrier(&other.buffer_); return; } @@ -756,7 +793,7 @@ class VectorBuffer : protected VectorBufferBase<T, Allocator> { other.buffer_ = other.InlineBuffer(); std::swap(size_, other.size_); ANNOTATE_NEW_BUFFER(other.buffer_, inlineCapacity, other.size_); - Allocator::BackingWriteBarrier(buffer_); + Allocator::BackingWriteBarrier(&buffer_); } else if (!this_source_begin && other_source_begin) { // Their buffer is inline, ours is not. DCHECK_NE(Buffer(), InlineBuffer()); @@ -766,7 +803,7 @@ class VectorBuffer : protected VectorBufferBase<T, Allocator> { buffer_ = InlineBuffer(); std::swap(size_, other.size_); ANNOTATE_NEW_BUFFER(buffer_, inlineCapacity, size_); - Allocator::BackingWriteBarrier(other.buffer_); + Allocator::BackingWriteBarrier(&other.buffer_); } else { // Both buffers are inline. DCHECK(this_source_begin); DCHECK(other_source_begin); @@ -850,15 +887,20 @@ class VectorBuffer : protected VectorBufferBase<T, Allocator> { using Base::Buffer; using Base::capacity; - bool HasOutOfLineBuffer() const { - return Buffer() && Buffer() != InlineBuffer(); - } + bool HasOutOfLineBuffer() const { return IsOutOfLineBuffer(Buffer()); } T** BufferSlot() { return &buffer_; } + const T* const* BufferSlot() const { return &buffer_; } protected: + using Base::BufferSafe; + using Base::size_; + bool IsOutOfLineBuffer(const T* buffer) const { + return buffer && buffer != InlineBuffer(); + } + private: using Base::buffer_; using Base::capacity_; @@ -1237,6 +1279,9 @@ class Vector void EraseAt(wtf_size_t position); void EraseAt(wtf_size_t position, wtf_size_t length); iterator erase(iterator position); + iterator erase(iterator first, iterator last); + // This is to prevent compilation of deprecated calls like 'vector.erase(0)'. + void erase(std::nullptr_t) = delete; // Remove the last element. Unlike remove(), (1) this function is fast, and // (2) only iterators pointing to the last element will be invalidated. Other @@ -1254,8 +1299,15 @@ class Vector // or copy-initialize all the elements. // // Fill(value) is a synonym for Fill(value, size()). - void Fill(const T&, wtf_size_t); - void Fill(const T& val) { Fill(val, size()); } + // + // The implementation of Fill uses std::fill which is not yet supported for + // garbage collected vectors. + template <typename A = Allocator> + std::enable_if_t<!A::kIsGarbageCollected> Fill(const T&, wtf_size_t); + template <typename A = Allocator> + std::enable_if_t<!A::kIsGarbageCollected> Fill(const T& val) { + Fill(val, size()); + } // Swap two vectors quickly. void swap(Vector& other) { @@ -1291,7 +1343,7 @@ class Vector } template <typename VisitorDispatcher, typename A = Allocator> - std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher); + std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher) const; class GCForbiddenScope { STACK_ALLOCATED(); @@ -1306,6 +1358,7 @@ class Vector using Base::ClearUnusedSlots; T** GetBufferSlot() { return Base::BufferSlot(); } + const T* const* GetBufferSlot() const { return Base::BufferSlot(); } private: void ExpandCapacity(wtf_size_t new_min_capacity); @@ -1326,14 +1379,12 @@ class Vector void ReallocateBuffer(wtf_size_t); - // This is to prevent compilation of deprecated calls like 'vector.erase(0)'. - void erase(std::nullptr_t) = delete; - - using Base::size_; - using Base::Buffer; - using Base::SwapVectorBuffer; using Base::AllocateBuffer; using Base::AllocationSize; + using Base::Buffer; + using Base::BufferSafe; + using Base::size_; + using Base::SwapVectorBuffer; }; // @@ -1550,8 +1601,9 @@ wtf_size_t Vector<T, inlineCapacity, Allocator>::ReverseFind( } template <typename T, wtf_size_t inlineCapacity, typename Allocator> -void Vector<T, inlineCapacity, Allocator>::Fill(const T& val, - wtf_size_t new_size) { +template <typename A> +std::enable_if_t<!A::kIsGarbageCollected> +Vector<T, inlineCapacity, Allocator>::Fill(const T& val, wtf_size_t new_size) { if (size() > new_size) { Shrink(new_size); } else if (new_size > capacity()) { @@ -1922,6 +1974,17 @@ inline auto Vector<T, inlineCapacity, Allocator>::erase(iterator position) } template <typename T, wtf_size_t inlineCapacity, typename Allocator> +inline auto Vector<T, inlineCapacity, Allocator>::erase(iterator first, + iterator last) + -> iterator { + DCHECK_LE(first, last); + const wtf_size_t index = static_cast<wtf_size_t>(first - begin()); + const wtf_size_t diff = std::distance(first, last); + EraseAt(index, diff); + return begin() + index; +} + +template <typename T, wtf_size_t inlineCapacity, typename Allocator> inline void Vector<T, inlineCapacity, Allocator>::EraseAt(wtf_size_t position, wtf_size_t length) { SECURITY_DCHECK(position <= size()); @@ -1976,29 +2039,37 @@ inline bool operator!=(const Vector<T, inlineCapacityA, Allocator>& a, template <typename T, wtf_size_t inlineCapacity, typename Allocator> template <typename VisitorDispatcher, typename A> std::enable_if_t<A::kIsGarbageCollected> -Vector<T, inlineCapacity, Allocator>::Trace(VisitorDispatcher visitor) { +Vector<T, inlineCapacity, Allocator>::Trace(VisitorDispatcher visitor) const { + // Bail out for concurrent marking. + if (visitor->ConcurrentTracingBailOut( + {this, [](blink::Visitor* visitor, const void* object) { + reinterpret_cast<const Vector<T, inlineCapacity, Allocator>*>( + object) + ->Trace(visitor); + }})) + return; + static_assert(Allocator::kIsGarbageCollected, "Garbage collector must be enabled."); - if (this->HasOutOfLineBuffer()) { - Allocator::TraceVectorBacking(visitor, Buffer(), Base::BufferSlot()); + const T* buffer = BufferSafe(); + if (Base::IsOutOfLineBuffer(buffer)) { + Allocator::TraceVectorBacking(visitor, buffer, Base::BufferSlot()); } else { // We should not visit inline buffers, but we still need to register the // slot for heap compaction. So, we pass nullptr to this method. Allocator::TraceVectorBacking(visitor, static_cast<T*>(nullptr), Base::BufferSlot()); - if (!Buffer()) + if (!buffer) return; // Inline buffer requires tracing immediately. - const T* buffer_begin = Buffer(); - const T* buffer_end = Buffer() + size(); + const T* buffer_begin = buffer; + const T* buffer_end = buffer + size(); if (IsTraceableInCollectionTrait<VectorTraits<T>>::value) { for (const T* buffer_entry = buffer_begin; buffer_entry != buffer_end; buffer_entry++) { - Allocator::template Trace<T, VectorTraits<T>>( - visitor, *const_cast<T*>(buffer_entry)); + Allocator::template Trace<T, VectorTraits<T>>(visitor, *buffer_entry); } - CheckUnusedSlots(Buffer() + size(), Buffer() + capacity()); } } } @@ -2035,7 +2106,7 @@ void Vector<T, inlineCapacity, Allocator>::ReallocateBuffer( ANNOTATE_DELETE_BUFFER(begin(), capacity(), size_); Base::DeallocateBuffer(begin()); buffer.MoveBufferInto(*this); - Allocator::BackingWriteBarrier(begin()); + Allocator::BackingWriteBarrier(Base::BufferSlot()); } } // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/vector_backed_linked_list.h b/chromium/third_party/blink/renderer/platform/wtf/vector_backed_linked_list.h new file mode 100644 index 00000000000..60383b8ac53 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/wtf/vector_backed_linked_list.h @@ -0,0 +1,597 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_VECTOR_BACKED_LINKED_LIST_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_VECTOR_BACKED_LINKED_LIST_H_ + +#include "base/macros.h" +#include "third_party/blink/renderer/platform/wtf/allocator/partition_allocator.h" +#include "third_party/blink/renderer/platform/wtf/hash_traits.h" +#include "third_party/blink/renderer/platform/wtf/sanitizers.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace WTF { + +template <typename VectorBackedLinkedListType> +class VectorBackedLinkedListIterator; +template <typename VectorBackedLinkedListType> +class VectorBackedLinkedListConstIterator; +template <typename VectorBackedLinkedListType> +class VectorBackedLinkedListReverseIterator; +template <typename VectorBackedLinkedListType> +class VectorBackedLinkedListConstReverseIterator; + +template <typename ValueType, typename Allocator> +class VectorBackedLinkedListNode { + USE_ALLOCATOR(VectorBackedLinkedListNode, Allocator); + + public: + VectorBackedLinkedListNode() = delete; + + VectorBackedLinkedListNode(wtf_size_t prev_index, wtf_size_t next_index) + : prev_index_(prev_index), next_index_(next_index) {} + + VectorBackedLinkedListNode(wtf_size_t prev_index, + wtf_size_t next_index, + const ValueType& value) + : prev_index_(prev_index), next_index_(next_index), value_(value) {} + + VectorBackedLinkedListNode(wtf_size_t prev_index, + wtf_size_t next_index, + ValueType&& value) + : prev_index_(prev_index), + next_index_(next_index), + value_(std::move(value)) {} + + VectorBackedLinkedListNode(const VectorBackedLinkedListNode& other) = default; + VectorBackedLinkedListNode(VectorBackedLinkedListNode&& other) = default; + VectorBackedLinkedListNode& operator=( + const VectorBackedLinkedListNode& other) = default; + VectorBackedLinkedListNode& operator=(VectorBackedLinkedListNode&& other) = + default; + + template <typename VisitorDispathcer, typename A = Allocator> + std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispathcer visitor) { + visitor->Trace(value_); + } + + // Those indices can be initialized with |kNotFound| (not with 0), since + // VectorBackedLinkedList won't be initialized with memset. + wtf_size_t prev_index_ = kNotFound; + wtf_size_t next_index_ = kNotFound; + ValueType value_ = HashTraits<ValueType>::EmptyValue(); +}; + +template <typename ValueType, typename Allocator> +struct VectorTraits<VectorBackedLinkedListNode<ValueType, Allocator>> + : VectorTraitsBase<VectorBackedLinkedListNode<ValueType, Allocator>> { + STATIC_ONLY(VectorTraits); + + static const bool kNeedsDestruction = + VectorTraits<ValueType>::kNeedsDestruction; + // VectorBackedLinkedList can't be initialized with memset, because we use + // kNotFound as sentinel value. + static const bool kCanInitializeWithMemset = false; + static const bool kCanClearUnusedSlotsWithMemset = + VectorTraits<ValueType>::kCanClearUnusedSlotsWithMemset; + static const bool kCanCopyWithMemcpy = + VectorTraits<ValueType>::kCanCopyWithMemcpy; + static const bool kCanMoveWithMemcpy = + VectorTraits<ValueType>::kCanMoveWithMemcpy; + + // Needs to be shadowing because |VectorTraitsBase::IsDeletedValue| uses call + // by value, which means we need to define copy constructor of + // |VectorBackedLinkedList|. We can remove this function if we change + // |VectorTraitsBase::IsDeletedValue| to use call by reference. + static bool IsDeletedValue( + const VectorBackedLinkedListNode<ValueType, Allocator>& node) { + NOTREACHED(); + return false; + } +}; + +// VectorBackedLinkedList maintains a linked list through its contents such that +// iterating it yields values in the order in which they were inserted. +// The linked list is implementing in a vector (with links being indexes instead +// of pointers), to simplify the move of backing during GC compaction. +template <typename ValueType, typename Allocator = PartitionAllocator> +class VectorBackedLinkedList { + USE_ALLOCATOR(VectorBackedLinkedList, Allocator); + + private: + using Node = VectorBackedLinkedListNode<ValueType, Allocator>; + using VectorType = Vector<Node, 0, Allocator>; + + public: + using Value = ValueType; + using iterator = VectorBackedLinkedListIterator<VectorBackedLinkedList>; + using const_iterator = + VectorBackedLinkedListConstIterator<VectorBackedLinkedList>; + friend class VectorBackedLinkedListConstIterator<VectorBackedLinkedList>; + using reverse_iterator = + VectorBackedLinkedListReverseIterator<VectorBackedLinkedList>; + using const_reverse_iterator = + VectorBackedLinkedListConstReverseIterator<VectorBackedLinkedList>; + + VectorBackedLinkedList(); + + VectorBackedLinkedList(const VectorBackedLinkedList&) = default; + VectorBackedLinkedList(VectorBackedLinkedList&&) = default; + VectorBackedLinkedList& operator=(const VectorBackedLinkedList&) = default; + VectorBackedLinkedList& operator=(VectorBackedLinkedList&&) = default; + + ~VectorBackedLinkedList() = default; + + void swap(VectorBackedLinkedList&); + + bool empty() const { return size_ == 0; } + wtf_size_t size() const { return size_; } + + iterator begin() { return MakeIterator(UsedFirstIndex()); } + const_iterator begin() const { return MakeConstIterator(UsedFirstIndex()); } + const_iterator cbegin() const { return MakeConstIterator(UsedFirstIndex()); } + iterator end() { return MakeIterator(anchor_index_); } + const_iterator end() const { return MakeConstIterator(anchor_index_); } + const_iterator cend() const { return MakeConstIterator(anchor_index_); } + reverse_iterator rbegin() { return MakeReverseIterator(UsedLastIndex()); } + const_reverse_iterator rbegin() const { + return MakeConstReverseIterator(UsedLastIndex()); + } + const_reverse_iterator crbegin() const { + return MakeConstReverseIterator(UsedLastIndex()); + } + reverse_iterator rend() { return MakeReverseIterator(anchor_index_); } + const_reverse_iterator rend() const { + return MakeConstReverseIterator(anchor_index_); + } + const_reverse_iterator crend() const { + return MakeConstReverseIterator(anchor_index_); + } + + Value& front(); + const Value& front() const; + Value& back(); + const Value& back() const; + + template <typename IncomingValueType> + iterator insert(const_iterator position, IncomingValueType&& value); + + template <typename IncomingValueType> + void push_front(IncomingValueType&& value) { + insert(cbegin(), std::forward<IncomingValueType>(value)); + } + + template <typename IncomingValueType> + void push_back(IncomingValueType&& value) { + insert(cend(), std::forward<IncomingValueType>(value)); + } + + // Moves |target| right before |new_position| in a linked list. This operation + // is executed by just updating indices of related nodes. + iterator MoveTo(const_iterator target, const_iterator new_position); + + iterator erase(const_iterator); + + void pop_front() { + DCHECK(!empty()); + erase(cbegin()); + } + void pop_back() { + DCHECK(!empty()); + erase(--cend()); + } + + // Removes all elements in a linked list. + void clear() { + RegisterModification(); + nodes_.clear(); + // Reinserts anchor so that we can insert elements after this operation. + nodes_.push_back(Node(anchor_index_, anchor_index_)); + free_head_index_ = anchor_index_; + size_ = 0; + } + + template <typename VisitorDispatcher, typename A = Allocator> + std::enable_if_t<A::kIsGarbageCollected> Trace(VisitorDispatcher visitor) { + nodes_.Trace(visitor); + } + +#if DCHECK_IS_ON() + int64_t Modifications() const { return modifications_; } + void RegisterModification() { modifications_++; } + void CheckModifications(int64_t mods) const { + // VectorBackedLinkedList iterators get invalidated when the container is + // modified. + DCHECK_EQ(mods, modifications_); + } +#else + ALWAYS_INLINE int64_t Modifications() const { return 0; } + ALWAYS_INLINE void RegisterModification() {} + ALWAYS_INLINE void CheckModifications() const {} +#endif + + private: + bool IsFreeListEmpty() const { return free_head_index_ == anchor_index_; } + + wtf_size_t UsedFirstIndex() const { + return nodes_[anchor_index_].next_index_; + } + wtf_size_t UsedLastIndex() const { return nodes_[anchor_index_].prev_index_; } + + iterator MakeIterator(wtf_size_t index) { return iterator(index, this); } + const_iterator MakeConstIterator(wtf_size_t index) const { + return const_iterator(index, this); + } + reverse_iterator MakeReverseIterator(wtf_size_t index) { + return reverse_iterator(index, this); + } + const_reverse_iterator MakeConstReverseIterator(wtf_size_t index) const { + return const_reverse_iterator(index, this); + } + + bool IsIndexValid(wtf_size_t index) const { + return 0 <= index && index < nodes_.size(); + } + + bool IsAnchor(wtf_size_t index) const { return index == anchor_index_; } + + void Unlink(const Node&); + + VectorType nodes_; + static constexpr wtf_size_t anchor_index_ = 0; + // Anchor is not included in the free list, but it serves as the list's + // terminator. + wtf_size_t free_head_index_ = anchor_index_; + wtf_size_t size_ = 0; +#if DCHECK_IS_ON() + int64_t modifications_ = 0; +#endif + + template <typename T, typename U> + friend class NewLinkedHashSet; +}; + +template <typename VectorBackedLinkedListType> +class VectorBackedLinkedListIterator { + DISALLOW_NEW(); + using ReferenceType = typename VectorBackedLinkedListType::Value&; + using PointerType = typename VectorBackedLinkedListType::Value*; + using const_iterator = + VectorBackedLinkedListConstIterator<VectorBackedLinkedListType>; + + public: + ReferenceType operator*() const { return *Get(); } + PointerType operator->() const { return Get(); } + + VectorBackedLinkedListIterator& operator++() { + ++iterator_; + return *this; + } + + VectorBackedLinkedListIterator& operator--() { + --iterator_; + return *this; + } + + VectorBackedLinkedListIterator& operator++(int) = delete; + VectorBackedLinkedListIterator& operator--(int) = delete; + + bool operator==(const VectorBackedLinkedListIterator& other) const { + return iterator_ == other.iterator_; + } + + bool operator!=(const VectorBackedLinkedListIterator& other) const { + return !(*this == other); + } + + operator const_iterator() const { return iterator_; } + + private: + VectorBackedLinkedListIterator(wtf_size_t index, + VectorBackedLinkedListType* container) + : iterator_(index, container) {} + + PointerType Get() const { return const_cast<PointerType>(iterator_.Get()); } + wtf_size_t GetIndex() const { return iterator_.GetIndex(); } + + const_iterator iterator_; + + template <typename T, typename Allocator> + friend class VectorBackedLinkedList; +}; + +template <typename VectorBackedLinkedListType> +class VectorBackedLinkedListConstIterator { + DISALLOW_NEW(); + using ReferenceType = const typename VectorBackedLinkedListType::Value&; + using PointerType = const typename VectorBackedLinkedListType::Value*; + using Node = typename VectorBackedLinkedListType::Node; + + public: + PointerType Get() const { + DCHECK(container_->IsIndexValid(index_)); + DCHECK(!container_->IsAnchor(index_)); + CheckModifications(); + const Node& node = container_->nodes_[index_]; + return &node.value_; + } + + ReferenceType operator*() const { return *Get(); } + PointerType operator->() const { return Get(); } + + wtf_size_t GetIndex() const { return index_; } + + VectorBackedLinkedListConstIterator& operator++() { + DCHECK(container_->IsIndexValid(index_)); + CheckModifications(); + index_ = container_->nodes_[index_].next_index_; + DCHECK(container_->IsIndexValid(index_)); + return *this; + } + + VectorBackedLinkedListConstIterator& operator--() { + DCHECK(container_->IsIndexValid(index_)); + CheckModifications(); + index_ = container_->nodes_[index_].prev_index_; + DCHECK(container_->IsIndexValid(index_)); + return *this; + } + + VectorBackedLinkedListConstIterator operator++(int) = delete; + VectorBackedLinkedListConstIterator operator--(int) = delete; + + bool operator==(const VectorBackedLinkedListConstIterator& other) const { + DCHECK_EQ(container_, other.container_); + return index_ == other.index_ && container_ == other.container_; + } + + bool operator!=(const VectorBackedLinkedListConstIterator& other) const { + return !(*this == other); + } + + protected: + VectorBackedLinkedListConstIterator( + wtf_size_t index, + const VectorBackedLinkedListType* container) + : index_(index), + container_(container) +#if DCHECK_IS_ON() + , + container_modifications_(container->modifications_) +#endif + { + DCHECK(container_->IsIndexValid(index_)); + } + + private: + wtf_size_t index_; + const VectorBackedLinkedListType* container_; +#if DCHECK_IS_ON() + void CheckModifications() const { + container_->CheckModifications(container_modifications_); + } + int64_t container_modifications_; +#else + void CheckModifications() const {} +#endif + + template <typename T, typename Allocator> + friend class VectorBackedLinkedList; + friend class VectorBackedLinkedListIterator<VectorBackedLinkedListType>; +}; + +template <typename VectorBackedLinkedListType> +class VectorBackedLinkedListReverseIterator { + using ReferenceType = typename VectorBackedLinkedListType::Value&; + using PointerType = typename VectorBackedLinkedListType::Value*; + using const_reverse_iterator = + VectorBackedLinkedListConstReverseIterator<VectorBackedLinkedListType>; + + public: + ReferenceType operator*() const { return *Get(); } + PointerType operator->() const { return Get(); } + + VectorBackedLinkedListReverseIterator& operator++() { + ++iterator_; + return *this; + } + + VectorBackedLinkedListReverseIterator& operator--() { + --iterator_; + return *this; + } + + VectorBackedLinkedListReverseIterator& operator++(int) = delete; + VectorBackedLinkedListReverseIterator& operator--(int) = delete; + + bool operator==(const VectorBackedLinkedListReverseIterator& other) const { + return iterator_ == other.iterator_; + } + + bool operator!=(const VectorBackedLinkedListReverseIterator& other) const { + return !(*this == other); + } + + operator const_reverse_iterator() const { return iterator_; } + + private: + VectorBackedLinkedListReverseIterator(wtf_size_t index, + VectorBackedLinkedListType* container) + : iterator_(index, container) {} + + PointerType Get() const { return const_cast<PointerType>(iterator_.Get()); } + wtf_size_t GetIndex() const { return iterator_.GetIndex(); } + + const_reverse_iterator iterator_; + + template <typename T, typename Allocator> + friend class VectorBackedLinkedList; +}; + +template <typename VectorBackedLinkedListType> +class VectorBackedLinkedListConstReverseIterator + : public VectorBackedLinkedListConstIterator<VectorBackedLinkedListType> { + using Superclass = + VectorBackedLinkedListConstIterator<VectorBackedLinkedListType>; + + public: + VectorBackedLinkedListConstReverseIterator& operator++() { + Superclass::operator--(); + return *this; + } + + VectorBackedLinkedListConstReverseIterator& operator--() { + Superclass::operator++(); + return *this; + } + + VectorBackedLinkedListConstReverseIterator operator++(int) = delete; + VectorBackedLinkedListConstReverseIterator operator--(int) = delete; + + private: + VectorBackedLinkedListConstReverseIterator( + wtf_size_t index, + const VectorBackedLinkedListType* container) + : Superclass(index, container) {} + + template <typename T, typename Allocator> + friend class VectorBackedLinkedList; + friend class VectorBackedLinkedListReverseIterator< + VectorBackedLinkedListType>; +}; + +template <typename T, typename Allocator> +VectorBackedLinkedList<T, Allocator>::VectorBackedLinkedList() { + // First inserts anchor, which serves as the beginning and the end of + // the used list. + nodes_.push_back(Node(anchor_index_, anchor_index_)); +} + +template <typename T, typename Allocator> +inline void VectorBackedLinkedList<T, Allocator>::swap( + VectorBackedLinkedList& other) { + nodes_.swap(other.nodes_); + std::swap(free_head_index_, other.free_head_index_); + std::swap(size_, other.size_); +#if DCHECK_IS_ON() + std::swap(modifications_, other.modifications_); +#endif +} + +template <typename T, typename Allocator> +T& VectorBackedLinkedList<T, Allocator>::front() { + DCHECK(!empty()); + return nodes_[UsedFirstIndex()].value_; +} + +template <typename T, typename Allocator> +const T& VectorBackedLinkedList<T, Allocator>::front() const { + DCHECK(!empty()); + return nodes_[UsedFirstIndex()].value_; +} + +template <typename T, typename Allocator> +T& VectorBackedLinkedList<T, Allocator>::back() { + DCHECK(!empty()); + return nodes_[UsedLastIndex()].value_; +} + +template <typename T, typename Allocator> +const T& VectorBackedLinkedList<T, Allocator>::back() const { + DCHECK(!empty()); + return nodes_[UsedLastIndex()].value_; +} + +template <typename T, typename Allocator> +template <typename IncomingValueType> +typename VectorBackedLinkedList<T, Allocator>::iterator +VectorBackedLinkedList<T, Allocator>::insert(const_iterator position, + IncomingValueType&& value) { + RegisterModification(); + wtf_size_t position_index = position.GetIndex(); + wtf_size_t prev_index = nodes_[position_index].prev_index_; + + wtf_size_t new_entry_index; + if (IsFreeListEmpty()) { + new_entry_index = nodes_.size(); + nodes_.push_back(Node(prev_index, position_index, + std::forward<IncomingValueType>(value))); + } else { + new_entry_index = free_head_index_; + Node& free_head = nodes_[free_head_index_]; + free_head_index_ = free_head.next_index_; + free_head = Node(prev_index, position_index, + std::forward<IncomingValueType>(value)); + } + nodes_[prev_index].next_index_ = new_entry_index; + nodes_[position_index].prev_index_ = new_entry_index; + size_++; + return iterator(new_entry_index, this); +} + +template <typename T, typename Allocator> +typename VectorBackedLinkedList<T, Allocator>::iterator +VectorBackedLinkedList<T, Allocator>::MoveTo(const_iterator target, + const_iterator new_position) { + DCHECK(target != end()); + RegisterModification(); + + wtf_size_t target_index = target.GetIndex(); + if (target == new_position) + return MakeIterator(target_index); + + Node& target_node = nodes_[target_index]; + wtf_size_t new_position_index = new_position.GetIndex(); + Node& new_position_node = nodes_[new_position_index]; + wtf_size_t prev_index = new_position_node.prev_index_; + + if (prev_index == target_index) + return MakeIterator(target_index); + + Unlink(target_node); + + nodes_[prev_index].next_index_ = target_index; + new_position_node.prev_index_ = target_index; + target_node.prev_index_ = prev_index; + target_node.next_index_ = new_position_index; + return MakeIterator(target_index); +} + +template <typename T, typename Allocator> +typename VectorBackedLinkedList<T, Allocator>::iterator +VectorBackedLinkedList<T, Allocator>::erase(const_iterator position) { + DCHECK(position != end()); + RegisterModification(); + wtf_size_t position_index = position.GetIndex(); + Node& node = nodes_[position_index]; + wtf_size_t next_index = node.next_index_; + + Unlink(node); + node.value_ = HashTraits<T>::EmptyValue(); + + node.next_index_ = free_head_index_; + node.prev_index_ = kNotFound; + free_head_index_ = position_index; + + size_--; + return iterator(next_index, this); +} + +template <typename T, typename Allocator> +void VectorBackedLinkedList<T, Allocator>::Unlink(const Node& node) { + wtf_size_t prev_index = node.prev_index_; + wtf_size_t next_index = node.next_index_; + + Node& prev_node = nodes_[prev_index]; + Node& next_node = nodes_[next_index]; + + prev_node.next_index_ = next_index; + next_node.prev_index_ = prev_index; +} + +} // namespace WTF + +using WTF::VectorBackedLinkedList; + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_VECTOR_BACKED_LINKED_LIST_H_ diff --git a/chromium/third_party/blink/renderer/platform/wtf/vector_backed_linked_list_test.cc b/chromium/third_party/blink/renderer/platform/wtf/vector_backed_linked_list_test.cc new file mode 100644 index 00000000000..4d7b743c02b --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/wtf/vector_backed_linked_list_test.cc @@ -0,0 +1,508 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/wtf/vector_backed_linked_list.h" + +#include "base/memory/ptr_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/wtf_test_helper.h" + +namespace WTF { + +TEST(VectorBackedLinkedListTest, Insert) { + using List = VectorBackedLinkedList<int>; + List list; + + EXPECT_TRUE(list.empty()); + EXPECT_TRUE(list.begin() == list.end()); + list.insert(list.end(), 1); + list.insert(list.begin(), -2); + list.insert(list.end(), 2); + + List::iterator it = list.begin(); + EXPECT_EQ(*it, -2); + ++it; + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 2); + + it = list.insert(++list.begin(), 0); + list.insert(it, -1); + + EXPECT_EQ(list.front(), -2); + EXPECT_EQ(list.back(), 2); + EXPECT_EQ(list.size(), 5u); + + int i = -2; + for (auto element : list) { + EXPECT_EQ(element, i); + i++; + } +} + +TEST(VectorBackedLinkedList, PushFront) { + using List = VectorBackedLinkedList<int>; + List list; + + EXPECT_TRUE(list.empty()); + list.push_front(3); + EXPECT_EQ(list.front(), 3); + list.push_front(2); + EXPECT_EQ(list.front(), 2); + list.push_front(1); + EXPECT_EQ(list.front(), 1); + + int i = 1; + for (auto element : list) { + EXPECT_EQ(element, i); + i++; + } +} + +TEST(VectorBackedLinkedList, PushBack) { + using List = VectorBackedLinkedList<int>; + List list; + + EXPECT_TRUE(list.empty()); + list.push_back(1); + EXPECT_EQ(list.back(), 1); + list.push_back(2); + EXPECT_EQ(list.back(), 2); + list.push_back(3); + EXPECT_EQ(list.back(), 3); + + int i = 1; + for (auto element : list) { + EXPECT_EQ(element, i); + i++; + } +} + +TEST(VectorBackedLinkedList, MoveTo) { + using List = VectorBackedLinkedList<int>; + List list; + + list.push_back(1); + list.MoveTo(list.begin(), list.end()); + List::iterator it = list.begin(); + EXPECT_EQ(*it, 1); + list.push_back(2); + list.push_back(3); + + List::iterator target = list.begin(); + list.MoveTo(target, list.end()); // {2, 3, 1} + + it = list.begin(); + EXPECT_EQ(*it, 2); + ++it; + EXPECT_EQ(*it, 3); + ++it; + EXPECT_EQ(*it, 1); + --it; + + target = it; + list.MoveTo(target, list.begin()); // {3, 2, 1} + it = list.begin(); + EXPECT_EQ(*it, 3); + ++it; + EXPECT_EQ(*it, 2); + ++it; + EXPECT_EQ(*it, 1); + + target = it; + list.MoveTo(target, --it); // {3, 1, 2} + it = list.begin(); + EXPECT_EQ(*it, 3); + ++it; + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 2); + + list.MoveTo(list.begin(), list.begin()); + it = list.begin(); + EXPECT_EQ(*it, 3); + ++it; + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 2); + + target = list.begin(); + List::iterator position = ++list.begin(); + list.MoveTo(target, position); + it = list.begin(); + EXPECT_EQ(*it, 3); + ++it; + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 2); +} + +TEST(VectorBackedLinkedList, Erase) { + using List = VectorBackedLinkedList<int>; + List list; + + List::iterator it = list.insert(list.end(), 1); + EXPECT_EQ(*it, 1); + list.push_back(2); + list.push_back(3); + list.push_back(4); + list.push_back(5); + EXPECT_EQ(list.size(), 5u); + + int i = 1; + for (auto element : list) { + EXPECT_EQ(element, i); + i++; + } + + List::iterator target = list.begin(); + ++target; + it = list.erase(target); // list = {1, 3, 4, 5} + EXPECT_EQ(*it, 3); + EXPECT_EQ(list.size(), 4u); + it = list.erase(++it); // list = {1, 3, 5} + EXPECT_EQ(*it, 5); + EXPECT_EQ(list.size(), 3u); + + it = list.erase(list.begin()); // list = {3, 5} + EXPECT_EQ(*it, 3); + EXPECT_EQ(list.size(), 2u); + + it = list.begin(); + EXPECT_EQ(*it, 3); + ++it; + EXPECT_EQ(*it, 5); + ++it; + EXPECT_TRUE(it == list.end()); + + list.push_back(6); + EXPECT_EQ(list.front(), 3); + EXPECT_EQ(list.back(), 6); +} + +TEST(VectorBackedLinkedList, PopFront) { + using List = VectorBackedLinkedList<int>; + List list; + + list.push_back(1); + list.push_back(2); + list.push_back(3); + + int i = 1; + for (auto element : list) { + EXPECT_EQ(element, i); + i++; + } + + list.pop_front(); + EXPECT_EQ(list.front(), 2); + EXPECT_EQ(list.back(), 3); + EXPECT_EQ(list.size(), 2u); + + list.pop_front(); + EXPECT_EQ(list.front(), 3); + EXPECT_EQ(list.back(), 3); + EXPECT_EQ(list.size(), 1u); + + list.pop_front(); + EXPECT_TRUE(list.empty()); +} + +TEST(VectorBackedLinkedList, PopBack) { + using List = VectorBackedLinkedList<int>; + List list; + + list.push_back(1); + list.push_back(2); + list.push_back(3); + + list.pop_back(); + EXPECT_EQ(list.front(), 1); + EXPECT_EQ(list.back(), 2); + EXPECT_EQ(list.size(), 2u); + + list.pop_back(); + EXPECT_EQ(list.front(), 1); + EXPECT_EQ(list.back(), 1); + EXPECT_EQ(list.size(), 1u); + + list.pop_back(); + EXPECT_TRUE(list.empty()); +} + +TEST(VectorBackedLinkedList, Clear) { + using List = VectorBackedLinkedList<int>; + List list; + + list.push_back(1); + list.push_back(2); + list.push_back(3); + + EXPECT_EQ(list.size(), 3u); + + list.clear(); + EXPECT_EQ(list.size(), 0u); + EXPECT_TRUE(list.empty()); + + EXPECT_TRUE(list.begin() == list.end()); + list.push_back(1); + EXPECT_EQ(list.front(), 1); + EXPECT_EQ(list.back(), 1); + EXPECT_EQ(list.size(), 1u); +} + +TEST(VectorBackedLinkedList, Iterator) { + using List = VectorBackedLinkedList<int>; + List list; + + list.push_back(1); + list.push_back(2); + list.push_back(3); + + List::iterator it = list.begin(); + + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(*it, 2); + ++it; + EXPECT_EQ(*it, 3); + *it = 4; // list: {1, 2, 4} + EXPECT_EQ(list.back(), 4); + ++it; + EXPECT_TRUE(it == list.end()); + --it; + --it; + --it; + EXPECT_TRUE(it == list.begin()); + EXPECT_EQ(list.front(), 1); + *it = 0; + EXPECT_EQ(list.front(), 0); // list: {0, 2, 4} + + List::reverse_iterator rit = list.rbegin(); + + EXPECT_EQ(*rit, 4); + ++rit; + EXPECT_EQ(*rit, 2); + ++rit; + EXPECT_EQ(*rit, 0); + EXPECT_FALSE(rit == list.rend()); + *rit = 1; // list: {1, 2, 4} + EXPECT_EQ(list.front(), 1); + ++rit; + EXPECT_TRUE(rit == list.rend()); + --rit; + EXPECT_EQ(*rit, 1); +} + +TEST(VectorBackedLinkedList, ConstIterator) { + using List = VectorBackedLinkedList<int>; + List list; + + list.push_back(1); + list.push_back(2); + list.push_back(3); + + List::const_iterator cit = list.cbegin(); + + EXPECT_EQ(*cit, 1); + ++cit; + EXPECT_EQ(*cit, 2); + ++cit; + EXPECT_EQ(*cit, 3); + ++cit; + EXPECT_TRUE(cit == list.cend()); + --cit; + --cit; + --cit; + EXPECT_TRUE(cit == list.cbegin()); + EXPECT_EQ(list.front(), 1); + + List::const_reverse_iterator crit = list.crbegin(); + + EXPECT_EQ(*crit, 3); + ++crit; + EXPECT_EQ(*crit, 2); + ++crit; + EXPECT_EQ(*crit, 1); + ++crit; + EXPECT_TRUE(crit == list.crend()); + --crit; + EXPECT_EQ(*crit, 1); +} + +TEST(VectorBackedLinkedList, String) { + using List = VectorBackedLinkedList<String>; + List list; + + EXPECT_TRUE(list.empty()); + + list.push_back("b"); + list.push_front("a"); + list.push_back("c"); + + EXPECT_EQ(list.front(), "a"); + EXPECT_EQ(list.back(), "c"); + EXPECT_EQ(list.size(), 3u); + + List::iterator it = list.begin(); + EXPECT_EQ(*it, "a"); + ++it; + EXPECT_EQ(*it, "b"); + List::iterator target = it; + ++it; + EXPECT_EQ(*it, "c"); + ++it; + EXPECT_TRUE(it == list.end()); + --it; + EXPECT_EQ(*it, "c"); + --it; + --it; + EXPECT_TRUE(it == list.begin()); + + list.erase(target); + it = list.begin(); + EXPECT_EQ(*it, "a"); + ++it; + EXPECT_EQ(*it, "c"); + ++it; + EXPECT_TRUE(it == list.end()); + + list.pop_back(); + EXPECT_EQ(list.front(), "a"); + EXPECT_EQ(list.back(), "a"); + EXPECT_EQ(list.size(), 1u); + + list.push_front("c"); + it = list.begin(); + EXPECT_EQ(*it, "c"); + ++it; + EXPECT_EQ(*it, "a"); + ++it; + EXPECT_TRUE(it == list.end()); + + list.clear(); + EXPECT_TRUE(list.empty()); + EXPECT_TRUE(list.begin() == list.end()); + + list.push_front("a"); + EXPECT_EQ(list.size(), 1u); + EXPECT_EQ(list.front(), "a"); + list.pop_back(); + EXPECT_TRUE(list.empty()); +} + +TEST(VectorBackedLinkedList, UniquePtr) { + using List = VectorBackedLinkedList<std::unique_ptr<Dummy>>; + List list; + + bool deleted1 = false, deleted2 = false, deleted3 = false; + std::unique_ptr<Dummy> ptr1 = std::make_unique<Dummy>(deleted1); + std::unique_ptr<Dummy> ptr2 = std::make_unique<Dummy>(deleted2); + std::unique_ptr<Dummy> ptr3 = std::make_unique<Dummy>(deleted3); + + Dummy* raw_ptr1 = ptr1.get(); + Dummy* raw_ptr2 = ptr2.get(); + Dummy* raw_ptr3 = ptr3.get(); + + list.push_front(std::move(ptr1)); + list.push_back(std::move(ptr3)); + List::iterator it = list.begin(); + ++it; + it = list.insert(it, std::move(ptr2)); + EXPECT_EQ(it->get(), raw_ptr2); + + EXPECT_EQ(list.size(), 3u); + EXPECT_EQ((list.front()).get(), raw_ptr1); + EXPECT_EQ((list.back()).get(), raw_ptr3); + + it = list.begin(); + EXPECT_EQ(it->get(), raw_ptr1); + ++it; + EXPECT_EQ(it->get(), raw_ptr2); + List::iterator target = it; + ++it; + EXPECT_EQ(it->get(), raw_ptr3); + ++it; + EXPECT_TRUE(it == list.end()); + --it; + EXPECT_EQ(it->get(), raw_ptr3); + --it; + --it; + EXPECT_TRUE(it == list.begin()); + + list.erase(target); + EXPECT_FALSE(deleted1); + EXPECT_TRUE(deleted2); + EXPECT_FALSE(deleted3); + EXPECT_EQ(list.size(), 2u); + it = list.begin(); + EXPECT_EQ(it->get(), raw_ptr1); + ++it; + EXPECT_EQ(it->get(), raw_ptr3); + ++it; + EXPECT_TRUE(it == list.end()); + + list.pop_front(); + EXPECT_TRUE(deleted1); + EXPECT_TRUE(deleted2); + EXPECT_FALSE(deleted3); + EXPECT_EQ(list.size(), 1u); + it = list.begin(); + EXPECT_EQ(it->get(), raw_ptr3); + ++it; + EXPECT_TRUE(it == list.end()); + + list.pop_back(); + EXPECT_TRUE(deleted1); + EXPECT_TRUE(deleted2); + EXPECT_TRUE(deleted3); + EXPECT_TRUE(list.empty()); + + bool deleted4 = false, deleted5 = false, deleted6 = false; + std::unique_ptr<Dummy> ptr4 = std::make_unique<Dummy>(deleted4); + std::unique_ptr<Dummy> ptr5 = std::make_unique<Dummy>(deleted5); + std::unique_ptr<Dummy> ptr6 = std::make_unique<Dummy>(deleted6); + + Dummy* raw_ptr4 = ptr4.get(); + Dummy* raw_ptr5 = ptr5.get(); + Dummy* raw_ptr6 = ptr6.get(); + + list.push_back(std::move(ptr4)); + list.push_back(std::move(ptr5)); + list.push_back(std::move(ptr6)); + + it = list.end(); + --it; + list.MoveTo(list.begin(), it); + it = list.begin(); + EXPECT_EQ(it->get(), raw_ptr5); + ++it; + EXPECT_EQ(it->get(), raw_ptr4); + ++it; + EXPECT_EQ(it->get(), raw_ptr6); + + list.MoveTo(list.begin(), list.begin()); + it = list.begin(); + EXPECT_EQ(it->get(), raw_ptr5); + ++it; + EXPECT_EQ(it->get(), raw_ptr4); + ++it; + EXPECT_EQ(it->get(), raw_ptr6); + + EXPECT_FALSE(deleted4); + EXPECT_FALSE(deleted5); + EXPECT_FALSE(deleted6); + + list.clear(); + EXPECT_TRUE(list.empty()); + EXPECT_EQ(list.size(), 0u); + + EXPECT_TRUE(deleted4); + EXPECT_TRUE(deleted5); + EXPECT_TRUE(deleted6); +} + +} // namespace WTF diff --git a/chromium/third_party/blink/renderer/platform/wtf/vector_test.cc b/chromium/third_party/blink/renderer/platform/wtf/vector_test.cc index 788cc2bb12d..1e5fd403ed8 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/vector_test.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/vector_test.cc @@ -101,22 +101,34 @@ TEST(VectorTest, EraseAtIndex) { } TEST(VectorTest, Erase) { - Vector<int> int_vector({0, 1, 2, 3}); + Vector<int> int_vector({0, 1, 2, 3, 4, 5}); - EXPECT_EQ(4u, int_vector.size()); + EXPECT_EQ(6u, int_vector.size()); EXPECT_EQ(0, int_vector[0]); EXPECT_EQ(1, int_vector[1]); EXPECT_EQ(2, int_vector[2]); EXPECT_EQ(3, int_vector[3]); + EXPECT_EQ(4, int_vector[4]); + EXPECT_EQ(5, int_vector[5]); auto* first = int_vector.erase(int_vector.begin()); - EXPECT_EQ(3u, int_vector.size()); + EXPECT_EQ(5u, int_vector.size()); EXPECT_EQ(1, *first); EXPECT_EQ(int_vector.begin(), first); - auto* last = std::lower_bound(int_vector.begin(), int_vector.end(), 3); + auto* last = std::lower_bound(int_vector.begin(), int_vector.end(), 5); auto* end = int_vector.erase(last); + EXPECT_EQ(4u, int_vector.size()); + EXPECT_EQ(int_vector.end(), end); + + auto* item2 = std::lower_bound(int_vector.begin(), int_vector.end(), 2); + auto* item4 = int_vector.erase(item2, item2 + 2); EXPECT_EQ(2u, int_vector.size()); + EXPECT_EQ(4, *item4); + + last = std::lower_bound(int_vector.begin(), int_vector.end(), 4); + end = int_vector.erase(last, int_vector.end()); + EXPECT_EQ(1u, int_vector.size()); EXPECT_EQ(int_vector.end(), end); } diff --git a/chromium/third_party/blink/renderer/platform/wtf/wtf.cc b/chromium/third_party/blink/renderer/platform/wtf/wtf.cc index c98254eec1d..c928e55082b 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/wtf.cc +++ b/chromium/third_party/blink/renderer/platform/wtf/wtf.cc @@ -45,23 +45,13 @@ namespace WTF { bool g_initialized; -void (*g_call_on_main_thread_function)(MainThreadFunction, void*); base::PlatformThreadId g_main_thread_identifier; -namespace internal { - -void CallOnMainThread(MainThreadFunction* function, void* context) { - (*g_call_on_main_thread_function)(function, context); -} - -} // namespace internal - bool IsMainThread() { return CurrentThread() == g_main_thread_identifier; } -void Initialize(void (*call_on_main_thread_function)(MainThreadFunction, - void*)) { +void Initialize() { // WTF, and Blink in general, cannot handle being re-initialized. // Make that explicit here. CHECK(!g_initialized); @@ -75,7 +65,6 @@ void Initialize(void (*call_on_main_thread_function)(MainThreadFunction, double_conversion::DoubleToStringConverter::EcmaScriptConverter(); internal::GetDoubleConverter(); - g_call_on_main_thread_function = call_on_main_thread_function; internal::InitializeMainThreadStackEstimate(); AtomicString::Init(); StringStatics::Init(); diff --git a/chromium/third_party/blink/renderer/platform/wtf/wtf.h b/chromium/third_party/blink/renderer/platform/wtf/wtf.h index 5bd960aa773..418cfca41eb 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/wtf.h +++ b/chromium/third_party/blink/renderer/platform/wtf/wtf.h @@ -36,18 +36,13 @@ namespace WTF { -typedef void MainThreadFunction(void*); WTF_EXPORT extern base::PlatformThreadId g_main_thread_identifier; // This function must be called exactly once from the main thread before using // anything else in WTF. -WTF_EXPORT void Initialize(void (*)(MainThreadFunction, void*)); +WTF_EXPORT void Initialize(); WTF_EXPORT bool IsMainThread(); -namespace internal { -void CallOnMainThread(MainThreadFunction*, void* context); -} // namespace internal - } // namespace WTF using WTF::IsMainThread; diff --git a/chromium/third_party/blink/renderer/platform/wtf/wtf_test_helper.h b/chromium/third_party/blink/renderer/platform/wtf/wtf_test_helper.h index 8f31365dcd0..35b1e73c81f 100644 --- a/chromium/third_party/blink/renderer/platform/wtf/wtf_test_helper.h +++ b/chromium/third_party/blink/renderer/platform/wtf/wtf_test_helper.h @@ -5,6 +5,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_TEST_HELPER_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_WTF_TEST_HELPER_H_ +#include <type_traits> + #include "base/macros.h" #include "base/memory/scoped_refptr.h" #include "testing/gtest/include/gtest/gtest.h" @@ -177,6 +179,75 @@ struct DefaultHash<CountCopy> { using Hash = CountCopyHash; }; +template <typename T> +class ValueInstanceCount final { + public: + ValueInstanceCount() : counter_(nullptr), value_(T()) {} + explicit ValueInstanceCount(int* counter, T value = T()) + : counter_(counter), value_(value) { + DCHECK(counter_); + *counter = 1; + } + ValueInstanceCount(const ValueInstanceCount& other) + : counter_(other.counter_), value_(other.value_) { + if (counter_) + ++*counter_; + } + ValueInstanceCount& operator=(const ValueInstanceCount& other) { + counter_ = other.counter_; + value_ = other.value_; + if (counter_) + ++*counter_; + return *this; + } + ~ValueInstanceCount() { + if (counter_) + --*counter_; + } + + const int* Counter() const { return counter_; } + const T& Value() const { return value_; } + + private: + int* counter_; + T value_; +}; + +template <typename T> +struct ValueInstanceCountHashTraits + : public GenericHashTraits<ValueInstanceCount<T>> { + static const bool kEmptyValueIsZero = false; + static const bool kHasIsEmptyValueFunction = true; + static bool IsEmptyValue(const ValueInstanceCount<T>& value) { + return !value.Counter(); + } + static void ConstructDeletedValue(ValueInstanceCount<T>& slot, bool) {} + static bool IsDeletedValue(const ValueInstanceCount<T>& value) { + return false; + } +}; + +template <typename T> +struct ValueInstanceCountHash : public PtrHash<const int*> { + static unsigned GetHash(const ValueInstanceCount<T>& value) { + return PtrHash<const int>::GetHash(value.Counter()); + } + static bool Equal(const ValueInstanceCount<T>& left, + const ValueInstanceCount<T>& right) { + return PtrHash<const int>::Equal(left.Counter(), right.Counter()); + } + static const bool safe_to_compare_to_empty_or_deleted = true; +}; + +template <typename T> +struct HashTraits<ValueInstanceCount<T>> + : public ValueInstanceCountHashTraits<T> {}; + +template <typename T> +struct DefaultHash<ValueInstanceCount<T>> { + using Hash = ValueInstanceCountHash<T>; +}; + class DummyRefCounted : public RefCounted<DummyRefCounted> { public: DummyRefCounted(bool& is_deleted) : is_deleted_(is_deleted) { |